/* Friends & Robots — Static Ad Factory landing.
   Hand-ported from the Lovable design (claude-files/fnrb-static-ad-factory-source).
   Fixed light page: <html data-theme="light"> pins tokens.css to its light
   palette; the Factory palette and page guards live in factory-tokens.css
   (shared with the app) — this file holds only the landing's components.
   Two site rules override the handoff: links are never underlined, and
   nothing moves on hover — the tile's hover lift (translate-y) is dropped.
   Its shadow bloom is reproduced as designed. */

/* ── Page guard ─────────────────────────────────────────────── */
/* The page never scrolls sideways: an element spilling past the viewport
   edge would otherwise widen the document, turning a horizontal swipe on
   a phone into the whole page panning. `clip` forbids horizontal
   scrolling outright; the `hidden` line is the fallback for browsers
   without it. Vertical scrolling is untouched. */
html,
body.saf {
  overflow-x: hidden;
  overflow-x: clip;
}

/* ── Header ─────────────────────────────────────────────────── */
/* The header itself is the shared site header (css/header.css +
   site-header.js). Only the footer's wordmark is sized here. */
.saf-logo { height: 16px; width: auto; display: block; }
@media (min-width: 640px) {
  .saf-logo { height: 20px; }
}

/* Orange CTA. Static shadow from the design; hover changes background only. */
.saf-cta {
  display: inline-block;
  border-radius: var(--radius-lg);
  background: var(--saf-brand);
  color: #FFFFFF;
  font-weight: var(--fw-bold);
  box-shadow: 0 4px 12px rgba(249, 115, 22, 0.2);
  transition: background-color var(--dur-base) var(--ease-standard);
}
.saf-cta:hover { background: var(--saf-brand-pressed); }
.saf-cta-sm { padding: 10px 20px; font-size: 14px; }
.saf-cta-lg { padding: 14px 24px; font-size: 16px; flex-shrink: 0; }
/* Ink CTA: the footer form's button, so the orange hero button stays the
   page's single loudest control. */
.saf-cta-ink { background: var(--saf-ink); box-shadow: 0 1px 2px rgba(18, 22, 29, 0.05); }
.saf-cta-ink:hover { background: var(--saf-ink-soft); }

/* ── Hero ───────────────────────────────────────────────────── */
/* Full-viewport opener: the URL field is the whole first screen, and the
   showcase starts exactly at the fold. An exact height — not a minimum —
   so the hero always matches the visible viewport: a short window renders
   a short hero. The site header is fixed and overlays the top, adding no
   flow height of its own. The vh line is the fallback for browsers
   without dvh support. */
.saf-hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  padding: 96px 0 64px;
}
/* On phones the shared header is two rows (~93px tall); the hero's
   top padding keeps the platforms pill from ever sliding under it
   when a short viewport packs the centered content upward. */
@media (max-width: 600px) {
  .saf-hero { padding-top: 112px; }
}
.saf-hero-wash {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(60rem 30rem at 50% -10%, var(--saf-brand-soft), transparent 70%),
    radial-gradient(40rem 24rem at 85% 10%, var(--saf-sky-soft), transparent 70%);
}
.saf-hero-inner {
  position: relative;
  width: 100%;
  max-width: 56rem;
  margin: 0 auto;
  padding: 0 var(--sp-lg);
  text-align: center;
}
/* Progressive enhancement: content is visible by default. The head script
   adds .anim-ready to <html>, which arms the hidden state only for elements
   that have NOT yet revealed (:not(.in)). Once .in is set the hidden rule
   stops matching, so it can never lose a specificity battle. Same mechanism
   as the studio landing (index.css). */
.reveal {
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
}
html.anim-ready .reveal:not(.in) {
  opacity: 0;
  transform: translateY(22px);
}
.reveal.d1 { transition-delay: 0.06s; }
.reveal.d2 { transition-delay: 0.12s; }
.reveal.d3 { transition-delay: 0.18s; }
/* The hero copy is the page's LCP element: it must never wait for the
   script to reveal it. These elements play the same rise as a CSS
   animation that starts as soon as styles apply — no JS involved. The
   animation's fill overrides the .anim-ready hidden state in both
   directions: backwards fill covers the delay, forwards fill keeps the
   element visible even before the script adds .in. */
@keyframes saf-hero-rise {
  from { opacity: 0; transform: translateY(22px); }
  to { opacity: 1; transform: none; }
}
.saf-h1.reveal, .saf-hero-sub.reveal, .saf-url-form.reveal {
  animation: saf-hero-rise 0.7s var(--ease-out) both;
}
.saf-hero-sub.reveal { animation-delay: 0.06s; }
.saf-url-form.reveal { animation-delay: 0.12s; }
@media (prefers-reduced-motion: reduce) {
  html.anim-ready .reveal:not(.in) { opacity: 1; transform: none; }
  .reveal { transition: none; }
  .saf-h1.reveal, .saf-hero-sub.reveal, .saf-url-form.reveal { animation: none; }
}
/* Platform pill: overlapping network logo chips + one line saying the
   ads are ready to run there. */
.saf-platforms {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-bottom: var(--sp-xl);
  padding: 6px 18px 6px 8px;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.8);
  box-shadow: 0 10px 30px -18px rgba(18, 22, 29, 0.25);
}
.saf-platform-logos { display: inline-flex; }
.saf-platform-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #FFFFFF;
  border: 1px solid var(--saf-border);
}
/* Chips overlap; later ones sit on top in DOM paint order. */
.saf-platform-logo + .saf-platform-logo { margin-left: -8px; }
.saf-platform-logo svg { display: block; width: 14px; height: 14px; }
.saf-platforms-text {
  font-size: 13px;
  font-weight: var(--fw-bold);
  color: var(--saf-ink);
}

.saf-h1 {
  margin: 0;
  font-size: clamp(38px, 4.6vw, 54px);
  font-weight: var(--fw-bold);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--saf-ink);
}
.saf-hero-sub {
  max-width: 42rem;
  margin: 20px auto 0;
  font-size: 16px;
  line-height: var(--lh-normal);
  color: var(--saf-ink-soft);
}

.saf-url-form { max-width: 36rem; margin: 36px auto 0; }
.saf-url-row { display: flex; flex-direction: column; gap: var(--sp-md); }
.saf-url-field {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  border: 2px solid var(--saf-input-border);
  border-radius: var(--radius-lg);
  background: #FFFFFF;
  box-shadow: 0 20px 25px -5px rgba(18, 22, 29, 0.05), 0 8px 10px -6px rgba(18, 22, 29, 0.05);
  transition: border-color var(--dur-base) var(--ease-standard), box-shadow var(--dur-base) var(--ease-standard);
}
.saf-url-field:hover { border-color: rgba(249, 115, 22, 0.3); }
.saf-url-field:focus-within {
  border-color: rgba(249, 115, 22, 0.4);
  box-shadow: 0 0 0 4px rgba(249, 115, 22, 0.1);
}
.saf-url-prefix {
  flex-shrink: 0;
  user-select: none;
  border-right: 1px solid var(--saf-border);
  padding: 14px 12px;
  font-size: 14px;
  font-weight: var(--fw-semibold);
  color: var(--saf-muted);
}
.saf-url-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  padding: 14px 12px;
  font-family: inherit;
  font-size: 16px;
  color: var(--saf-ink);
}
/* The field signals focus itself (border + glow on :focus-within). Without
   this override the global `.saf :focus-visible` outline outranks the plain
   `outline: none` above and paints an orange bar — the outline's left edge,
   the rest clipped by the field's overflow:hidden — after the prefix. */
.saf .saf-url-input:focus-visible { outline: none; }
.saf-url-input::placeholder { color: var(--saf-muted); }
.saf-url-hint { margin: var(--sp-md) 0 0; font-size: 14px; color: var(--saf-ink-soft); }
.saf-url-hint.error { color: var(--saf-destructive); }

/* Rotating ad preview: a coverflow under the hero form. The current ad
   faces forward at full opacity; the previous and next sit angled toward
   it, dimmed and scaled back; the remaining cards wait hidden behind the
   center. Depth is emulated per card — perspective()/rotateY()/scale()
   inside each card's own transform — rather than a real 3D context:
   WebKit hit-tests preserve-3d planes at their untransformed positions,
   which made the side cards unclickable. 2D-transformed cards hit-test
   exactly where they're drawn, in every browser. The stage height tracks
   the viewport so the hero keeps fitting inside its exact 100dvh. */
.saf-hero-spin {
  --spin-h: clamp(165px, 26.4vh, 297px);
  margin-top: 36px;
}
.saf-spin-stage {
  position: relative;
  height: var(--spin-h);
  /* Horizontal touch gestures belong to the roll (a swipe turns it, in
     static-ad-factory.js); only vertical panning is the browser's. */
  touch-action: pan-y;
}
.saf-spin-card {
  position: absolute;
  top: 0;
  left: 50%;
  height: 100%;
  aspect-ratio: 9 / 16;
  width: auto;
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  border-radius: var(--radius-lg);
  /* Solid backing + border: some renders have transparent regions, and a
     dimmed card must read as a card, not blend into whatever is behind. */
  background: #FFFFFF;
  border: 1px solid var(--saf-border);
  box-shadow: 0 18px 45px -18px rgba(18, 22, 29, 0.35);
  opacity: 0;
  /* Every state keeps the same transform function list, so transitions
     interpolate each function cleanly instead of falling back to matrix
     blending. Hidden cards can't swallow clicks: only the five visible
     positions re-enable pointer events. */
  pointer-events: none;
  z-index: 1;
  transform: translateX(-50%) perspective(1100px) rotateY(0deg) scale(0.78);
  transition: transform 0.65s var(--ease-standard), opacity 0.65s var(--ease-standard);
}
.saf-spin-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.saf-spin-card.is-center {
  opacity: 1;
  pointer-events: auto;
  z-index: 5;
  transform: translateX(-50%) perspective(1100px) rotateY(0deg) scale(1);
}
/* translateX percentages are of the card's own width, so the spacing
   scales with the stage and no card ever overlaps its neighbor — each
   one reads as a separate ad. Positive rotateY on the left and negative
   on the right angle every card toward the center, like a ring turning
   around a vertical axis behind it; each tier out scales smaller and
   dims. z-index stands in for depth order. */
.saf-spin-card.is-prev {
  opacity: 0.35;
  pointer-events: auto;
  z-index: 4;
  transform: translateX(-150%) perspective(1100px) rotateY(26deg) scale(0.89);
}
.saf-spin-card.is-next {
  opacity: 0.35;
  pointer-events: auto;
  z-index: 4;
  transform: translateX(50%) perspective(1100px) rotateY(-26deg) scale(0.89);
}
.saf-spin-card.is-prev2 {
  opacity: 0.15;
  pointer-events: auto;
  z-index: 3;
  transform: translateX(-250%) perspective(1100px) rotateY(32deg) scale(0.83);
}
.saf-spin-card.is-next2 {
  opacity: 0.15;
  pointer-events: auto;
  z-index: 3;
  transform: translateX(150%) perspective(1100px) rotateY(-32deg) scale(0.83);
}
/* Side cards invite the click: hovering lifts the dim — a pure fade on
   the existing opacity transition, nothing moves. */
.saf-spin-card.is-prev:hover,
.saf-spin-card.is-next:hover { opacity: 0.75; }
.saf-spin-card.is-prev2:hover,
.saf-spin-card.is-next2:hover { opacity: 0.45; }
@media (prefers-reduced-motion: reduce) {
  .saf-spin-card { transition: none; }
}

@media (min-width: 640px) {
  .saf-hero { padding: 112px 0 80px; }
  .saf-url-row { flex-direction: row; align-items: center; }
  .saf-url-prefix { padding: 16px; font-size: 16px; }
  .saf-url-input { padding: 16px 0 16px 16px; }
  .saf-cta-lg { padding: 16px 32px; }
}

/* ── Section headings ───────────────────────────────────────── */
.saf-eyebrow {
  margin: 0;
  font-size: 12px;
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--saf-brand);
}
.saf-h2 {
  margin: 0;
  font-size: 30px;
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
  color: var(--saf-ink);
}
/* The showcase opens at the full h2 size; the sections after it step down
   one notch so the results stay the page's loudest heading. */
.saf-h2-sm { font-size: 24px; }
.saf-h3 {
  margin: 0;
  font-size: 20px;
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  color: var(--saf-ink);
}

@media (min-width: 640px) {
  .saf-h2 { font-size: 36px; }
  .saf-h2-sm { font-size: 30px; }
  .saf-h3 { font-size: 24px; }
}

/* ── The page is the brief ──────────────────────────────────── */
/* Surface band between the brand-read section and Formats, both white,
   so it needs only a rule at the top — the Formats section carries its
   own along the seam below. One address bar that types itself through
   three paths, with the ads each one yields wired underneath it. */
.saf-brief {
  border-top: 1px solid var(--saf-border);
  background: var(--saf-surface);
}
.saf-brief-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 64px var(--sp-lg);
}
.saf-brief-head { max-width: 42rem; }
.saf-brief-head .saf-h2 { margin-top: var(--sp-md); }
.saf-brief-sub { margin: var(--sp-lg) 0 0; font-size: 14px; line-height: var(--lh-relaxed); color: var(--saf-ink-soft); }
.saf-brief-demo { max-width: 34rem; margin-top: 36px; }
/* Mono, on white, in a field: an address being entered, not a headline.
   The whole line is one text run so the caret sits tight against the
   path however long it has grown. */
.saf-brief-bar {
  margin: 0;
  padding: 16px 20px;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-xl);
  background: var(--saf-bg);
  font-family: var(--font-mono);
  font-size: 15px;
  line-height: 1.4;
  color: var(--saf-ink);
  word-break: break-word;
}
.saf-brief-domain { color: var(--saf-muted); }
.saf-brief-path { font-weight: var(--fw-semibold); }
.saf-brief-caret {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: 2px;
  vertical-align: text-bottom;
  background: var(--saf-ink);
  animation: saf-caret-blink 1.1s steps(1, end) infinite;
}
@keyframes saf-caret-blink {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}
/* A wire from the bar down to what that address yields. */
.saf-brief-flow { position: relative; padding-top: 30px; }
.saf-brief-flow::before {
  content: '';
  position: absolute;
  top: 0;
  left: 24px;
  width: 1px;
  height: 22px;
  background: var(--saf-border);
}
/* The three results share one cell, so the block keeps the height of the
   tallest and nothing shifts as they swap. */
.saf-brief-list {
  display: grid;
  margin: 0;
  padding: 0 0 0 24px;
  list-style: none;
}
.saf-brief-item {
  grid-area: 1 / 1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-sm);
  opacity: 0;
  transition: opacity 0.32s var(--ease-standard);
}
.saf-brief-item.is-on { opacity: 1; }
.saf-brief-label { font-size: 18px; font-weight: var(--fw-bold); color: var(--saf-ink); }
.saf-brief-tail { font-size: 14px; color: var(--saf-ink-soft); }
@media (prefers-reduced-motion: reduce) {
  .saf-brief-caret { animation: none; }
  .saf-brief-item { transition: none; }
}

/* ── Formats ────────────────────────────────────────────────── */
.saf-formats { border-top: 1px solid var(--saf-border); background: var(--saf-bg); }
.saf-formats-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 64px var(--sp-lg);
}
.saf-formats-head { max-width: 42rem; }
.saf-formats-head .saf-h2 { margin-top: var(--sp-md); }
.saf-formats-body { margin: var(--sp-lg) 0 0; font-size: 14px; line-height: var(--lh-relaxed); color: var(--saf-ink-soft); }

/* Social ratios, shown at a common height so the ratio difference reads as
   width — the point of the section. */
.saf-format-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-xl);
  margin-top: 40px;
}
.saf-format-figure { margin: 0; display: flex; flex-direction: column; gap: var(--sp-md); }
.saf-format-shot {
  /* On phones narrower than a shot's fixed width (the 1:1 at 360px),
     the shot caps at the column and its picture cover-crops sideways,
     keeping the common height the ratio comparison reads by. */
  max-width: 100%;
  overflow: hidden;
  border-radius: var(--radius-2xl);
  background: var(--saf-surface);
  box-shadow: 0 24px 60px -20px rgba(18, 22, 29, 0.18);
}
.saf-format-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.saf-format-shot-9x16 { height: 360px; width: 202px; }
.saf-format-shot-4x5 { height: 360px; width: 288px; }
.saf-format-shot-1x1 { height: 360px; width: 360px; }
.saf-format-caption { display: flex; align-items: baseline; gap: var(--sp-sm); }
.saf-format-ratio { font-size: 14px; font-weight: var(--fw-bold); color: var(--saf-ink); }
.saf-format-use { font-size: 12px; color: var(--saf-ink-soft); }

/* Ad copy sheet: the platform text delivered with every ad, laid out
   field by field like it lands — headline, caption, CTA. */
/* A surface band across the section's white, like the brief section's,
   with a rule at each end since white sits above and below it. */
.saf-copy {
  border-top: 1px solid var(--saf-border);
  border-bottom: 1px solid var(--saf-border);
  background: var(--saf-surface);
}
.saf-copy-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 56px var(--sp-lg);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-2xl);
}
.saf-copy-head .saf-h3 { margin-top: var(--sp-md); }
.saf-copy-sheet {
  align-self: start;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-2xl);
  background: var(--saf-bg);
  box-shadow: 0 24px 60px -20px rgba(18, 22, 29, 0.18);
  overflow: hidden;
}
.saf-copy-row {
  display: grid;
  grid-template-columns: 86px minmax(0, 1fr);
  gap: var(--sp-lg);
  align-items: baseline;
  padding: 20px 24px;
}
.saf-copy-row + .saf-copy-row { border-top: 1px solid var(--saf-border); }
.saf-copy-label {
  font-size: 11px;
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--saf-muted);
}
.saf-copy-value {
  margin: 0;
  font-size: 15px;
  line-height: var(--lh-relaxed);
  color: var(--saf-ink);
}
/* The CTA field rendered as the button it becomes in the ad manager. */
.saf-copy-cta {
  display: inline-block;
  border-radius: var(--radius-lg);
  background: var(--saf-brand);
  color: #FFFFFF;
  font-size: 13px;
  font-weight: var(--fw-bold);
  padding: 8px 16px;
}

/* The copy band above closes with its own rule, so this block needs no
   divider of its own — its wrapper's padding sets it apart. */
.saf-google {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-2xl);
}
.saf-google-head .saf-h3 { margin-top: var(--sp-md); }
.saf-google-head .saf-formats-body + .saf-formats-body { margin-top: var(--sp-md); }
.saf-google-specs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm);
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
}
.saf-google-spec {
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-full);
  background: var(--saf-surface);
  padding: 6px 12px;
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--saf-ink);
}
/* The landscape asset leads at full column width; the square and the
   portrait sit beneath it side by side, so the three shapes read against
   each other rather than in a list. */
.saf-google-shots { display: flex; flex-direction: column; gap: 20px; }
.saf-google-pair { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 20px; }
.saf-google-pair .saf-google-figure { flex: 1 1 180px; min-width: 0; }
.saf-google-figure { margin: 0; display: flex; flex-direction: column; gap: var(--sp-sm); }
.saf-google-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 18px 45px -20px rgba(18, 22, 29, 0.22);
}
.saf-google-caption { font-size: 12px; font-weight: var(--fw-semibold); color: var(--saf-ink-soft); }

@media (min-width: 640px) {
  .saf-formats-inner { padding: 96px var(--sp-lg); }
  .saf-formats-body { font-size: 16px; }
  /* The sub-block columns' copy stays at 14px — only the section lead steps up. */
  .saf-google-head .saf-formats-body,
  .saf-copy-head .saf-formats-body { font-size: 14px; }
  .saf-format-row { gap: var(--sp-2xl); }
}
@media (min-width: 1024px) {
  .saf-copy-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: var(--sp-3xl); }
  .saf-google { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: var(--sp-3xl); }
}

/* ── Edits ──────────────────────────────────────────────────── */
/* Surface band between Formats' white and the pricing section's, which
   carries its own rule along the seam below — so only a rule on top,
   the brief section's arrangement. One real edit end to end: the
   instruction as a sent chat message, and the before/after renders
   either side of an arrow — copy on the left, demo on the right on
   wide screens, like the copy and Google bands. */
.saf-edit {
  border-top: 1px solid var(--saf-border);
  background: var(--saf-surface);
}
.saf-edit-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 64px var(--sp-lg);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-2xl);
  align-items: center;
}
.saf-edit-head { max-width: 42rem; }
.saf-edit-head .saf-h2 { margin-top: var(--sp-md); }
.saf-edit-sub { margin: var(--sp-lg) 0 0; font-size: 14px; line-height: var(--lh-relaxed); color: var(--saf-ink-soft); }
/* The instruction as the chat message it was: a sent bubble hugging
   its text, the bottom-left corner tightened toward the renders it
   acted on. */
.saf-edit-msg {
  display: inline-block;
  margin: 0;
  max-width: 26rem;
  padding: 12px 18px;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-xl);
  border-bottom-left-radius: 6px;
  background: var(--saf-bg);
  color: var(--saf-ink);
  font-size: 14px;
  line-height: 1.5;
}
.saf-edit-flow {
  display: flex;
  align-items: center;
  gap: var(--sp-lg);
  margin-top: 20px;
}
.saf-edit-figure { margin: 0; display: flex; flex-direction: column; gap: var(--sp-md); }
.saf-edit-shot {
  width: min(160px, 34vw);
  aspect-ratio: 9 / 16;
  overflow: hidden;
  border-radius: var(--radius-2xl);
  background: var(--saf-bg);
  box-shadow: 0 24px 60px -20px rgba(18, 22, 29, 0.18);
}
.saf-edit-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.saf-edit-caption { font-size: 14px; font-weight: var(--fw-bold); color: var(--saf-ink); text-align: center; }
.saf-edit-arrow { color: var(--saf-ink-soft); }
.saf-edit-arrow svg { display: block; }
@media (min-width: 640px) {
  .saf-edit-inner { padding: 96px var(--sp-lg); }
  .saf-edit-sub { font-size: 16px; }
  .saf-edit-flow { gap: var(--sp-xl); }
}
@media (min-width: 1024px) {
  .saf-edit-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: var(--sp-3xl); }
}

/* ── Volume ─────────────────────────────────────────────────── */
/* The case for a factory, made by a conveyor: a slow, full-bleed belt
   of real previews from the film roll, a few tagged with their fate —
   winner, fatigued, fresh. The claims beneath carry the message. */
.saf-volume { padding-bottom: 64px; }
.saf-volume-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 var(--sp-lg);
}
/* The head's border-top is the separator at the hero's exact fold. */
.saf-volume-head {
  border-top: 1px solid var(--saf-border);
  padding-top: 32px;
}
.saf-volume-head .saf-h2 { margin-top: var(--sp-md); }

.saf-belt {
  overflow: hidden;
  margin-top: 40px;
  /* The belt runs edge to edge; its ends fade out rather than cut. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  /* Horizontal touch gestures belong to the belt — a finger grabs the
     track and scrubs it (static-ad-factory.js drives all its motion);
     vertical panning stays the browser's, and a scrub never starts a
     selection. */
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
}
/* The card list is doubled in the markup; the script slides the track
   and wraps on the halfway seam, whose pixels the second half repeats,
   so the belt runs forever. It also holds the drift under a mouse
   hover, so a click can be aimed. */
.saf-belt-track {
  display: flex;
  gap: 14px;
  width: max-content;
}
.saf-belt-card {
  flex-shrink: 0;
  height: 190px;
  aspect-ratio: 9 / 16;
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--saf-border);
  /* Surface tone, not white: a card whose lazy image is still arriving
     reads as a loading plate rather than a blank gap in the band. */
  background: var(--saf-surface);
  cursor: pointer;
}
.saf-belt-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.saf-volume-claims {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  margin-top: 44px;
}
.saf-claim-body { margin: var(--sp-md) 0 0; font-size: 14px; line-height: var(--lh-relaxed); color: var(--saf-ink-soft); max-width: 34rem; }
@media (min-width: 768px) {
  .saf-volume-claims { grid-template-columns: 1fr 1fr; gap: var(--sp-2xl); }
}

/* ── Ad types ───────────────────────────────────────────────── */
/* The factory's range as a production manifest: dense, numbered,
   typographic — a spec sheet, not a card grid. Mirrors the categories
   the API's brief writer plans every batch from. */
.saf-types {
  border-top: 1px solid var(--saf-border);
  background: var(--saf-surface);
}
/* Copy beside the deck rather than above it, as in the pricing section:
   stacked, the hand of cards pushed the section far taller than the
   little it has to say. */
.saf-types-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 64px var(--sp-lg) 72px;
  display: grid;
  /* minmax(0, 1fr), not bare 1fr: the track must be able to shrink
     below the dealt hand's content minimum, or the column widens past
     a phone and the cards never get the narrow width their own
     shrink rule answers to. */
  grid-template-columns: minmax(0, 1fr);
  gap: 44px;
  align-items: center;
}
.saf-types-head { max-width: 42rem; }
.saf-types-head .saf-h2 { margin-top: var(--sp-md); }
.saf-types-sub { margin: var(--sp-lg) 0 0; font-size: 14px; line-height: var(--lh-relaxed); color: var(--saf-ink-soft); }

/* The deck: three type-cards dealt onto the table with a slight fan —
   middle card straight, outer cards tilted — with the control that deals
   the next hand under them. Cards with a render show it; the rest show
   their brief ticket. */
.saf-deck {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}
.saf-hand {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: clamp(14px, 3vw, 28px);
  width: 100%;
}
/* The width is set by the pictures, not the layout: the cards draw the
   film roll's previews, which are 337px across, and a card wider than
   half that has the browser inventing pixels on a 2x screen. The hero
   roll sizes its cards off the same arithmetic. */
.saf-deal-card {
  --deal-order: 0;
  /* A definite width, not just a flex basis: the deck's grid column is
     sized by the hand's intrinsic width, and with width:auto a card's
     intrinsic width comes from its content — near zero while its image
     is still downloading — so each fresh deal would collapse and then
     jump wider as the renders arrive. A definite width keeps the
     hand's intrinsic width at three full cards regardless of image
     state; flex-shrink still lets the cards narrow on phones. */
  width: 170px;
  flex: 0 1 170px;
  max-width: 170px;
  /* Overrides the flex minimum so the three cards shrink in step on
     phones narrower than the hand — at the 170px basis the hand is
     wider than a phone's screen. Card height comes from the body's
     aspect ratio, so a shrunken card keeps the loaded card's shape. */
  min-width: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-xl);
  background: #FFFFFF;
  overflow: hidden;
  box-shadow: 0 18px 40px -22px rgba(18, 22, 29, 0.35);
  opacity: 0;
}
/* Dealt in with a rise and a settle into the fan tilt, staggered like a
   dealer's hand. The tilt is per position: middle straight, sides turned. */
.saf-deal-card.is-dealt {
  animation: saf-deal-in 0.5s var(--ease-standard) both;
  animation-delay: calc(var(--deal-order) * 110ms);
  transform: rotate(calc((var(--deal-order) - 1) * 1.6deg));
}
@keyframes saf-deal-in {
  from { opacity: 0; transform: translateY(22px) rotate(0deg); }
  to { opacity: 1; transform: translateY(0) rotate(calc((var(--deal-order) - 1) * 1.6deg)); }
}
@media (prefers-reduced-motion: reduce) {
  .saf-deal-card.is-dealt { animation: none; opacity: 1; transform: none; }
}
/* Sized against the longest type name — "Editorial share", "Benefit
   callout" — in the width the previews allow the card: tight padding, a
   narrow gap, and letter-spacing pulled back close to none, since at
   this size it costs most of a character per word. */
.saf-deal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--saf-border);
}
.saf-deal-type {
  font-size: 10px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--saf-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Never truncated, so it is the fixed cost the type name is measured
   against — kept a step under it to give the name the room. */
.saf-deal-index {
  font-size: 9px;
  font-weight: var(--fw-semibold);
  color: var(--saf-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The body holds the 337:600 shape of the film-roll previews it draws,
   at whatever width the card has, so a brief ticket stands at the same
   size as a loaded render. */
.saf-deal-body {
  flex: 1;
  display: flex;
  aspect-ratio: 337 / 600;
}
/* The render is a button into the viewer; visually it disappears into
   the body it fills. */
.saf-deal-view {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
/* The image sizes itself from its own width and the previews' ratio
   rather than from the body's height, so an image still downloading
   occupies the exact box its loaded pixels will fill and a deal never
   changes the section's dimensions. */
.saf-deal-body img {
  width: 100%;
  height: auto;
  aspect-ratio: 337 / 600;
  object-fit: cover;
  display: block;
}
/* No render yet: the card shows its brief — the instruction the ad
   builder receives — on the factory's paper. Set a size down from the
   body copy elsewhere, because the card is only as wide as the previews
   beside it allow. */
.saf-deal-brief {
  margin: 0;
  align-self: center;
  padding: var(--sp-md);
  font-size: 13px;
  line-height: var(--lh-relaxed);
  font-weight: var(--fw-semibold);
  color: var(--saf-ink-soft);
  background: var(--saf-surface);
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
}
.saf-deal-card.is-wildcard { border-style: dashed; border-color: var(--saf-brand); }
.saf-deal-card.is-wildcard .saf-deal-type { color: var(--saf-brand); }

.saf-deal-more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-full);
  background: #FFFFFF;
  font-size: 13px;
  font-weight: var(--fw-bold);
  color: var(--saf-ink);
  transition: background-color var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard);
}
.saf-deal-more:hover { background: var(--saf-surface); border-color: var(--saf-ink-soft); }
.saf-deal-more svg { flex-shrink: 0; color: var(--saf-brand); }

@media (min-width: 960px) {
  /* The deck's column is sized by the hand itself, as the pricing
     section's is by its roll, so the slack lands in the copy. */
  .saf-types-inner { grid-template-columns: minmax(0, 1fr) auto; gap: 56px; }
}

/* ── Brand read ─────────────────────────────────────────────── */
/* The app's "Brand ready" panel, shown for the factory's own
   fnrb.com campaign, ending with the ads its brief produced. */
.saf-brand-read-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 64px var(--sp-lg) 72px;
}
.saf-brand-read-head { max-width: 42rem; }
.saf-brand-read-head .saf-h2 { margin-top: var(--sp-md); }
.saf-brand-read-sub { margin: var(--sp-lg) 0 0; font-size: 14px; line-height: var(--lh-relaxed); color: var(--saf-ink-soft); }


.saf-panel {
  margin-top: 44px;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-2xl);
  background: #FFFFFF;
  padding: 20px;
  box-shadow: 0 24px 60px -30px rgba(18, 22, 29, 0.25);
}
.saf-panel-top { padding: 4px 4px 18px; }
.saf-panel-kicker {
  margin: 0;
  font-size: 11px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--saf-muted);
}
.saf-panel-url { margin: 2px 0 0; font-size: 20px; font-weight: var(--fw-bold); color: var(--saf-ink); }

.saf-panel-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-md);
}
.saf-panel-card {
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-xl);
  background: #FFFFFF;
  padding: 18px;
}
.saf-panel-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-sm);
}
.saf-panel-title { margin: 0; font-size: 15px; font-weight: var(--fw-bold); color: var(--saf-ink); }
.saf-panel-done { font-size: 12px; font-weight: var(--fw-semibold); color: var(--saf-muted); }
.saf-panel-desc { margin: 6px 0 0; font-size: 13px; line-height: var(--lh-normal); color: var(--saf-ink-soft); }

.saf-panel-logo { display: flex; flex-wrap: wrap; gap: var(--sp-sm); margin-top: 16px; }
/* The app's logo plates, one per delivered variant; each is a button
   into the logo viewer. A plate shows the variant meant for it — the
   light-background logo on white, the dark-background one on ink —
   because the background is part of the subject. */
.saf-panel-logo-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 128px;
  height: 64px;
  padding: 0 var(--sp-sm);
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard);
}
.saf-panel-logo-tile:hover { box-shadow: 0 1px 2px rgba(18, 22, 29, 0.12); }
.saf-panel-logo-tile img { max-height: 26px; max-width: 100%; display: block; }
.saf-panel-logo-tile-light { border-color: var(--saf-border); background: #FFFFFF; }
.saf-panel-logo-tile-light:hover { border-color: var(--saf-ink-soft); }
.saf-panel-logo-tile-dark { background: var(--saf-ink); }

.saf-swatches { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.saf-swatch {
  position: relative;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-full);
  border: 1px solid rgba(18, 22, 29, 0.12);
}
/* The app's swatch tooltip: hex, role and where the brand uses the
   colour. Built here rather than with `title`, which waits about a
   second and cannot be styled. Only opacity changes on hover. */
.saf-swatch[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  width: max-content;
  max-width: 15rem;
  border-radius: var(--radius-md);
  background: var(--saf-ink);
  padding: 6px 10px;
  font-size: 11px;
  line-height: var(--lh-normal);
  font-weight: var(--fw-semibold);
  color: #FFFFFF;
  text-align: center;
  white-space: pre-line;
  box-shadow: 0 4px 12px rgba(18, 22, 29, 0.18);
}
.saf-swatch[data-tip]::before {
  content: '';
  position: absolute;
  bottom: calc(100% + 3px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  border: 5px solid transparent;
  border-top-color: var(--saf-ink);
}
.saf-swatch[data-tip]::after,
.saf-swatch[data-tip]::before {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-standard);
}
.saf-swatch[data-tip]:hover::after,
.saf-swatch[data-tip]:hover::before { opacity: 1; }
.saf-panel-fonts { display: flex; flex-wrap: wrap; gap: var(--sp-lg); margin-top: 14px; }
.saf-panel-font {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: 19px;
  color: var(--saf-ink);
}
.saf-panel-font-role {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--saf-muted);
}
/* The specimens are set in the brand's real faces — the files the
   campaign lifted from fnrb.com. Quicksand Bold is the page's own
   display family (tokens.css); Inter Regular is declared here from
   the same woff2 the extractor collected. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/108d3685.woff2") format("woff2");
}
.saf-panel-font-display { font-family: "Quicksand", sans-serif; font-weight: var(--fw-bold); }
.saf-panel-font-body { font-family: "Inter", sans-serif; font-weight: 400; }
.saf-panel-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
.saf-panel-chip {
  padding: 5px 11px;
  border-radius: var(--radius-full);
  background: var(--saf-surface);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--saf-ink-soft);
}
.saf-panel-thumbs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 16px; }
.saf-panel-thumbs img {
  height: 54px;
  width: auto;
  display: block;
  border-radius: var(--radius-md);
  border: 1px solid var(--saf-border);
}
.saf-panel-count { margin: 12px 0 0; font-size: 12px; color: var(--saf-muted); }

/* The proof: the ads this exact brief produced. */
.saf-brand-out {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-xl);
  margin-top: 36px;
}
.saf-brand-out-line {
  margin: 0;
  max-width: 15rem;
  font-size: 15px;
  line-height: var(--lh-normal);
  color: var(--saf-ink-soft);
}
.saf-brand-out-line strong { color: var(--saf-ink); }
.saf-brand-out-thumbs { display: flex; flex-wrap: wrap; gap: var(--sp-md); }
/* Each ad is a button into the ad viewer — variants and post copy, the
   same structure as the hero roll. */
.saf-out-ad {
  height: 150px;
  aspect-ratio: 9 / 16;
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  border-radius: var(--radius-lg);
  border: 1px solid var(--saf-border);
  background: #FFFFFF;
  box-shadow: 0 12px 30px -18px rgba(18, 22, 29, 0.25);
  transition: border-color var(--dur-base) var(--ease-standard);
}
.saf-out-ad:hover { border-color: var(--saf-ink-soft); }
.saf-out-ad img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The logo viewer's plate (the app's logo stage) keeps the tone of the
   tile that opened it: a mark drawn for dark backgrounds is invisible
   on white, so the background is part of the subject. */
.logo-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: min(640px, 90vw);
  height: min(400px, 55vh);
  padding: 48px;
  border-radius: var(--radius-2xl);
  /* The stage always fits the window; the auto margins center it in
     the scroll-safe dialog (see .saf-lightbox). */
  margin-block: auto;
}
.logo-stage-light { background: #FFFFFF; }
.logo-stage-dark { background: var(--saf-ink); }
.logo-viewer-img { max-width: 100%; max-height: 100%; object-fit: contain; }
@media (min-width: 900px) {
  .saf-panel { padding: 24px; }
  .saf-panel-grid { grid-template-columns: 1fr 1.15fr 1.15fr; }
}

/* ── Ad viewer (lightbox) ───────────────────────────────────── */
/* Mirrors the app's full-resolution ad viewer: a dark scrim, the render
   floating free with its format strip beneath it, the platform copy in a
   white column beside it, and the controls pinned to the window's edges. */
.saf-lightbox {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  /* The dialog itself is the scroller: on a phone the render, strip
     and copy stack taller than the window, and the stack scrolls from
     its top edge. The auto block margins on the dialogs' content keep
     it vertically centered whenever it is shorter than the window;
     centering with align-items instead would clip an overflowing
     stack at both ends, its top unreachable. */
  overflow-y: auto;
  /* The top band is reserved for the fixed count and Close button, so
     the render can never slide under them. Side bands for the arrows
     come in at the width that shows them. */
  padding: 72px 16px 24px;
  background: rgba(18, 22, 29, 0.8);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.ad-viewer {
  /* Width of the copy column, and of the spacer that balances it. */
  --ad-copy-width: 14rem;
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  max-width: 96vw;
  min-height: 0;
  margin-block: auto;
}
/* The render and its format strip are one block; the copy sits beside it
   on wide screens and under it on narrow ones. */
.ad-viewer-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-lg);
  min-height: 0;
}
.ad-viewer-main {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-md);
  min-height: 0;
}
/* Pinned to the window's edges, not the render's, so they stay put as the
   ad changes shape between formats. */
.ad-viewer-step {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-full);
  font-size: 22px;
  line-height: 1;
  color: #FFFFFF;
  transition: background-color var(--dur-base) var(--ease-standard);
}
.ad-viewer-step:hover { background: rgba(255, 255, 255, 0.12); }
.ad-viewer-step-prev { left: var(--sp-2xl); }
.ad-viewer-step-next { right: var(--sp-2xl); }

.ad-viewer-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 200px;
  min-height: 200px;
  overflow: hidden;
  border-radius: var(--radius-lg);
}
.ad-viewer-img {
  display: block;
  /* Phone bounds: nearly the full width inside the scrim's 16px side
     bands, with enough height held back (250px) that the render and
     the format strip under it fit the first screen whole in every
     ratio — the copy below reads by scrolling the dialog. The vh line
     is the fallback for browsers without dvh.
     --render-max-w/-h, set per load by the viewer, is the largest CSS
     size at which the file still maps one-to-one onto this screen's
     pixels; drawn past it the browser interpolates and the squarer
     formats read as blur on dense displays. Unset (before the first
     load), the fallback leaves only the viewport bounds. */
  max-width: min(100vw - 32px, var(--render-max-w, 99999px));
  max-height: min(100vh - 250px, var(--render-max-h, 99999px));
  max-height: min(100dvh - 250px, var(--render-max-h, 99999px));
  object-fit: contain;
}
/* A ratio the ad hasn't been rendered in yet — the same geometry the
   image would take, drawn as a dashed placeholder on the dark scrim. */
.saf-lightbox-ondemand {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-sm);
  padding: var(--sp-lg);
  text-align: center;
  border: 1px dashed rgba(255, 255, 255, 0.35);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.06);
}
.saf-lightbox-headline { margin: 0; font-size: 14px; font-weight: var(--fw-semibold); color: #FFFFFF; }
.saf-lightbox-rationote { margin: 0; font-size: 12px; color: rgba(255, 255, 255, 0.6); }

/* Format strip: thumbnails of this ad in each ratio, under the render. */
.ad-viewer-ratios {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: var(--sp-md);
  flex-shrink: 0;
}
.ad-viewer-ratio {
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs);
  width: 44px;
  cursor: pointer;
}
.ad-viewer-thumb {
  display: block;
  width: 100%;
  overflow: hidden;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.12);
  opacity: 0.65;
  transition: opacity var(--dur-base) var(--ease-standard), box-shadow var(--dur-base) var(--ease-standard);
}
.ad-viewer-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.ad-viewer-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: 8px;
  font-weight: var(--fw-semibold);
  color: rgba(255, 255, 255, 0.6);
}
.ad-viewer-ratio:hover .ad-viewer-thumb { opacity: 1; }
.ad-viewer-ratio[aria-pressed="true"] .ad-viewer-thumb {
  opacity: 1;
  box-shadow: 0 0 0 2px #FFFFFF;
}
.ad-viewer-ratio-label {
  font-size: 10px;
  font-weight: var(--fw-semibold);
  color: rgba(255, 255, 255, 0.6);
}
.ad-viewer-ratio[aria-pressed="true"] .ad-viewer-ratio-label { color: #FFFFFF; }

/* The platform text that travels with the ad but is not drawn into it.
   On phones it flows under the render at its natural height — the
   dialog scrolls to it; on wide screens the column scrolls itself. */
.ad-copy {
  display: flex;
  flex-direction: column;
  gap: var(--sp-lg);
  width: 100%;
  max-width: var(--ad-copy-width);
  flex-shrink: 0;
}
.ad-copy-block { display: flex; flex-direction: column; gap: 2px; }
.ad-copy-label {
  margin: 0;
  font-size: 11px;
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.5);
}
.ad-copy-value {
  margin: 0;
  font-size: 13px;
  line-height: var(--lh-normal);
  color: #FFFFFF;
}

/* Top corners, on the arrows' margin. */
.ad-viewer-count {
  position: fixed;
  top: var(--sp-2xl);
  left: var(--sp-2xl);
  margin: 0;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
  font-variant-numeric: tabular-nums;
}
.ad-viewer-count:empty { display: none; }
.ad-viewer-close-corner {
  position: fixed;
  top: var(--sp-2xl);
  right: var(--sp-2xl);
}
.ad-viewer-close {
  flex-shrink: 0;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-full);
  padding: 6px var(--sp-md);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: #FFFFFF;
  transition: background-color var(--dur-base) var(--ease-standard);
}
.ad-viewer-close:hover { background: rgba(255, 255, 255, 0.12); }

/* Narrow screens keep the thumbnails but drop the arrows: there is no
   room beside the render, and the left/right keys still step. */
@media (min-width: 640px) {
  /* Side bands reserved for the arrows, so they never sit over content. */
  .saf-lightbox { padding: 72px 104px 24px; }
  .ad-viewer-step { display: flex; }
  /* Everything fits the window here — the copy column beside the render
     carries its own scroll — so the dialog itself never scrolls. */
  .ad-viewer { max-height: 100%; }
  /* Bounded against the viewport, as in the app: sideways the render
     leaves the copy column its room, and vertically the chrome above
     plus the format strip beneath — and never past the file's own
     pixels (--render-max-w/-h). */
  .ad-viewer-img {
    max-width: min(72vw, 940px, var(--render-max-w, 99999px));
    max-height: min(100vh - 220px, var(--render-max-h, 99999px));
  }
  /* Three columns with the copy's width mirrored on the empty left, so
     the render sits on the window's centre line rather than being pushed
     off it by the column beside it — the app's layout. */
  .ad-viewer-row {
    display: grid;
    grid-template-columns: var(--ad-copy-width) auto var(--ad-copy-width);
    align-items: center;
    justify-items: center;
  }
  .ad-viewer-main { grid-column: 2; }
  .ad-copy {
    grid-column: 3;
    justify-self: start;
    width: var(--ad-copy-width);
    max-height: calc(100vh - 168px);
    overflow-y: auto;
  }
}

/* ── Pricing comparison ─────────────────────────────────────── */
/* White band between Formats and the FAQ, both white too, so it carries
   a rule at each end to keep the three apart. Copy and a compact
   comparison table; when the showcase-ad figure is in the markup the
   section splits into two columns on wide screens, ad on the right, and
   stacks on narrow ones with the ad last. */
.saf-compare {
  border-top: 1px solid var(--saf-border);
  border-bottom: 1px solid var(--saf-border);
  background: var(--saf-bg);
}
.saf-compare-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 64px var(--sp-lg);
  display: grid;
  /* minmax(0, 1fr), not bare 1fr: a 1fr track refuses to shrink below
     its content's minimum, and the comparison table's 34rem minimum
     would widen the whole column past a phone — pushing the heading
     and the ad roll off the screen edge. With a 0 minimum the column
     holds the viewport and the table scrolls inside its wrap. */
  grid-template-columns: minmax(0, 1fr);
  gap: 48px;
  align-items: center;
}
.saf-compare-copy .saf-h2 { margin-top: var(--sp-md); }
.saf-compare-sub {
  margin: var(--sp-md) 0 0;
  max-width: 36rem;
  font-size: 14px;
  line-height: var(--lh-relaxed);
  color: var(--saf-ink-soft);
}
/* The table keeps its four columns at every width and scrolls inside
   its wrapper on phones rather than wrapping into a mess. */
.saf-compare-table-wrap { max-width: 48rem; margin-top: var(--sp-xl); overflow-x: auto; }
.saf-compare-table {
  width: 100%;
  min-width: 34rem;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 14px;
  line-height: var(--lh-normal);
}
.saf-compare-table th,
.saf-compare-table td {
  padding: 12px 14px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--saf-border);
}
/* Every header carries a 2px top rule so the row's text aligns; only
   the factory's is painted, a brand bar marking the column like a tab. */
.saf-compare-table thead th {
  padding-top: 10px;
  border-top: 2px solid transparent;
  font-size: 15px;
  font-weight: var(--fw-bold);
  letter-spacing: -0.01em;
  color: var(--saf-ink);
}
.saf-compare-table tbody th {
  font-size: 11px;
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--saf-muted);
  white-space: nowrap;
}
.saf-compare-table td {
  font-weight: var(--fw-medium);
  color: var(--saf-ink-soft);
}
/* The factory column is emphasised by weight and ink, not a wash: brand
   header under the bar, bold full-ink cells against the soft columns,
   and the per-ad figure in brand ink. */
.saf-compare-table thead .is-us { border-top-color: var(--saf-brand); color: var(--saf-brand); }
.saf-compare-table td.is-us { font-weight: var(--fw-bold); color: var(--saf-ink); }
.saf-compare-table tbody tr:last-child td.is-us { color: var(--saf-brand-pressed); }
.saf-compare-table tbody tr:last-child th,
.saf-compare-table tbody tr:last-child td { border-bottom: 0; }
/* Ink button under the table, left-aligned with it; inline-flex sits
   the arrow beside the label. */
.saf-compare-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-sm);
  margin-top: var(--sp-xl);
}

/* The showcase roll (see the section's markup comment): the shared
   .saf-out-ad tile, same viewer wiring as the brand-read strip, turning
   on the hero roll's coverflow — the same ring of cards angled toward a
   full-size centre, but only one card deep on each side, since this one
   sits beside the table rather than under the headline.
   Card height is capped at the hero's own, because both draw the same
   600px-tall preview files — bigger than the hero shows them and the
   render starts to soften. Below the cap it tracks the viewport, so the
   roll never squeezes the comparison table beside it. */
.saf-compare-ad {
  --roll-h: clamp(150px, 17vw, 290px);
  margin: 0;
  justify-self: center;
}
/* Three cards wide: the centre one and its two neighbours sit edge to
   edge, so that span is the whole block. */
.saf-compare-ad-frame {
  position: relative;
  width: calc(var(--roll-h) * 0.5625 * 3);
  height: var(--roll-h);
}
/* As in the hero, the perspective lives inside each card's own transform
   rather than in a real 3D context, so every card hit-tests where it is
   drawn. Height-driven rather than the base tile's fixed 150px; the 9:16
   shape comes from the base rule either way. Cards away from the three
   visible places sit dead centre, small and invisible, and can't swallow
   a click. */
.saf-compare-ad-frame .saf-out-ad {
  position: absolute;
  top: 0;
  left: 50%;
  height: 100%;
  width: auto;
  box-shadow: 0 18px 45px -18px rgba(18, 22, 29, 0.35);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
  transform: translateX(-50%) perspective(1100px) rotateY(0deg) scale(0.78);
  transition: transform 0.65s var(--ease-standard),
              opacity 0.65s var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard);
}
.saf-compare-ad-frame .saf-out-ad.is-center {
  opacity: 1;
  pointer-events: auto;
  z-index: 5;
  transform: translateX(-50%) perspective(1100px) rotateY(0deg) scale(1);
}
/* translateX percentages are of the card's own width, so the neighbours
   sit flush against the centre card at every size. Each is angled toward
   it, like a ring turning around a vertical axis behind the stage. */
.saf-compare-ad-frame .saf-out-ad.is-prev {
  opacity: 0.35;
  pointer-events: auto;
  z-index: 4;
  transform: translateX(-150%) perspective(1100px) rotateY(26deg) scale(0.89);
}
.saf-compare-ad-frame .saf-out-ad.is-next {
  opacity: 0.35;
  pointer-events: auto;
  z-index: 4;
  transform: translateX(50%) perspective(1100px) rotateY(-26deg) scale(0.89);
}
/* The side cards invite the click: hovering lifts the dim — a pure fade
   on the existing opacity transition, nothing moves. */
.saf-compare-ad-frame .saf-out-ad.is-prev:hover,
.saf-compare-ad-frame .saf-out-ad.is-next:hover { opacity: 0.75; }
@media (prefers-reduced-motion: reduce) {
  .saf-compare-ad-frame .saf-out-ad { transition: none; }
}

@media (min-width: 768px) {
  .saf-compare-inner { padding: 80px var(--sp-lg); }
}
/* Two columns only when the ad figure is present; without it the copy
   block takes the full width. */
@media (min-width: 960px) {
  /* The roll's column is sized by the roll itself, so the slack goes to
     the copy — which fills it with the table — instead of pooling as a
     gulf between the two halves. */
  .saf-compare-inner:has(.saf-compare-ad) { grid-template-columns: minmax(0, 1fr) auto; gap: 56px; }
  /* Centred on the copy block, the roll sits optically high against it:
     the eyebrow and heading carry far less weight than the table below
     them, so the column's visual centre is lower than its measured one.
     A nudge down settles the two halves onto the same line. */
  .saf-compare-ad { position: relative; top: 24px; }
}

/* ── FAQ ────────────────────────────────────────────────────── */
/* White band below the pricing comparison (which draws the seam
   between them) and above the closing CTA's surface band, which
   carries its own border; the section needs none. Rows are native
   details/summary — the chevron flips on [open], and hover recolors
   only. */
.saf-faq { background: var(--saf-bg); }
.saf-faq-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 64px var(--sp-lg);
}
.saf-faq-head { max-width: 42rem; }
.saf-faq-head .saf-h2 { margin-top: var(--sp-md); }
.saf-faq-list {
  max-width: 48rem;
  margin-top: 40px;
  border-top: 1px solid var(--saf-border);
}
.saf-faq-item { border-bottom: 1px solid var(--saf-border); }
.saf-faq-q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-lg);
  padding: 20px 0;
  font-size: 16px;
  font-weight: var(--fw-bold);
  letter-spacing: -0.01em;
  color: var(--saf-ink);
  cursor: pointer;
  /* Hides the UA's disclosure triangle; the chevron is the marker. */
  list-style: none;
}
.saf-faq-q::-webkit-details-marker { display: none; }
.saf-faq-q:hover { color: var(--saf-brand); }
.saf-faq-chevron {
  flex: none;
  color: var(--saf-muted);
  transition: transform 0.2s ease;
}
.saf-faq-item[open] .saf-faq-chevron { transform: rotate(180deg); }
.saf-faq-a {
  margin: 0 0 20px;
  max-width: 40rem;
  font-size: 14px;
  line-height: var(--lh-relaxed);
  color: var(--saf-ink-soft);
}
.saf-faq-a a {
  font-weight: var(--fw-semibold);
  color: var(--saf-brand);
}
.saf-faq-a a:hover { color: var(--saf-brand-pressed); }

@media (min-width: 640px) {
  .saf-faq-inner { padding: 96px var(--sp-lg); }
  .saf-faq-q { font-size: 18px; }
}

/* ── Closing CTA ────────────────────────────────────────────── */
.saf-closing {
  border-top: 1px solid var(--saf-border);
  border-bottom: 1px solid var(--saf-border);
  background: var(--saf-surface);
}
.saf-closing-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 56px var(--sp-lg);
}
.saf-closing-card {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: var(--sp-2xl);
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-2xl);
  background: var(--saf-bg);
  padding: var(--sp-xl);
  box-shadow: 0 1px 2px rgba(18, 22, 29, 0.05);
}
.saf-closing-card .saf-h2 { margin-top: var(--sp-md); }
.saf-closing-body { margin: var(--sp-md) 0 0; font-size: 14px; line-height: var(--lh-relaxed); color: var(--saf-ink-soft); }

/* The closing form is the hero form on a surface background: no drop
   shadow, one-line border, ink button. */
.saf-url-form-closing { max-width: none; margin: 0; width: 100%; }
.saf-url-form-closing .saf-url-field {
  border-width: 1px;
  border-color: var(--saf-border);
  background: var(--saf-bg);
  box-shadow: 0 1px 2px rgba(18, 22, 29, 0.05);
}
.saf-url-form-closing .saf-url-hint:empty { display: none; }

@media (min-width: 640px) {
  .saf-closing-inner { padding: 80px var(--sp-lg); }
  .saf-closing-card { padding: 40px; }
  .saf-closing-body { font-size: 16px; }
  /* The closing field and button keep their 14px vertical padding at every
     width; only the hero's grow to 16px. */
  .saf-url-form-closing .saf-url-prefix { padding: 14px 16px; }
  .saf-url-form-closing .saf-url-input { padding: 14px 0 14px 16px; }
  .saf-url-form-closing .saf-cta-lg { padding: 14px 32px; }
}
@media (min-width: 1024px) {
  .saf-closing-card { grid-template-columns: 1fr 1.25fr; gap: 48px; padding: 56px; }
}

/* ── Disclaimer ─────────────────────────────────────────────── */
/* Sits above the footer, covering the real-brand demo ads in the
   roll, belt and deck. */
.saf-disclaimer { border-top: 1px solid var(--saf-border); }
.saf-disclaimer p {
  max-width: 72rem;
  margin: 0 auto;
  padding: 24px var(--sp-lg);
  font-size: 12px;
  line-height: var(--lh-normal);
  /* The sub-text tone the page's section subheads use, so the notice
     reads as page copy rather than fading into the border above it. */
  color: var(--saf-ink-soft);
  text-align: center;
}

/* ── Footer ─────────────────────────────────────────────────── */
/* The site-wide footer, as on the index page. Rules mirror index.css;
   tokens.css resolves to its light palette here (the page pins
   data-theme="light"), and the background matches the marketing
   footer's light value. */
.footer {
  border-top: 1px solid var(--border-1);
  padding-block: 56px 40px;
  background: #F7F8FA;
}
.footer .wrap {
  max-width: 1200px;
  margin: 0 auto;
  padding-inline: clamp(20px, 5vw, 64px);
}
.footer-grid {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: clamp(32px, 6vw, 96px);
  padding-bottom: 44px;
  border-bottom: 1px solid var(--border-1);
}
.footer-brand .brandmark { margin-bottom: 22px; }
.footer-brand .brandmark .brand-logo { height: 26px; }
.footer h5 {
  font-family: var(--font-mono);
  font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--fg-3); margin: 0 0 14px; font-weight: 500;
}
.footer-about {
  max-width: 480px;
  margin: 0;
  font-size: 14.5px;
  color: var(--fg-3);
  line-height: 1.65;
}
.footer-company { flex-shrink: 0; padding-top: 48px; }
.footer-company p { margin: 0; font-size: 14px; color: var(--fg-3); line-height: 1.75; }
.footer-company p strong { color: var(--fg-2); font-weight: 600; }
.footer-company a { transition: color var(--dur-fast) var(--ease-standard); }
.footer-company a:hover { color: var(--fg-1); }
.footer-bottom {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 26px;
  font-size: 13px; color: var(--fg-3);
}
.footer-legal { display: flex; gap: 22px; align-items: center; }
.footer-legal a { font-size: 13px; color: var(--fg-3); transition: color var(--dur-fast) var(--ease-standard); }
.footer-legal a:hover { color: var(--fg-1); }
.footer-x svg { display: block; width: 15px; height: 15px; }
@media (max-width: 700px) {
  .footer-grid { flex-direction: column; }
  .footer-company { padding-top: 0; }
  .footer-bottom { flex-direction: column; gap: 14px; align-items: flex-start; }
}

/* ── Sign-in dialog ─────────────────────────────────────────── */
/* Its own overlay rather than the lightbox's: the design dims to 40%,
   and the scrim is a real button so the backdrop is reachable by
   keyboard as well as by pointer. */
.saf-signin {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--sp-lg);
}
.saf-signin-scrim {
  position: absolute;
  inset: 0;
  cursor: default;
  background: rgba(18, 22, 29, 0.4);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.saf-signin-panel {
  position: relative;
  width: 100%;
  max-width: 28rem;
  border: 1px solid var(--saf-border);
  border-radius: var(--radius-2xl);
  background: var(--saf-bg);
  padding: var(--sp-2xl);
  box-shadow: 0 20px 25px -5px rgba(18, 22, 29, 0.1), 0 8px 10px -6px rgba(18, 22, 29, 0.1);
}
.saf-signin-logo { display: block; height: 20px; width: auto; }
.saf-signin-title {
  margin: var(--sp-2xl) 0 0;
  font-size: 24px;
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  color: var(--saf-ink);
}
.saf-signin-sub { margin: var(--sp-sm) 0 0; font-size: 14px; color: var(--saf-ink-soft); }
.saf-signin-url { font-weight: var(--fw-semibold); color: var(--saf-ink); }

.saf-signin-providers {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  margin-top: var(--sp-2xl);
}
.saf-signin-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-md);
  width: 100%;
  border-radius: var(--radius-lg);
  padding: 14px 20px;
  font-size: 16px;
  font-weight: var(--fw-bold);
  transition: background-color var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard);
}
.saf-signin-btn svg { width: 20px; height: 20px; flex: 0 0 auto; }
.saf-signin-btn-apple { background: var(--saf-ink); color: #FFFFFF; }
.saf-signin-btn-apple:hover { background: var(--saf-ink-soft); }
.saf-signin-btn-google {
  border: 2px solid var(--saf-input-border);
  background: var(--saf-bg);
  color: var(--saf-ink);
}
.saf-signin-btn-google:hover { border-color: rgba(249, 115, 22, 0.3); background: var(--saf-surface); }

/* The email escape hatch: quiet by default so the providers stay the
   obvious choice. */
.saf-signin-alt {
  display: block;
  width: 100%;
  margin-top: var(--sp-lg);
  padding: var(--sp-sm);
  text-align: center;
  font-size: 13px;
  font-weight: var(--fw-semibold);
  color: var(--saf-muted);
  transition: color var(--dur-base) var(--ease-standard);
}
.saf-signin-alt:hover { color: var(--saf-ink); }
.saf-signin-email { display: flex; flex-direction: column; gap: var(--sp-md); margin-top: var(--sp-lg); }
.saf-signin-input {
  width: 100%;
  border: 1px solid var(--saf-input-border);
  border-radius: var(--radius-lg);
  background: var(--saf-bg);
  padding: 12px 14px;
  font-family: inherit;
  font-size: 14px;
  color: var(--saf-ink);
  outline: none;
  transition: border-color var(--dur-base) var(--ease-standard);
}
.saf-signin-input::placeholder { color: var(--saf-muted); }
.saf-signin-input:focus { border-color: rgba(249, 115, 22, 0.4); }
.saf-signin-btn-email {
  border: 1px solid var(--saf-border);
  background: var(--saf-surface);
  color: var(--saf-ink);
  font-size: 14px;
  padding: 12px 20px;
}
.saf-signin-btn-email:hover { background: var(--saf-bg); border-color: var(--saf-input-border); }
.saf-signin-btn-email:disabled { opacity: 0.6; cursor: not-allowed; }

.saf-signin-message { margin: var(--sp-lg) 0 0; font-size: 13px; color: var(--saf-destructive); }
.saf-signin-message:empty { display: none; }
.saf-signin-legal {
  margin: var(--sp-xl) 0 0;
  font-size: 12px;
  line-height: var(--lh-relaxed);
  color: var(--saf-muted);
}
.saf-signin-legal a { color: inherit; transition: color var(--dur-base) var(--ease-standard); }
.saf-signin-legal a:hover { color: var(--saf-ink); }

@media (min-width: 640px) {
  .saf-signin-panel { padding: 40px; }
}
