@font-face {
  font-family: "Tomato Grotesk Medium";
  src: url("../assets/fonts/tomato-grotesk-medium.woff2") format("woff2");
  font-display: swap;
  font-style: normal;
  font-weight: 500;
}
/* SemiBold (600) is the heaviest real weight this family ships — the
   Framer reference site itself only ever uses Medium/500 or SemiBold/600,
   there's no true Bold/700 file. Used here as "bold" for the hero title. */
@font-face {
  font-family: "Tomato Grotesk SemiBold";
  src: url("../assets/fonts/tomato-grotesk-semibold.woff2") format("woff2");
  font-display: swap;
  font-style: normal;
  font-weight: 600;
}

/* ========================================================================== 
   Quacko marketing site — standalone stylesheet (no dependency on quacko_app)

   Breakpoints (mobile-first, matching the most common industry defaults):
     base   : 0–639px    phone
     sm 640 : 640–767px  large phone / small tablet
     md 768 : 768–1023px tablet (iPad portrait)
     lg 1024: 1024–1279px small desktop / iPad landscape
     xl 1280: 1280px+    desktop
   ========================================================================== */

:root {
  --color-bg: #FFFFFF;
  --color-bg-alt: #FAF9F8;
  --color-ink: #14120F;
  --color-ink-soft: #514C46;
  --color-border: #E7E3DE;
  /* Shared dark-panel bg for the brands marquee bar and the footer — keep
     these two on the same token so they can never drift apart again. */
  --color-dark-panel: #1E0E10;

  --color-brand: #FF5A1F;
  --color-brand-dark: #E14A12;
  --color-brand-tint: #FFEFE7;

  --color-green: #3B725B;
  --color-green-bg: #E7F3EE;
  --color-blue: #2B5FA8;
  --color-blue-bg: #E9F0FA;
  --color-orange-bg: #FFF1E6;

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --radius-pill: 999px;

  --shadow-card: 0 1px 2px rgba(20, 18, 15, 0.04), 0 8px 24px rgba(20, 18, 15, 0.06);
  --shadow-card-hover: 0 10px 15px -3px rgba(0, 0, 0, 0.10), 0 4px 6px -4px rgba(0, 0, 0, 0.10);

  --container-max: 1200px;
  --header-h: 72px;

  color-scheme: light;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* overflow-x: hidden guards against a real bug: [data-visual-reveal]'s
   pre-reveal scale(1.15) on the AI Creator Search/Automated Outreach/
   Video Finder cards (~line 1146) scales from center, so before an
   element has scrolled into view it overhangs both its own edges equally
   — and nothing in its ancestor chain clips that. With no guard here,
   that makes the whole *page* wider than the viewport and horizontally
   scrollable, which is what actually produces a visible gap of page
   background past a full-bleed section's edge (reported on mobile FAQ,
   well after those cards) instead of the bleed just staying invisible
   off-screen. Set on html only, not body — body would turn overflow-y
   into a second, competing scroll container (per the CSS overflow spec's
   "the other axis computes to auto" rule) and risk breaking the
   position:sticky sections (.why-quacko-copy, .faq-aside) and the fixed
   header that assume the real viewport is the one scrolling. */
html { scroll-behavior: smooth; scroll-padding-top: 144px; overflow-x: hidden; }
/* main.js toggles this on <html> while the full-screen mobile menu
   (#mobileNav) is open, so the page behind it can't scroll. */
html.nav-mobile-open, html.nav-mobile-open body { overflow: hidden; }
body {
  margin: 0;
  padding-top: 126px; /* clears the fixed floating nav pill (scaled 1.5x) */
  /* Global dot-grid ground — was hero-only, now applies behind every
     section. Sections with their own background (cards, colored panels,
     the Product Highlight rows) simply paint over it as normal. */
  background-color: #F5F3F1;
  background-image: radial-gradient(rgba(20, 18, 15, 0.11) 1px, transparent 1px);
  background-size: 16px 16px;
  color: var(--color-ink);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, p { margin: 0; }
button { font: inherit; cursor: pointer; }
input { font: inherit; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 12px;
  top: -48px;
  background: var(--color-ink);
  color: #fff;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  z-index: 200;
  transition: top .15s ease;
}
.skip-link:focus { top: 12px; }

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: 16px;
}

.section { padding-block: 64px; }
.section-alt { background: var(--color-bg-alt); }

.section-head { max-width: 640px; margin-bottom: 40px; }
.section-head h2 { margin-top: 12px; }
.section-sub {
  margin-top: 14px;
  color: var(--color-ink-soft);
  font-size: 1.125rem;
}

.eyebrow {
  display: inline-block;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-brand-dark);
}

h1 {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  /* Fluid instead of a flat size — a flat size shared by every width from
     a 320px phone up to a 1023px tablet isn't actually "responsive". This
     scales continuously with viewport width instead, capped well below
     the size where the hero title's own manual line break
     ("...Matching.<br />Faster...") stops holding "Smarter Creator" /
     "Faster Revenue" together on one line each and starts breaking one
     word per line (measured per-width in a real browser: 38px is the
     largest safe size at 320px, 47px at 390px, 59px at 480px — this
     formula tracks a couple px under that line at every width in
     between). 1024px+ still gets its own fixed sizes below (3rem, then
     4rem at 1280px) — that's a real layout change (row instead of
     stacked), not just more space, so it stays a deliberate snap, not
     fluid. */
  font-size: clamp(2.25rem, -0.25rem + 12.5vw, 4rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
/* DESIGN_GUIDELINES.md: every section-level h2 uses the Display title
   treatment. Keep it at the base level so newly added sections cannot
   accidentally fall back to Inter/28px when a page-specific selector is
   missing. More specific component rules may still adjust layout only. */
h2 {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
h3 { font-size: 1.125rem; font-weight: 700; }

/* ---------- Staggered text reveal ---------- */
/*
 * Hand-rolled equivalent of React Bits Pro's "Staggered Text" component
 * (see https://pro.reactbits.dev/docs/components/staggered-text) — that
 * package needs a React project + shadcn + a Pro license key, none of
 * which this static site has, so js/main.js (staggerText) reproduces the
 * effect directly: any [data-stagger-text] title has its words wrapped in
 * an overflow-hidden mask on load, each word riding its own rise-and-fade
 * animation with an increasing delay. Add the attribute to any other
 * title to opt it into the same entrance.
 */
[data-stagger-text] .stagger-word-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
[data-stagger-text] .stagger-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(115%);
}
/* Words stay hidden until an IntersectionObserver adds this class — the
   hero title gets it almost immediately (already in view on load); every
   other title only plays once scrolled into view, and only once. */
[data-stagger-text].is-stagger-active .stagger-word {
  animation: staggerWordIn 0.75s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: var(--stagger-delay, 0ms);
}
@keyframes staggerWordIn {
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  [data-stagger-text] .stagger-word { animation: none; opacity: 1; transform: none; }
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 20px;
  border-radius: var(--radius-pill);
  font-weight: 600;
  font-size: 0.9375rem;
  border: 1px solid transparent;
  white-space: nowrap;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, transform .15s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary {
  background: var(--color-brand);
  color: #fff;
}
.btn-primary:hover { background: var(--color-brand-dark); }
.btn-secondary {
  background: #fff;
  color: var(--color-ink);
  border-color: var(--color-border);
}
.btn-secondary:hover { border-color: var(--color-ink); }
/*
 * Hero CTA pair — same pill shape + arrow hover motion as the AI Creator
 * Search row's "Learn More" button (.ai-arrow: rotates 45deg and nudges
 * right on hover/focus, see below), just in the site's own orange/white
 * palette instead of that section's lime-on-dark treatment.
 */
.btn-hero-primary {
  background: var(--color-brand);
  color: #fff;
}
/* Deliberately no hover background-color change — only the arrow rotation
   and the per-character text reveal (see DESIGN_GUIDELINES.md "Hero CTA
   按钮") carry the hover feedback now. */
.btn-hero-secondary {
  background: #fff;
  color: var(--color-ink);
  border-color: var(--color-border);
}
.btn-hero-secondary:hover { background: var(--color-bg-alt); border-color: var(--color-ink); }
.btn-ghost {
  background: transparent;
  color: var(--color-ink-soft);
}
.btn-ghost:hover { color: var(--color-ink); }
/* Outreach accordion's "Explore Search/Connect/…" — sizing now matches the
 * site-standard pill button spec in DESIGN_GUIDELINES.md ("全站 Pill 按钮
 * 标准尺寸与样式": padding 12px 22px, 18px/600 text, 24px text↔arrow gap),
 * same structure as .btn-hero-primary/secondary (arrow inline in the same
 * pill, not a separate circle). Color stays this component's own black —
 * per that doc, only size is unified, palette stays per-button.
 */
.btn-dark {
  gap: 24px;
  padding: 12px 22px;
  font-size: 1.125rem;
  background: var(--color-ink);
  color: #fff;
}
.btn-dark:hover { background: #2a251e; }
.btn-lg { padding: 13px 26px; font-size: 1rem; }
.btn-block { width: 100%; }

/* ==========================================================================
   Header
   ========================================================================== */
/*
 * Floating glass pill nav — read out of the published Quacko Framer site
 * (incomparable-employees-345264.framer.app): a fixed, inset pill (not a
 * full-width bar) with a translucent blurred background, sitting clear of
 * the page edges. Logo mark (assets/quacko-logo-mark.svg) is the icon
 * exported straight from the Figma source (node 3148:100256); the
 * wordmark next to it is real text (see .logo-word) rather than baked
 * into the asset.
 */
.site-header {
  position: fixed;
  top: 24px;
  left: 0;
  right: 0;
  z-index: 100;
  transform: translateY(0);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Auto-hide on scroll: js/main.js toggles this while scrolling down past
   the top of the page, and removes it the moment the user scrolls back up
   (or the mobile menu is open) — translateY clears the pill plus its
   24px top offset (scaled 1.5x) so it doesn't peek in from behind the page edge. */
.site-header.is-header-hidden {
  transform: translateY(calc(-100% - 24px));
}
@media (prefers-reduced-motion: reduce) {
  .site-header { transition: none; }
}
.header-inner {
  position: relative;
  display: flex;
  flex-direction: column;
}
.nav-pill {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-radius: var(--radius-pill);
  padding: 10px 10px 10px 24px;
  box-shadow: var(--shadow-card);
}
.logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  font-weight: 800;
  font-size: 1.3rem;
  color: var(--color-ink);
  /* Mobile browsers paint their own rectangular tap-highlight overlay on
     :active taps regardless of the link's own shape — same issue as
     .menu-toggle above, just on the logo link this time. Disable it. */
  -webkit-tap-highlight-color: transparent;
}
/*
 * Header logo: real "Quacko" text (not a flattened wordmark path) set in
 * the site's own Tomato Grotesk Medium, matching the Figma source
 * (node 3148:100256) — icon + text sized off its 38.6:44.5 icon and
 * 130:55 wordmark boxes at the same 26px icon height (whole nav scaled ~1.15x).
 */
.logo-icon { display: block; height: 30px; width: auto; }
.logo-word {
  font-family: "Tomato Grotesk Medium", Arial, sans-serif;
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1;
}
/* simple text-lockup variant, used on the dark footer where the header's
   SVG (which has a black wordmark baked in) wouldn't read */
.logo-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: var(--color-brand);
  color: #fff;
  font-weight: 800;
  font-size: 0.9375rem;
}

/* Sits a fixed 32px from the logo (per request) instead of the old
   position:relative;left:-40px pull against .nav-pill's own
   justify-content:space-between — that hack pulled nav-desktop toward the
   logo by a constant amount regardless of how much space space-between
   actually handed it, which is exactly what caused the 1024-1279px
   collision tuned around below. Fixed margins on both sides instead:
   margin-left anchors this to the logo at an exact distance; margin-right
   is just a minimum-gap floor before .header-actions, which uses its own
   margin-left:auto to soak up whatever's left and sit flush right. */
.nav-desktop {
  display: none;
  align-items: center;
  gap: 3px;
  margin-left: 32px;
  margin-right: 16px;
}
.nav-desktop::before {
  content: none;
  position: absolute;
  top: 0;
  left: 0;
  width: var(--nav-hover-width, 0px);
  height: 48px;
  border-radius: var(--radius-pill);
  background: rgba(20, 18, 15, 0.055);
  opacity: var(--nav-hover-opacity, 0);
  transform: translate3d(var(--nav-hover-x, 0px), 0, 0);
  /* transform/width transition only while .is-nav-gliding (below) — without
     it, the very first hover after the indicator is hidden animates in
     from its default 0px/0px (parked at the nav's left edge, under
     "Product") instead of appearing already under whichever item was
     actually hovered, e.g. landing on "Pricing" first visibly grows/slides
     the pill in from the wrong end of the bar for ~300ms. js/main.js
     positions the indicator instantly (this rule's plain 180ms opacity
     fade only) on that first hover, then adds .is-nav-gliding so hovering
     between items afterward keeps the smooth glide the shared indicator
     is for. */
  transition: opacity 180ms ease;
  pointer-events: none;
  will-change: transform, width;
}
.nav-desktop.is-nav-gliding::before {
  transition:
    transform 460ms cubic-bezier(.16, 1, .3, 1),
    width 460ms cubic-bezier(.16, 1, .3, 1),
    opacity 180ms ease;
}
.nav-desktop a {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  height: 48px;
  padding: 0 18px;
  border-radius: var(--radius-pill);
  font-size: 1.0625rem;
  font-weight: 500;
  color: var(--color-ink-soft);
  transition: color 260ms ease, background-color 260ms ease;
}
.nav-desktop a:hover,
.nav-desktop a:focus-visible {
  color: var(--color-ink);
  background: rgba(20, 18, 15, 0.055);
}
.nav-label-split {
  display: inline-flex;
  overflow: hidden;
  white-space: pre;
}
.nav-label-char {
  display: inline-block;
  opacity: 1;
  transform: translateY(0);
  will-change: transform, opacity;
}
.nav-label-split.is-animating .nav-label-char {
  animation: nav-label-char-in 555ms cubic-bezier(.215, .61, .355, 1) both;
  animation-delay: var(--char-delay);
}
@keyframes nav-label-char-in {
  from {
    opacity: 0;
    transform: translateY(40px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  /* Soaks up whatever space .nav-pill has left and pushes this flush
     right — .nav-pill dropped justify-content:space-between (see
     .nav-desktop) so this replaces the job that used to do. */
  margin-left: auto;
}
.nav-desktop-only { display: none; }
.nav-link-plain {
  padding: 10px 18px;
  font-size: 1.0625rem;
  font-weight: 500;
  color: var(--color-ink);
}
/* Header's own "Log in" matches .nav-desktop a's hover exactly (same
   48px/pill box, same muted->ink color swap, same gray pill background on
   hover) — it just can't share that link group's sliding-indicator ::before
   since it lives in .header-actions, not .nav-desktop, and it's the only
   link here so there's nothing for an indicator to slide between anyway;
   a plain hover background gives the same result for a single item. */
.header-actions .nav-link-plain {
  /* display comes from .nav-desktop-only (none on mobile, inline-flex from
     1024px) — setting it here too would out-specificity that toggle and
     leave "Log in" visible outside the mobile menu, which is the one
     header-actions link that carries both classes. */
  align-items: center;
  height: 48px;
  padding: 0 18px;
  border-radius: var(--radius-pill);
  color: var(--color-ink-soft);
  transition: color 260ms ease, background-color 260ms ease;
}
.header-actions .nav-link-plain:hover,
.header-actions .nav-link-plain:focus-visible {
  color: var(--color-ink);
  background: rgba(20, 18, 15, 0.055);
}
.btn-pill-outline {
  background: #fff;
  color: var(--color-ink);
  border-color: var(--color-border);
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  font-size: 1.0625rem;
  font-weight: 600;
}
.btn-pill-outline:hover { background: var(--color-bg-alt); }
.btn-pill-solid {
  background: var(--color-brand);
  color: #fff;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  font-size: 1.0625rem;
  font-weight: 600;
}
/* No hover background-color change — same "Try For Free" pill rule as
   .btn-hero-primary (see DESIGN_GUIDELINES.md "Hero CTA 按钮"): stays the
   same orange on hover everywhere it appears (header nav, mobile menu,
   footer). */

/* Reference-style mobile toggle: the plain "+" makes a long spring turn
   into an "x". Container is circular (per request) but stays unfilled —
   no white/tinted chip behind it, just the round hit-area/shape. Mobile
   browsers paint their own tap-highlight overlay on :active though, and
   that overlay is a plain rectangle regardless of the element's own
   border-radius — hence the square flash on tap. Disabled outright since
   there's no visible fill for it to shade anyway. */
.menu-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: none;
  border-radius: 50%;
  background: transparent;
  -webkit-tap-highlight-color: transparent;
  flex-shrink: 0;
}
.menu-toggle-icon {
  position: relative;
  width: 26px;
  height: 26px;
  transform: rotate(0deg);
  transition: transform 600ms cubic-bezier(.22, 1, .36, 1);
  will-change: transform;
}
.menu-toggle-icon::before,
.menu-toggle-icon::after {
  content: "";
  position: absolute;
  background: var(--color-brand);
  border-radius: 2px;
}
.menu-toggle-icon::before { left: 0; top: 50%; width: 100%; height: 2.5px; transform: translateY(-50%); }
.menu-toggle-icon::after { top: 0; left: 50%; width: 2.5px; height: 100%; transform: translateX(-50%); }
.menu-toggle[aria-expanded="true"] .menu-toggle-icon { transform: rotate(135deg); }
/* Per feedback the "x" stays brand orange once open too — it read fine
   against the white .nav-mobile-panel already, no need for the ink swap
   that used to live here. */

/* ---------- Product mega menu ---------- */
/* Ported layout/interaction from linktr.ee's "Product" nav dropdown:
   3 columns (categories / active-category detail / featured card) in one
   rounded card below the floating nav pill. Copy is carried over close to
   verbatim from that reference (see the HTML comment above the markup) —
   swap in Quacko's real IA once it's settled. */
.product-mega-menu {
  position: absolute;
  top: calc(100% + 12px);
  left: 0;
  right: 0;
  /* left:0/right:0 resolve against .header-inner's *padding* box (it
     doubles as .container, so that's 32px further out on each side than
     #navPill's own content box — .container's padding-inline). Matching
     that padding here, instead of relying on .product-mega-menu-inner's
     old max-width+margin-inline cap, is what makes the dropdown land
     exactly as wide as the pill above it. */
  padding-inline: 16px;
  z-index: 2;
  display: flex;
  justify-content: center;
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 260ms cubic-bezier(.16, 1, .3, 1), transform 260ms cubic-bezier(.16, 1, .3, 1);
}
.product-mega-menu.is-open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) {
  .product-mega-menu { transition: opacity 120ms linear; }
}
/* Per-column stagger on open — same beat as React Bits' CardNav cards
   (rise + fade, ~80ms apart), replayed on every open via the
   is-animating-in reflow trick in main.js rather than only once on scroll
   like the page's stagger-word headings. */
.product-mega-menu-inner > .product-mega-col {
  opacity: 0;
  transform: translateY(24px);
}
.product-mega-menu.is-animating-in .product-mega-menu-inner > .product-mega-col {
  animation: productMegaCardIn 420ms cubic-bezier(.16, 1, .3, 1) both;
}
.product-mega-menu.is-animating-in .product-mega-menu-inner > .product-mega-col:nth-child(1) { animation-delay: 0ms; }
.product-mega-menu.is-animating-in .product-mega-menu-inner > .product-mega-col:nth-child(2) { animation-delay: 80ms; }
.product-mega-menu.is-animating-in .product-mega-menu-inner > .product-mega-col:nth-child(3) { animation-delay: 160ms; }
@keyframes productMegaCardIn {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .product-mega-menu-inner > .product-mega-col {
    opacity: 1;
    transform: none;
    animation: none;
  }
}
.product-mega-menu-inner {
  display: grid;
  grid-template-columns: 260px 1fr 300px;
  /* Matches #navPill's own width exactly — both are unconstrained children
     of .header-inner, so both stretch to the same box. (Used to be capped
     at max-width:var(--container-max) with a 20px margin-inline, which
     sat 28px narrower than the pill on each side.) */
  width: 100%;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card-hover);
  overflow: hidden;
}
.product-mega-col { padding: 16px; }
.product-mega-col--categories { border-right: 1px solid var(--color-border); padding: 12px; }
.product-mega-col--detail { border-right: 1px solid var(--color-border); padding: 28px 24px; }
.product-mega-col--featured { padding: 24px; display: flex; flex-direction: column; }

/* One panel per category, swapped by js/main.js on hover/focus of a
   .product-mega-category (toggling [hidden] on the rest). Carries the
   flex/gap .product-mega-col--detail used to own directly back when it
   only ever rendered one category's content. The [hidden] override is
   required, not decorative: author-stylesheet rules beat the UA
   stylesheet's own `[hidden]{display:none}` regardless of specificity, so
   this class's unqualified `display:flex` would otherwise make every
   category's panel render at once, active or not. */
.product-mega-detail-panel { display: flex; flex-direction: column; gap: 22px; }
.product-mega-detail-panel[hidden] { display: none; }

.product-mega-category {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  border-radius: var(--radius-md);
  color: var(--color-ink);
  transition: background 160ms ease;
}
.product-mega-category:hover,
.product-mega-category:focus-visible,
.product-mega-category.is-active { background: var(--color-bg-alt); }
.product-mega-category-icon {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--color-ink-soft);
}
.product-mega-category-icon img[src*="about-quacko-menu.svg"] {
  display: block;
  width: 20px;
  height: 20px;
  opacity: .72;
}
.product-mega-category-label { flex: 1; font-size: 0.9375rem; font-weight: 500; }
.product-mega-category-chevron {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--color-ink-soft);
}

.product-mega-detail-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
a.product-mega-detail-group--link:hover .product-mega-detail-title { color: var(--color-brand-dark); }
.product-mega-detail-title { font-size: 0.9375rem; font-weight: 600; color: var(--color-ink); }
.product-mega-detail-desc { font-size: 0.8125rem; color: var(--color-ink-soft); }

.product-mega-featured-label { font-size: 0.9375rem; font-weight: 600; margin-bottom: 14px; }
.product-mega-featured-media {
  position: relative;
  display: block;
  border-radius: var(--radius-md);
  overflow: hidden;
  aspect-ratio: 262 / 158;
}
.product-mega-featured-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.product-mega-featured-badge {
  position: absolute;
  top: 14px;
  right: 14px;
  font-size: 1.5rem;
  color: rgba(255, 255, 255, 0.9);
}
.product-mega-featured-card {
  position: absolute;
  inset: 16% 8%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-align: center;
  padding: 14px 16px;
  background: var(--color-bg-alt);
  border: 1.5px solid var(--color-ink);
  border-radius: var(--radius-sm);
}
.product-mega-featured-card-title { font-family: "Tomato Grotesk Medium", Arial, sans-serif; font-size: 1.375rem; font-weight: 500; color: var(--color-ink); }
.product-mega-featured-card-desc { font-size: 0.75rem; line-height: 1.35; color: var(--color-ink-soft); }
.product-mega-featured-heading { margin-top: 18px; margin-bottom: 0; font-size: 1rem; }
.product-mega-featured-copy { margin-top: 4px; font-size: 0.8125rem; line-height: 1.5; color: var(--color-ink-soft); }

@media (max-width: 1023px) {
  .product-mega-menu { display: none; }
}

/* ---------- Resources dropdown ---------- */
/* Same component as the Product mega menu above — #resourcesDropdown is
   itself a second .product-mega-menu, reusing every one of its classes
   (card, category rows, detail panel, hover-swap JS in js/main.js)
   verbatim — just a 2-column layout (categories + detail) instead of 3,
   since Resources only has two destinations and no third column's worth
   of real "Featured" content yet. Each detail panel carries its own
   supporting image instead — see .product-mega-detail-media below. */
.product-mega-menu-inner--2col { grid-template-columns: 260px 1fr; }
/* No third column follows in this layout, so the detail column shouldn't
   carry the 3-col divider .product-mega-col--detail draws for the
   featured column after it — it'd read as a stray line against the
   card's own right edge/border. */
.product-mega-menu-inner--2col .product-mega-col--detail { border-right: none; }
/* Text left, image on the far right of the same row — not stacked — so the
   image reads as the panel's one clickable link sitting beside its own
   description, matching linktr.ee's Learn menu reference. Scoped to this
   dropdown's own detail panels only: Product's still stack multiple
   .product-mega-detail-group items in a column, no image involved. */
#resourcesDropdown .product-mega-detail-panel { flex-direction: row; align-items: flex-start; }
#resourcesDropdown .product-mega-detail-group { flex: 1; }
.product-mega-detail-media {
  display: block;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  max-width: 320px;
  aspect-ratio: 16 / 10;
}
.product-mega-detail-media img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
/* The "How to use Quacko" panel's image is a real app screenshot (1600x380
   — a short wide strip, not this box's 16:10), so a centered crop lands on
   an arbitrary sliver of mid-page text. Anchor to the top-left corner
   instead, where the sidebar rail and page title actually read as "this is
   the app" at a glance. */
.product-mega-detail-media--top-left img { object-position: left top; }

/* Full-screen takeover, not the old floating dropdown card — see the HTML
   comment above #mobileNav. Sits under .site-header (z-index 100) so the
   toggle button itself stays visible/clickable on top of it while open. */
.nav-mobile {
  position: fixed;
  inset: 0;
  z-index: 90;
  visibility: hidden;
  pointer-events: none;
  /* visibility:hidden isn't itself animatable — without a delay it snaps
     the instant .is-open is removed, cutting the color-layer wipe and
     panel fade below off before they get a single frame to play. Hold it
     visible for as long as the slowest closing piece takes (layer--1: 90ms
     delay + 500ms transition = 590ms) so the reverse wipe is actually seen
     before the panel leaves the render tree. */
  transition: visibility 0s linear 590ms;
}
.nav-mobile.is-open { visibility: visible; pointer-events: auto; transition-delay: 0s; }

.nav-mobile-layers { position: absolute; inset: 0; overflow: hidden; }
.nav-mobile-layer {
  position: absolute;
  inset: 0;
  transform: translateX(100%);
  transition: transform 500ms cubic-bezier(.22, 1, .36, 1);
}
/* Resting (closing) order: the top layer (2, solid brand orange) retreats
   first, the tint layer behind it (1) lingers ~90ms longer — reverse of
   the opening stagger below, so the wipe reads the same both directions. */
.nav-mobile-layer--1 { background: var(--color-brand-tint); transition-delay: 90ms; }
.nav-mobile-layer--2 { background: var(--color-brand); transition-delay: 0ms; }
.nav-mobile.is-open .nav-mobile-layer { transform: translateX(0%); }
.nav-mobile.is-open .nav-mobile-layer--1 { transition-delay: 0ms; }
.nav-mobile.is-open .nav-mobile-layer--2 { transition-delay: 90ms; }

.nav-mobile-panel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 32px;
  padding: 30px;
  /* Top-aligned below the floating header pill rather than vertically
     centered — the Product accordion's category/detail views run much
     taller than the 4 flat links, so centering would push content (the
     "Product" row itself included) above the fold once it's expanded. */
  padding-top: calc(var(--header-h) + 24px);
  overflow-y: auto;
  background: var(--color-bg);
  opacity: 0;
  transition: opacity 220ms ease;
}
.nav-mobile.is-open .nav-mobile-panel {
  opacity: 1;
  /* Only fade the dark panel in once the color layers have swept fully
     across (500ms transition + the 90ms extra delay above). */
  transition-delay: 340ms;
}

.nav-mobile-links { display: flex; flex-direction: column; }
/* Drilled into a detail panel (main.js: enterDetailMode) reads as its own
   dedicated sub-page, not a nested accordion row — so "Product" itself,
   the flat Use Cases/Resources/Pricing links below it, and the bottom
   Log in/Book Demo/Try For Free actions all get out of the way while one
   is open. Scoped from .nav-mobile-panel (not .nav-mobile-links) since
   .nav-mobile-actions is a sibling of .nav-mobile-links, not inside it.
   "> a" only reaches the three flat links (see the comment above .nav-
   mobile-links > a for why the accordion's own nested <a> links can't
   match this anyway — they're several levels deep, not direct children). */
.nav-mobile-panel.is-drilled-in .nav-mobile-links > a,
.nav-mobile-panel.is-drilled-in .nav-mobile-accordion-trigger,
.nav-mobile-panel.is-drilled-in .nav-mobile-actions { display: none; }
/* Direct-child combinator matters here: the Product accordion nests its
   own <a> links several levels deep (category rows are buttons, but the
   detail items and featured card are real anchors) for tap-to-navigate,
   and they must NOT inherit the top-level stagger's opacity:0 initial
   state or delayed keyframe below — those are scoped to the four
   top-level rows only (now: this accordion + Use Cases/Resources/Pricing). */
.nav-mobile-links > a {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 16px 4px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 1.75rem;
  font-weight: 600;
  color: var(--color-ink);
  border-bottom: 1px solid var(--color-border);
  opacity: 0;
  transform: translateY(24px);
}
.nav-mobile-link-index { font-size: 0.875rem; font-weight: 600; color: var(--color-brand); }
.nav-mobile-link-label { flex: 1; }

/* Per-link stagger on open — same beat/technique as the mega menu's
   is-animating-in cards, replayed each time via the reflow trick in
   main.js. Held open by the animation's `both` fill-mode; harmless while
   closed since .nav-mobile itself is hidden then. */
.nav-mobile.is-animating-in .nav-mobile-links > a {
  animation: navMobileItemIn 480ms cubic-bezier(.16, 1, .3, 1) both;
}
.nav-mobile.is-animating-in .nav-mobile-links > a:nth-child(1) { animation-delay: 380ms; }
.nav-mobile.is-animating-in .nav-mobile-links > a:nth-child(2) { animation-delay: 440ms; }
.nav-mobile.is-animating-in .nav-mobile-links > a:nth-child(3) { animation-delay: 500ms; }
.nav-mobile.is-animating-in .nav-mobile-links > a:nth-child(4) { animation-delay: 560ms; }
@keyframes navMobileItemIn {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes navMobileCategoryIn {
  from { opacity: 0; transform: translateX(-20px); }
  to { opacity: 1; transform: translateX(0); }
}

/* Product accordion — mirrors the desktop header's Product mega menu
   (same 6 categories/detail items, sourced from the same markup, see
   js/main.js) so mobile carries the identical IA instead of a dead-end
   link. .nav-mobile-accordion sits where the old "Product" <a> used to
   be (first child of .nav-mobile-links), so it picks up the stagger
   entrance in its own right rather than through the `a` selectors above. */
.nav-mobile-accordion { opacity: 0; transform: translateY(24px); }
.nav-mobile.is-animating-in .nav-mobile-accordion {
  animation: navMobileItemIn 480ms cubic-bezier(.16, 1, .3, 1) both;
  animation-delay: 380ms;
}
/* Resources reuses this same accordion component for its own 3rd-row spot
   (Product / Use Cases / Resources / Pricing) — the rule above hardcodes
   Product's fixed first-row 380ms, so this second instance overrides back
   to the 500ms an `<a>` at position 3 gets from the nth-child rules above,
   keeping the four rows staggering in sequence instead of Resources
   popping in alongside Product. */
.nav-mobile.is-animating-in .nav-mobile-accordion--resources { animation-delay: 500ms; }
/* Plain link rows inside the Resources panel — simpler than the Product
   accordion's icon/title/desc detail items since there's no metadata to
   show, just two flat destinations. */
.nav-mobile-accordion-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 4px;
  border-radius: var(--radius-md);
  color: var(--color-ink);
  transition: background 160ms ease;
}
.nav-mobile-accordion-link:hover,
.nav-mobile-accordion-link:focus-visible { background: var(--color-bg-alt); }
.nav-mobile-accordion-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  width: 100%;
  padding: 16px 4px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 1.75rem;
  font-weight: 600;
  color: var(--color-ink);
  border: none;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  cursor: pointer;
  text-align: left;
  transition: border-color 200ms ease;
}
/* While open, the divider belongs at the bottom of the opened content
   (after the last category row), not right under the "Product" label —
   it reads as a stray line sitting mid-accordion otherwise. Move it down
   to .nav-mobile-accordion-panel-inner, scoped to the expanded state so
   it doesn't linger as a visible 1px line while the panel is collapsed
   to 0 height (a border renders regardless of box height). */
.nav-mobile-accordion-trigger[aria-expanded="true"] { border-bottom-color: transparent; }
.nav-mobile-accordion-trigger[aria-expanded="true"] + .nav-mobile-accordion-panel .nav-mobile-accordion-panel-inner {
  border-bottom: 1px solid var(--color-border);
}
/* ...but not while drilled into a detail panel (Search/Connect/etc) — that
   view ends on its own content (e.g. the Featured card), not the category
   list, so the divider has nothing to separate and just reads as a stray
   trailing line under it. Needs to out-specificity the rule above, which
   is why the trigger/panel chain is repeated here instead of just keying
   off .is-drilled-in. */
.nav-mobile-panel.is-drilled-in .nav-mobile-accordion-trigger[aria-expanded="true"] + .nav-mobile-accordion-panel .nav-mobile-accordion-panel-inner {
  border-bottom-color: transparent;
}
.nav-mobile-accordion-chevron {
  display: inline-flex;
  flex-shrink: 0;
  /* Bigger/bolder than the markup's own 18px/stroke-2 — CSS wins over
     those inline SVG attributes, no HTML edit needed. */
  width: 22px;
  height: 22px;
  color: var(--color-ink-soft);
  /* ArrowUp01Icon (main.js/HTML swap) points up natively, opposite of the
     old down-chevron this replaced — rotate 180° at rest so collapsed
     still reads as "points down, tap to expand", then un-rotate on
     expand so it reads as up again. Same two states as before, just the
     rotation is now inverted to match the new icon's native direction. */
  transform: rotate(180deg);
  transition: transform 260ms ease;
}
.nav-mobile-accordion-chevron svg { width: 100%; height: 100%; display: block; }
.nav-mobile-accordion-chevron svg path { stroke-width: 2; }
.nav-mobile-accordion-trigger[aria-expanded="true"] .nav-mobile-accordion-chevron { transform: rotate(0deg); }
/* Same "grid-template-rows: 0fr -> 1fr" trick as .outreach-accordion-panel
   (see that rule's comment) so the category list animates open/closed
   instead of the old instant hidden-attribute snap — no JS height
   measuring needed. -inner is the overflow:hidden layer that actually
   gets clipped mid-transition; padding lives there instead of on the
   grid track itself so it collapses away too, not just the content. */
.nav-mobile-accordion-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
.nav-mobile-accordion-trigger[aria-expanded="true"] + .nav-mobile-accordion-panel {
  grid-template-rows: 1fr;
}
.nav-mobile-accordion-panel-inner {
  overflow: hidden;
  padding: 4px 0 16px;
}

/* Category list — same row anatomy/behavior as .product-mega-category,
   just full-width and larger touch targets for mobile. */
.nav-mobile-category-list { display: flex; flex-direction: column; }
.nav-mobile-category-list[hidden] { display: none; }
/* Category list <-> detail panel drill (main.js: drillToDetail/
   drillBackToList) — push/pop like iOS Settings instead of the old
   instant hidden-attribute swap. Both views share the same transition;
   JS toggles these transient classes off/on around the hidden-attribute
   flip via a reflow restart, same trick as .nav-mobile's own stagger. */
.nav-mobile-category-list,
.nav-mobile-detail-panel {
  transition: opacity 200ms ease, transform 200ms ease;
}
.nav-mobile-drill-out-left,
.nav-mobile-drill-in-left { opacity: 0; transform: translateX(-16px); }
.nav-mobile-drill-out-right,
.nav-mobile-drill-in-right { opacity: 0; transform: translateX(16px); }
.nav-mobile-category-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  /* 4px horizontal to line up with .nav-mobile-accordion-trigger's own
     4px inset above — was 12px all around, which left the row's icon
     sitting 8px right of the "Product" title it sits under. */
  padding: 12px 4px;
  border: none;
  background: transparent;
  border-radius: var(--radius-md);
  color: var(--color-ink);
  cursor: pointer;
  text-align: left;
  transition: background 160ms ease;
  /* Visible by default so accordion content never disappears if motion is
     interrupted or JavaScript initializes late. The replay class below
     supplies the animated from-state through its keyframes. */
  opacity: 1;
  transform: translateX(0);
}
.nav-mobile-category-row:hover,
.nav-mobile-category-row:focus-visible,
.nav-mobile-category-row:active { background: var(--color-bg-alt); }
/* Per-row stagger, replayed on every reveal (accordion expand, or drilling
   back from a detail panel) via the same reflow-restart trick main.js uses
   for .nav-mobile's own item stagger — see replayCategoryStagger(). */
.nav-mobile-category-list.is-animating-in .nav-mobile-category-row {
  animation: navMobileCategoryIn 420ms cubic-bezier(.16, 1, .3, 1) both;
}
.nav-mobile-category-list.is-animating-in .nav-mobile-category-row:nth-child(1) { animation-delay: 0ms; }
.nav-mobile-category-list.is-animating-in .nav-mobile-category-row:nth-child(2) { animation-delay: 60ms; }
.nav-mobile-category-list.is-animating-in .nav-mobile-category-row:nth-child(3) { animation-delay: 120ms; }
.nav-mobile-category-list.is-animating-in .nav-mobile-category-row:nth-child(4) { animation-delay: 180ms; }
.nav-mobile-category-list.is-animating-in .nav-mobile-category-row:nth-child(5) { animation-delay: 240ms; }
.nav-mobile-category-list.is-animating-in .nav-mobile-category-row:nth-child(6) { animation-delay: 300ms; }
.nav-mobile-category-icon { display: inline-flex; flex-shrink: 0; color: var(--color-ink-soft); }
.nav-mobile-accordion--resources .nav-mobile-category-icon { color: #000; }
.nav-mobile-category-icon img[src*="about-quacko-menu.svg"] {
  display: block;
  width: 20px;
  height: 20px;
  opacity: .72;
}
.nav-mobile-category-label { flex: 1; font-size: 1.1875rem; font-weight: 600; }
/* Same "bigger/bolder via CSS, not the inline SVG attributes" approach as
   .nav-mobile-accordion-chevron above. */
.nav-mobile-category-chevron {
  display: inline-flex;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  color: var(--color-ink-soft);
}
.nav-mobile-category-chevron svg { width: 100%; height: 100%; display: block; }
.nav-mobile-category-chevron svg path { stroke-width: 2; }

/* Detail drill-in — tapping a category swaps .nav-mobile-category-list
   out for the matching .nav-mobile-detail-panel (main.js), same
   category -> detail relationship as the desktop mega menu but as a
   full view swap instead of a side-by-side column (no room for that on
   mobile), plus a back row to return to the category list. */
.nav-mobile-detail-panel { display: flex; flex-direction: column; }
.nav-mobile-detail-panel[hidden] { display: none; }
/* Reads as this detail view's own page title now, not a small "back"
   affordance — same spec as .nav-mobile-accordion-trigger ("Product")
   above so drilling into e.g. "Search" feels like arriving on its own
   dedicated screen rather than a sub-row still nested under Product
   (which .is-drilled-in above hides for exactly that reason). */
.nav-mobile-detail-back {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px 4px;
  margin-bottom: 0;
  border: none;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-ink);
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 1.75rem;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
}
/* Search has a longer four-link detail list and its heading should flow
   directly into those links on tablet/mobile, without an extra rule. */
.nav-mobile-detail-panel[data-mobile-detail="search"] .nav-mobile-detail-back {
  border-bottom: 0;
}
.nav-mobile-panel.is-drilled-in .nav-mobile-accordion-panel-inner {
  border-bottom: 0;
}
.nav-mobile-detail-back-chevron {
  display: inline-flex;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  color: var(--color-ink-soft);
}
.nav-mobile-detail-back-chevron svg { width: 100%; height: 100%; display: block; }
.nav-mobile-detail-back-chevron svg path { stroke-width: 2; }
.nav-mobile-detail-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 12px;
  border-radius: var(--radius-md);
  color: var(--color-ink);
  /* Rest state for the slide-in-from-left stagger below — same technique
     as .nav-mobile-category-row (see that rule's own comment). */
  opacity: 0;
  transform: translateX(-20px);
}
a.nav-mobile-detail-item--link:hover,
a.nav-mobile-detail-item--link:focus-visible { background: var(--color-bg-alt); }
.nav-mobile-detail-item-text { display: flex; flex-direction: column; gap: 3px; }
/* Matches .nav-mobile-category-label ("Search" etc. one level up) so a
   detail item's own title reads at the same size as the row that led here. */
.nav-mobile-detail-item-title { font-size: 1.1875rem; font-weight: 600; }
.nav-mobile-detail-item-desc { font-size: 0.875rem; color: var(--color-ink-soft); line-height: 1.4; }
/* Bigger than the markup's own 16px — CSS wins over that inline SVG
   attribute, same trick as .nav-mobile-category-chevron. */
.nav-mobile-detail-item-arrow {
  display: inline-flex;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  color: var(--color-ink-soft);
  margin-top: 2px;
}
.nav-mobile-detail-item-arrow svg { width: 100%; height: 100%; display: block; }
/* Per-item stagger, replayed every time this panel is drilled into — see
   enterDetailMode/replayStagger in main.js. :nth-of-type (not nth-child)
   since .nav-mobile-detail-back is a <button> and .nav-mobile-featured a
   <div> ahead of/after these <a> siblings — nth-of-type counts only among
   same-tag siblings, so item 1 is still delay 0 regardless. */
.nav-mobile-detail-panel.is-animating-in .nav-mobile-detail-item {
  animation: navMobileCategoryIn 420ms cubic-bezier(.16, 1, .3, 1) both;
}
.nav-mobile-detail-panel.is-animating-in .nav-mobile-detail-item:nth-of-type(1) { animation-delay: 0ms; }
.nav-mobile-detail-panel.is-animating-in .nav-mobile-detail-item:nth-of-type(2) { animation-delay: 60ms; }
.nav-mobile-detail-panel.is-animating-in .nav-mobile-detail-item:nth-of-type(3) { animation-delay: 120ms; }
.nav-mobile-detail-panel.is-animating-in .nav-mobile-detail-item:nth-of-type(4) { animation-delay: 180ms; }

.nav-mobile-featured {
  margin: 12px 12px 0;
  padding-top: 16px;
  border-top: 1px solid var(--color-border);
}
.nav-mobile-featured-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-ink-soft);
  margin-bottom: 10px;
}
.nav-mobile-featured-media {
  display: block;
  width: 100%;
  overflow: hidden;
  aspect-ratio: 262 / 158;
  border-radius: var(--radius-md);
}
.nav-mobile-featured-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.nav-mobile-featured-heading {
  margin: 12px 0 0;
  font-size: 1rem;
  line-height: 1.25;
}
.nav-mobile-featured-copy {
  margin: 4px 0 0;
  color: var(--color-ink-soft);
  font-size: 0.8125rem;
  line-height: 1.5;
}
.nav-mobile-featured-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border-radius: var(--radius-md);
  background: var(--color-bg-alt);
  color: var(--color-ink);
}
.nav-mobile-featured-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: var(--color-brand-tint);
  color: var(--color-brand);
  font-size: 1rem;
  flex-shrink: 0;
}
.nav-mobile-featured-card-text { display: flex; flex-direction: column; gap: 2px; }
.nav-mobile-featured-card-title { font-size: 0.9375rem; font-weight: 700; }
.nav-mobile-featured-card-desc { font-size: 0.8125rem; color: var(--color-ink-soft); line-height: 1.4; }

.nav-mobile-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* Docks to the bottom of .nav-mobile-panel's flex column regardless of
     how tall the links block above it is (4 flat links, or the Product
     accordion expanded) — only collapses back to a normal top-margin
     gap once content actually overflows into scroll. */
  margin-top: auto;
  opacity: 0;
  transform: translateY(24px);
}
.nav-mobile.is-animating-in .nav-mobile-actions {
  animation: navMobileItemIn 480ms cubic-bezier(.16, 1, .3, 1) both;
  animation-delay: 620ms;
}
.nav-mobile-actions .btn { justify-content: center; }
@media (prefers-reduced-motion: reduce) {
  .nav-mobile,
  .nav-mobile-layer,
  .nav-mobile-panel,
  .nav-mobile-accordion-panel,
  .nav-mobile-category-list,
  .nav-mobile-detail-panel { transition-duration: 1ms; transition-delay: 0ms; }
  .nav-mobile-links > a,
  .nav-mobile-accordion,
  .nav-mobile-actions,
  .nav-mobile-category-row,
  .nav-mobile-detail-item { animation: none !important; opacity: 1; transform: none; }
  .nav-mobile-accordion-chevron { transition-duration: 1ms; }
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  /* pull the fill up behind the fixed header (body's 84px offset) so the
     now-global dot background reaches the very top of the page, not just
     below the nav pill; padding-top restores the content to its original
     position. The background itself now lives on body, not here. */
  margin-top: -126px;
  padding-top: calc(126px + 72px);
  padding-bottom: 64px;
  /* Real bug on narrow phones: .hero-card--2's prop image (the headphones,
     rotated 32.61deg) is deliberately positioned to hang past its own
     card's edge into the surrounding .hero-visual gap — by design, same
     idea as .hero-visual-pattern's swirl bleed — but the rotated bounding
     box happened to clear the *page's* right edge too, by about a pixel,
     at narrow mobile widths specifically. Per the overflow-x:hidden note
     on <html> above (search "reported on mobile FAQ"): even a sub-pixel
     unclipped overflow like this makes the whole page wider than the
     viewport, which is what actually surfaces as a visible sliver of
     plain page background past this section's edge, not just an inert
     off-screen bleed. Clipped right at the section's own boundary (not
     tighter, e.g. not on .hero-visual) so the intentional overhang inside
     .hero is completely unaffected — only the ~1px that escaped .hero
     itself gets trimmed. */
  overflow-x: hidden;
}
.hero-inner {
  display: flex;
  flex-direction: column;
  gap: 32px;
}
/* no max-width here — .hero-sub already caps its own line length (560px)
   for readability; the column itself should be free to fill whatever
   the flex split (.hero-copy/.hero-visual below) gives it, which is what
   the hero title needs room to size up. */
.hero-copy h1 { margin-top: 14px; }
/* Below 1024px, initHeroMobileReorder (main.js) moves #heroVisual out of
   its normal spot at the end of .hero-inner to sit inside .hero-copy,
   between the title and .hero-sub — title -> image -> sub copy/buttons.
   This selector only ever matches in that nested state, so it's naturally
   inert once the same JS moves it back out at 1024px+; a plain CSS
   grid/order approach can't do this same reorder without spanning
   .hero-visual across two grid rows sized off the (short) text, which
   blows the row heights out to the image's own full height instead. */
.hero-copy .hero-visual { margin-block: 28px; }
.hero-sub {
  margin-top: 18px;
  font-size: 1.125rem;
  color: var(--color-ink-soft);
  max-width: 720px;
}
/* Mobile/tablet hero text scaled up a step past the site's normal sizes to
   match the bigger h1 from the last pass — sub copy and note bumped by eye
   to stay in proportion. Scoped to .hero-copy so the shared .eyebrow/
   .cta-note classes used elsewhere (Benefits, Testimonials, FAQ, Pricing)
   are untouched. */
@media (max-width: 1023.98px) {
  /* Fluid like the h1 above, not a flat size — a flat 1.21875rem (+50% on
     the site's normal 0.8125rem, tuned/confirmed at 390px) still wrapped
     "MARKETING FOR TIKTOK SHOP" to 2 lines on a narrow ~320px phone. 5vw
     lands exactly on that validated 1.21875rem right at 390px (5vw*390px
     = 19.5px) and exactly on the 1rem floor at 320px (5vw*320px = 16px),
     so it keeps the one-line eyebrow across the phones in between too. */
  .hero-copy .eyebrow { font-size: clamp(1rem, 5vw, 1.21875rem); }
  .hero-sub { font-size: 1.25rem; }
  .hero-copy .cta-note { font-size: 0.9375rem; }
  /* On mobile the reordered .hero-visual (see initHeroMobileReorder above)
     sits directly between the title and .hero-sub, and the default 28px
     margin-block read too tight on both sides of the banner image — per
     the user's request, opened up above (title→image) and below
     (image→copy) alike. Bottom pushed another 20px past the top per a
     follow-up request, so top/bottom are no longer equal — split instead
     of margin-block. */
  .hero-copy .hero-visual { margin-top: 44px; margin-bottom: 64px; }
}
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 28px;
}
/* Match the Product Highlight "Learn More" pill's standard size (gap 24px,
   padding 12px 22px, font-size 18px/600) documented in DESIGN_GUIDELINES.md
   — colors stay btn-hero-primary/secondary's own, only sizing is shared.
   Higher specificity than .btn-lg so no need to touch the markup. */
.hero-actions .btn {
  gap: 24px;
  padding: 12px 22px;
  font-size: 1.125rem;
}

/* iPad — at their own content width "Try For Free"/"Book Demo" land
   visibly mismatched (longer label, wider pill), same problem the footer's
   identical button pair solves with a 2-column grid (see .footer-actions-row)
   instead of a plain flex row. Reuse that here: an auto-sized 1fr/1fr grid
   sizes both tracks to the wider button's own max-content, no hardcoded
   width needed. Mobile already gets equal width its own way (flex:1 1 0
   below) and desktop keeps the natural mismatched flex-wrap look. */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .hero-actions {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    align-items: center;
  }
}

@media (max-width: 639.98px) {
  /* Desktop's 72px net top padding (126px header offset + 72px) reads as
     a huge gap under the compact mobile pill — tighten to 32px so the
     eyebrow sits close under the header like the rest of the page's
     mobile rhythm. */
  .hero { padding-top: calc(126px + 32px); }
  .hero-actions {
    flex-wrap: nowrap;
  }
  .hero-actions .btn {
    flex: 1 1 0;
    min-width: 0;
    padding-inline: 0;
    gap: 8px;
    font-size: 1.0625rem;
    justify-content: center;
  }
  .ai-rows .ai-eyebrow {
    font-size: clamp(2.25rem, 11vw, 2.75rem);
  }
  .ai-rows .ai-copy-text {
    font-size: 1.125rem;
    line-height: 1.45;
  }
  .ai-rows .ai-visual {
    margin-inline: auto;
  }
}

/* perspective here (not on .hero-card-stage itself, which already has its
   own 1400px perspective for the card-flip's rotateY) is what gives the
   pointer-tilt handler's rotateX/rotateY on .hero-card-stage (main.js
   [data-tilt], set on .hero-visual below) an actual 3D vanishing point —
   same division of roles as .cta-visual/.cta-illustration. */
.hero-visual { position: relative; display: flex; justify-content: center; perspective: 900px; }
/* Decorative swirl (Figma node 3281:107372) sitting behind/below the
   flipping card stage — a static background flourish, not animated
   itself; z-index keeps it under .hero-card-stage (which gets its own
   z-index just below), and pointer-events:none so it never intercepts
   clicks meant for the cards in front. Figma's own stroke color
   (#737373) reads fine as-is against this section's light dot-grid
   ground, just dialed back with opacity so it stays a background detail
   instead of competing with the cards. */
.hero-visual-pattern {
  position: absolute;
  /* Centered directly behind the card (both axes) per feedback/reference
     — mostly hidden behind the opaque card, with the ellipses' left/right
     lobes the only part actually showing, rather than sitting below it. */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 190%;
  /* Flat 580px cap, with no viewport term, meant nothing scaled it back
     down on narrow screens — .hero-visual on mobile (where JS reorders it
     into a single ~358px-wide column, see initHeroMobileReorder) sits
     centered at a flat 580px regardless, overflowing the actual page by
     ~95px on each side. Real bug, not just an invisible clipped decal:
     html's overflow-x:hidden should swallow that silently, but on-device
     it was reachable via a horizontal drag/rubber-band, revealing the
     plain page background past the section's own right edge — exactly
     what looked like a stray "gap" past the container. The 92vw term
     keeps every viewport at least an 8vw margin narrower than the page
     itself, so the pattern can never actually push scrollWidth past the
     viewport; resolves to the unchanged 580px on anything wider than
     ~630px, so desktop/tablet are untouched. */
  max-width: min(580px, 92vw);
  z-index: 0;
  opacity: 0.3;
  pointer-events: none;
}

/* ---- Hero visual: flipping product-card carousel (Figma "Section 2",
   node 3265:107269) ----
   Replaced the old single static hero-visual.png + pointer-tilt collage
   with this — three UGC-style phone cards (Moisturizing Cream / Fasion
   Sneaker[sic, Figma's own spelling] / Pot+Gloves) sit stacked in the
   exact same spot and take turns facing the viewer via a continuous
   Y-axis flip — card 1 flips away as card 2 flips in, then card 2 flips
   away as card 3 flips in, then card 3 flips back to card 1, looping
   forever. All three run the same @keyframes, just phased a third of the
   cycle apart (negative animation-delay) — the classic "N items, one
   shared timeline" trick, no JS state machine needed.
   The pointer-tilt from that old collage came back on request, layered on
   TOP of the flip rather than replacing it: main.js's [data-tilt] handler
   (the same vanilla TiltedCard port .cta-illustration already uses) is
   set on .hero-visual, targeting THIS element (added to that handler's
   selector list) — see the rotateX/rotateY/scale below. Since the flip's
   own animation lives on each .hero-card CHILD, not on .hero-card-stage
   itself, the two transforms never touch: the pointer tilts the whole
   stage as one rigid object (perspective for that lives on .hero-visual,
   not here — see its own comment) while the cards keep flipping inside
   it, completely undisturbed, the same way tilting a snow globe doesn't
   stop whatever's moving inside it.

   Every element below is positioned as a percentage of one shared design
   canvas (599.57 × 1029, card 1's own bounding box in Figma — the widest
   of the three) so the whole thing scales fluidly with .hero-card-stage's
   own rendered width instead of needing fixed px + a transform: scale()
   hack. Figment images (photos, floating prop icons, reaction icons) are
   the real exported Figma assets in assets/img/hero-cards/, not redrawn. */
.hero-card-stage {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 320px;
  aspect-ratio: 599.57 / 1029;
  perspective: 1400px;
  transform: rotateX(var(--tilt-rx, 0deg)) rotateY(var(--tilt-ry, 0deg)) scale(var(--tilt-scale, 1));
  transition: transform 0.15s ease-out;
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .hero-card-stage { transition: none; }
}
/* On a real phone width:100% still fills nearly the whole column, so the
   flipping card reads oversized against the copy around it. Capped
   smaller here per the user's request; 192px (a 20% cut from an earlier
   240px step) read too small once this rule actually took effect, so
   settled here instead. Placed after the base rule above (not inside the
   earlier @media (max-width: 639.98px) block near .hero-actions) so it
   actually wins the cascade — same selector, same specificity, so source
   order decides, and the earlier block lost to the unconditional rule
   above it. */
@media (max-width: 639.98px) {
  .hero-card-stage { max-width: 258px; }
}
/* Figma's "leaning back into space" look was a 2D fake — the same
   rotate/skewX/scaleY squash pasted onto every leaf layer, because Figma
   has no real 3D to rotate into. Pasting that fake tilt onto every child
   AND spinning the whole card for real with rotateY (the flip) was the
   bug: two different, physically-inconsistent ways of faking depth
   stacked on each other, so the card visibly warped instead of turning
   like a rigid object. Fixed by throwing the skew hack out and doing the
   tilt as real rotation instead — rotateZ for the in-plane lean, rotateX
   for the "tipped back" depth — composed on .hero-card itself, the same
   element that does the flip, so the whole card is one single rigid body
   in one consistent 3D space. Every child (phone, product tag, icons,
   props) inherits this for free through ordinary nested rendering under
   transform-style: preserve-3d — nothing below needs its own copy of it,
   unlike the old skew version. */
.hero-card {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  animation: heroCardFlip 15s linear infinite;
  will-change: transform;
}
.hero-card--1 { animation-delay: 0s; }
.hero-card--2 { animation-delay: -12s; }
.hero-card--3 { animation-delay: -9s; }
/* 4th card (Skateboard, Figma node 3319:109040) added after --3 in the
   sequence per feedback, then a 5th (Music Player, node 3320:109067)
   added after --4 in a follow-up — the loop went 9s/3-way → 12s/4-way →
   15s/5-way, each step keeping every card's own visible arc at exactly 3s
   (see this rule's own comment right below, still accurate: it's the
   invariant that matters, just for 5 slots now instead of 3).
   The delay→display-order mapping got mixed up on the first 12s/4-way
   pass (shipped --3 at -3s/--2 at -9s, which — worked through the actual
   phase math below — plays --2 second and --3 fourth, not the intended
   --1→--3→--4→--2 order) and wasn't caught until visual feedback. Fixed
   here alongside the 5th-card retiming: for N equal slots on a P-second
   lap, the card that should become visible at wall-clock time t needs
   animation-delay = -((P - t) mod P) — i.e. delay magnitude counts
   backward from the END of the lap, not forward from the start, since a
   negative delay pre-advances a card's own clock.
   Display order went through two revisions: first --1 → --3 → --4 → --5
   → --2 (inserting the two new cards right after --3, leaving --2 at the
   end), then per feedback --2 moved up to sit right after --1 instead —
   plain sequential order --1 → --2 → --3 → --4 → --5, one slot each at
   t=0/3/6/9/12 of the 15s lap. That gives: --1 → 0s, --2 → -12s,
   --3 → -9s, --4 → -6s, --5 → -3s. */
.hero-card--4 { animation-delay: -6s; }
.hero-card--5 { animation-delay: -3s; }
/* Timing solved so the (now 5, originally 3) staggered copies hand off
   with zero dead gap.
   The invariant that actually matters for that isn't any particular
   "hold / flip / hidden" split itself — it's that each card's VISIBLE
   arc (the front-facing hold plus the two slivers of flip-out/flip-in
   that fall before/after the 90° backface threshold) totals exactly
   15s÷5 = 3s, contiguous, once per 15s lap (originally 9s÷3 — see
   .hero-card--4's own comment above for why/how this widened, twice). As
   long as that holds, the five staggered copies always tile the lap with
   no gap and no overlap, regardless of how the remaining 12s is split
   between "sitting flat" and "mid-flip".
   Only the ARRIVAL leg is shaped — the leg that ends with the card facing
   front; departure is a single plain 180° sweep, unshaped, since nobody's
   watching how a card leaves. Every version that applied a fast/slow
   split directly to the ARRIVAL's full 180° (150/30, 120/60, 100/80 —
   all tried) read as no variation at all, confirmed both by a real
   screen recording and by pulling actual frames: with the visible arc
   only the LAST 90° of the 180°, any "fast" leg allocated ≥90° spends
   almost all of itself in the invisible first 90°, so the eye never
   catches a fast moment to contrast the slow one against — it only ever
   sees the slow leg's own sin-shaped taper, which alone just reads as
   "the card arrived," not "sped up then slowed down." Perceiving
   deceleration needs to SEE the deceleration happen relative to
   something faster, not just experience one continuously-decaying rate.
   Fix: split the 180° into three legs instead of two — an invisible
   "coast" (the first 90°, arbitrary pace, nobody sees it) followed by a
   fast leg and a slow leg that are BOTH entirely inside the visible last
   90°, so both are actually on screen for the eye to compare. Degrees:
   coast 90° / fast 30° / slow 60° — the 60° slow-leg span isn't
   arbitrary, it's the same 60°-from-front floor established earlier (a
   3D rotateY's apparent width changes at a rate proportional to
   sin(angle-from-front), which is why a slow leg confined to under
   ~60°-from-front reads as "paused" no matter its duration — see the
   surrounding comment history).
   First retiming pass chased a reference recording of a snappier
   card-flip carousel (a different site, used purely as a speed
   reference) too literally: that reference's whole visible changeover
   measured ~200ms frame-by-frame, so fast/slow got scaled down ~4x along
   with everything else (fast 100ms→30ms, slow 400ms→90ms). That broke
   exactly the thing this section's own history already warned about —
   with only 120ms of visible arrival total, split 30/90, the eye no
   longer had enough absolute time to register the RATE CHANGE between
   the two legs; it just read as one quick blur to full size, i.e. "an
   instant 180° snap," the same failure mode as the direct-180°-split
   attempts above, just arrived at from the other direction (too little
   time instead of too much of it wasted on the invisible half). Confirmed
   by the same complaint that originally motivated this file's fast/slow
   split in the first place.
   Fix: only shrink the parts that were never carrying the fast/slow
   signal. The 100ms/400ms fast/slow split itself is back to its
   original, proven-legible values, untouched. What's actually shorter
   than the very first version of this component: flip-out 700ms→160ms
   (departure was always unshaped and unwatched — see above — so it can
   be quick with no perceptual cost) and coast 200ms→100ms (fully
   invisible, so shortening it only trims dead air before the reveal
   starts, not anything the eye is judging speed by).
   Visible-arc math (still needs to total exactly 3s, see below):
     visible flip-out sliver: single 180° sweep, half of it (0°→90°) is
       visible → (90°/180°)×160ms = 80ms
     visible flip-in sliver: 100ms + 400ms = 500ms (fast + slow, unchanged)
     hold: 3s − 80ms − 500ms = 2.42s
   Cycle (originally, 9s/3-way): hold 2.42s (26.889%) + flip 0.16s
   (1.778%) + hidden 5.82s (64.667%) + flip 0.6s (6.667%, coast 100ms +
   fast 100ms + slow 400ms) = 9s.
   Extended to 12s/4-way (4th card added after --3), then again to
   15s/5-way (5th card added after --4): the 3s-per-card visible budget
   above is untouched at each step — hold/flip-out/flip-in keep their
   exact same absolute durations, every extra 3s goes entirely into the
   invisible hidden-flat middle (5.82s → 8.82s → 11.82s), since
   stretching a constant-value plateau changes nothing on screen. Every
   keyframe % below is just that same math re-expressed as a fraction of
   the current total (12s, then 15s) — front-anchored stops (0%/20.000%
   etc.) scale by ×(old-total/new-total) each time, end-anchored stops
   (94.834%/95.667%/98.167%/100% etc.) preserve their absolute distance
   FROM the end instead, since it's the END of the lap they're timed
   against, not the start. Touching any of these without redoing that
   same arithmetic reopens the gap.
   Because fast+slow (the last 500ms of the lap) are unchanged in
   absolute time, their percentages (measured from the END of the lap)
   are just that fixed 500ms re-expressed at whatever the current total
   is — currently 96.534%/97.334%-ish mark fast-start/fast-end at 15s
   (was 95.667%/96.667% at 12s, 94.444%/95.556% at 9s), same underlying
   instants, regardless of how hold/hidden/coast earlier in the lap were
   resized.
   heroProductPop/heroIconPop below rely on that: only their "settled
   after flip-out" cutoff (31.667%→26.889%... see each's own comment)
   needed touching, not their fast-leg sync points.
   skewX/skewY, rotateZ/rotateX (the resting lean) are held constant
   across every stop and rotateY (the flip) is the only thing that
   animates — rotateY is listed AFTER the lean functions in each stop's
   transform, so it spins around the card's own already-tilted, already-
   sheared local vertical axis, not the flat-on world Y axis — a card
   leaning in space turning on its own spine, not a flat card spinning in
   front of a tilted backdrop. skewX(-2deg)/skewY(16deg) sit first (i.e.
   apply to the flattest, most local frame) so they establish part of
   that same rigid resting shape alongside rotateZ/rotateX, rather than
   being pasted onto child leaves independently of the parent's own spin
   — see the .hero-card comment above for why that old per-leaf-skew +
   real-rotateY combination warped instead of turning like a rigid body;
   a single skew on the same element that owns the spin, composed before
   it, doesn't have that problem. Every rotateY value is negative (0 →
   −180 → −300 → −360) rather than the positive sweep this used to run —
   CSS's rotateY(+) turns counterclockwise seen from above (bird's-eye),
   so negating it is what makes the spin read as clockwise.
   The arrival's two legs are both plain linear (the global animation's
   own default — no per-stop override needed), not eased: an earlier
   cubic-bezier version hit exactly zero velocity right at the 0°/360°
   resting frame, which read as "paused" rather than "slow", verified
   against a real screen recording — a curve that starts/ends at zero
   speed spends its first/last stretch looking stopped, however smoothly
   the math accelerates underneath. Legible two-speed motion needs a
   clearly nonzero constant speed for the slow leg, not an ease fading in
   from stillness. animation-timing-function set ON a stop still governs
   the leg FROM that stop to the next one, in case a curve goes back on
   either leg later — there's just nothing to set right now. */
@keyframes heroCardFlip {
  /* The first 90deg of arrival is behind the hidden back face, so the
     speed change is placed entirely in the visible half of the turn:
     90deg invisible approach in 100ms, 40deg visibly fast in 120ms,
     then the final 50deg visibly settles over 400ms. */
  /* Front-to-back departure: the first 90deg takes 300ms, then the
     remaining 90deg completes in 100ms (400ms total). */
  0%, 15.200% { transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg) rotateY(0deg); }
  17.200% { transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg) rotateY(-90deg); }
  17.867% { transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg) rotateY(-180deg); }
  95.867% { transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg) rotateY(-180deg); }
  96.534% { transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg) rotateY(-270deg); }
  97.334% { transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg) rotateY(-310deg); }
  100% { transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg) rotateY(-360deg); }
}
@media (prefers-reduced-motion: reduce) {
  /* Same "settle on the resting frame" rule as the rest of the site's
     continuous loops (marquee, shop-tile reveals, etc.) — stop spinning,
     show card 1 only, no partial/mid-flip state left on screen. The lean
     itself (skewX/skewY/rotateZ/rotateX) is the resting pose, not motion,
     so it stays — only the rotateY spin is motion and gets removed. Same deal for
     the product tag's own pop-in loop (heroProductPop) — settle it on
     its own resting frame (fully grown, no pending shrink-to-0) instead
     of leaving its independent animation running under a reduced-motion
     ask. */
  .hero-card { animation: none; transform: skewX(-2deg) skewY(16deg) rotateZ(0deg) rotateX(0deg); opacity: 0; }
  .hero-card--1 { opacity: 1; }
  .hero-card-product { animation: none; transform: translateZ(22px) scale(1); }
}

/* Depth (real parallax, not a flat cutout) — the reference composition
   isn't one flat picture, it's several physically separate layers at
   different distances from the viewer (background pattern behind the
   photo, the phone itself, the product tag sitting in front of it, the
   reaction icons floating further out, the prop — headphones/sunglasses/
   veggies — closest of all, clearly detached from the card). Giving every
   layer the exact same rigid rotation (previous pass) reads as one flat
   image because it IS effectively one flat image at that point — nothing
   moves relative to anything else. translateZ pushes each layer to its
   own distance from the camera inside the shared perspective/preserve-3d
   space already set up on .hero-card-stage/.hero-card, so as the card
   rotates, nearer layers swing further and faster than far ones — genuine
   parallax, same depth-layer idea as .cta-illustration's own
   [data-tilt-depth] system elsewhere on this page, just static offsets
   here instead of pointer-driven ones since this loops on its own. Every
   translateZ'd element needs its own backface-visibility: hidden restated
   too — see the note on .hero-card above; that Chromium quirk fires for
   ANY own transform, translateZ included, not just rotation. */
/* Fake phone THICKNESS, not just a thin rect with a hairline border
   pretending to be a bezel. Two earlier attempts tried to fake it as a
   separate "side wall" layer offset behind the phone — a translateZ'd
   plate (hit a genuine Chromium bug: composing translateZ() before
   translate() under a skewed preserve-3d parent flips backface-
   visibility's idea of "front," even at rotateY(0)) and then a hard
   box-shadow — both technically worked but read as a rendering glitch on
   a real screenshot (a thin, slightly-offset dark sliver looks like
   mis-registration, not a deliberate edge), per direct feedback against
   a reference phone-mockup image showing a genuinely chunky, uniform
   case body instead.
   The reference made the actual fix obvious: don't fake a side wall as a
   separate element at all, just add a plain hard-edged box-shadow on the
   phone itself — straight off the actual Figma effect (originally X 12 /
   Y -8, later dialed down to X 6 / Y -3 — see below for why — blur 0 /
   spread 0, same color as the border itself at 100%, not a darker
   shade). Border briefly went 3px→11px to make the phone read as
   chunkier on its own too, but that made the whole bezel too thick — back
   to 3px, with the box-shadow alone carrying the "thickness" impression
   on the top-right corner it's offset toward. Same color as the border is
   what makes it read as "this one shape has thickness" instead of
   "there's a second shape behind this one" — the earlier darker-shade
   sliver looked like a mis-registered duplicate specifically because it
   was a DIFFERENT color from what it was supposedly extending.
   The X12/Y-8 version had its own artifact, spotted on a real screenshot
   of the top-left corner specifically (the corner the shadow moves AWAY
   from, not toward): box-shadow with 0 blur/spread is a plain translated
   copy of the box's own rounded-rect shape, and a rounded corner's own
   arc doesn't stay tangent to a diagonal translation — past a certain
   offset size relative to the border-radius, the copy's arc peeks out
   from behind the original as a visibly SEPARATE, disconnected curve
   (two overlapping circles, not one continuous shape) instead of reading
   as extra thickness on that corner. Dialing the offset down to X6/Y-3
   (half) keeps the peeking sliver small enough relative to the 20px
   radius that its arc stays visually merged with the border's own
   corner instead of splitting into a second visible curve — same
   direction, just less of it. Offset is up-right, not down-right —
   that's the direction in the Figma source, not derived from the card's
   own skewY lean; no need for the two to agree. Zero blur/spread is what
   keeps this a crisp step (a "second layer" reads as thickness) instead
   of a soft drop shadow (which reads as the card floating above the
   page, a different effect entirely). No new layer, no offset math on a
   translateZ'd element, no z-ordering to get wrong — it's paint on
   .hero-card-phone's own box, so it automatically inherits that
   element's transform/backface-visibility and moves/hides with it
   during the flip for free. */
.hero-card-phone {
  position: absolute;
  left: 6.505%;
  top: 12.148%;
  width: 67.048%;
  height: 84.937%;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 3px solid transparent;
  /* Every crop percentage on the children below (photo, bg-pattern) was
     worked out against Figma's true 402×874 content frame. The site-wide
     `*{box-sizing:border-box}` reset (see top of file) would otherwise
     fold this element's own 3px border into that 67.048%/84.937%-of-stage
     box, shrinking what children actually resolve their percentages
     against by 2×3px on each axis — a small but real systematic offset
     (confirmed by measuring computed left/width against the expected
     values: off by exactly the border amount) that was making the photo
     and pixel-pattern land in the wrong place. content-box makes the
     declared width/height the actual content box again, matching what
     the percentages below assume; the border just adds outside it. */
  box-sizing: content-box;
  transform: translateZ(0px);
  backface-visibility: hidden;
}
.hero-card--1 .hero-card-phone { background: #fdb8ca; border-color: #930b00; box-shadow: 6px -3px 0 0 #930b00; }
.hero-card--2 .hero-card-phone { background: #64dcc9; border-color: #0067de; box-shadow: 6px -3px 0 0 #0067de; }
.hero-card--3 .hero-card-phone { background: #cbf484; border-color: #027b91; box-shadow: 6px -3px 0 0 #027b91; }
.hero-card--4 .hero-card-phone { background: #f4dbc2; border-color: #512c00; box-shadow: 6px -3px 0 0 #512c00; }
.hero-card--5 .hero-card-phone { background: #c7b5fb; border-color: #000000; box-shadow: 6px -3px 0 0 #000000; }
/* An earlier pass tried an experimental per-feedback bump here — taller
   than the shared 12.148%/84.937% box above, just for this card
   (top: 7%; height: 90%) — to pull card 3's photo up per a one-off "try
   making this taller" request, back when it was one of only 3 cards.
   Reverted per later feedback: with 5 cards now playing in sequence,
   card 3 sitting alone at a different height/position than the other
   four read as broken, not intentional — back to the shared box so all
   five phones line up. */

/* .hero-card-bg-pattern is a plain <div> (not the <img> directly) with an
   <img> filling it at width/height:100% — two levels, not one. A single
   <img> with top/right/bottom/left set but no explicit width/height does
   NOT stretch to fill that box (CSS replaced-element sizing falls back to
   the image's own intrinsic aspect ratio instead — confirmed by measuring
   computed style, it rendered at its native ~1.22 aspect ratio, running
   into the girl's face on card 1). Adding width/height:100% directly on
   that same element doesn't fix it either — percentage height on an
   absolutely positioned box resolves against the containing block, which
   then over-constrains against the bottom offset and CSS drops bottom
   instead, stretching it to the full card height. A plain (non-replaced)
   <div> with all four insets set doesn't have either problem — it just
   sizes itself to exactly the box they imply — so the fill has to happen
   one level in, same shape Figma's own export used (wrapper div with the
   insets, inner img at size-full) though this got flattened away below
   when the crops were pre-multiplied to one level for an unrelated
   Chromium bug — that trick doesn't apply here, this isn't oversized/
   cropped, just filled. */
.hero-card-bg-pattern { position: absolute; transform: translateZ(-18px); backface-visibility: hidden; }
.hero-card-bg-pattern img { display: block; width: 100%; height: 100%; }
.hero-card--1 .hero-card-bg-pattern { top: 3.32%; right: 4.59%; bottom: 62.55%; left: 4.73%; }
.hero-card--2 .hero-card-bg-pattern { top: 22.31%; right: -15.37%; bottom: 47.6%; left: 20.9%; }
.hero-card--3 .hero-card-bg-pattern { top: 2.52%; right: 6.22%; bottom: 60.17%; left: 6.47%; }
/* Cards 4/5's pattern insets read off Figma's own CSS `inset()` calc
   strings for these vectors (e.g. card 4's was
   `inset(calc(10.3%-2.38px) 50% calc(53.43%+0.21px) calc(-28.86%-4.73px))`
   in top/right/bottom/left order) — the px terms are Figma's own sub-pixel
   correction on top of its percentage, folded in here as one combined
   percentage against this box's true 402x874 (same conversion the other
   three cards' numbers above came from, just done by hand from raw Figma
   node coordinates instead since these two weren't in the original
   figma-to-code pass). */
.hero-card--4 .hero-card-bg-pattern { top: 10.03%; right: 50%; bottom: 53.45%; left: -30.04%; }
.hero-card--5 .hero-card-bg-pattern { top: -3.92%; right: 37.63%; bottom: 61.18%; left: -17.67%; }

/* All five photos: Figma's raw asset export kept coming back blank (cards
   1-3) or a flattened, un-cropped, fully-opaque fill (cards 4/5 — same
   underlying export quirk, just surfacing as "wrong" pixels instead of a
   blank one) for these, so all five are get_screenshot captures instead —
   clean transparent-background portrait cutouts, already close to the
   card's own aspect ratio, so a plain object-fit: cover fill (small
   ~5%/~2% edge crop on cards 1/3, near-exact match on the rest) is all
   they need — no more per-card crop-of-a-crop percentage math to
   hand-derive from Figma's nested fill transform, and none of that math's
   fragility. Cards 4/5 specifically are screenshots of the whole
   402x874 phone-frame node (not just the inner photo layer) — Figma
   auto-clips a contentsOnly screenshot to its nearest overflow:hidden
   ancestor, so this conveniently comes back already zoomed/cropped
   exactly as it appears through the phone frame's own window, matching
   cards 1-3's photos being close-to-drop-in crops too. */
.hero-card-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; max-width: none; transform: translateZ(6px); backface-visibility: hidden; }

.hero-card-prop {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* Floating props (headphones/sunglasses/pepper/basil) inherit the card's
   real 3D tilt like everything else (no transform of their own needed for
   that part), plus their own gentle, continuous orbit drift on top — a
   small independent translate loop around their resting spot within that
   already-tilted plane, echoing the satellite-decoration motion on
   linktr.ee's own marketing hero (the phone stays put, the record/
   headphones/social icons around it keep drifting). Runs independently of
   the card-flip cycle and of the other props' own drift — each prop gets
   its own duration/delay/direction so they don't move in lockstep. This
   translate is its own transform, so — same Chromium quirk as noted on
   .hero-card — it needs its own backface-visibility: hidden restated. */
.hero-card-prop-box {
  position: absolute;
  animation: heroPropOrbit 8s ease-in-out infinite;
  backface-visibility: hidden;
  will-change: transform;
}
.hero-card--1 .hero-card-prop-box { left: 41.865%; top: 0%; width: 58.144%; height: 34.365%; animation-duration: 7.5s; }
.hero-card--2 .hero-card-prop-box { left: 48.702%; top: 0%; width: 66.446%; height: 31.708%; animation-duration: 8.6s; animation-direction: reverse; }
.hero-card--3 .hero-card-prop-box.is-pepper { left: 56.212%; top: 0%; width: 46.868%; height: 26.725%; animation-duration: 9.2s; }
.hero-card--3 .hero-card-prop-box.is-basil { left: 49.65%; top: 30%; width: 70.13%; height: 39.014%; animation-duration: 7s; animation-delay: -3s; animation-direction: reverse; }
/* Football helmet (Figma "image 1513") and guitar (Figma "image 1517") —
   get_screenshot contentsOnly captures of their own node, same fix as
   the person photos above (the first pass here used download_assets'
   raw-image export instead, which came back with the studio black
   backdrop baked in for the helmet — screenshot-of-the-node is the
   transparent-cutout version Figma actually displays). Neither needs
   is-pepper/is-basil's oversized-sprite object-fit:fill treatment; the
   base .hero-card-prop rule's plain contain works fine on both. The
   guitar's own -7.24deg Figma tilt is already baked into its screenshot's
   pixels (captured from its rotated wrapper node), so no extra CSS
   rotate is needed here the way sunglasses/basil need one. Position/size
   for both read off their frame's own coords relative to the overall
   stage group (Figma nodes 3319:109040 / 3320:109067), converted through
   the phone-frame anchor the same way the bg-pattern insets above were. */
.hero-card--4 .hero-card-prop-box.is-helmet { left: 37.03%; top: -0.87%; width: 61.22%; height: 32.27%; animation-duration: 8.3s; }
.hero-card--5 .hero-card-prop-box.is-guitar { left: 37.69%; top: 2.38%; width: 65.72%; height: 58.73%; animation-duration: 7.8s; animation-direction: reverse; }
/* translateZ(-40px) — BEHIND the phone card (which sits at 0px) — matches
   Figma's own layer order: in the source file every floating prop (the
   headphones, the sunglasses, the pepper/basil) is stacked underneath its
   iPhone frame, so it peeks out from behind the card's silhouette rather
   than floating in front of it. The orbit drift on top just keeps it from
   looking pinned in place. */
@keyframes heroPropOrbit {
  0%   { transform: translateZ(-40px) translate(0%, 0%); }
  25%  { transform: translateZ(-40px) translate(4%, -5%); }
  50%  { transform: translateZ(-40px) translate(0%, -7%); }
  75%  { transform: translateZ(-40px) translate(-4%, -2%); }
  100% { transform: translateZ(-40px) translate(0%, 0%); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-card-prop-box { animation: none; transform: translateZ(-40px); }
}
/* Card 3's two floating props (pepper/basil) were exported by Figma as two
   crops of the SAME shared sprite sheet (a 3x3 veggie/spice grid — a
   Figma asset-bundling quirk, not a mistake on our end) rather than two
   standalone images like cards 1 & 2's headphones/sunglasses — so these
   need the same crop-by-percentage treatment as the phone photos above
   instead of a plain object-fit: contain. */
.hero-card-prop-box.is-pepper,
.hero-card-prop-box.is-basil { overflow: hidden; }
.hero-card-prop-box.is-pepper .hero-card-prop,
.hero-card-prop-box.is-basil .hero-card-prop { width: auto; height: auto; max-width: none; object-fit: fill; }
.hero-card-prop-box.is-pepper .hero-card-prop { left: -20.18%; top: -111.47%; width: 331.84%; height: 339.45%; }
.hero-card-prop-box.is-basil .hero-card-prop { left: -9.22%; top: -254.55%; width: 327.43%; height: 373.74%; }
/* Figma rotates several floating props on their own axis, independent of
   the shared card tilt — a per-prop styling flourish (a "tossed" pose),
   confirmed straight from Figma's own layer transforms, not a guess: the
   sunglasses sit at rotate(32.61deg) and the basil sprig at
   rotate(-154.09deg) (near upside-down), while the headphones and pepper
   carry no extra rotation of their own. Sunglasses use plain
   object-fit: contain, so the rotation goes straight on .hero-card-prop.
   Basil is a crop-of-a-sprite-sheet (see above) — its crop math already
   depends on left/top/width/height on that same element, so rotating that
   element too would pivot around the oversized, off-center crop source
   instead of the visible tile; needs its own unrotated wrapper between
   the overflow:hidden crop box and the rotation, matching Figma's actual
   nesting (rotate wraps the crop, not the other way around). */
.hero-card--2 .hero-card-prop { transform: rotate(32.61deg); backface-visibility: hidden; }
.hero-card-prop-tilt { position: absolute; inset: 0; transform: rotate(-154.09deg); backface-visibility: hidden; }

/* transform-style: preserve-3d here is the actual fix for the icon
   "残影" ghosting reported on a real phone (invisible in a desktop-
   Chromium screenshot — see the will-change comment on the img rule
   below for that same caveat; this needed the real-device report to
   even see, then a structural read of the DOM to explain).
   Every OTHER animated layer in this card (.hero-card-phone,
   .hero-card-photo, .hero-card-product, .hero-card-prop-box) has its own
   translateZ/backface-visibility set directly on the element that's a
   DIRECT child of the rotating .hero-card. This one doesn't: .hero-card
   > .hero-card-icons > img is two levels deep, with .hero-card-icons
   itself carrying no transform of its own — meaning it defaults to
   transform-style: flat. A flat box sitting between a preserve-3d
   ancestor that's actually ROTATING (.hero-card's rotateY flip) and a
   further-transformed descendant (each img's own translateZ +
   backface-visibility: hidden) is exactly the shape that trips a real
   WebKit bug: the flat wrapper can flatten the 3D scene at that point,
   so each img's backface-visibility ends up computed against its own
   lone translateZ instead of the FULL accumulated rotation up through
   .hero-card — which never itself includes a rotateY, so the icon
   never actually reads as "facing away" and paints through even while
   its own card is supposedly hidden mid-flip (seen as another card's
   icons, in that card's own tint, bleeding through at a mirrored
   position). preserve-3d keeps this wrapper in the same continuous 3D
   context as its ancestor and descendant instead of breaking the chain
   between them — no other property here needed to change. */
.hero-card-icons {
  position: absolute;
  left: 79.724%;
  top: 70.263%;
  width: 12.472%;
  height: 26.856%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  transform-style: preserve-3d;
}
/* will-change is what's missing here, not just backface-visibility — every
   OTHER layer in this stack that runs its own animation every 9s lap
   (.hero-card, .hero-card-product, .hero-card-prop-box) already carries
   will-change: transform; these icons never got it. Without it, a mobile
   GPU compositor can fall back to re-rastering this element's bitmap each
   frame instead of keeping it on its own promoted layer — during the fast
   pop-in (opacity+scale both moving at once, see heroIconPop1/2/3 below)
   that shows up as a trailing "残影" ghost of the icon's previous frame,
   reported on a real phone (not reproducible in a desktop-Chromium
   screenshot, which is why this needed the real-device report to catch). */
.hero-card-icons img { display: block; width: 100%; aspect-ratio: 1; backface-visibility: hidden; will-change: transform, opacity; }
/* Entrance: the 3 reaction icons read as one (the heart) until the card
   turns to face forward, then fan out into their column, staggered heart →
   comment → bookmark. Riding the exact same 9s heroCardFlip duration and
   per-card animation-delay as .hero-card (0s/-6s/-3s) is what syncs the pop
   to "the instant this card is readable" instead of an arbitrary loop of
   its own. Comment/bookmark start collapsed onto the heart's own slot —
   translateY by their flex-column offset (space-between math: icon
   12.472% stage-width square, container 26.856% stage-height) rather than
   a guessed number — and slide down into place a beat after it, each with
   a quick overshoot-then-settle scale for a "popping" feel instead of a
   flat fade.
   Originally this whole cascade ran AFTER landing (0%→5.5% of the hold),
   and stayed settled (opacity 1, scale 1) for the entire rest of the 9s
   — icons are a persistent UI element, visible through basically the
   whole loop, not just a momentary flash.
   Retimed the ARRIVAL to sync with heroCardFlip's fast leg — same
   94.444%→95.556% window (the fast leg — see heroProductPop's own
   comment below for why that's the sync point, not wherever the slow
   leg happens to end). This sync point didn't move even when the flip
   around it got retimed twice (see heroCardFlip's own comment for that
   history) — fast/slow ended up back at their original 100ms/400ms
   values, so 94.444%/95.556% are the exact same numbers this component
   has always used. First pass moved the whole keyframe wholesale and
   accidentally inverted which part of the cycle is "hidden" vs
   "settled": it had icons hidden from 0% all the way to the reveal
   window (i.e. invisible for the entire hold — the one time they're
   supposed to be sitting there plainly visible!) and only actually on
   screen for the brief tail. Fixed by keeping heroProductPop's own shape:
   settled (visible) across 0%→26.889% (heroCardFlip's own flip-out-end —
   this IS the one number here that tracks the flip's own retiming, same
   boundary reused here so this doesn't need its own separate derivation),
   THEN drift down to the hidden/collapsed starting pose sometime in the
   long invisible 26.889%→hidden-until stretch — moot on screen either
   way, backface-visibility hides the whole card then — arriving at
   collapsed exactly at each icon's own hidden-until point so the
   reveal→settle stagger (heart→comment→bookmark, same relative rhythm
   throughout this component's history) still lands inside it, ending
   together with the tag and the flip's fast leg. */
.hero-card-icons img { animation: heroIconPop1 15s linear infinite; }
.hero-card-icons img:nth-child(2) { animation-name: heroIconPop2; }
.hero-card-icons img:nth-child(3) { animation-name: heroIconPop3; }
.hero-card--1 .hero-card-icons img { animation-delay: 0s; }
.hero-card--2 .hero-card-icons img { animation-delay: -12s; }
.hero-card--3 .hero-card-icons img { animation-delay: -9s; }
.hero-card--4 .hero-card-icons img { animation-delay: -6s; }
.hero-card--5 .hero-card-icons img { animation-delay: -3s; }
/* Percentages rescaled for the 15s/5-way lap — see heroCardFlip's own
   comment for the method (front-anchored stops ×(old/new), end-anchored
   stops keep their absolute distance from the end instead) and for the
   --1/--3/--4/--5/--2 delay fix that came with this same pass. */
@keyframes heroIconPop1 {
  0%, 16.000% { opacity: 1; transform: translateZ(34px) scale(1); }
  96.534% { opacity: 0; transform: translateZ(34px) scale(0.5); }
  97.000% { opacity: 1; transform: translateZ(34px) scale(1.1); }
  97.334%, 100% { transform: translateZ(34px) scale(1); }
}
@keyframes heroIconPop2 {
  0%, 16.000% { opacity: 1; transform: translateZ(34px) translateY(0%) scale(1); }
  96.734% { opacity: 0; transform: translateZ(34px) translateY(-134.75%) scale(0.5); }
  97.200% { opacity: 1; transform: translateZ(34px) translateY(0%) scale(1.1); }
  97.534%, 100% { transform: translateZ(34px) translateY(0%) scale(1); }
}
@keyframes heroIconPop3 {
  0%, 16.000% { opacity: 1; transform: translateZ(34px) translateY(0%) scale(1); }
  96.934% { opacity: 0; transform: translateZ(34px) translateY(-269.47%) scale(0.5); }
  97.400% { opacity: 1; transform: translateZ(34px) translateY(0%) scale(1.1); }
  97.734%, 100% { transform: translateZ(34px) translateY(0%) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-card-icons img { animation: none; transform: translateZ(34px); opacity: 1; }
}

/* Single-tag product card (cards 1 & 2) — same box both cards. */
.hero-card-product {
  position: absolute;
  left: 0%;
  top: 66.958%;
  width: 67.715%;
  height: 33.042%;
  border-radius: var(--radius-md);
  padding: 4.9% 4.9% 0;
  display: flex;
  flex-direction: column;
  transform-origin: 100% 100%;
  animation: heroProductPop 15s linear infinite;
  backface-visibility: hidden;
  will-change: transform;
}
.hero-card--1 .hero-card-product { background: #4d3fe9; animation-delay: 0s; }
.hero-card--2 .hero-card-product { background: #131870; animation-delay: -12s; }
.hero-card--3 .hero-card-product { animation-delay: -9s; }
.hero-card--4 .hero-card-product { background: #cbf484; animation-delay: -6s; }
.hero-card--5 .hero-card-product { background: #fee26b; animation-delay: -3s; }
/* This product tag is its own layer (translateZ'd in front of the phone,
   see the parallax comment above .hero-card-phone), so it can carry its
   own pop-in without touching .hero-card's rotation or any sibling layer
   — riding the same 9s cycle + per-card delay as heroCardFlip is what
   syncs it. Growth is anchored at the tag's own bottom-right corner
   (transform-origin above).
   Settles to scale(1) at 95.556% — the same instant heroCardFlip's fast
   leg (30° in 100ms) completes, so it still finishes in sync with that
   flip, NOT with the (longer, 400ms) slow leg that follows it — the sync
   point is the fast leg specifically, unaffected by that leg's own
   duration changing. This briefly widened to the whole fast+slow window
   during a retiming pass that shrank the fast leg down to 30ms (too
   quick on its own for a grow to read) — see heroCardFlip's own comment
   for why that pass got reverted; fast is back to 100ms, so the original
   sync-to-fast-leg-only shape works again unchanged. The grow spans the
   FULL 94.444%→95.556% window (all 100ms of the fast leg): with
   heroCardFlip's own coast leg soaking up the invisible 0°→90° travel
   first, this fast leg starts already inside the visible arc, not
   edge-on — unlike the direct-180°-split versions tried before, where
   the fast leg's own opening stretch was still off-screen and the grow
   had to be confined to a truncated visible tail to be seen at all.
   Plain, single scale(0)→scale(1) — cubic-bezier eased for a silky (not
   linear/mechanical) feel — no overshoot, that was tried and explicitly
   not wanted, just a clean grow landing exactly when the fast leg does. */
@keyframes heroProductPop {
  0%, 16.000% { transform: translateZ(22px) scale(1); }
  96.534% { transform: translateZ(22px) scale(0); animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  98.534%, 100% { transform: translateZ(22px) scale(1); }
}
.hero-card-product-photo {
  flex: 1 1 auto;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.hero-card--1 .hero-card-product-photo { background: #cde900; }
.hero-card--2 .hero-card-product-photo { background: #c7b5fb; }
.hero-card--4 .hero-card-product-photo { background: #131870; }
.hero-card--5 .hero-card-product-photo { background: #d24028; }
.hero-card-product-photo img { width: 100%; height: 100%; object-fit: cover; }
.hero-card--1 .hero-card-product-photo img { object-fit: contain; }
.hero-card--2 .hero-card-product-photo img { transform: scaleX(-1); backface-visibility: hidden; }
/* Card 5's cassette-player photo (Figma "image 1516") sits noticeably
   smaller than its own backer box in the source (a centered, floating
   placement, not an edge-to-edge fill like the skateboard/cream-jar
   tags) — same "contain, not cover" treatment as card 1's headphone jar
   for the same reason: the photo is meant to float with visible padding,
   not crop to fill. */
.hero-card--5 .hero-card-product-photo img { object-fit: contain; }
.hero-card-product-label {
  padding: 10% 0 8%;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-weight: 600;
  /* Was a vw-relative clamp(0.9375rem, 4.6vw, 1.125rem) — that tracked
     VIEWPORT width on the assumption .hero-card-stage's own width tracks
     it too. It doesn't: .hero-card-stage is a flat 320px at every width
     from 640px up (measured — it hits its own max-width immediately) and
     a flat 258px at every width below that (the mobile override further
     down). A vw-relative font over a viewport-independent box is why
     "Moisturizing Cream" grew right past the card's edge on real phones
     wider than ~390px, even though it fit fine on paper at 320px. Fixed
     size instead, tuned to the 320px-stage regime (measured to exactly
     fill the tag's own width, see the ≤639.98px override below for the
     258px regime's own size). */
  font-size: 1.125rem;
  white-space: nowrap;
}
/* Same-specificity override, so — same trap .hero-card-stage's own
   ≤639.98px rule already ran into (see that comment) — this has to come
   AFTER the unconditional rule above to actually win the cascade; placing
   it back up near .hero-card-stage's own mobile override would silently
   lose to this later rule instead. Measured against "Moisturizing Cream",
   the longest label this tag ever holds, at the 258px stage width that
   override cap produces. */
@media (max-width: 639.98px) {
  .hero-card-product-label { font-size: 0.875rem; }
}
.hero-card--1 .hero-card-product-label { color: #ffc1d7; }
.hero-card--2 .hero-card-product-label { color: #cde900; }
.hero-card--4 .hero-card-product-label { color: #7b0006; }
.hero-card--5 .hero-card-product-label { color: #027b91; }

/* Twin overlapping tag cards (card 3 only: Pot behind, Gloves in front,
   same "single tag" internals but half the width and centered text). */
.hero-card-product--twin {
  width: 39.56%;
  height: 33.042%;
  top: 66.084%;
  /* Percentage padding resolves against the CONTAINING BLOCK's width — for
     an absolutely positioned element that's .hero-card (i.e. the stage),
     not this card's own (much narrower, 39.56%-of-stage) width. The old
     "8% 8% 0" was sized as if it were 8% of the card itself; against the
     real containing block that came out to ~25px of padding on a ~125px-
     wide card, squeezing the inner photo box down to ~60% width instead
     of Figma's ~85% — exactly the "container's too small" mismatch. These
     numbers are the Figma pixel offsets (17.78/19px on a 237.175px-wide,
     599.62px-stage-relative card) converted directly to %-of-stage-width. */
  padding: 3.169% 3% 0;
  text-align: center;
}
.hero-card-product--twin.is-pot { left: -12.51%; background: #cde900; }
/* Pot and Gloves don't actually overlap in Figma — confirmed straight from
   its own layout numbers, there's a small gap between them — so both stay
   at the same translateZ(22px) as the base .hero-card-product rule
   (inherited, no override here). An earlier translateZ(26px) "put Gloves
   in front" push (from before this was checked against Figma) was pulling
   it measurably closer to the camera than Pot, which under perspective
   both enlarges it and shifts its projected position — rendering it
   visibly bigger and overlapping Pot instead of the plain side-by-side
   pair Figma actually shows. */
.hero-card-product--twin.is-gloves { left: 28.781%; background: #027b91; }
.hero-card-product--twin .hero-card-product-photo { background: #e6efd3; }
.hero-card-product--twin.is-gloves .hero-card-product-photo { background: #cbf484; }
/* Pot now uses the same plain object-fit:contain fill as every other
   product photo — the earlier oversized+offset crop-then-rotate tile
   (read off Figma's own fill transform) was fighting a source photo that
   had a lot of empty canvas baked in, and even with the rotation removed
   it still left the teapot visibly stretched (object-fit: fill doesn't
   preserve aspect ratio). Swapped the source asset for a tightly-cropped,
   level render (Figma node 3275:107330) instead of re-deriving crop math
   for the old one, so the plain shared rule below is enough. */
.hero-card-product--twin .hero-card-product-photo img { object-fit: contain; }
.hero-card-product--twin.is-gloves .hero-card-product-photo img { object-fit: cover; }
/* Box sizes stay shared between the twins (layout/label positions must
   match) — sized the two photos relative to each other with a plain
   scale on the img itself instead, still centered by the flex box above
   and clipped by its overflow:hidden. */
.hero-card-product--twin.is-pot .hero-card-product-photo img { transform: scale(1.15); }
.hero-card-product--twin.is-gloves .hero-card-product-photo img { transform: scale(0.85); }
.hero-card-product--twin.is-pot .hero-card-product-label { color: #131870; }
.hero-card-product--twin.is-gloves .hero-card-product-label { color: #cde900; }

/* ==========================================================================
   AI feature rows — read out of the Quacko Framer site: dot+label eyebrow,
   big paragraph, dark pill CTA, grain-gradient card with a white content
   card + dark status pill. Card content is invented for Quacko's product;
   the composition (colors, layering, pill-under-card) is the real thing.
   ========================================================================== */
.ai-rows {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  gap: 56px;
  background-color: var(--ai-rows-color, #007b91);
  transition: background-color 80ms linear;
  will-change: background-color;
  /* Real, recurring bug (same root cause as the ones already fixed on
     .hero and .why-quacko-card--stat, and the one already documented on
     <html>'s overflow-x:hidden rule — "reported on mobile FAQ"): every
     .ai-visual card ([data-visual-reveal]) starts scaled up 15% before
     JS's IntersectionObserver reveals it, overhanging ~11px past both of
     its own edges until that fires. Unclipped, that's real page-level
     overflow, not an inert bleed — reachable via a horizontal drag/pan on
     a real device regardless of scroll position, which is exactly why it
     was showing up as a "gap" all the way down at the FAQ section, well
     after these cards. <html>'s own overflow-x:hidden alone isn't
     reliably enough to stop that on-device. .ai-rows is itself already a
     top-level, un-padded, full-viewport-width <section> (confirmed:
     left/right match the viewport exactly), so clipping here removes the
     overflow without cutting into anything — .ai-row's own ::before/
     ::after full-bleed color-transition backgrounds (100vw + translateX
     centering) already resolve to that exact same width, so they're
     unaffected. */
  overflow-x: hidden;
}
.ai-rows > .container {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 32px;
}
.ai-row {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  gap: 32px;
  padding-block: 32px;
}
.ai-row > * {
  position: relative;
  z-index: 1;
}
/* One viewport-anchored grid shared by all three feature rows. Using the
   same value on both background axes guarantees square cells; anchoring
   the pattern to the viewport's right edge prevents a clipped final
   column and avoids the content container leaving an unpainted strip. */
.ai-rows::before {
  --mosaic-cell: clamp(56px, 5.5vw, 96px);
  content: "";
  position: absolute;
  inset-block: 0;
  right: 0;
  z-index: 0;
  display: none;
  width: 44vw;
  overflow: hidden;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(20, 18, 15, 0.13) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(20, 18, 15, 0.13) 1px, transparent 1px);
  background-size: var(--mosaic-cell) var(--mosaic-cell);
  background-position: right top;
}
/* Retained in the markup for compatibility, but the shared section layer
   above now draws the grid and these elements must never become flex items. */
.ai-row-mosaic {
  display: none;
}
.ai-row::before,
.ai-row::after {
  content: "";
  position: absolute;
  inset-block: 0;
  left: 50%;
  z-index: 0;
  width: 100vw;
  transform: translateX(-50%);
  pointer-events: none;
  display: none;
}
.ai-row::after {
  opacity: var(--row-color-progress, 0);
  transition: opacity 140ms linear;
  will-change: opacity;
}
.ai-row:nth-child(1)::before {
  background: #fff;
}
.ai-row:nth-child(1)::after {
  background: #007b91;
}
.ai-row:nth-child(2)::before {
  background: #007b91;
}
.ai-row:nth-child(2)::after {
  background: #ff9e5c;
}
.ai-row:nth-child(3)::before {
  background: #ff9e5c;
}
.ai-row:nth-child(3)::after {
  background: #f5c3d6;
}
.ai-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.ai-copy-bottom { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
/* Icon badges above each row's title — Figma "Quacko Team" file, node
   3240:106600 ("icons", above the Product Highlight headings). Each row
   gets its own fixed badge/icon color pair straight from that frame,
   independent of the row's own background color. Figma frame itself is
   96x96; sized down 20% from that (96 -> 76.8px, radius scaled the same
   25% ratio) per spec, then a further 10% (76.8 -> 69.12px, radius
   19.2 -> 17.28px) on request. The search/outreach/video icon SVGs inside
   are scaled the same factor in their width/height attributes (index.html)
   so the whole badge shrinks as one proportional unit, not just its
   container. */
.ai-icon-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 69.12px;
  height: 69.12px;
  border-radius: 17.28px;
}
.ai-icon-badge--search { background: #c7b5fb; }
.ai-icon-badge--outreach { background: #d3f300; }
.ai-icon-badge--video { background: #4d3fe9; }
/* Pricing hero's money-bag badge — same 96x96-Figma-frame/0.72-scale
   family as the three above (see .ai-icon-badge comment), own color pair
   from its own Figma frame (node 3252:106699). */
.ai-icon-badge--pricing { background: #ffc1d7; }
.ai-icon-badge--getting-started { background: #FF984B; }
/* Blog hero's grid/table badge — same family (node 3326:109137), own
   Figma frame's own color pair happens to match .ai-icon-badge--outreach's
   background exactly, but gets its own modifier rather than overloading
   that one (same reasoning as .ai-icon-badge--profile above). */
.ai-icon-badge--blog { background: #d3f300; }
/* Icon badge bounce-in entrance — js/main.js adds .is-revealed once in
   view (data-bounce-reveal, reusing the same addRiseReveal one-shot
   IntersectionObserver as [data-visual-reveal] elsewhere in this file):
   the badge starts scaled down and invisible, then springs into its
   resting pose with a single overshoot (scale only, no rotation, no
   repeated bouncing) — a hand-rolled stand-in for React Bits' BounceCards
   (gsap elastic.out(1, 0.5)), matching this file's existing no-gsap,
   no-React approach to that package's other entrance effects (see the
   staggered text reveal). Used by all four .ai-icon-badge instances
   (search/outreach/video here, plus the pricing hero's money-bag badge)
   — any badge in view on load plays almost immediately (same as the h1
   stagger); ones further down the page play on scroll-into-view instead. */
[data-bounce-reveal] {
  opacity: 0;
  transform: scale(0.3);
}
[data-bounce-reveal].is-revealed {
  animation: badgeBounceIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes badgeBounceIn {
  0%   { opacity: 0; transform: scale(0.3); }
  60%  { opacity: 1; transform: scale(1.08); }
  100% { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  /* JS skips the reveal trigger under reduced motion (see reduceStaggerMotion
     in main.js), so the scaled/hidden starting state above would otherwise
     be the badge's permanent, broken look. */
  [data-bounce-reveal] {
    opacity: 1;
    transform: none;
  }
}
.ai-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: #d9f20f;
}
/* Site-wide display-title wrapping: use the available line width evenly so
   headings that fit in two lines do not fall into an avoidable third line. */
h1,
h2,
.ai-eyebrow {
  text-wrap: balance;
}
.ai-dot { display: none; }
.ai-copy-text {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 1.125rem;
  font-weight: 500;
  line-height: 1.4;
  color: #fff;
  max-width: 520px;
}
.btn-pill-outline-dark {
  display: inline-flex;
  align-items: center;
  gap: 24px;
  margin-top: 8px;
  background: #d9f20f;
  color: #14120f;
  padding: 12px 22px;
  border-radius: var(--radius-pill);
  font-weight: 600;
  font-size: 1.0625rem;
}
.btn-pill-outline-dark:hover { background: #e5fa34; }
.ai-button-label {
  display: inline-flex;
  align-items: center;
  padding-block: 4px;
  margin-block: -4px;
  overflow: hidden;
}
.ai-button-text-track {
  display: inline-flex;
  align-items: center;
}
.ai-button-text-track > span {
  display: inline;
}
.ai-button-text-track > span[aria-hidden="true"] { display: none; }
.btn-pill-outline-dark:hover .ai-button-text-track { transform: none; }
.ai-arrow {
  display: inline-block;
  transform: translateX(0) rotate(0deg);
  transform-origin: center;
  transition: transform 420ms cubic-bezier(.16, 1, .3, 1);
  will-change: transform;
}
.btn-pill-outline-dark:hover .ai-arrow,
.btn-pill-outline-dark:focus-visible .ai-arrow,
.btn-hero-primary:hover .ai-arrow,
.btn-hero-primary:focus-visible .ai-arrow,
.btn-hero-secondary:hover .ai-arrow,
.btn-hero-secondary:focus-visible .ai-arrow,
.btn-dark:hover .ai-arrow,
.btn-dark:focus-visible .ai-arrow,
.pricing-table-head-btn:hover .ai-arrow,
.pricing-table-head-btn:focus-visible .ai-arrow {
  transform: translateX(2px) rotate(45deg);
}
.ai-row:nth-child(3) .ai-eyebrow {
  color: #850010;
}
.ai-row:nth-child(2) .ai-eyebrow {
  color: #6b2d00;
}
.ai-row:nth-child(2) .btn-pill-outline-dark {
  background: #6b2d00;
  color: #fff;
}
.ai-row:nth-child(2) .btn-pill-outline-dark:hover,
.ai-row:nth-child(2) .btn-pill-outline-dark:focus-visible {
  background: #823900;
}
.ai-row:nth-child(2) .btn-pill-outline-dark .ai-arrow {
  color: #fff;
}
.ai-row:nth-child(2) .ai-copy-text {
  color: #14120f;
}
.ai-row:nth-child(3) .ai-copy-text {
  color: #14120f;
}
.ai-row:nth-child(3) .btn-pill-outline-dark {
  background: #850010;
  color: #fff;
}
.ai-row:nth-child(3) .btn-pill-outline-dark:hover,
.ai-row:nth-child(3) .btn-pill-outline-dark:focus-visible {
  background: #9b1021;
}
.ai-row:nth-child(3) .btn-pill-outline-dark .ai-arrow {
  color: #fff;
}
/* Mobile only (desktop's row layout below 1024px keeps the left-aligned
   copy that sits naturally beside the visual) — .ai-row is still a
   single stacked column here, so center the eyebrow/body/button like
   .audience-stats-copy does for its own stacked mobile layout. */
@media (max-width: 1023px) {
  .ai-copy, .ai-copy-bottom { align-items: center; }
  /* .ai-eyebrow's inline-flex display (a leftover from once laying out an
     icon dot beside the text — .ai-dot is display:none now) gives its text
     an anonymous flex-item box with a content-based min-width, so it can't
     shrink below one unbroken line and overflows narrow phones instead of
     wrapping (worse once centering made that overflow symmetric on both
     edges). Dropping to a plain block sidesteps the flex minimum-size
     quirk entirely and lets the heading wrap like normal text. */
  .ai-eyebrow { display: block; text-align: center; max-width: 100%; }
  .ai-copy-text { text-align: center; margin-inline: auto; }
  /* .ai-visual was never centered on the stacked mobile/tablet layout — it
     has no align-items override of its own, so .ai-row's default
     align-items:stretch just left it pinned to the row's start (left)
     edge while the copy above it (both rules right above) is explicitly
     centered. Real bug: on tablet widths the demo card visibly sat off to
     the left under centered text. margin-inline:auto centers it the same
     way .ai-copy-text is centered above, whenever it's narrower than the
     row (i.e. past 460px — see .ai-visual's own max-width — which on
     phones is the full-bleed width anyway, so this is a no-op there). */
  .ai-visual { margin-inline: auto; }
}

/*
 * These are real screen-recorded product-demo videos from the Quacko
 * Framer site (downloaded from their Framer CDN), not a CSS mockup — the
 * gradient background, white card, and dark status pill are all baked
 * into the footage itself, so the wrapper just sizes/rounds/loops it.
 * The gradient fill behind is a same-hue fallback while the video loads.
 */
.ai-visual {
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  aspect-ratio: 619 / 519;
  width: 100%;
  max-width: 460px;
}
/* js/main.js adds .is-revealed once scrolled into view (data-visual-reveal):
   the card starts zoomed in and faded out, then settles down to its resting
   scale/opacity — a "pop up" entrance rather than a plain fade. */
[data-visual-reveal] {
  opacity: 0;
  transform: scale(1.15);
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease-out;
}
[data-visual-reveal].is-revealed {
  opacity: 1;
  transform: scale(1);
}
@media (prefers-reduced-motion: reduce) {
  /* JS skips the reveal trigger under reduced motion, so the zoomed/hidden
     starting state would otherwise be the card's permanent, broken look. */
  [data-visual-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
.ai-visual--violet { background: linear-gradient(165deg, #F472B6, #E879F9); }
.ai-visual--orange { background: linear-gradient(165deg, #FDBA74, var(--color-brand)); }
.ai-visual--pink    { background: linear-gradient(165deg, #F9A8D4, #DB2777); }
.ai-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ==========================================================================
   Audience / performance stats — bento grid + headline
   ========================================================================== */
/*
 * Swapped from the bento stats graphic (GMV Tracked / Active Creators / DM
 * Messages / Email) to the UFO/duck illustration from Figma node
 * 3183:105078 — a single exported asset, same pattern as the hero's
 * collage-image, background chroma-keyed to transparent on export.
 */
/* .ai-rows above has its own background color filling its full padding box
   (not blank space), so the entire visible gap above this panel has to come
   from this section's own padding-top — hence 110px here, not a 55/55 split. */
.section.audience-stats { padding-top: 110px; padding-bottom: 55px; }
.audience-stats-inner {
  background: #E6EFD3;
  border-radius: 32px;
  padding-block: 130px;
  padding-inline: 56px;
  display: flex;
  align-items: center;
  gap: clamp(40px, 6vw, 96px);
}
.audience-stats-grid {
  /* 966px cap (~AI Creator Search's .ai-visual height) scaled down 20%,
     then reduced by another 1/3 per request. */
  flex: 0 1 515px;
  max-width: 515px;
  min-width: 0;
}
.audience-stats-bento {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 254 / 200;
}

.audience-stats-copy { flex: 1 1 auto; min-width: 0; }
.audience-stats-copy h2 {
  max-width: 620px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
.audience-stats-copy .section-sub {
  max-width: 520px;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 1.125rem;
  font-weight: 500;
  line-height: 1.4;
  color: var(--color-ink);
}
.audience-stats-copy .btn { margin-top: 28px; }

@media (max-width: 1023px) {
  /* Desktop's 110px padding-top exists to hold the gap above the card at
     that same 110px the sections around it use (see the comment above
     .section.audience-stats) — on mobile that reads as a huge dead gap
     under the AI rows section, so match it to the card's own 16px side
     margin right below instead. */
  .section.audience-stats { padding-top: 16px; }
  /* .audience-stats-inner doubles as .container (see the markup), so its
     own padding-inline is normally *internal* padding, not a gap to the
     viewport edge — the colored card sits flush against the screen with
     nothing outside it. Mobile needs an actual outside gap, so swap
     .container's width:100%/margin-inline:auto for an explicit 16px
     margin instead (width:auto lets the box shrink to fit between them). */
  .audience-stats-inner { width: auto; margin-inline: 16px; flex-direction: column; align-items: stretch; padding: 32px 24px; border-radius: 24px; }
  .audience-stats-grid { flex: none; max-width: 307px; margin-inline: auto; width: 100%; }
  .audience-stats-copy { text-align: center; }
  .audience-stats-copy h2, .audience-stats-copy .section-sub { margin-inline: auto; }
}
/* .audience-stats-inner's 16px side margin above was hardcoded for phones
   and never followed .container's own responsive bump to 32px at 768px+
   (see .container's min-width:768px rule) — real bug on iPad, where every
   other section/card on the page (.why-quacko-card included) sits a full
   32px off the screen edge while this one card alone stayed pinned at
   16px, visibly tighter than its neighbors. Matched here, and padding-top
   goes with it (kept equal to the side margin on purpose, same as the
   16px pairing right above, so the gap above the card still reads the
   same size as the gap beside it). */
@media (min-width: 768px) and (max-width: 1023px) {
  .section.audience-stats { padding-top: 32px; }
  .audience-stats-inner { margin-inline: 32px; }
}
/* ==========================================================================
   Outreach demo — checklist card + phone mockup + pull-quote
   ========================================================================== */
/*
 * Layout pattern brought over from ManyChat's "See how it works" section:
 * a white checklist card and a phone mid-conversation, both floating on a
 * bold color field, with a short quote off to the side. Copy is Quacko's
 * own outreach-automation feature, not ManyChat's IG/FB DM flow.
 */
/* Paired with .audience-stats's own 55px bottom padding above, so the gap
   between the "We are quacko" panel and this one totals 110px. Bottom is
   paired the same way with .why-quacko's own 55px top padding below. */
.section.outreach-demo { padding-top: 55px; padding-bottom: 55px; }
/* Pinned scroll-through (desktop, motion-ok only — see the combined media
   query below): the section becomes one 100vh "stage", 4 more 70vh
   "steps" (one per transition between the 5 accordion rows), plus a final
   30vh hold after Insight has opened —
   and .outreach-demo-sticky fills the viewport for that whole span, so the
   photo+card stay vertically centered while initOutreachScrollPin (main.js) advances the
   accordion under scroll instead of requiring a click. 70vh rather than a
   full 100vh per step keeps the total distance (~380vh) from feeling
   excessive for 5 rows — a feel knob, not a hard requirement, and main.js
   doesn't hardcode this number back: it measures the actual rendered
   scroll range and divides it evenly by (row count − 1) itself, so this
   height is the only place step length is tuned. The 55px top/bottom gap
   to the neighboring sections moves from the section's own padding (now
   0, since the section's height is spoken for by the scroll distance)
   into the centered card's available viewport space while pinned.
   The card keeps its own height and remains centered on the viewport's
   vertical midpoint throughout the pinned scroll range. */
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  .section.outreach-demo { height: calc(100vh + 4 * 70vh + 30vh); padding-top: 0; padding-bottom: 0; }
  .outreach-demo-sticky {
    position: sticky;
    top: 0;
    height: 100vh;
  }
  .outreach-demo-sticky > .outreach-demo-inner {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    width: 100%;
    transform: translateY(-50%);
  }
}
/* The photo is a full-bleed background behind the whole card (position:
   absolute, cropped to the rounded corners by overflow:hidden here); the
   white accordion card floats on top of it, inset by this same padding
   instead of sitting beside it in a flex row. */
/* Height is pinned rather than left to size off the card + padding, per
   feedback: padding went back down to 32px on every side, but the section
   itself should stay exactly as tall as it already was — so the card
   stretches (align-items: stretch, the flex default, on this single-item
   row) to fill the extra room instead of the box shrinking around it. */
/* White (not the old purple) so it reads correctly as the backdrop for
   rows with no dedicated .outreach-demo-bg photo (currently Manage/Promos)
   — main.js fades that img's opacity to 0 for those rows instead
   of stretching a photo that isn't sized/cropped for this container. */
.outreach-demo-inner {
  position: relative;
  overflow: hidden;
  background: #fff;
  border-radius: 32px;
  padding: 32px;
  min-height: 818px;
  display: flex;
  color: #fff;
}
/* .container's own ≥768px padding-inline rule (further down, same
   specificity) shares this element and comes later in the cascade, so it
   was clobbering the side padding above back down to 32px whenever the
   value here differed — pin it explicitly so left/right can never drift
   from top/bottom again, whatever either rule's own value is. */
@media (min-width: 1024px) {
  .outreach-demo .outreach-demo-inner { padding-inline: 32px; }
}
/* Stacked crossfade layers (#outreachDemoBgA/B) — both sit in the exact
   same box via position:absolute+inset:0 here, so animating one's opacity
   down while the other's goes up reads as one photo dissolving into the
   next, not a swap. 420ms/ease-in-out rather than the old 220ms/ease reads
   noticeably calmer for a scroll-driven change than for a click-driven
   one — a click is a deliberate one-off act that can afford to feel snappy,
   but scrolling fires this continuously, so a quick fade there reads as a
   flicker rather than a transition. */
.outreach-demo-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 420ms ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
  .outreach-demo-bg { transition: none; }
}
/* Search-bar UI chip (Figma node 3190:59121, corrected for density against
   3195:59192/3195:59312 — same component, every measurement scaled 1.241x:
   padding 19.853 vs 16px, icon 29.78 vs 24px, card radius 17.372 vs 14px,
   etc., all the same 1.241x), floating over the open photo to the right of
   the card. Was a single flat PNG screenshot capped at a tiny 460px — too
   small — so it's rebuilt as real markup (see index.html), sized to fill
   the open photo exactly: `left` clears the card's own fixed box (32px
   inset + 400px max-width, confirmed constant at every width the chip is
   shown at) by the same 48px gap `right` already keeps from the photo's
   own edge, so both sides read as one even margin instead of the old
   bottom-right hug.
   Every measurement past this point is an `em` against 3190:59121's own
   16px unit (×1.241 for the 3195 density, i.e. 0.021211 of the box's own
   width — 19.853/936), so this one font-size scales icons/padding/text
   together like resizing the original component. It's spelled out as
   `calc()` off `vw` rather than a self-referencing `cqw` on this same
   element: that measured ~1.9x too large in testing (Chromium resolved it
   against some ancestor's box, not this element's own — self-reference on
   the container-establishing element isn't reliably this element's own
   size across engines). `min(100vw, 1200px)` mirrors .container's own cap
   below the 1600px tier, where a separate rule below takes over to mirror
   its edge-to-edge `margin-inline: 110px` swap; both are exact, not
   approximated, so the aspect ratio matches the Figma component instead of
   drifting from a mismatched width/font-size pair. */
.outreach-search-chip {
  position: absolute;
  z-index: 1;
  left: 480px;
  right: 48px;
  /* The white chip card itself isn't what needs to land on .outreach-card's
     32px bottom inset — .outreach-search-chip-highlight (the semi-
     transparent backdrop behind it) extends 0.375em further down than the
     chip on every side, so that's the edge actually visible as this box's
     bottom. +0.375em here pushes the *chip* up by exactly that much so the
     highlight's true bottom edge — not the white card's — lands on the
     32px line. */
  bottom: calc(32px + 0.375em);
  font-size: min(calc((min(100vw, 1200px) - 528px) * 0.021211), 24px);
  filter: drop-shadow(0 20px 40px rgba(10, 6, 60, 0.35));
  opacity: 1;
  transform: scale(1) translateY(0);
  transition: opacity 380ms cubic-bezier(0.22, 1, 0.36, 1), transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .outreach-search-chip { transition: none; }
}
/* Mocks the Search bar specifically, so it only makes sense floating over
   Search's own photo — hide it once another pillar (with its own photo,
   e.g. Connect) is the active row. */
.outreach-demo-inner:not([data-active-pillar="search"]) .outreach-search-chip {
  opacity: 0;
  transform: scale(0.8) translateY(24px);
  pointer-events: none;
}
@media (min-width: 1600px) {
  .outreach-search-chip { font-size: min(calc((100vw - 748px) * 0.021211), 24px); }
}
@media (max-width: 1023px) {
  .outreach-search-chip { display: none; }
}
.outreach-search-chip-highlight {
  position: absolute;
  inset: -0.375em -0.375em -0.375em -0.375em;
  top: -0.3125em;
  background: #f5f3f1;
  opacity: 0.8;
  border-radius: 1.1em;
}
.outreach-search-chip-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.75em;
  width: 100%;
  padding: 1em;
  background: #fff;
  border-radius: 0.875em;
  box-shadow: 0 1px 2px rgba(10, 6, 60, 0.06);
}
/* Gradient ring via mask instead of `border` + the dual-background-layer
   border-box/padding-box trick that drew it before: Blink snaps a plain
   `border-width` to whole CSS pixels for rendering (confirmed empirically —
   anything from 1.0px up to just under 2px paints identically at 1px), so
   the requested ~1.3px come out indistinguishable from the original 1px.
   `padding` on this pseudo isn't subject to that snapping, so it's what
   actually sets the ring's thickness; the two masks XOR'd together punch
   the middle out, leaving just that padding-width ring painted in the
   gradient. */
.outreach-search-chip-card::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 0.08125em;
  border-radius: inherit;
  background: linear-gradient(135deg, #ea580c, #e2e2e2);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.outreach-search-chip-row { display: flex; align-items: center; justify-content: space-between; gap: 0.75em; width: 100%; }
.outreach-search-chip-query { display: flex; align-items: center; gap: 0.75em; min-width: 0; }
.outreach-search-chip-icon { flex: none; width: 1.5em; height: 1.5em; }
.outreach-search-chip-placeholder {
  margin: 0;
  overflow: hidden;
  white-space: nowrap;
  font-size: 0.875em;
  color: #737373;
}
/* Query text is typed/held/deleted in real time by initChipTypewriter in
   main.js (a vanilla-JS port of quacko_app's own useTypewriter — same
   three PROMPT_EXAMPLES, same TYPE_MS/DELETE_MS/HOLD_MS/PAUSE_MS timings),
   so — matching that real `<input>`, which shows no caret while unfocused
   — there's no CSS-driven reveal or blinking caret here to fight with it. */
.outreach-search-chip-toggle { display: flex; align-items: center; gap: 0.25em; flex: none; font-size: 0.75em; color: #0a0a0a; white-space: nowrap; }
.outreach-search-chip-toggle-track { display: flex; align-items: center; gap: 0.25em; margin-inline: 0.25em; }
.outreach-search-chip-toggle-track i { display: block; width: 1.6875em; height: 0.625em; border-radius: 0.3125em; background: #e5e5e5; font-style: normal; }
/* #F76A6E: quacko_app's own BOARD_COLORS[1], the default-selected middle
   option — not the Figma mockup's #ff5e69. */
.outreach-search-chip-toggle-track i.is-active { background: #f76a6e; }
.outreach-search-chip-tools { display: flex; align-items: center; gap: 0.5em; }
.outreach-search-chip-tool {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2em;
  height: 2em;
  border: 0.0625em solid #e5e5e5;
  border-radius: 999px;
}
/* size-4 (16px) icon in a size-9 (36px) button in quacko_app — 0.444 of
   the button box, same ratio applied to this box's own 2em. */
.outreach-search-chip-tool svg { width: 0.889em; height: 0.889em; }
.outreach-search-chip-actions { display: flex; align-items: center; gap: 0.5em; }
.outreach-search-chip-pill {
  display: flex;
  align-items: center;
  gap: 0.28em;
  height: 2em;
  padding-inline: 0.67em;
  border: 0.0625em solid #e5e5e5;
  background: #fff;
  color: #000;
  white-space: nowrap;
}
.outreach-search-chip-pill svg { flex: none; }
/* "15" credits pill: quacko_app's is `rounded-full`, `text-base
   font-semibold` + a size-4 icon in its own size-9 pill — same 0.444
   ratio as the tool icons above, and semibold, larger text to match. */
.outreach-search-chip-pill-credits { border-radius: 999px; font-size: 0.889em; font-weight: 600; }
.outreach-search-chip-pill-credits svg { width: 0.889em; height: 0.889em; }
/* "Match by" pill: quacko_app's is `rounded-card` (14px, not a full pill)
   with `text-sm` + a muted size-3.5 Atom and size-4 ChevronDown. */
.outreach-search-chip-pill-match { border-radius: 0.875em; font-size: 0.778em; }
.outreach-search-chip-pill-match svg:first-child { width: 0.778em; height: 0.778em; }
.outreach-search-chip-pill-match svg:last-child { width: 0.889em; height: 0.889em; }
.outreach-search-chip-submit {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2em;
  height: 2em;
  flex: none;
  background: #000;
  border-radius: 999px;
}
.outreach-search-chip-submit svg { width: 0.889em; height: 0.889em; }

/* Connect pillar overlay — the "Message 1" composer block from
   quacko_app's outreach wizard (step-message-setup.tsx's
   MessageBlockView), same real-icons/real-copy/real-spacing-tokens reuse
   as .outreach-search-chip above rather than a fresh mock. Same left/right
   insets and font-size formula as .outreach-search-chip so its width
   matches exactly, and — like that chip — bottom-anchored so its
   .outreach-connect-chip-highlight's true bottom edge (0.375em past the
   chip on every side) lands on .outreach-card's 32px bottom inset, same
   alignment as .outreach-search-chip's own `bottom` value. Height is
   otherwise intrinsic to the message text, so it grows upward from that
   fixed bottom edge instead of stretching/centering. */
.outreach-connect-chip {
  position: absolute;
  z-index: 1;
  left: 480px;
  right: 48px;
  bottom: calc(32px + 0.375em);
  font-size: min(calc((min(100vw, 1200px) - 528px) * 0.021211), 24px);
  filter: drop-shadow(0 20px 40px rgba(10, 6, 60, 0.35));
  opacity: 1;
  transform: scale(1) translateY(0);
  transition: opacity 380ms cubic-bezier(0.22, 1, 0.36, 1), transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .outreach-connect-chip { transition: none; }
}
/* Mocks the Connect step's message composer specifically, so it only
   makes sense floating over Connect's own photo. */
.outreach-demo-inner:not([data-active-pillar="connect"]) .outreach-connect-chip {
  opacity: 0;
  transform: scale(0.8) translateY(24px);
  pointer-events: none;
}
@media (min-width: 1600px) {
  .outreach-connect-chip { font-size: min(calc((100vw - 748px) * 0.021211), 24px); }
}
@media (max-width: 1023px) {
  .outreach-connect-chip { display: none; }
}
.outreach-connect-chip-highlight {
  position: absolute;
  inset: -0.375em;
  background: #f5f3f1;
  opacity: 0.8;
  border-radius: 1.1em;
}
/* All sizing below is scaled to 0.85× the original (a flat "S=0.85"
   shrink — text and spacing both a bit smaller, so the whole card sits
   shorter) per the user's request to make the message chip more compact.
   Where a property sits on the SAME element as that element's own
   font-size (templates/text/token/save/add-btn all set their own
   font-size), only the font-size line below was actually edited — its
   height/padding/gap em-numbers were deliberately left AS-IS, because an
   em value resolves against its own element's font-size: cutting that
   font-size alone already shrinks every other em property on the same
   element (and on any child that doesn't set its own font-size) by the
   same 0.85 automatically. Only the handful of properties that are
   root-relative (no font-size in their own ancestor chain) — card
   gap/padding, header gap/icon, count, emoji, divider margin — needed
   their literal em number multiplied by 0.85 by hand. */
.outreach-connect-chip-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.85em;
  width: 100%;
  padding: 0.85em;
  background: #fff;
  border: 0.0625em solid #e5e5e5;
  border-radius: 0.875em;
  box-shadow: 0 1px 2px rgba(10, 6, 60, 0.06);
}
.outreach-connect-chip-header { display: flex; align-items: center; gap: 0.425em; }
.outreach-connect-chip-header > svg { flex: none; width: 0.85em; height: 0.85em; }
.outreach-connect-chip-title { font-size: 0.85em; line-height: 1.5em; font-weight: 500; color: #0a0a0a; }
.outreach-connect-chip-templates {
  display: flex;
  align-items: center;
  gap: 0.375em;
  flex: none;
  margin-left: auto;
  height: 2.25em;
  padding-inline: 0.875em;
  border: 0.0625em solid #e5e5e5;
  border-radius: 999px;
  font-size: 0.7438em; /* 0.875 * 0.85 */
  font-weight: 500;
  color: #0a0a0a;
  white-space: nowrap;
}
.outreach-connect-chip-templates svg { flex: none; width: 1em; height: 1em; }
.outreach-connect-chip-box {
  position: relative;
  border: 0.0625em solid #e5e5e5;
  border-radius: 0.875em;
  background: #faf9f8;
}
.outreach-connect-chip-text {
  margin: 0;
  padding: 0.75em 0.75em 1.5em;
  font-size: 0.7438em; /* 0.875 * 0.85 */
  line-height: 1.4285em;
  color: #0a0a0a;
  font-weight: 400;
}
.outreach-connect-chip-text b { font-weight: 500; }
.outreach-connect-chip-count {
  position: absolute;
  right: 0.75em;
  bottom: 0.5em;
  font-size: 0.6375em; /* 0.75 * 0.85, root-relative (box sets no font-size) */
  color: #737373;
}
.outreach-connect-chip-actions { display: flex; align-items: center; justify-content: space-between; gap: 0.5em; }
.outreach-connect-chip-tokens { display: flex; align-items: center; gap: 0.5em; }
.outreach-connect-chip-token {
  display: flex;
  align-items: center;
  gap: 0.25em;
  flex: none;
  height: 1.75em;
  padding-inline: 0.625em;
  border: 0.0625em solid #e5e5e5;
  border-radius: 999px;
  font-size: 0.68em; /* 0.8 * 0.85 */
  font-weight: 500;
  color: #0a0a0a;
  white-space: nowrap;
}
.outreach-connect-chip-token svg { flex: none; width: 0.875em; height: 0.875em; }
/* Matches .outreach-connect-chip-token's actual height — NOT the same
   "1.75em" written there, because that em resolves against the token's
   own font-size: 0.68em, i.e. 1.75 * 0.68 = 1.19em of the shared root
   size. This element doesn't reduce its own font-size, so it needs the
   already-multiplied 1.19em/0.595em values to land on the same physical
   size instead of coming out 1.75/0.875 (too big) — same self-relative
   trap as noted on .outreach-manage-chip-category/-tier above. */
.outreach-connect-chip-emoji {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.19em;
  height: 1.19em;
  border: 0.0625em solid #e5e5e5;
  border-radius: 999px;
}
.outreach-connect-chip-emoji svg { width: 0.595em; height: 0.595em; }
.outreach-connect-chip-save {
  display: flex;
  align-items: center;
  gap: 0.25em;
  flex: none;
  font-size: 0.6375em; /* 0.75 * 0.85 */
  font-weight: 500;
  color: #737373;
  white-space: nowrap;
}
.outreach-connect-chip-save svg { flex: none; width: 0.875em; height: 0.875em; }
.outreach-connect-chip-divider { border-top: 0.0625em solid #e5e5e5; margin-inline: -0.85em; }
.outreach-connect-chip-add { display: flex; gap: 0.5em; }
.outreach-connect-chip-add-btn {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  height: 2.25em;
  border: 0.0625em solid #e5e5e5;
  border-radius: 999px;
  font-size: 0.7438em; /* 0.875 * 0.85 */
  font-weight: 500;
  color: #0a0a0a;
}
.outreach-connect-chip-add-btn svg { flex: none; width: 1em; height: 1em; }

/* Manage pillar overlay — the "My creators" CRM table's first 3 real mock
   rows (affiliate-crm/creators-view.tsx), same real-data reuse and same
   positioning system (left/right insets, font-size formula, bottom-anchor
   onto .outreach-card's 32px inset) as .outreach-search-chip/
   .outreach-connect-chip above. */
.outreach-manage-chip {
  position: absolute;
  z-index: 1;
  left: 480px;
  right: 48px;
  bottom: calc(32px + 0.375em);
  font-size: min(calc((min(100vw, 1200px) - 528px) * 0.021211), 24px);
  filter: drop-shadow(0 20px 40px rgba(10, 6, 60, 0.35));
  opacity: 1;
  transform: scale(1) translateY(0);
  transition: opacity 380ms cubic-bezier(0.22, 1, 0.36, 1), transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .outreach-manage-chip { transition: none; }
}
.outreach-demo-inner:not([data-active-pillar="manage"]) .outreach-manage-chip {
  opacity: 0;
  transform: scale(0.8) translateY(24px);
  pointer-events: none;
}
@media (min-width: 1600px) {
  .outreach-manage-chip { font-size: min(calc((100vw - 748px) * 0.021211), 24px); }
}
@media (max-width: 1023px) {
  .outreach-manage-chip { display: none; }
}
.outreach-manage-chip-highlight {
  position: absolute;
  inset: -0.375em;
  background: #f5f3f1;
  opacity: 0.8;
  border-radius: 1.1em;
}
.outreach-manage-chip-card {
  position: relative;
  overflow: hidden;
  width: 100%;
  background: #fff;
  border: 0.0625em solid #e5e5e5;
  border-radius: 0.875em;
  box-shadow: 0 1px 2px rgba(10, 6, 60, 0.06);
}
.outreach-manage-chip-table { width: 100%; table-layout: fixed; border-collapse: collapse; }
/* #e5e5e5 (same hex as every other line in this table) reads visibly
   fainter against this row's warm #faf9f8 fill than it does against the
   body rows' white — same line, less contrast, looks like a different
   color. #e0dfde compensates so the header's own lines read as the same
   weight as the body's. */
.outreach-manage-chip-table thead tr { height: 2.1em; background: #faf9f8; border-bottom: 0.0625em solid #e0dfde; }
/* th/td each set their own font-size (0.6875em/0.8125em of the chip's own
   root em — shrunk from the original 0.75/0.875 so the whole table reads
   smaller and rows can be shorter) to size their plain text correctly.
   Every OTHER em value nested inside them — icons, the avatar, pills, the
   tier badge — is resolving against that already-shrunk font-size, not
   the chip's root, so each one below is deliberately written as
   (intended-root-ratio / 0.6875 or 0.8125) instead of the plain
   intended-root-ratio. Skipping that division was the exact bug that made
   the emoji circle 25% oversized earlier and the L7/L6/L8 tier badge
   nearly illegible here — see the comment on .outreach-connect-chip-emoji
   above for the first instance of this same trap. */
.outreach-manage-chip-table th {
  padding-inline: 0.5455em; /* 0.375 root / 0.6875 */
  text-align: left;
  font-size: 0.6875em;
  font-weight: 500;
  color: #737373;
  white-space: nowrap;
}
.outreach-manage-chip-table th svg { flex: none; width: 1.1636em; height: 1.1636em; vertical-align: -0.2em; margin-left: 0.2909em; } /* 0.8 root / 0.6875 */
/* 30D GMV / 30D Live GMV: extra left padding shifts their numbers right,
   off the Product category divider, without moving the column boundary
   itself (right padding stays the base value above). */
.outreach-manage-chip-table th:nth-child(4),
.outreach-manage-chip-table th:nth-child(5) {
  padding-left: 2.3273em; /* 1.6 root / 0.6875 */
}
.outreach-manage-chip-table tbody tr { height: 3.4em; }
.outreach-manage-chip-table tbody tr:not(:first-child) { border-top: 0.0625em solid #e5e5e5; }
.outreach-manage-chip-table td { padding-inline: 0.4615em; font-size: 0.8125em; color: #0a0a0a; vertical-align: middle; } /* 0.375 root / 0.8125 */
.outreach-manage-chip-table td:nth-child(4),
.outreach-manage-chip-table td:nth-child(5) {
  padding-left: 1.9692em; /* 1.6 root / 0.8125 */
}
/* Creator column divider — same "pinned column" cue the real CRM table
   uses (a border on the edge of its sticky Creator column) even though
   nothing actually scrolls here. Split into two colors for the same
   reason as thead's border-bottom above: the th portion sits on
   #faf9f8 and needs the darker #e0dfde to read as the same line weight
   as the td portion, which sits on white and uses the plain #e5e5e5
   every other body-row line uses. */
.outreach-manage-chip-table th:first-child { border-right: 0.0625em solid #e0dfde; padding-left: 1.0909em; } /* 0.75 root / 0.6875 */
.outreach-manage-chip-table td:first-child { border-right: 0.0625em solid #e5e5e5; padding-left: 0.9231em; } /* 0.75 root / 0.8125 */
.outreach-manage-chip-creator { display: flex; align-items: center; gap: 0.5em; min-width: 0; }
.outreach-manage-chip-creator img { flex: none; width: 2.4615em; height: 2.4615em; border-radius: 999px; object-fit: cover; } /* 2 root / 0.8125 */
.outreach-manage-chip-creator-text { display: flex; flex-direction: column; min-width: 0; }
/* No own font-size — td's own 0.8125em already IS the intended
   0.8125-root text size, so inheriting it directly (rather than writing
   an em value here, which would re-multiply) is what's actually
   correct. */
.outreach-manage-chip-name { overflow: hidden; font-weight: 500; color: #0a0a0a; white-space: nowrap; text-overflow: ellipsis; }
.outreach-manage-chip-handle { font-size: 0.8462em; color: #737373; } /* 0.6875 root / 0.8125 */
.outreach-manage-chip-star-cell { text-align: center; }
/* Reused on the "Starred" <th> itself so its label centers over the star
   icons below instead of following the other headers' left-align — needs
   the extra specificity to win over .outreach-manage-chip-table th's own
   text-align: left. */
.outreach-manage-chip-table th.outreach-manage-chip-star-cell { text-align: center; }
.outreach-manage-chip-star-cell svg { width: 1.3846em; height: 1.3846em; } /* 1.125 root / 0.8125 */
.outreach-manage-chip-categories { display: flex; flex-wrap: wrap; gap: 0.25em; }
.outreach-manage-chip-category {
  display: inline-flex;
  align-items: center;
  /* height/padding/border are on this same element, so — once font-size
     below lands its OWN font-size at 0.6875 root — these resolve against
     that 0.6875, not root either: each is (intended-root / 0.6875). */
  height: 1.8182em;
  padding-inline: 0.8em;
  border: 0.0909em solid #f5f3f1;
  border-radius: 999px;
  background: #faf9f8;
  font-size: 0.8462em; /* 0.6875 root / 0.8125 (td's context) → own font-size lands at 0.6875 root */
  font-weight: 500;
  color: #737373;
  white-space: nowrap;
}
.outreach-manage-chip-gmv { display: inline-flex; align-items: center; gap: 0.375em; }
.outreach-manage-chip-tier {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  /* Same self-relative trap as .outreach-manage-chip-category above: once
     font-size lands this element's own font-size at 0.4375 root, width/
     height (intended 1.3125/0.8203 root) resolve against that 0.4375,
     not root — each is (intended-root / 0.4375), i.e. 3em/1.875em. */
  width: 3em;
  height: 1.875em;
  border-radius: 999px;
  font-size: 0.5385em; /* 0.4375 root / 0.8125 (td's context) → own font-size lands at 0.4375 root */
  font-weight: 700;
  line-height: 1;
  color: #fff;
}

/* Promos pillar overlay — the "Contest type" card grid from quacko_app's
   contest editor (content/contest-editor-view.tsx, CONTEST_TYPES), same
   real-icons/real-copy reuse and same positioning system as
   .outreach-search-chip/.outreach-connect-chip/.outreach-manage-chip
   above. Unlike those, nothing here sets its own font-size except the two
   text leaves (-title/-desc) and the label, so every other dimension
   below is a plain (real-px / 20) root em — no self-relative division
   needed, since 1 chip-root em ≈ 20 real px here (cross-checked against
   .outreach-manage-chip's own icon/tier-badge root ratios above). */
.outreach-promos-chip {
  position: absolute;
  z-index: 1;
  left: 480px;
  right: 48px;
  bottom: calc(32px + 0.375em);
  font-size: min(calc((min(100vw, 1200px) - 528px) * 0.021211), 24px);
  filter: drop-shadow(0 20px 40px rgba(10, 6, 60, 0.35));
  opacity: 1;
  transform: scale(1) translateY(0);
  transition: opacity 380ms cubic-bezier(0.22, 1, 0.36, 1), transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .outreach-promos-chip { transition: none; }
}
.outreach-demo-inner:not([data-active-pillar="promos"]) .outreach-promos-chip {
  opacity: 0;
  transform: scale(0.8) translateY(24px);
  pointer-events: none;
}
@media (min-width: 1600px) {
  .outreach-promos-chip { font-size: min(calc((100vw - 748px) * 0.021211), 24px); }
}
@media (max-width: 1023px) {
  .outreach-promos-chip { display: none; }
}
.outreach-promos-chip-highlight {
  position: absolute;
  inset: -0.375em;
  background: #f5f3f1;
  opacity: 0.8;
  border-radius: 1.1em;
}
.outreach-promos-chip-card {
  position: relative;
  width: 100%;
  padding: 0.85em;
  background: #fff;
  border: 0.0625em solid #e5e5e5;
  border-radius: 0.875em;
  box-shadow: 0 1px 2px rgba(10, 6, 60, 0.06);
}
.outreach-promos-chip-label {
  display: block;
  font-size: 0.7em; /* 14px root */
  font-weight: 500;
  color: #0a0a0a;
  margin-bottom: 0.5714em; /* 8px gap / 14px own font-size, self-relative */
}
.outreach-promos-chip-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.4em; } /* 8px root */
.outreach-promos-chip-item {
  display: flex;
  align-items: center;
  gap: 0.4em; /* 8px root */
  min-height: 3.4em; /* 68px root */
  padding: 0.8em; /* 16px root */
  border: 0.0625em solid #e5e5e5;
  border-radius: 0.7em; /* 14px root */
  background: #fff;
}
.outreach-promos-chip-item.is-active { background: #faf9f8; box-shadow: 0 1px 2px rgba(10, 6, 60, 0.06); }
.outreach-promos-chip-icon {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 1.8em; /* 36px root */
  height: 1.8em;
  border-radius: 999px;
}
.outreach-promos-chip-icon svg { width: 1em; height: 1em; } /* 20px root */
.outreach-promos-chip-text { min-width: 0; flex: 1; }
.outreach-promos-chip-title-row { display: flex; align-items: center; gap: 0.2em; } /* 4px root */
.outreach-promos-chip-title-row svg { flex: none; width: 0.7em; height: 0.7em; } /* 14px root, matches title's own size below */
.outreach-promos-chip-title { min-width: 0; font-size: 0.7em; line-height: 1.1429em; font-weight: 500; color: #0a0a0a; } /* 14px root, 16px line-height */
.outreach-promos-chip-desc {
  display: block;
  margin-top: 0.3333em; /* 4px gap / 12px own font-size, self-relative — same trap as .outreach-manage-chip-tier above */
  font-size: 0.6em; /* 12px root */
  line-height: 1.0833em; /* 13px line-height */
  color: #737373;
}

/* Insight pillar overlay — the brand-detail header card from quacko_app's
   Social Intelligence (social-intelligence/brand-detail-view.tsx), same
   real-icons/real-copy/real-mock-data reuse and positioning system as the
   chips above. Two stacked cards instead of the real page's one, and every
   gap/size tightened from the real row's much wider spacing (it's meant to
   span a full page, gap-8 between stats) to fit chip width — same
   no-nested-font-size convention as .outreach-promos-chip above, so
   dimensions below are plain (real-px / 20) chip-root em unless commented
   otherwise. */
.outreach-insight-chip {
  position: absolute;
  z-index: 1;
  left: 480px;
  right: 48px;
  bottom: calc(32px + 0.375em);
  display: flex;
  flex-direction: column;
  gap: 0.4em; /* 8px root */
  font-size: min(calc((min(100vw, 1200px) - 528px) * 0.021211), 24px);
  filter: drop-shadow(0 20px 40px rgba(10, 6, 60, 0.35));
  opacity: 1;
  transform: scale(1) translateY(0);
  transition: opacity 380ms cubic-bezier(0.22, 1, 0.36, 1), transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .outreach-insight-chip { transition: none; }
}
.outreach-demo-inner:not([data-active-pillar="insight"]) .outreach-insight-chip {
  opacity: 0;
  transform: scale(0.8) translateY(24px);
  pointer-events: none;
}
@media (min-width: 1600px) {
  .outreach-insight-chip { font-size: min(calc((100vw - 748px) * 0.021211), 24px); }
}
@media (max-width: 1023px) {
  .outreach-insight-chip { display: none; }
}
/* Sits behind both stacked cards as one shared warm backdrop, same visual
   role as every other chip's -highlight — inset is relative to the flex
   column's own box, which already wraps both cards + their gap. */
.outreach-insight-chip-highlight {
  position: absolute;
  inset: -0.375em;
  background: #f5f3f1;
  opacity: 0.8;
  border-radius: 1.1em;
}
.outreach-insight-chip-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.5em; /* 10px root, real gap-4=16px */
  padding: 0.75em; /* 15px root, real p-4=16px */
  background: #fff;
  border: 0.0625em solid #e5e5e5;
  border-radius: 0.7em; /* 14px root, rounded-card */
  box-shadow: 0 1px 2px rgba(10, 6, 60, 0.06);
}
.outreach-insight-chip-logo {
  flex: none;
  width: 2em; /* 40px root, real size-[58px] */
  height: 2em;
  border-radius: 0.35em;
  border: 0.0625em solid #e5e5e5;
  object-fit: cover;
}
.outreach-insight-chip-info { display: flex; flex-direction: column; gap: 0.15em; } /* 3px root */
.outreach-insight-chip-name { font-size: 0.9em; font-weight: 600; line-height: 1.2; color: #0a0a0a; } /* 18px root */
.outreach-insight-chip-rating {
  display: flex;
  align-items: center;
  gap: 0.2em; /* 4px of this element's own font-size below, self-relative — but same ratio (0.286) either way since 4/14=0.286 */
  font-size: 0.7em; /* 14px root */
  color: #737373;
  white-space: nowrap;
}
.outreach-insight-chip-rating svg { flex: none; width: 1em; height: 1em; } /* 14px, matches text size like the real size-3.5/text-sm pair */
/* Stats gap opened up well past the "shortened" 0.5em it started at — the
   user's own complaint was that this side read too cramped, so this now
   sits closer to (though still under) the real page's gap-8, not a small
   nudge off the compact starting point. */
.outreach-insight-chip-stats { display: flex; align-items: center; gap: 1.1em; margin-left: auto; } /* 22px root, real gap-8=32px */
.outreach-insight-chip-stat { display: flex; flex-direction: column; gap: 0.1em; } /* 2px root */
.outreach-insight-chip-fav { align-items: center; }
.outreach-insight-chip-stat-label { font-size: 0.65em; color: #737373; white-space: nowrap; } /* 13px root */
.outreach-insight-chip-stat-value { font-size: 0.8em; font-weight: 600; color: #0a0a0a; white-space: nowrap; } /* 16px root */
.outreach-insight-chip-fav svg { width: 0.9em; height: 0.9em; } /* 18px root */

.outreach-card {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 400px;
  background: #fff;
  border-radius: 24px;
  padding: 40px 36px;
  color: var(--color-ink);
  box-shadow: 0 24px 60px rgba(10, 6, 60, 0.35);
}
.outreach-card h3 {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 28px;
}
/* Single-open accordion replacing the old flat outreach-list checklist —
   see the HTML comment above .outreach-accordion for why. Each row's panel
   uses the "grid-template-rows: 0fr -> 1fr" trick so it can transition
   height without JS ever measuring scrollHeight; .outreach-accordion-panel
   is the animated grid track and .outreach-accordion-panel-inner is the
   overflow:hidden layer that actually gets clipped mid-transition. */
/* Each item is an equal-height flex slot (flex:1) spreading the 5 rows
   evenly across the card's full (stretched) height, same effect as the old
   space-between — but now the item's own box spans its whole slot (divider
   to divider) instead of just wrapping the button, so justify-content:
   center below actually centers the icon+label in the middle of that slot
   instead of leaving it pinned to the top with dead space underneath. */
.outreach-accordion { display: flex; flex-direction: column; flex: 1; }
.outreach-accordion-item { border-top: 1px solid var(--color-border); flex: 1; display: flex; flex-direction: column; justify-content: center; }
.outreach-accordion-item:first-child { border-top: none; }
.outreach-accordion-trigger {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  /* padding-inline: 0 is load-bearing, not decoration — this is a real
     <button>, and `border:none` below clears the UA default border but
     never touched the UA default padding (Chromium ships one of
     ~6px/side). That leftover padding was invisible while this sat as a
     plain collapsed accordion row, but once mobile (below) turns every
     pillar into its own card with the description paragraph flush-left
     right underneath, it reads as the icon sitting ~6px right of that
     text — reported as "icon isn't left-aligned" from a screenshot of
     exactly that mobile card. Fixing it here (not just on mobile) is
     correct either way: it was never an intentional 6px on desktop
     either, just an unreset default nobody had a flush-left neighbor to
     notice it against until now. */
  padding-block: 18px;
  padding-inline: 0;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--color-ink-soft);
  transition: color 220ms ease;
}
/* Fixed-width column, icon centered inside it — the icons themselves are
   different native widths (Search's 32px vs the other four's 26px, plus
   the rocket/sparkle glyphs aren't square within their own viewBox), which
   left each row's label starting at a different x position when the icon
   just sized to its own content. */
.outreach-accordion-icon {
  display: inline-flex;
  flex-shrink: 0;
  width: 32px;
  justify-content: center;
  color: inherit;
}
.outreach-accordion-item.is-open .outreach-accordion-trigger { color: var(--color-ink); }
.outreach-accordion-trigger:hover { color: var(--color-ink); }
.outreach-accordion-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
.outreach-accordion-item.is-open .outreach-accordion-panel { grid-template-rows: 1fr; }
.outreach-accordion-panel-inner { overflow: hidden; }
.outreach-accordion-desc {
  color: var(--color-ink-soft);
  font-size: 1rem;
  line-height: 1.5;
  padding-top: 4px;
  padding-bottom: 20px;
}
.outreach-accordion-panel-inner .btn { margin-bottom: 24px; }
/* Desktop-only markup carries the crossfading full-bleed .outreach-demo-bg
   photo + floating pillar chips instead — this per-item <img> exists purely
   for the mobile stacked layout below and stays out of the way here. */
.outreach-accordion-image { display: none; }
@media (prefers-reduced-motion: reduce) {
  .outreach-accordion-panel { transition: none; }
}

/* FAQ — two-column layout ported from the Framer reference
   (incomparable-employees-345264.framer.app): a sticky intro/CTA aside on
   the left (orange-dot kicker, big headline, CTA pill + circular arrow
   button), and an accordion of rounded card rows on the right, all sitting
   on the section's own tinted background so the open (white) row reads as
   a raised card. Same grid-template-rows expand/collapse trick as
   .outreach-accordion above, but its own classes/JS handler: unlike that
   showcase accordion (single row open, tied to a bg-image crossfade), this
   plain FAQ list lets any number of items stay open at once. */
/* Dark brand-purple treatment: closed rows are a faint white-on-purple
   tint (so they read as cards without reintroducing the warm cream that
   clashed with this background), text goes white, and only the opened
   row (plus its answer panel) flips back to a light, near-white surface
   so the dark ink-soft answer copy stays legible. */
.faq-section { background: #572077; }
.faq-layout {
  display: grid;
  grid-template-columns: minmax(220px, 320px) 1fr;
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}
.faq-aside { position: sticky; top: 110px; }
/* Reuses the shared .eyebrow class but recolored for this dark section —
   the base class's --color-brand-dark (orange) reads muddy on the purple
   background, so this row gets its own lime accent instead. */
.faq-aside .eyebrow { color: #CCEA01; }
/* Heading reuses Why Quacko's exact text treatment (2.75rem Tomato
   Grotesk h2) recolored to white for this dark section. */
.faq-heading {
  margin-top: 16px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
  color: #fff;
}

/* Card blocks — each row is its own rounded/filled container on the dark
   purple background (a faint white tint, brightening slightly on hover),
   flipping to a solid white card once opened so the dark ink-soft answer
   copy stays legible. */
.faq-accordion { display: flex; flex-direction: column; gap: 12px; }
.faq-item {
  background: rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: background-color 220ms ease;
}
.faq-item:not(.is-open):hover { background: rgba(255, 255, 255, 0.16); }
.faq-item.is-open { background: #fff; }
.faq-item-heading { margin: 0; }
.faq-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  width: 100%;
  padding: 26px 28px;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2rem; /* matches .quote-card-headline ("Smart Flow runs like a second person...") */
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: #fff;
  transition: color 220ms ease;
}
.faq-item.is-open .faq-trigger { color: var(--color-ink); }
/* Reuses the site's standard circular arrow-icon-button verbatim (see
   .quote-stack-arrow / DESIGN_GUIDELINES.md "箭头图标按钮"): same ring +
   arrow geometry, same outline-default/solid-on-hover crossfade, just
   smaller and rotated to point down (the un-rotated art points right, so
   +90° = down) instead of the previous plain "+" glyph. Expanding the row
   rotates it another 180° to point up, the same 4-direction/90°-steps
   relationship the doc describes, rather than swapping to a separate
   "×" icon. */
.faq-icon {
  position: relative;
  flex-shrink: 0;
  display: inline-flex;
  width: 32px;
  height: 32px;
  color: #fff;
  transform: rotate(90deg);
  transition: transform 260ms ease;
}
/* Opened row's icon sits on the now-white card, so it switches to the
   section's own purple background color (#572077, same as .faq-section)
   instead of white — reads as a "punch-through" back to the page bg. */
.faq-item.is-open .faq-icon { transform: rotate(270deg); color: #572077; }
.faq-icon-layer { position: absolute; inset: 0; display: flex; transition: opacity .15s ease; }
.faq-icon-layer svg { width: 100%; height: 100%; display: block; }
.faq-icon-layer--solid { opacity: 0; }
.faq-trigger:hover .faq-icon-layer--outline,
.faq-trigger:focus-visible .faq-icon-layer--outline { opacity: 0; }
.faq-trigger:hover .faq-icon-layer--solid,
.faq-trigger:focus-visible .faq-icon-layer--solid { opacity: 1; }
.faq-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 380ms cubic-bezier(0.22, 1, 0.36, 1);
}
.faq-item.is-open .faq-panel { grid-template-rows: 1fr; }
.faq-panel-inner { overflow: hidden; background: #fff; }
.faq-answer { padding: 0 28px 28px; color: var(--color-ink-soft); font-size: 1.125rem; line-height: 1.6; } /* matches .ai-copy-text's body size, under the "Automated Outreach" row */
.faq-answer p + p,
.faq-answer ul { margin-top: 12px; }
.faq-answer ul { padding-left: 20px; list-style: disc; display: flex; flex-direction: column; gap: 6px; }
@media (max-width: 1023px) {
  .faq-layout { grid-template-columns: 1fr; gap: 32px; }
  .faq-aside { position: static; }
  .faq-trigger { font-size: 1.375rem; }
}
@media (max-width: 480px) {
  .faq-trigger { padding: 20px; font-size: 1.125rem; }
  .faq-answer { padding: 0 20px 20px; }
}
@media (prefers-reduced-motion: reduce) {
  .faq-panel, .faq-icon, .faq-item, .faq-trigger { transition: none; }
}

@media (max-width: 1023px) {
  /* min-height/display are desktop-only (they pin the section's height and
     stretch the card into it) — undo both so mobile keeps auto-sizing to
     the card's own natural, narrower height like before. overflow reverts
     to visible: it only existed to clip the full-bleed bg photo (now
     hidden below) to the rounded corners, and a leftover hidden would
     otherwise crop the accordion cards' own box-shadow at this box's edge.
     padding-inline is set explicitly to .container's own default gutter
     (16px, 32px from 768px up — matched below) rather than left to fall
     through to .container's rule: this element doubles as .container (see
     the markup) but its own un-scoped `padding: 32px` rule further up
     already pins inline sizing too, at equal specificity but later in the
     cascade, so .container's value never actually wins on its own. Without
     this the cards sit at that unrelated 32px on every mobile width
     instead of matching .why-quacko-inner's own cards' margin below. */
  .outreach-demo-inner { display: block; min-height: 0; padding-block: 0; padding-inline: 16px; border-radius: 0; background: none; overflow: visible; }
  /* Same tight gap under the section above as .audience-stats — desktop's
     55px reads as a big dead gap once the mobile stack above no longer
     has that section's own bottom padding to pair against. */
  .section.outreach-demo { padding-top: 16px; }
  /* No more floating white card over a photo to hold onto now that each
     pillar carries its own image below — flatten .outreach-card down to a
     plain wrapper (just the heading) so the section reads as normal page
     content, at the standard .container gutter, not a boxed-in panel.
     No max-width/centering either (that old 600px cap left the accordion
     cards' own left/right margin drifting wider than .why-quacko-card's
     below at tablet widths) — full container width, same as that grid. */
  .outreach-card { max-width: none; background: none; padding: 0; border-radius: 0; box-shadow: none; }
  .outreach-card h3 { text-align: center; margin-bottom: 16px; }

  /* Mobile drops the desktop "single row open, full-bleed photo behind an
     accordion card" pattern entirely — no room for a floating photo behind
     a small card, and a collapsed side-menu of just icons+labels hides
     most of the section's content. Instead every pillar is shown fully
     expanded, stacked in document order: label, description, button, then
     that pillar's own photo — so scrolling the card *is* "see how it
     works" instead of requiring taps to reveal each row. */
  .outreach-demo-bg { display: none; }
  .outreach-accordion-panel { grid-template-rows: 1fr; }
  .outreach-accordion-trigger { cursor: default; padding-block: 0; }
  .outreach-accordion-image {
    display: block;
    width: 100%;
    height: auto;
    margin-top: 12px;
    border-radius: var(--radius-md);
  }

  /* Each pillar becomes its own card — same fill/border/shadow recipe as
     .why-quacko-card just below this section, so the two card grids read
     as one consistent system — instead of a row separated from its
     neighbors by a single top divider. */
  .outreach-accordion { gap: 16px; }
  .outreach-accordion-item {
    border-top: none;
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 20px;
  }
}

/* ==========================================================================
   Why Quacko — bento benefits (copy + CTA left, 2x2 image grid right)
   ========================================================================== */
/* Paired with .outreach-demo's own 55px bottom padding above, so the gap
   between that panel and this section totals 110px. */
.section.why-quacko { padding-top: 55px; }
.why-quacko-inner {
  display: flex;
  align-items: flex-start;
  gap: clamp(40px, 6vw, 96px);
}
/* Sticks to the top of the viewport (110px, matching the section's own
   left/right gutter) while the taller card column keeps scrolling past
   it, then scrolls away with the rest of the page once the grid — its
   sticky-positioning container — runs out of room beneath it. */
.why-quacko-copy {
  flex: 0 1 340px;
  min-width: 0;
  position: sticky;
  top: 110px;
}
.why-quacko-copy h2 {
  margin-top: 16px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
.why-quacko-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 28px;
}
/* "Book a Demo" now reuses the Hero secondary button verbatim (one pill,
   arrow inline) instead of a separate pill + standalone orange arrow
   circle — that two-piece shape read as a different button style from
   the Hero CTAs. Same standard size as .hero-actions .btn / Product
   Highlight's "Learn More": padding 12px 22px, font-size 18px/600, 24px
   text/arrow gap. */
.why-quacko-actions .btn {
  gap: 24px;
  padding: 12px 22px;
  font-size: 1.125rem;
}

/* 6-card masonry — two independent columns, each stacking its own three
   cards top to bottom. Not a shared CSS grid: the reference's row lines
   never line up across columns (col 1's stat card is much shorter than
   col 2's second photo card), which is exactly what two flex columns with
   their own gap give you for free, no masonry/grid-row juggling needed. */
.why-quacko-grid {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  gap: 20px;
}
.why-quacko-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.why-quacko-card {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.why-quacko-card:not(.why-quacko-card--media) { padding: 24px; }

/* Media cells (photo collage / product shot) — plain placeholders standing
   in for real photography, swap the inner .why-quacko-media div for an
   <img> when real assets are ready. */
.why-quacko-card--media {
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.why-quacko-media {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-alt);
  color: var(--color-border);
}
/* Figma node 3213:59509 — keep the complete two-message composition
   visible inside the taller card media slot. The exported node's own
   pale-green field continues behind the contained image, so the media
   area reads as one full-bleed visual without stretching or cropping the
   message bubbles. */
.why-quacko-media--creator-messages {
  position: relative;
  overflow: hidden;
  background-color: #e6efd3;
}
/* Was a square graph-paper grid (linear-gradient lines masked to the green
   field via creator-profile-messages-grid-mask.png, lining up pixel-for-
   pixel with the bubble image below) — removed per feedback. Left the
   plain #e6efd3 field on .why-quacko-media--creator-messages above as the
   card's background, matching Figma node 3213:59509's own flat pale-green
   field with no grid texture in it. */
.creator-message-layer {
  position: absolute;
  left: 0;
  width: 100%;
  height: 50%;
  display: block;
  overflow: hidden;
  opacity: 0;
  transform: translate3d(var(--message-start-x), 8px, 0);
  will-change: transform, opacity;
  backface-visibility: hidden;
}
.creator-message-layer img {
  position: absolute;
  left: 0;
  width: 100%;
  height: 200%;
  object-fit: contain;
}
.creator-message-layer--outgoing {
  --message-start-x: 38px;
  top: 0;
}
.creator-message-layer--outgoing img {
  top: 0;
}
.creator-message-layer--reply {
  --message-start-x: -38px;
  bottom: 0;
}
.creator-message-layer--reply img {
  bottom: 0;
}
[data-profile-messages-reveal].is-revealed .creator-message-layer {
  animation: creatorMessageSend 680ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
[data-profile-messages-reveal].is-revealed .creator-message-layer--reply {
  animation-delay: 180ms;
}
@keyframes creatorMessageSend {
  0% {
    opacity: 0;
    transform: translate3d(var(--message-start-x), 8px, 0);
  }
  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .creator-message-layer {
    opacity: 1;
    transform: none;
    animation: none;
  }
}
.why-quacko-caption {
  height: 64px;
  flex-shrink: 0;
  padding-inline: 20px;
  display: flex;
  align-items: center;
  font-weight: 600;
}
/* Shop product visual — Figma node 3179:102280 (Bs95DokMdAVE8W5APlm6hI):
   three tilted product cards (sneaker/lipstick/t-shirt) in one row over
   pink, same one-row treatment as .why-quacko-media--outreach below. Each
   tile keeps Figma's 58:69 aspect via aspect-ratio so it scales cleanly at
   any card width; image/price positions are percentages of that tile
   (lifted directly from Figma's ml/mt-in-58×69 offsets) rather than fixed
   px, for the same reason. data-shop-reveal page-turns the three tiles in
   one after another (see js/main.js): each starts rotated back on its own
   left edge, like a page not yet turned, then swings to flat — the
   perspective on the container is what makes that rotation read as a
   turning card instead of a flat squash. */
.why-quacko-media--shop {
  position: relative;
  background: #f3c1d3;
  overflow: hidden;
  align-items: center;
  gap: 8px;
  padding: 16px 14px 18px;
  perspective: 900px;
}
.why-quacko-shop-tile {
  position: relative;
  flex: 1 1 0;
  max-width: 150px;
  aspect-ratio: 58 / 69;
  border-radius: 6px;
  overflow: hidden;
  opacity: 0;
  transform-origin: left center;
  transform: rotateY(-100deg);
  backface-visibility: hidden;
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s ease-out;
}
.why-quacko-media--shop.is-revealed .why-quacko-shop-tile {
  opacity: 1;
  transform: rotateY(0deg);
}
.why-quacko-shop-tile--sneaker { background: #027b91; }
.why-quacko-shop-tile--lipstick { background: #ccea00; transition-delay: 0.18s; }
.why-quacko-shop-tile--tshirt { background: #572077; transition-delay: 0.36s; }

.why-quacko-shop-image {
  position: absolute;
  object-fit: cover;
}
.why-quacko-shop-tile--sneaker .why-quacko-shop-image {
  left: 6.9%;
  top: 10.14%;
  width: 82.76%;
  height: 55.07%;
  object-fit: contain;
}
.why-quacko-shop-tile--tshirt .why-quacko-shop-image {
  left: 10.34%;
  top: 0;
  width: 79.31%;
  height: 84.06%;
  object-fit: contain;
  transform: scaleX(-1); /* Figma mirrors this one horizontally (rotate 180 + flip Y, net scaleX) */
}
/* Lipstick's raw source is a plain square product photo — cropped to
   Figma's 27×40 portrait window (as a % of the 58×69 tile) and tilted, in
   place of the manual mask-position math Figma's own export used for the
   same crop. */
.why-quacko-shop-lipstick-crop {
  position: absolute;
  left: 28.41%;
  top: 9.7%;
  width: 46.55%;
  height: 57.97%;
  border-radius: 3px;
  overflow: hidden;
  transform: rotate(14.13deg);
}
.why-quacko-shop-lipstick-crop .why-quacko-shop-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.why-quacko-shop-price {
  position: absolute;
  top: 71%;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  text-align: center;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-weight: 600;
  font-size: 20px;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.why-quacko-shop-tile--sneaker .why-quacko-shop-price { color: #ccea00; }
.why-quacko-shop-tile--lipstick .why-quacko-shop-price { color: #572077; }
.why-quacko-shop-tile--tshirt .why-quacko-shop-price { color: #ff9e5d; }
@media (prefers-reduced-motion: reduce) {
  .why-quacko-shop-tile { transition: none; opacity: 1; transform: rotateY(0deg); }
}
.why-quacko-media-heading {
  padding: 20px 20px 0;
  max-width: 260px;
}
/* "Personalized outreach at scale" is one line longer than the 260px cap
   above allows for — this card is wide enough to hold it on one line, so
   drop the cap instead of narrowing the copy. Typography and box height
   match .why-quacko-caption (16px/600, 64px tall, vertically centered)
   instead of the default h3 (18px/700, top-aligned) — per feedback that
   this title should read the same as, and line up with the same white
   strip height as, the GMV card's "Real-time GMV & payout tracking"
   caption diagonally across from it in the bento grid. Scoped to this
   --wide variant only, not the base .why-quacko-media-heading, since
   "Built for TikTok Shop teams" wraps to two lines and a fixed height
   would clip it. */
.why-quacko-media-heading--wide {
  /* Vertically centered via line-height rather than flex: this heading
     carries [data-stagger-text], which wraps each word in its own span
     with plain whitespace text nodes between them — inside a flex
     container those whitespace-only text nodes collapse away as
     anonymous flex items, silently eating the spaces between words. */
  height: 64px;
  line-height: 64px;
  flex-shrink: 0;
  padding: 0 20px;
  max-width: none;
  white-space: nowrap;
  font-size: 1rem;
  font-weight: 600;
}
.why-quacko-card--h-lg { height: 360px; }
.why-quacko-card--h-md { height: 320px; }

/* GMV chart card — Figma node 3171:100964 (Bs95DokMdAVE8W5APlm6hI), scaled
   up from its 217×110 reference into the taller bento cell: teal media
   area (same media+caption structure as the other cards in this column),
   GMV value up top, lime line-chart stretched to fill the width below.
   The chart SVG uses preserveAspectRatio="none" so the *line* stretches
   to fill the card — fine for a line, but a scaled <circle> would turn
   into an ellipse under that non-uniform stretch, so the two point
   markers are separate fixed-size HTML dots positioned by percentage
   instead of living inside the scaled SVG; the line/grid strokes use
   vector-effect="non-scaling-stroke" so their width doesn't stretch
   either. */
.why-quacko-media--gmv {
  position: relative;
  flex-direction: column;
  align-items: stretch;
  justify-content: space-between;
  background: #027b91;
  padding: 24px;
}
.why-quacko-gmv-label {
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.85);
}
.why-quacko-gmv-value {
  margin-top: 4px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #d9f20f;
}
.why-quacko-gmv-chart-wrap {
  position: relative;
  width: 100%;
  height: 130px;
}
.why-quacko-gmv-chart {
  display: block;
  width: 100%;
  height: 100%;
}
/* Traveling marker — positioned in JS via getPointAtLength() along
   #gmvChartPath rather than SVG <circle>/motion-path, because the chart
   SVG stretches non-uniformly (preserveAspectRatio="none"): a shape
   scaled inside that box would distort into an ellipse, so this is a
   fixed-size HTML dot placed with plain top/left instead. Starts hidden
   (opacity 0) until the entrance animation begins. */
.why-quacko-gmv-dot {
  position: absolute;
  top: 0;
  left: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #027b91;
  border: 2px solid #d9f20f;
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
}

/* Outreach card — Figma node 3173:100969 (Bs95DokMdAVE8W5APlm6hI): two
   sample DM cards side by side (one row) over the section's orange. Each
   card is a plain rectangular SVG (see assets/img/outreach-cards/) sheared
   into the parallelogram Figma shows via the exact
   rotate(-30deg) skewX(-30deg) scaleY(.87) transform its export used — the
   slot around it keeps that shape's layout footprint so the row spacing
   matches. js/main.js adds .is-revealed once scrolled into view
   (data-outreach-reveal): both cards rise up from below the container's
   bottom edge (clipped by this card's own overflow:hidden until then),
   card B rising a clear beat after card A rather than in lockstep. The
   skew itself stays constant through the transition — only translateY
   (in the card's own pre-skew axis, so it rises straight rather than
   along the sheared angle) and opacity animate, keeping the shear
   glitch-free. */
.why-quacko-media--outreach {
  position: relative;
  background: #ff9e5d;
  overflow: hidden;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 24px;
  padding-top: 80px;
}
.why-quacko-outreach-slot {
  /* Narrower than the (transformed) card it holds, same as Figma's own
     61px slot around a 70.4px-wide card — the two slots' overlap is what
     fans the cards close together instead of leaving a gap between them. */
  flex: 0 0 auto;
  width: 145px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
.why-quacko-outreach-card {
  flex-shrink: 0; /* img{max-width:100%} would otherwise clamp this down to the slot's width */
  width: 168px;
  height: auto;
  border-radius: 8px;
  box-shadow: 0 10px 24px rgba(107, 45, 0, 0.18);
  opacity: 0;
  transform: rotate(-30deg) skewX(-30deg) scaleY(0.87) translateY(240px);
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease-out;
}
.why-quacko-outreach-card--b { transition-delay: 0.25s; }
.why-quacko-media--outreach.is-revealed .why-quacko-outreach-card {
  opacity: 1;
  transform: rotate(-30deg) skewX(-30deg) scaleY(0.87) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  /* JS skips the reveal trigger under reduced motion, so the sheared
     resting pose is the only state these ever render in. */
  .why-quacko-outreach-card {
    transition: none;
    opacity: 1;
    transform: rotate(-30deg) skewX(-30deg) scaleY(0.87) translateY(0);
  }
}

/* Stat + avatar group, side by side (text left, avatars right, same row) */
.why-quacko-card--stat {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  /* .why-quacko-avatars has flex-shrink:0 (via its own flex:0 0 auto) and
     the stat number/label next to it don't shrink either, so on the
     narrowest real phones (~320px) the two together no longer fit this
     card's own width — nowrap let the avatar row run past the card and
     the page's right edge, another case of the same "unclipped overflow
     makes the whole page wider than the viewport" bug documented on
     <html>'s overflow-x:hidden rule above. A no-op everywhere the row
     already fits (which is every width down to ~360px); only kicks in
     below that. */
  flex-wrap: wrap;
}
.why-quacko-stat-copy { display: flex; flex-direction: column; align-items: flex-start; }
.why-quacko-stat-value {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.5rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.why-quacko-stat-label { margin-top: 4px; color: var(--color-ink-soft); }
/* Photo avatars, per Figma node 3169:100935 (Bs95DokMdAVE8W5APlm6hI): 36px
   circles, 2px white ring, overlapping by ~9px — replaces the earlier
   colored-initial placeholder spans. */
.why-quacko-avatars { display: flex; flex: 0 0 auto; }
.why-quacko-avatars img {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: 2px solid #fff;
  margin-left: -16px;
  display: block;
  object-fit: cover;
}
.why-quacko-avatars img:first-child { margin-left: 0; }

/* Icon-corner cell (stand-in for the reference's "Tailored to your vision"
   card: short, just a heading with a badge pinned to the corner) */
.why-quacko-card--icon {
  /* Last card in its column — grows to soak up the leftover space left by
     .why-quacko-grid stretching both columns to the taller one's height,
     so this card's own bottom edge lines up with the other column's last
     card instead of leaving blank background beneath it. */
  flex: 1 1 auto;
  position: relative;
  min-height: 140px;
  display: flex;
  align-items: center;
}
.why-quacko-card--icon h3 { max-width: 200px; }
.why-quacko-icon-badge {
  position: absolute;
  right: 24px;
  bottom: 24px;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--color-brand);
  color: #fff;
}

@media (max-width: 1023px) {
  .why-quacko-inner { flex-direction: column; align-items: stretch; gap: 40px; }
  .why-quacko-copy { flex: none; text-align: center; position: static; }
  .why-quacko-actions { justify-content: center; }
  .why-quacko-grid { flex-direction: column; }
}

/* ==========================================================================
   Features
   ========================================================================== */
.feature-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
.feature-card {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 24px;
  transition: box-shadow .15s ease, transform .15s ease;
}
.feature-card:hover { box-shadow: var(--shadow-card-hover); transform: translateY(-2px); }
.feature-icon {
  width: 44px; height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  background: var(--color-brand-tint);
  color: var(--color-brand-dark);
  margin-bottom: 16px;
}
.feature-card h3 { margin-bottom: 8px; }
.feature-card p { color: var(--color-ink-soft); font-size: 0.9375rem; }

/* ==========================================================================
   Steps
   ========================================================================== */
.steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
/* Rebuilt from a plain numbered list (bare "01"/"02"/"03" + heading + copy,
   no card) into a lime-icon-badge card grid — same "circle icon, bold
   title, description" pattern as generic feature/benefit grids (e.g.
   linktr.ee's link-shortener features section), reskinned with the site's
   own lime accent (#D9F20F, "全站唯一的高饱和强调色" per DESIGN_GUIDELINES.md
   色彩系统) instead of introducing a new accent color. Cards sit directly on
   the page's own dot-grid ground (no section background change needed —
   plain white card is enough contrast against #F5F3F1). */
.step {
  background: #fff;
  border-radius: var(--radius-lg);
  padding: 32px;
}
.step-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: #D9F20F;
  color: var(--color-ink);
  margin-bottom: 24px;
}
.step h3 { margin-bottom: 8px; font-size: 1.125rem; }
.step p { color: var(--color-ink-soft); font-size: 0.9375rem; line-height: 1.55; }
/* Same platform-wide 110px gutter as .feature-highlights' own 1600px+ rule
   above (see DESIGN_GUIDELINES.md "通栏板块左右留白") — this is a plain grid
   of cards with no shared section background of its own, so it's the
   margin-inline form (padding-inline:0 to clear .container's own 32px)
   rather than the padding-inline form full-bleed sections use. Was missing
   here even though .feature-highlights right above it already had it —
   same page, same card-grid shape, should align to the same gutter. */
@media (min-width: 1600px) {
  .feature-steps .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
}

/* ==========================================================================
   Creator marquee — was a dark bar of scrolling brand wordmarks; replaced
   per feedback with a direct copy of manychat.com's "❤️'d by 1M+
   creators, marketers & brands" strip (real photos/names/quotes, see the
   HTML comment above this section). manychat itself renders this
   light-on-white, but per feedback it's filled with the same dark panel
   color as .site-footer instead (--color-dark-panel) rather than painting
   over the page's dot-grid ground — same #C9C4BC-on-dark / #fff-heading
   text treatment .site-footer already uses, so the two dark bars on the
   page read as one consistent system. */
.creator-marquee-section.section {
  background: var(--color-dark-panel);
  color: #C9C4BC;
  border-block: 1px solid rgba(255, 255, 255, 0.14);
  padding-block: 0;
}
.creator-marquee-inner {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.creator-marquee-heading {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 20px 24px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}
/*
 * WE LOVE DUCK MARK (Figma node 3278:107352) — the frame itself is
 * 185.46x45.2px; every child below is positioned at its exact Figma
 * offset within that frame rather than eyeballed, so this stays a
 * faithful reproduction of the 4 exported layers (not a screenshot).
 */
.we-love-mark {
  position: relative;
  display: block;
  width: 185.46px;
  height: 45.2px;
  flex: none;
  /* Scaled down from the Figma node's native size per feedback — a
     transform keeps every child's hand-placed offset/skew proportional
     instead of having to re-derive each one at a smaller scale. */
  transform: scale(0.8);
}
@media (max-width: 767.98px) {
  /* Bigger on mobile specifically, per feedback — .creator-marquee-heading
     is this mark's own full-width row down here (see the ≥768px rule
     further below for where it instead becomes a narrow side rail next to
     the quote cards), so there's headroom to size the mark up without
     fighting anything else for space. Same scale() trick as the base rule
     above: one number, every child's hand-placed offset/skew stays
     proportional. */
  .we-love-mark { transform: scale(1.1); }
}
.we-love-mark-we {
  position: absolute;
  left: 3.84px;
  top: 9.58px;
  width: 64.53px;
  height: 31.5px;
  transform: skewX(-7deg) scaleY(0.99);
}
.we-love-mark-heart {
  position: absolute;
  left: 81.99px;
  top: 5.9px;
  width: 45.44px;
  height: 39.3px;
}
.we-love-mark-duck {
  position: absolute;
  left: 141.24px;
  top: 0;
  width: 44.22px;
  height: 41.69px;
}
.we-love-mark-duck-body {
  position: absolute;
  left: 4.02px;
  top: 0;
  width: 40.2px;
  height: 41.69px;
}
.we-love-mark-duck-beak {
  position: absolute;
  left: 0;
  top: 9.55px;
  width: 11.6px;
  height: 8.76px;
}

/*
 * Scroll mechanic (translateX + duplicated children) is the same one
 * js/main.js already drives via the generic data-marquee-bend/-track
 * attributes — see the "Brands marquee" comment there. Only the visual
 * treatment is new: cards instead of bare wordmarks, so this needs its
 * own height/mask tuning rather than reusing .marquee-bend.
 */
.creator-marquee {
  display: flex;
  align-items: center;
  overflow: hidden;
  padding-block: 24px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}
.creator-marquee-track {
  display: flex;
  align-items: center;
  will-change: transform;
}
.creator-marquee-item {
  display: flex;
  align-items: center;
  flex: none;
  /* Equal both sides so the star separator sits exactly centered in the
     whitespace gap between a quote and the next avatar (an asymmetric
     12px/24px split read as off-center, not "closer to this quote" as
     intended). Bumped up from 18px per feedback — more breathing room,
     still symmetric so the star stays centered. */
  margin-inline: 30px;
}
/* Star/sparkle separator between cards — same icon+treatment the old
   brand-wordmark marquee used for .marquee-bend-sep, swapped in here in
   place of the thin border-right divider. */
.creator-marquee-sep {
  display: inline-flex;
  flex: none;
  align-items: center;
  color: #fff;
  opacity: 0.4;
}
.creator-marquee-sep svg { width: 16px; height: 16px; }
.creator-marquee-photo {
  flex: none;
  width: 48px;
  height: 48px;
  margin-right: 12px;
  border-radius: 50%;
  object-fit: cover;
}
/* Fixed width + normal wrapping (not the earlier nowrap/max-content
   version) — these quotes got longer/more specific per feedback, so
   nowrap either overflowed past its own box into the next card, or a
   max-width cap alongside nowrap did nothing (nowrap ignores wrapping
   regardless of container width). A fixed width that actually wraps
   settles both: every card reserves the same real layout footprint, and
   long quotes read as ~2 lines instead of one runaway line. No name
   label anymore either, per feedback — just the quote. */
/* width:max-content (not a fixed px box) — each quote now carries an
   explicit <br> splitting it into two hand-balanced lines instead of
   relying on the browser's auto-wrap point, so the box hugs the longer
   line exactly. A fixed width left the browser free to wrap wherever,
   and a short last line inside a wide box read as a big ragged gap
   before the star separator — "in the middle" visually, not just by
   margin math, is what broke. */
.creator-marquee-copy {
  width: max-content;
}
.creator-marquee-quote {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.45;
  color: #fff;
  white-space: normal;
}
@media (min-width: 768px) {
  .creator-marquee-inner { flex-direction: row; align-items: stretch; }
  .creator-marquee-heading {
    flex: 0 0 auto;
    padding: 24px 32px;
    border-bottom: 0;
    border-right: 1px solid rgba(255, 255, 255, 0.14);
  }
  .creator-marquee { flex: 1; min-width: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .creator-marquee { overflow-x: auto; }
  .creator-marquee-track { transform: none !important; }
}

/* ==========================================================================
   Quote Stack — lighter-weight testimonial moment under Why Quacko, see the
   HTML comment above .quote-stack for why this is separate from the fuller
   #testimonials card grid below. Each .quote-card is photo+meta on the
   left, a bold headline + supporting paragraph on the right, with a large
   quote-mark glyph anchored to the card's bottom-right corner. One card is
   shown at a time in a swipeable/draggable carousel — see initQuoteCarousel
   in main.js for the drag handling and the dot/arrow controls below.
   ========================================================================== */
/* Title matches "We Are Quacko"'s h2 (.audience-stats-copy h2) exactly —
   same family/size/weight/tracking/leading — instead of the generic h2
   used by other section-heads. Wider than .section-head's default 640px
   cap so "What brand teams say about Quacko" stays on one line at this
   size instead of wrapping (the generic cap was sized for smaller-type
   section intros, not this bigger borrowed style). */
.quote-stack .section-head {
  max-width: none;
}
.quote-stack .section-head h2 {
  max-width: none;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
.quote-stack-viewport {
  position: relative;
  border-radius: 16px;
}
.quote-stack-list {
  display: flex;
  scroll-snap-type: x mandatory;
  overflow-x: auto;
  cursor: grab;
  /* Hide the native scrollbar — dots + arrows are the visible affordance,
     scroll-snap + drag/swipe is how it actually moves. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.quote-stack-list::-webkit-scrollbar { display: none; }
.quote-stack-list.is-dragging {
  cursor: grabbing;
  scroll-snap-type: none;
}
/* Rebuilt from Figma node 3221:106305 (Quacko Team file) — see the HTML
   comment above the first .quote-card for the structural notes. Same
   outer card recipe as .why-quacko-card (white bg, 1px border, no
   shadow); border-radius is the one place this deliberately doesn't
   match the site's --radius-lg/md tokens — Figma specifies 16px exactly
   here (between the two), so that's used as a literal value. */
.quote-card {
  position: relative;
  flex: 0 0 100%;
  scroll-snap-align: start;
  display: flex;
  height: 480px;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 16px;
}
/* Not flush to the outer card's edges — its own floating rounded box,
   inset from the card, matching .quote-card-copy's inset on the other
   side. Uniform 24px on every side, so the gap to the cream box matches
   this box's own gap to the outer card (top/bottom) instead of being a
   wider, separately-chosen value. */
.quote-card-media {
  flex: 0 0 340px;
  width: 340px;
  height: 432px;
  margin: 24px;
  border-radius: 16px;
  overflow: hidden;
}
.quote-card-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* x is tuned per-photo below: each subject sits at a slightly
     different horizontal offset in their own source image, and
     .quote-card-media's tall crop window (post enlarge) is narrow
     enough that a plain 50% center reads as visibly off. */
  object-position: 58% 50%;
  /* All three cards now use real licensed photos from the Figma source,
     at their native ~1200px export (a downsized source read as blurry
     once the card/photo were enlarged). Background colors below are
     just the load-in fallback, matching each photo's own backdrop. */
  background: #D7ECA3;
}
.quote-card-photo--2 {
  background: #BDA7F9;
  object-position: 52% 50%;
}
.quote-card-photo--3 {
  background: #027A99;
  object-position: 48% 50%;
}
/* Card-in-card content box, inset from the outer card on top/left/bottom
   — right side gets a wide margin instead (110px) to leave the arrow
   buttons + quote-mark room to sit on the plain white card background,
   same as Figma reserves space for them there rather than overlapping
   this box. */
.quote-card-copy {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  margin: 24px 110px 24px 0;
  padding: 32px 40px;
  background: var(--color-bg-alt);
  border-radius: 16px;
}
.quote-card-headline {
  margin: 0;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--color-ink);
}
.quote-card-body {
  margin: 16px 0 0;
  font-size: 1.125rem;
  line-height: 1.6;
  color: #737373;
}
/* Pushed to the bottom of the box via margin-top:auto (flex column),
   matching Figma's name position sitting well below a short quote rather
   than immediately following the body text. */
.quote-card-name {
  display: block;
  margin-top: auto;
  padding-top: 16px;
  font-size: 1.0625rem;
  font-weight: 500;
  color: var(--color-ink);
}
.quote-card-mark {
  position: absolute;
  right: 32px;
  bottom: 28px;
  color: var(--color-brand);
}
/* Was max-width: 767.98px — real iPad/tablet widths (768px portrait up
   through ~1000px, iPad landscape included below the site's own 1024px
   desktop cutover) fell through to the side-by-side desktop layout below,
   whose fixed 340px media + 110px right margin leaves the copy column too
   narrow: quote text wrapped to a sliver and vertically overflowed/clipped
   past the card's fixed height. Found as a real bug at exactly 768px (iPad
   portrait) and still broken up to ~1000px. Raised to match every other
   component's mobile/desktop split on this page (nav, hero-visual reorder,
   etc. all use 1023.98/1024px) so the stacked layout now covers the whole
   tablet range instead of stopping short of it. */
@media (max-width: 1023.98px) {
  .quote-card {
    flex-direction: column;
    height: auto;
  }
  .quote-card-media { width: auto; height: 220px; margin: 12px 12px 0; }
  .quote-card-copy { margin: 12px; padding: 20px; }
  .quote-card-headline { font-size: 1.5rem; }
  .quote-card-body { font-size: 1rem; }
  .quote-card-mark { right: 16px; bottom: 16px; }
  .quote-card-mark svg { width: 48px; height: 33px; }
}

/* Pinned to the card's top-right corner (see the HTML comment above
   .quote-stack-arrows) instead of sitting in the control row below —
   outline-circle buttons with a full shaft+arrowhead icon, per the
   reference. Inset matches .quote-card's own 16px padding so the circles
   line up with the card's inner edge. */
.quote-stack-arrows {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 2;
  display: flex;
  gap: 10px;
}
/* The ring/disc is drawn by the SVGs themselves (see the HTML comment
   above .quote-stack-arrows) — this is just their 48px hit area, no CSS
   border/background of its own. Needs an explicit appearance/border/
   background reset: unlike .btn, this class doesn't go through that
   shared reset, and the site's global `button` rule (line ~83) only sets
   font/cursor — so without this, the browser's native button chrome (a
   default border + gray 3D background) shows through around/behind the
   SVG instead of the SVG being the only thing drawn. */
.quote-stack-arrow {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  color: var(--color-ink);
}
/* Outline state (ring+arrow) is the default; the solid state (dark disc,
   arrow cut out) crossfades in on hover/focus. Both fill with currentColor
   — only the geometry differs between the two exported paths, not the
   color — so this is a pure opacity swap, no color transition needed. */
.quote-stack-arrow-icon {
  position: absolute;
  inset: 0;
  display: flex;
  transition: opacity .15s ease;
}
.quote-stack-arrow-icon svg { width: 100%; height: 100%; display: block; }
.quote-stack-arrow-icon--solid { opacity: 0; }
/* Prev reuses Next's exact SVG, rotated — see the HTML comment above
   .quote-stack-arrow--prev for why this is the accurate reproduction,
   not a shortcut. */
.quote-stack-arrow--prev .quote-stack-arrow-icon { transform: rotate(180deg); }
.quote-stack-arrow:hover .quote-stack-arrow-icon--outline,
.quote-stack-arrow:focus-visible .quote-stack-arrow-icon--outline {
  opacity: 0;
}
.quote-stack-arrow:hover .quote-stack-arrow-icon--solid,
.quote-stack-arrow:focus-visible .quote-stack-arrow-icon--solid {
  opacity: 1;
}
/* Matches .quote-card's own breakpoint above (also raised from 767.98px
   to 1023.98px) — the arrows need to shrink into the stacked layout's
   compact corner position at the same width the card itself stacks. */
@media (max-width: 1023.98px) {
  .quote-stack-arrows { top: 12px; right: 12px; gap: 6px; flex-direction: column; }
  .quote-stack-arrow { width: 32px; height: 32px; }
}
/* Phone specifically (not the whole ≤1023.98px tablet-and-down stack
   above) sized back up a step per feedback — 32px read too small next to
   .quote-card-media's own 220px-tall photo at this width, which has
   plenty of headroom for a bigger hit target/circle. Same cascade-order
   requirement as every other same-specificity mobile override in this
   file: this needs to come AFTER the 1023.98px block above to win. */
@media (max-width: 639.98px) {
  .quote-stack-arrows { gap: 8px; }
  .quote-stack-arrow { width: 40px; height: 40px; }
}

/* ==========================================================================
   Testimonials
   ========================================================================== */
/* Single-card drag/swipe carousel — see the HTML comment above #testimonials
   for why (React Bits' Carousel, ported without Framer Motion) and
   initTestimonialCarousel in main.js for the interaction. Narrower and
   centered (unlike Quote Stack's edge-to-edge single card above) so it
   reads as one focused card in a deck, closer to the reference component's
   own compact usage than stretching a quote box across the full section. */
.testimonial-carousel-viewport {
  position: relative;
  overflow: hidden;
  max-width: 580px;
  margin-inline: auto;
}
.testimonial-carousel-track {
  display: flex;
  scroll-snap-type: x mandatory;
  overflow-x: auto;
  cursor: grab;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.testimonial-carousel-track::-webkit-scrollbar { display: none; }
.testimonial-carousel-track.is-dragging {
  cursor: grabbing;
  scroll-snap-type: none;
}
.testimonial-card {
  flex: 0 0 100%;
  scroll-snap-align: start;
  margin: 0;
  background: var(--color-bg-alt);
  border-radius: var(--radius-md);
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.testimonial-card blockquote {
  margin: 0;
  font-size: 1rem;
  color: var(--color-ink);
  line-height: 1.6;
}
.testimonial-card figcaption {
  display: flex;
  align-items: center;
  gap: 12px;
}
.testimonial-avatar {
  width: 40px; height: 40px;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, #FFD9C2, var(--color-brand));
  flex-shrink: 0;
}
.testimonial-name { display: block; font-weight: 700; font-size: 0.9375rem; }
.testimonial-role { display: block; font-size: 0.8125rem; color: var(--color-ink-soft); }

.testimonial-carousel-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin-top: 24px;
}
.testimonial-carousel-arrow {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: #fff;
  color: var(--color-ink);
  transition: background-color 160ms ease, border-color 160ms ease, opacity 160ms ease;
}
.testimonial-carousel-arrow:hover { background: var(--color-bg-alt); border-color: var(--color-ink); }
/* loop: false — the deck stops at either end instead of wrapping, so the
   arrow just goes inert there rather than looping like Quote Stack's. */
.testimonial-carousel-arrow:disabled { opacity: 0.35; pointer-events: none; }
.testimonial-carousel-dots {
  display: flex;
  align-items: center;
  gap: 8px;
}
.testimonial-carousel-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-border);
  transition: background-color 200ms ease, transform 200ms ease;
}
.testimonial-carousel-dot:hover { background: var(--color-ink-soft); }
.testimonial-carousel-dot.is-active {
  background: var(--color-brand);
  transform: scale(1.25);
}

/* ==========================================================================
   Pricing
   ========================================================================== */
.pricing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
  align-items: stretch;
}
.price-card {
  position: relative;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 28px;
  display: flex;
  flex-direction: column;
}
.price-card-featured {
  border-color: var(--color-brand);
  box-shadow: var(--shadow-card-hover);
}
.price-badge {
  position: absolute;
  top: -12px;
  left: 28px;
  background: var(--color-brand);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
}
.price-value {
  font-size: 2rem;
  font-weight: 800;
  margin-top: 14px;
}
.price-value span { font-size: 1rem; font-weight: 500; color: var(--color-ink-soft); }
.price-desc { margin-top: 8px; color: var(--color-ink-soft); font-size: 0.9375rem; }
.price-list { margin: 24px 0 28px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.price-list li {
  font-size: 0.9375rem;
  padding-left: 24px;
  position: relative;
}
.price-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 6px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--color-green-bg);
  box-shadow: inset 0 0 0 2px var(--color-green);
}

/* ==========================================================================
   CTA
   ========================================================================== */
/* Full-bleed 50/50 split (copy left, illustration panel right) — the panel
   runs edge-to-edge to the viewport on both sides, so .cta-inner deliberately
   is NOT a .container: the usual max-width/gutter would leave the purple
   panel short of the browser edge instead of filling its half. */
/* Two classes (not just .cta-section) so this beats .section's own
   padding-block at every breakpoint, including the xl tier's 88px bump
   below — same trick .creator-marquee-section.section already uses for
   this exact same-specificity/source-order problem. */
.section.cta-section { padding-block: 0; overflow: hidden; }
.cta-inner {
  display: flex;
  flex-direction: column;
}
.cta-copy {
  padding: 64px 24px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  /* Same dot-grid ground as body's own global background (see body's
     background-image above) instead of the old brand-tint gradient, so
     this panel reads as one system with the rest of the page. */
  background-color: #F5F3F1;
  background-image: radial-gradient(rgba(20, 18, 15, 0.11) 1px, transparent 1px);
  background-size: 16px 16px;
}
.cta-copy p { color: var(--color-ink-soft); }
/* Avatar strip, per Figma node 3234:106442 (Bs95DokMdAVE8W5APlm6hI): 3 real
   creator photos + a lavender "+" chip, laid out with a small positive gap
   (not the overlapping -16px margin .why-quacko-avatars uses) — a distinct
   pattern, so it gets its own class rather than reusing that one. Sits
   above the headline as social-proof; margin-bottom stands in for the
   flex gap since it's the first child. */
.cta-avatars { display: flex; gap: 8px; margin-bottom: 20px; }
.cta-avatars img {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid #fff;
  box-shadow: 0 1px 2px rgba(20, 18, 15, 0.12);
}
/* The "+" chip is its own flattened Figma asset (lavender circle + plus
   glyph already drawn into the SVG) — the photo ring above would just
   double up on top of its existing ring. Wrapped in its own span (rather
   than styled as .cta-avatars img:last-child) because a plain <img> can't
   host the ::after pulse ring below — replaced elements don't render
   generated content. */
.cta-avatars-plus { position: relative; display: inline-flex; flex: 0 0 auto; }
/* Replaces the old two-effect treatment (glyph overshooting past its own
   size to 1.12 + a separate expanding/fading ring) with one simple loop:
   the chip breathes between a smaller resting state and its true, normal
   size — never overshoots past 1. Lives on the <img>, not the
   .cta-avatars-plus span: the span's own transform is spoken for by the
   one-shot rise-in below, and animating both there would have this loop
   clobber that transition. */
.cta-avatars-plus img {
  border: 0;
  box-shadow: none;
  position: relative;
  z-index: 1;
  transform-origin: center;
  animation: cta-avatars-plus-breathe 1.8s ease-in-out infinite;
}
@keyframes cta-avatars-plus-breathe {
  0%, 100% { transform: scale(0.85); }
  50% { transform: scale(1); }
}
/* Entrance: js/main.js's shared addRiseReveal() (same one-shot
   IntersectionObserver used by the outreach/shop reveals) adds
   .is-revealed to the row once scrolled into view; each avatar pops up
   with a short stagger via its own transition-delay below. */
[data-cta-avatars-reveal] img,
[data-cta-avatars-reveal] .cta-avatars-plus {
  opacity: 0;
  transform: translateY(10px) scale(0.7);
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s ease-out;
}
[data-cta-avatars-reveal].is-revealed img,
[data-cta-avatars-reveal].is-revealed .cta-avatars-plus { opacity: 1; transform: translateY(0) scale(1); }
[data-cta-avatars-reveal] img:nth-child(1) { transition-delay: 0ms; }
[data-cta-avatars-reveal] img:nth-child(2) { transition-delay: 80ms; }
[data-cta-avatars-reveal] img:nth-child(3) { transition-delay: 160ms; }
[data-cta-avatars-reveal] .cta-avatars-plus { transition-delay: 240ms; }
@media (prefers-reduced-motion: reduce) {
  /* JS skips the reveal trigger under reduced motion (see addRiseReveal),
     so the hidden/scaled-down starting state would otherwise stick. The
     breathe loop is genuine ongoing motion (not a one-shot entrance), so it
     gets turned off outright rather than just having its trigger skipped. */
  [data-cta-avatars-reveal] img,
  [data-cta-avatars-reveal] .cta-avatars-plus {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .cta-avatars-plus img { animation: none; }
}
/* Headline promoted from the generic "标准 h2" (Inter/800/28px) to the
   design system's Display tier — same exact recipe as .audience-stats-copy
   h2 (fixed 2.75rem, not the further-scaling h1/.ai-eyebrow breakpoints),
   since per DESIGN_GUIDELINES.md's "Display 档" rule this is a page-level
   anchor headline, not a section-internal subheading. The intro paragraph
   stays Inter — it's "正文 Lead" (.section-sub/.hero-sub) copy, sized up to
   18px/regular, not Tomato Grotesk; only the headline gets the display
   font. .cta-note (the small "No credit card required" line) is untouched,
   it's a caption. */
.cta-copy h2 {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
.cta-copy p:not(.cta-note) {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 1.125rem;
  font-weight: 400;
  line-height: 1.4;
}
/* .hero-actions' own global margin-top (28px) reads fine below Hero's
   longer .hero-sub paragraph, but on top of .cta-copy's tighter 8px flex
   gap it left the button row sitting too close to the intro copy here —
   scoped bump to give the buttons clearer separation from the text above. */
.cta-copy .hero-actions {
  margin-top: 40px;
}
.cta-form {
  margin-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  max-width: 420px;
}
/* Illustration pulled from Figma (node 3231:106349): an inline SVG at its
   native 403x220 viewBox (the button row is cropped out — it's a real
   .btn-dark below, not drawn into the graphic) so every layer — including
   the skewed card/text groups — scales together as one vector unit instead
   of drifting apart at different breakpoints. The panel's own #4b3fe0 fill
   (not the SVG) is what fills the full half-viewport bleed; the SVG carries
   no background rect of its own so it just floats centered on top of it.
   The grid is the same radial-dot technique body uses for its own ground
   (see body's background-image above), swapped for a square-cell line
   grid at a lighter tint of this panel's own purple. */
.cta-visual {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 40px 12px;
  /* Matches .cta-copy's own natural mobile height (measured ~500px with
     its avatar row + heading + intro + two buttons + note) so the two
     stacked halves read as equal panels instead of the illustration
     panel looking short. Less horizontal padding than before (24px ->
     12px) so the illustration itself also gets noticeably more room
     inside that taller box, not just extra empty space around the same
     size image. */
  min-height: 500px;
  background-color: #4b3fe0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.09) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.09) 1px, transparent 1px);
  background-size: 32px 32px;
  /* Reference frame for the [data-tilt] pointer handler (main.js) — same
     role .collage plays for the hero visual: this element's own
     getBoundingClientRect() is what pointer offset is measured against,
     and perspective here (not on .cta-illustration itself) is what gives
     that element's rotateX/rotateY an actual 3D vanishing point. */
  position: relative;
  perspective: 900px;
}
.cta-illustration {
  width: 100%;
  max-width: 720px;
  height: auto;
  /* Actual tilt target — reads the same --tilt-rx/--tilt-ry/--tilt-scale
     custom properties main.js's [data-tilt] handler already drives for
     .collage-image, see the HTML comment there for why this is a vanilla
     port of React Bits' TiltedCard rather than the real component. The
     leading translateY nudges the whole illustration up 20px; a
     margin-top here would have looked like nothing since .cta-visual's
     justify-content:center just re-centers the group and eats half of
     any margin change — transform isn't part of that layout math, so it
     lands as a real, exact 20px shift instead. */
  transform: translateY(-20px) rotateX(var(--tilt-rx, 0deg)) rotateY(var(--tilt-ry, 0deg)) scale(var(--tilt-scale, 1));
  transition: transform 0.15s ease-out;
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .cta-illustration { transition: none; }
}
@media (max-width: 1023px) {
  /* A bit bigger still, past just the extra room the reduced side padding
     above already freed up — bleeds slightly past .cta-visual's own edges
     (safe: .section.cta-section has overflow:hidden), centered by the
     matching negative margin. */
  .cta-illustration { width: 115%; margin-inline: -7.5%; }
}
/* Depth parallax on top of the whole-illustration tilt above: the back
   donut card and its "71%" label (data-tilt-depth="0.4", see index.html)
   slide less than the book, the front chart card group
   (data-tilt-depth="1.8") slides more — same --layer-tx/--layer-ty driven
   by main.js's [data-tilt] handler, each element's own multiplier baked
   into its inline transform. This is what actually reads as "space"
   between the three planes; the outer rotateX/rotateY alone just tilts
   everything as one rigid card. Elements with no data-tilt-depth (the
   book itself) fall back to the CSS default (0px) and only get that
   shared rotation. */
.cta-illustration [data-tilt-depth] {
  transition: transform 0.15s ease-out;
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .cta-illustration [data-tilt-depth] { transition: none; }
}
.cta-input {
  padding: 12px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: #fff;
  font-size: 0.9375rem;
  width: 100%;
}
.cta-input:focus { outline: 2px solid var(--color-brand); outline-offset: 2px; }
.cta-note { margin-top: 4px; font-size: 0.8125rem; font-style: italic; color: var(--color-ink-soft); }
/* Hero's own button→note gap reads tighter than the "Find your next top
   creator" CTA section's, because .cta-copy is a flex column with its own
   8px gap stacking on top of .cta-note's 4px margin (12px total) while
   .hero-copy is a plain block with only the 4px margin. Bump it here to
   match that 12px, using the CTA section as the standard. */
.hero-copy .cta-note { margin-top: 12px; }
.cta-note.is-success { color: var(--color-green); font-weight: 600; }

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer { background: var(--color-dark-panel); color: #C9C4BC; padding-top: 80px; position: relative; overflow: hidden; }
.footer-inner {
  display: flex;
  flex-direction: column;
  gap: 40px;
  padding-bottom: 24px;
}
/* Sized up from the shared header lockup (30px icon / 24px word) — the
   footer mark stands alone on its own line with room to breathe, instead
   of being squeezed into a 40px-tall nav pill, so it can read bigger.
   Whole lockup then scaled up another 50% past that (icon 40->60px, word
   2rem->3rem, gap 14->21px) as one proportional unit, per spec. */
.footer-brand .logo { color: #fff; gap: 21px; }
.footer-brand .logo-icon { height: 60px; }
.footer-brand .logo-word { font-size: 3rem; }
.footer-brand p { margin-top: 12px; font-size: 0.875rem; max-width: 260px; }
/* align-items stays the default "stretch" (not flex-end) so .footer-login
   below can go width:100% and pick up .footer-actions-row's own natural
   width — the container's shrink-to-fit sizing uses the row's preferred
   width first, then stretches the login link to match it. */
.footer-actions { display: flex; flex-direction: column; gap: 12px; }
/* Grid, not flex — "Try For Free" is longer than "Book Demo", so a plain
   flex row (each button sized to its own content) leaves them visibly
   mismatched widths. An auto-sized grid with two 1fr columns sizes both
   tracks to the wider button's own max-content instead, which is the
   standard way to get equal-width cells without hardcoding a width up
   front — .footer-login below still stretches to match this row's
   resulting (now wider, evenly-split) total width same as before. */
.footer-actions-row { display: grid; grid-template-columns: repeat(2, 1fr); align-items: center; gap: 12px; }
.footer-actions .nav-link-plain { color: #C9C4BC; }
.footer-actions .nav-link-plain:hover { color: #fff; }
.footer-actions .footer-login { width: 100%; text-align: center; }
/* White pill, same shape/sizing as the sibling .btn-pill-solid ("Try For
   Free") so the two read as a matched pair — just recolored to plain white
   since this one isn't the primary brand CTA. */
.footer-book-demo {
  background: #fff;
  color: var(--color-ink);
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  font-size: 1.0625rem;
  font-weight: 600;
}
.footer-book-demo:hover { background: var(--color-bg-alt); }
/* Pill button, not just recolored text — same shape/sizing as the
   sibling .btn-pill-solid ("Try For Free") so the two read as a matched
   button pair, just swapped to the site's lime accent instead of orange. */
.footer-actions .footer-login {
  background: #CCEA01;
  color: var(--color-ink);
  border-radius: var(--radius-pill);
}
.footer-actions .footer-login:hover { background: #E5FA34; color: var(--color-ink); }
/* 60px under the Login button, per spec — .footer-actions' own 12px gap
   already adds space above every child, so this only needs the remaining
   48px (12 + 48 = 60) rather than the full 60px on top of that gap. */
.footer-copyright { margin: 48px 0 0; text-align: center; font-size: 0.8125rem; }
/* Giant faded wordmark bleeding off the bottom edge — purely decorative,
   sized off the container width so it scales with viewport instead of a
   fixed px value; overflow:hidden on .site-footer above clips it instead
   of introducing horizontal scroll. */
.footer-wordmark {
  margin: 0;
  padding-top: 0;
  text-align: center;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-weight: 600;
  line-height: 0.8;
  letter-spacing: -0.02em;
  font-size: 22vw;
  color: rgba(255,255,255,0.06);
  /* translateY is a % of the wordmark's own box height, and .site-footer's
     overflow:hidden clips exactly the pushed-down portion (transforms don't
     add to layout height, so the parent's box still ends where the
     untransformed text would) — 10% down leaves ~90% of the glyphs visible
     above the cropped edge, per spec. */
  transform: translateY(10%);
  user-select: none;
}
/* Content-sized columns, not stretched — grid-template-columns stays
   repeat(N, 1fr) only up through the tablet breakpoint below, where the
   grid still needs to fill the row edge-to-edge on its own line. At
   desktop (1024px+) footer-cols sits inline next to footer-brand instead
   and switches to auto-width columns (see that breakpoint) so the four
   columns cluster together after the logo rather than stretching to fill
   whatever width flex handed them. */
.footer-cols {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px;
}
.footer-col { display: flex; flex-direction: column; gap: 12px; }
/* margin: 0 — h4 was never in the global reset's `h1, h2, h3, p { margin: 0; }`
   list, so it was still carrying the browser's default ~1.33em top margin,
   pushing "PRODUCT/COMPANY/…" ~17px below the row's actual top instead of
   lining up with the logo icon and the Log in/Try For Free buttons, which
   have no such margin. */
.footer-col h4 { margin: 0; color: #fff; font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.04em; font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; }
.footer-col a { font-size: 0.875rem; }
.footer-col a:hover { color: #fff; }
.footer-bottom { padding-block: 20px 32px; font-size: 0.8125rem; }

/* ==========================================================================
   sm — 640px and up
   ========================================================================== */
@media (min-width: 640px) {
  /* h1 itself is already 3.25rem at base now (mobile bump) — nothing to
     raise it to here anymore, just .ai-eyebrow (a different element that
     happens to share h1's size upstream). */
  .ai-eyebrow { font-size: 3.25rem; }
  /* Mobile-only line break for "AI Creator Search" (index.html) — was only
     hidden at 1024px+, so every tablet width (640-1023px, iPad portrait
     included) kept forcing "AI Creator" / "Search" onto two lines even
     though the text comfortably fits on one: measured ~420px wide at this
     tier's 3.25rem size, well under the ~590px+ of column room tablets
     actually have. Real phones below this tier still need the break
     (~355px of text vs as little as ~300px of room), so it only moves up
     to here, not further. */
  .ai-eyebrow-break { display: none; }
  .hero-actions { flex-wrap: nowrap; }
  .cta-form { flex-direction: row; }
  .cta-form .btn { flex-shrink: 0; }
  .footer-cols { grid-template-columns: repeat(4, 1fr); }
}

/* ==========================================================================
   md — 768px and up (tablet / iPad portrait)
   ========================================================================== */
@media (min-width: 768px) {
  .container { padding-inline: 32px; }
  /* Mirrors .container's own bump above — see the padding-inline note on
     .product-mega-menu for why the dropdown needs this to stay pill-width. */
  .product-mega-menu { padding-inline: 32px; }
  h2 { font-size: 2rem; }
  .feature-grid { grid-template-columns: repeat(2, 1fr); }
  /* 2 across through iPad (both orientations); 1024px+ below bumps this
     back to 3 for real desktop, per feedback — was jumping straight from
     1 to 3 columns at this same breakpoint, cramming 3 cards into an
     iPad-portrait-width row. */
  .steps { grid-template-columns: repeat(2, 1fr); gap: 24px; }
  .pricing-grid { grid-template-columns: repeat(3, 1fr); }
  /* Mirrors .container's own bump right above — see the explicit
     padding-inline note on .outreach-demo-inner's max-width:1023 rule for
     why this can't just fall through to .container's rule on its own. */
  .outreach-demo-inner { padding-inline: 32px; }
  /* .ai-copy-text's base 520px cap was tuned for the (much wider) desktop
     row layout, where it sits in a half-width column next to .ai-visual —
     on the mobile/tablet stacked layout it's centered under the heading
     instead with a full container's width to use, and 520px wrapped the
     "AI Creator Search" body copy to 3 lines with room to spare. Widened
     just for this tablet tier (768px still comfortably fits it within the
     container, unlike the 640px tier below where it would nearly touch
     the edges) so it settles at 2 lines instead. */
  .ai-rows .ai-copy-text { max-width: 620px; }
  /* .ai-visual's base 460px cap matches a phone's own edge-to-edge width,
     so on tablet it was reading small and lopsided next to the much wider
     centered column above it. Sized up (not all the way to desktop's own
     619px cap at 1024px+, so tablet still reads a size step below full
     desktop) and centered (see the max-width:1023px rule) to actually use
     the extra room this tier has. */
  .ai-visual { max-width: 560px; }
}

/* ==========================================================================
   lg — 1024px and up (small desktop / iPad landscape)
   ========================================================================== */
@media (min-width: 1024px) {
  .ai-rows::before { display: block; }
  /* Back to 3 across — see the 768px rule's own comment for why iPad
     stops at 2. */
  .steps { grid-template-columns: repeat(3, 1fr); }
  /* .ai-eyebrow-break (the mobile-only "AI Creator Search" line break) is
     already hidden from 640px up — see that rule's comment — so nothing
     to redeclare here. At this width .ai-eyebrow is back to inline-flex,
     where a *visible* <br> wouldn't force a line break but would still
     count as its own flex item and pick up the row's 16px gap on both
     sides; staying hidden here avoids that stray gap too, same as it
     avoids the forced break below 1024px. */
  .nav-desktop { display: flex; }
  .nav-desktop-only { display: inline-flex; }
  .menu-toggle { display: none; }
  .nav-mobile { display: none !important; }

  /* Nav pill widened a bit past the site's standard 1200px container,
     same "own wider container" escape hatch as .hero .container below —
     gives the logo/links/buttons row more breathing room now that it's
     carrying the full desktop nav instead of just logo + hamburger. */
  .site-header .container { max-width: 1320px; }

  .hero-inner {
    flex-direction: row;
    align-items: center;
    gap: clamp(40px, 6vw, 80px);
  }
  .hero-copy { min-width: 0; flex: 1 1 62%; }
  .hero-visual { min-width: 0; flex: 1 1 38%; }
  .collage { max-width: min(460px, 100%); }

  .cta-inner { flex-direction: row; min-height: 560px; }
  .cta-copy {
    flex: 0 0 50%;
    text-align: left;
    align-items: flex-start;
    padding: 64px clamp(24px, 5vw, 64px) 64px clamp(24px, 6vw, 96px);
  }
  .cta-visual { flex: 0 0 50%; padding: 48px; min-height: auto; }
  .cta-illustration { max-width: 810px; }

  h1 { font-size: 3rem; }
  .ai-eyebrow { font-size: 3rem; }
  .feature-grid { grid-template-columns: repeat(3, 1fr); }

  /* Left-aligned, not space-between — the four link columns should sit
     clustered together right after the brand block, not stretch (via
     flex:1 + an even grid) all the way out to the container's right edge.
     flex-wrap: wrap is load-bearing, not decorative: brand (260px) + the
     four auto-width columns (64px gaps + 96px margin-left) + the actions
     block (329px fixed + 40px padding-left) add up to ~1210-1290px, more
     than the 1200px container ever gives below the min-width:1600px tier
     (where .site-footer .container switches to a fluid 110px-gutter width
     instead of the 1200px cap). Without wrap, .footer-actions — flex-
     shrink:0, pinned flush right via margin-left:auto — was overflowing
     the container by 68px+ at 1440px and clear off the (clipped, since
     html has overflow-x:hidden) edge of the viewport by 240px+ at 1024px:
     the "Book Demo"/"Try For Free" buttons were invisible. Found across
     the whole 1024-1599px range, not just tablet widths. Wrapping lets
     .footer-actions drop to its own row once it doesn't fit — margin-left:
     auto still right-aligns it there, so it reads as a deliberate second
     row rather than a broken one. */
  .footer-inner { flex-direction: row; flex-wrap: wrap; align-items: flex-start; justify-content: flex-start; }
  .footer-brand { max-width: 260px; flex-shrink: 0; }
  .footer-cols {
    grid-template-columns: repeat(4, auto);
    gap: 64px;
    margin-left: 96px;
  }
  .footer-actions { margin-left: auto; flex-shrink: 0; padding-left: 40px; }

  /*
   * AI rows — real spacing measured directly off the published Framer site
   * (incomparable-employees-345264.framer.app) via a headless-browser pass
   * at a 1440px viewport: row gap 150px, row min-height 829px, copy/visual
   * column height 519px (both, so the copy stack is space-between'd to
   * match the visual card's height), visual card fixed at 619×519 always.
   * The copy column itself is the one fluid piece — it fills whatever
   * space is left (container width − 619 − 150), capped at 831px, which
   * is the exact width the 1600px+ tier below was hand-measured at (that
   * capture just happened to have a container wide enough to hit the cap).
   * Replaces the earlier 40px-gap/40%-split guess, which didn't match.
   */
  .ai-rows { gap: 0; }
  .ai-rows > .container { gap: 0; }
  .ai-row {
    flex-direction: row;
    align-items: center;
    gap: clamp(40px, 6vw, 80px);
    min-height: clamp(620px, 70vw, 829px);
    justify-content: center;
    padding-block: 0;
  }
  .ai-copy {
    flex: 1 1 0;
    min-width: 0;
    max-width: 831px;
    height: auto;
    justify-content: center;
    gap: 16px;
  }
  .ai-copy-bottom { gap: 16px; }
  .ai-visual {
    flex: 0 1 52%;
    width: auto;
    max-width: 619px;
    height: auto;
    aspect-ratio: 619 / 519;
  }
}

/* "Try For Free" was overflowing the nav pill and getting clipped by
   html's overflow-x:hidden across the 1024-1279px range (iPad landscape
   and other "small desktop" widths) — trims link padding to free up room
   on the right. The matching .nav-desktop { left: -16px } that used to
   live here is gone: that was softening the old space-between pull-left
   hack so it wouldn't overlap the logo at this range, which the fixed
   margin-left:32px above makes structurally impossible now. */
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .nav-desktop a { padding-inline: 12px; }
}

/* ==========================================================================
   xl — 1280px and up (desktop)
   ========================================================================== */
@media (min-width: 1280px) {
  .section { padding-block: 88px; }
  .hero { padding-block: 120px 96px; }
  .hero-inner { gap: 32px; }
  h1 { font-size: 4rem; }
  .ai-eyebrow { font-size: 4rem; }
  /* fixed-width copy column (not a flex ratio) so it always keeps the
     ~780px it needs to hold the title at exactly two lines down to this
     tier's own 1280px floor, while every pixel gap/container savings goes
     to the visual card instead of being split between both. */
  .hero-copy { min-width: 0; flex: 1 1 62%; margin-left: 0; }
  .hero-visual { min-width: 0; flex: 1 1 38%; }
  .collage { max-width: min(552px, 100%); }
  /* the hero title needs more room than the standard container to land
     "Smarter Creator Matching." / "Faster Revenue Growth" as exactly two
     lines at this size — widen just this section's container rather than
     the site-wide one. */
  .hero .container { max-width: 1600px; }
  /* Same "own wider container" fix as .hero .container above, same
     breakpoint, same value — .feature-hero is this page's own hero
     section, so it follows .hero's own margin convention directly rather
     than the generic full-bleed-section 110px rule further down (that
     rule is for content sections like Product Highlight, not a page's
     own top hero). */
  .feature-hero .container { max-width: 1600px; }
}

/* Framer desktop composition, measured from the published page at the
   reference screenshot's 2462px viewport. */
@media (min-width: 1600px) {
  .ai-rows.section { padding-block: 0; }
  .ai-rows > .container {
    width: 100%;
    max-width: none;
    /* 115px, not the platform's usual 110px — .ai-row below used to reach
       115px on its left edge only via a 5px transform:translateX nudge
       (see that rule's own comment), leaving the right edge 5px short at
       105px. Per feedback, standardized both sides on that same 115px
       instead of reverting to a symmetric 110px. */
    padding-inline: 115px;
  }
  /* Per feedback: the homepage's own top hero was still on the centered
     max-width:1600px treatment from the 1280px tier above (kept for the
     "needs room for a two-line h1" reason in that rule's own comment),
     which insets content by (viewport-1600)/2 + 32px — 96px at 1728px
     wide, 192px at 1920px — instead of the flat 110px every other
     platform-wide-gutter section (Product Highlight/.ai-rows included)
     lands on at this same breakpoint. That left the hero title drifting
     out of alignment with .ai-rows' own "Video Finder"/etc. row titles
     directly below it. Switched to the same full-bleed padding-inline
     pattern as .ai-rows > .container right above — same breakpoint, same
     section (no own background of its own; .hero reveals the global body
     dot-grid, so there's no bg-box to keep full-bleed either way) — rather
     than just capping max-width tighter, since the two-line-h1 headroom
     this tier's .hero .container override exists for only grows with a
     flat edge inset, never shrinks. DESIGN_GUIDELINES.md's "通栏板块左右留白"
     exception paragraph named this exact selector as staying on the
     centered pattern — now stale, updated alongside this. */
  .hero .container {
    width: 100%;
    max-width: none;
    padding-inline: 110px;
  }
  /* Same feedback, same fix, for the bottom CTA ("Find Your Next Top
     Creator?"): .cta-copy isn't a .container at all (see the "deliberately
     NOT a .container" comment on .cta-inner above — the purple visual
     panel needs the other 50% to run flush to the edge), it's its own
     padding-left clamp(24px, 6vw, 96px), topping out at 96px and never
     reaching the platform's 110px even at this same 1600px+ tier. Pinned
     to flat 110px here to match; padding-right is untouched (not part of
     this — .cta-visual's own edge is intentionally flush). */
  .cta-copy { padding-left: 110px; }
  /* row gap/height/copy/visual sizing itself lives in the 1024px tier
     above — same values, just needed sooner. Only the full-bleed
     container unlock, per-row gradient backdrop, and larger-viewport
     typography are unique to this ultra-wide tier. The old
     transform: translateX(5px) here (a Framer-reference-screenshot
     measurement) is gone — it shifted the row 5px right without adding
     any width, so it read as 115px on the left edge but only 105px on
     the right; the container's own padding-inline above now supplies a
     true, symmetric 115px on both sides instead. .ai-rows::before (the
     background grid) was already independently positioned off the
     section, not this row, so dropping the transform doesn't touch it. */
  .ai-row {
    position: relative;
    isolation: isolate;
    gap: 150px;
    min-height: 829px;
  }
  .ai-copy {
    height: 519px;
  }
  .ai-visual {
    flex: 0 0 619px;
    width: 619px;
    height: 519px;
    aspect-ratio: auto;
  }
  .ai-row::before {
    content: "";
    position: absolute;
    inset-block: 0;
    left: 50%;
    z-index: 0;
    width: 100vw;
    transform: translateX(calc(-50% - 5px));
  }
  .ai-row::after {
    content: "";
    position: absolute;
    inset-block: 0;
    left: 50%;
    z-index: 0;
    width: 100vw;
    opacity: var(--row-color-progress, 0);
    transform: translateX(calc(-50% - 5px));
    transition: opacity 140ms linear;
    will-change: opacity;
    pointer-events: none;
  }
  .ai-row:nth-child(1)::before {
    background: #fff;
  }
  .ai-row:nth-child(1)::after {
    background: #007b91;
  }
  .ai-row:nth-child(2)::before {
    background: #007b91;
  }
  .ai-row:nth-child(2)::after {
    background: #ff9e5c;
  }
  .ai-row:nth-child(3)::before {
    background: #ff9e5c;
  }
  .ai-row:nth-child(3)::after {
    background: #f5c3d6;
  }
  .ai-eyebrow {
    gap: 18px;
    font-size: 4rem;
    font-weight: 600;
    line-height: 1.12;
    letter-spacing: -0.02em;
  }
  .ai-dot { display: none; }
  .ai-copy-text {
    max-width: none;
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    font-size: 18px;
    font-weight: 500;
    line-height: 1.4;
  }
  .btn-pill-outline-dark {
    position: relative;
    width: 200px;
    height: 59px;
    justify-content: center;
    gap: 24px;
    padding: 0 22px;
    border: 0;
    border-radius: 60px;
    background: #d9f20f;
    overflow: hidden;
    font-size: 18px;
    font-weight: 600;
    line-height: 22.5px;
    letter-spacing: -0.6px;
    transition: background-color 200ms cubic-bezier(0.4, 0, 0.2, 1);
  }
  .btn-pill-outline-dark:hover { background: #e5fa34; }
  .btn-pill-outline-dark .ai-button-label {
    width: auto;
    height: auto;
    padding: 0;
    border-radius: 0;
    background: transparent;
  }
  .btn-pill-outline-dark .ai-button-text-track > span {
    display: inline;
  }
  .btn-pill-outline-dark .ai-button-text-track > span[aria-hidden="true"] { display: none; }
  .btn-pill-outline-dark .ai-arrow {
    display: inline-flex;
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    align-items: center;
    justify-content: center;
    border-radius: 0;
    background: transparent;
    color: #14120f;
    font-size: 24px;
    line-height: 1;
    transition: transform 420ms cubic-bezier(.16, 1, .3, 1);
  }
  .btn-pill-outline-dark:hover .ai-arrow,
  .btn-pill-outline-dark:focus-visible .ai-arrow {
    transform: translateX(2px) rotate(45deg);
  }

  /* This card (not a full-bleed section background like .ai-rows) should sit
     inset from the viewport edges by the same 110px gutter, so its left/right
     edges line up with where the AI Creator Search rows' text and video start
     — margin here, not padding, since padding would push the card itself
     flush to the viewport edge. */
  .outreach-demo .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
  }
  /* Same treatment so the "We are quacko" panel matches the outreach demo
     card's width exactly, instead of capping at a fixed 1600px. */
  .audience-stats .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
  }
  /* Illustration + copy together cap out well short of this now-edge-to-edge
     card on wide/4K screens. .audience-stats-copy's flex-grow was eating all
     the leftover width itself (its box growing full-width while the capped
     h2/p text sat pinned to its left edge) — justify-content alone can't
     center anything while a flex-grow child still exists to consume the
     free space first. Stop the copy from growing so the pair becomes a
     fixed-width unit, then center that unit so empty space splits evenly on
     both sides instead of piling up on the right. */
  .audience-stats-inner {
    justify-content: center;
  }
  .audience-stats-copy {
    flex: 0 1 620px;
  }
  /* Same 110px gutter so "Why Quacko" lines up with every other section's
     left/right edge instead of sitting inside the narrower 1200px cap.
     padding-inline reset to 0 here — unlike outreach-demo-inner/
     audience-stats-inner, this container has no colored card of its own to
     absorb .container's default 32px padding, so left uncleared it stacks
     with the margin and pushes the real inset to 142px instead of 110px. */
  .why-quacko .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
  /* Quote Stack sits directly under Why Quacko and should line up with it
     edge-to-edge rather than capping at the narrower 1200px default —
     same 110px gutter, same padding-inline reset, for the same reason. */
  .quote-stack .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
  /* FAQ is also a full-bleed tinted section (like Why Quacko/Quote Stack
     above it) — same 110px gutter so its cards line up with every other
     section's left/right edge instead of sitting inside the narrower
     1200px cap. */
  .faq-section .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
  /* Footer's background (dark ink) is painted on .site-footer itself, full
     width — same "background-filled section" case as ai-rows, so it takes
     the padding-inline form of the 110px gutter (not margin-inline like the
     card sections above). Covers both .footer-inner and .footer-bottom,
     which share this selector, so the logo/columns row and the copyright
     line underneath line up at the same left edge as every other section. */
  .site-footer .container {
    width: 100%;
    max-width: none;
    padding-inline: 110px;
  }
}

/* ==========================================================================
   Pricing page
   ========================================================================== */
/*
 * New standalone page (pricing.html), not a section on index.html — the
 * header/footer/nav markup and every design token below (colors, radii,
 * shadows, .btn/.eyebrow/.section/[data-stagger-text]/.faq-* patterns) are
 * reused verbatim from the homepage rather than introduced fresh, per this
 * site's own design system doc (DESIGN_GUIDELINES.md). Content structure
 * (billing toggle, plan cards, feature comparison table, FAQ) follows the
 * layout pattern of linktr.ee/s/pricing, but linktr.ee only serves an
 * agent-facing text stub with no real prices/features to a plain fetch
 * (its actual page is client-rendered) — so the tiers/copy here are a
 * first-pass draft mapped to Quacko's own product surfaces (AI Creator
 * Search, Video Finder, Outreach/Smart Flow, Insight), not scraped numbers.
 * Swap in real pricing once finalized.
 */

/* Top padding is the platform's non-homepage top-of-page rule (see
   DESIGN_GUIDELINES.md "非首页顶部留白") — body's own 126px padding-top
   already exactly clears the fixed nav pill with zero gap, so this 70px
   sits on top of that as the actual visible whitespace, replacing
   .section's own default (64px, 88px at the xl tier) which isn't this
   page-specific rule's number. Bottom stays independent at 32px. */
.pricing-hero { padding-top: 70px; padding-bottom: 32px; text-align: center; }
/* .ai-icon-badge is inline-flex, so .pricing-hero's text-align: center
   above already centers it like the old .eyebrow text did — this only
   needs to add back the gap to the h1 below (.eyebrow had none itself;
   spacing came from the two sitting flush, which reads fine for a text
   label but not for a 69px badge). 16px matches .ai-copy's own
   badge-to-heading gap in the AI feature rows this badge borrows from. */
.pricing-hero-badge { margin-bottom: 16px; }
/* Same gap as .pricing-hero-badge above (blog's own hero already centers
   via .blog-hero's own text-align: center). */
.blog-hero-badge { margin-bottom: 16px; }
/* Top padding only — .section's default (64px, 88px at the xl tier) plus
   .pricing-hero's own 32px bottom padding above left the cards sitting
   ~120px below the toggle. Bottom padding stays at the .section default
   so the gap before the comparison table below is untouched. */
.section.pricing-plans { padding-top: 24px; }
/* Widened from 680px: h1's own manual break ("...grow with<br />your
   creator program", see markup) needs more room at 4rem Tomato Grotesk —
   680px still wrapped the second line again into 3 lines total. .hero-sub
   caps its own line length below independent of this, so the paragraph
   doesn't stretch with it. */
.pricing-hero-inner { max-width: 800px; margin-inline: auto; }
.pricing-hero .hero-sub { max-width: 680px; margin-inline: auto; }
/* The manual break above is only needed once h1 reaches its fixed 1024px+
   sizes (3rem/4rem) — below that h1 is fluid and already wraps "Plans
   that grow with your creator program" into a clean 2 lines on its own;
   forcing the same break there on top of that natural wrap is what pushed
   it to 4 lines instead. Same off-at-small-widths idea as
   .ai-eyebrow-break, just inverted (this one's needed at the *larger*
   sizes, not the smaller ones). */
@media (max-width: 1023.98px) {
  .pricing-hero-title-break { display: none; }
}

/* ---- Billing toggle ---- */
.pricing-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-top: 32px;
  padding: 4px;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-card);
}
/* Sliding dark pill behind whichever button is active — js/main.js sizes
   and positions it off the active button's own offsetWidth/offsetLeft (the
   two buttons aren't the same width, "Annual" carries the Save-20% chip
   too) and animates transform/width on every click; the buttons themselves
   no longer paint their own background (see .is-active below), just their
   text color, so this is the only thing moving. Skips the transition on
   the very first paint (js sets transition:none for that one call) so it
   doesn't slide in from the left corner on load. */
.pricing-toggle-thumb {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  border-radius: var(--radius-pill);
  background: var(--color-ink);
  transition: transform .35s cubic-bezier(.22, 1, .36, 1), width .35s cubic-bezier(.22, 1, .36, 1);
  will-change: transform, width;
}
.pricing-toggle-btn {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-ink-soft);
  font-weight: 600;
  font-size: 0.9375rem;
  transition: color .2s ease;
}
/* "Annual" is the only button trailed by the Save-20% chip, and the
   shared 20px right padding above left a visibly bigger gap after the
   chip than the pill's own 10px top/bottom — tightened just here so all
   three sides around the chip read as one consistent inset. */
.pricing-toggle-btn[data-billing="annual"] { padding-right: 10px; }
.pricing-toggle-btn.is-active {
  color: #fff;
}
.pricing-toggle-save {
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--color-green-bg);
  color: var(--color-green);
  font-size: 0.75rem;
  font-weight: 700;
}
.pricing-toggle-btn.is-active .pricing-toggle-save {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

/* ---- Plan cards ---- */
.pricing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
/* Same platform-wide 110px gutter as index.html's card/grid sections (see
   DESIGN_GUIDELINES.md "通栏板块左右留白") — this is a plain grid of cards
   with no shared background of its own, so it's the margin-inline form
   (padding-inline:0 to clear .container's own 32px, same reasoning as
   .why-quacko/.quote-stack/.faq-section's own 1600px+ rules) rather than
   the padding-inline form full-bleed sections use. Applies to a standalone
   page, not just index.html — the rule was never index.html-specific. */
@media (min-width: 1600px) {
  .pricing-plans .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
}
.pricing-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 28px;
  box-shadow: none;
  transition: box-shadow .3s ease-out, transform .3s ease-out;
}
/* quacko_app's platform "card hover — lift + scale" standard
   (docs/design-system/misc-patterns.md: AI Search's exact hover motion —
   translateY(-4px) scale(1.02), shadow-lg, 300ms ease-out, z-10 so a
   lifted card paints above its still-flat neighbors), ported from its
   Tailwind classes to this site's plain CSS. */
.pricing-card:hover {
  z-index: 10;
  transform: translateY(-4px) scale(1.02);
  box-shadow: none;
}
/* Popular card fills with the same dark panel color as .site-footer and
   .creator-marquee-section (--color-dark-panel), which alone is what sets
   it apart from the other (white) cards now — no orange border ring on
   top of that; border/shadow fall back to .pricing-card's own default.
   Everything below overrides the default light-card text colors for
   contrast on that dark fill. */
.pricing-card.is-popular {
  background: var(--color-dark-panel);
  color: #fff;
}
.pricing-card.is-popular .pricing-card-blurb,
.pricing-card.is-popular .pricing-card-period,
.pricing-card.is-popular .pricing-card-note {
  color: #C9C4BC;
}
.pricing-card.is-popular .pricing-card-features { border-top-color: rgba(255, 255, 255, 0.16); }
/* .pricing-check now carries its own grey-circle background + dark stroke
   (not currentColor), so it already reads fine against this card's dark
   fill without a per-card contrast override — see .pricing-check below. */
/* Corner ribbon, not an inline flex item: absolutely positioned against
   .pricing-card's own position:relative so it reads as pinned to the
   card's top-right corner regardless of how tall the heading/blurb below
   it are, instead of pushing them down from an in-flow first child. */
/* Lime bg + teal text per explicit hex from the user (#D9F20E / #197E8C),
   plus a leading star icon (same filled-star path as the Outreach Demo's
   "Starred" column, recolored via currentColor). */
.pricing-card-badge {
  position: absolute;
  top: 20px;
  right: 20px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: #D9F20E;
  color: #197E8C;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.pricing-card-badge-icon { width: 12px; height: 12px; flex-shrink: 0; }
.pricing-card-head h3 { font-size: 1.25rem; }
.pricing-card-blurb {
  margin-top: 8px;
  min-height: 40px;
  color: var(--color-ink-soft);
  font-size: 0.9375rem;
  line-height: 1.45;
}
.pricing-card-price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 20px;
}
.pricing-card-amount {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.5rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  transition: opacity .16s ease;
}
.pricing-card-period { color: var(--color-ink-soft); font-size: 0.875rem; }
.pricing-card-note {
  margin-top: 4px;
  min-height: 18px;
  color: var(--color-ink-soft);
  font-size: 0.8125rem;
  transition: opacity .16s ease;
}
/* js/main.js adds this for the ~160ms it takes to swap an amount/note's
   text between billing periods, then removes it — a quick cross-fade
   instead of the number just snapping to the new value mid-toggle. */
.pricing-card-amount.is-swapping,
.pricing-card-note.is-swapping {
  opacity: 0;
}
/* Platform-standard pill button sizing (DESIGN_GUIDELINES.md "全站 Pill
   按钮标准尺寸与样式": 12px 22px padding, 18px/600 text, 24px text↔arrow
   gap) — .btn-dark already hardcodes this on itself so Free/Starter/
   Premium's CTAs already matched, but Growth's .btn-hero-primary doesn't
   carry a fixed size of its own and was falling back to .btn's base
   15px/0.9375rem, one size smaller than the other three. Scoped here
   (same "container .btn" pattern as .hero-actions .btn/
   .why-quacko-actions .btn) so all four stay in lockstep regardless of
   which color class each one uses. */
.pricing-card .btn {
  margin-top: 20px;
  padding: 12px 22px;
  gap: 24px;
  font-size: 1.125rem;
  font-weight: 600;
}
.pricing-card-features {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 24px;
  padding-top: 24px;
  border-top: 1px solid var(--color-border);
}
.pricing-card-features li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 0.9375rem;
  line-height: 1.4;
}
/* Grey-circle badge per request (was a bare currentColor checkmark on the
   card's own fill). The svg's own viewBox content fills its content-box,
   so the padding here shows as a ring of background visible around the
   stroke — same technique as a padded <img> circle-crop. Its own `color`
   (still what the stroke's currentColor resolves to) is set directly on
   this element, so it wins over the inherited white text color on the
   dark .is-popular card without needing a per-card override there. */
.pricing-check {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  padding: 4px;
  border-radius: 50%;
  background: var(--color-border);
  color: var(--color-ink);
}

/* ---- Comparison table ---- */
/* Same light-green fill as .audience-stats-inner ("We Are Quacko" panel),
   overriding .section-alt's own default --color-bg-alt for this section. */
.pricing-compare { background: #E6EFD3; }
/* "Compare plans" is a page-level section title, same tier as this page's
   other two section headings ("Billing questions?" .faq-heading, "Find
   your next top creator?" .cta-copy h2) — per DESIGN_GUIDELINES.md's own
   Display-vs-standard-h2 rule it belongs on the Display tier, not the
   plain h2 default it was inheriting. Same fixed 2.75rem recipe as those
   two (not the further-scaling Hero/.ai-eyebrow breakpoints). */
.pricing-compare .section-head h2 {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
/* Same platform-wide 110px gutter as .pricing-plans' own 1600px+ rule
   above (see DESIGN_GUIDELINES.md "通栏板块左右留白") — margin-inline
   form since the table itself has its own white background/border,
   not a full-bleed section background to pad inward. */
@media (min-width: 1600px) {
  .pricing-compare .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
}
/* Rebuilt from a semantic <table> to a CSS Grid of role="table"/"row"/
   "cell" divs (see pricing.html) specifically so the Growth column header
   (the "Most Popular" panel) can genuinely overlap the row above it —
   table cells don't support margin and can't overlap a sibling row at
   all, which made that ask impossible in the old <table> markup. Each
   .pricing-table-row is `display:contents` so its cells become direct
   grid children laid out by .pricing-table's own column template.
   `overflow: visible` on the wrap (not the old overflow-x:auto) is what
   lets the popular panel actually escape its row instead of being
   clipped — this codebase's gotchas.md note about overflow-x:auto forcing
   overflow-y to clip too is exactly why the old table couldn't do this.
   Dropped the horizontal-scroll safety net that gave it below 768px,
   where columns just shrink instead.
   The section group-title rows ("Creator Search & Discovery" etc.) that
   used to break the feature list up were removed per the user's request —
   plain flat list of features now, no group headers. */
.pricing-table-wrap { position: relative; overflow: visible; border-radius: var(--radius-lg); background: #fff; }
.pricing-table {
  display: grid;
  grid-template-columns: minmax(160px, 1.7fr) repeat(4, minmax(96px, 1fr));
}
.pricing-table-row { display: contents; }
.pricing-table-cell, .pricing-table-head-cell {
  padding: 14px 20px;
  text-align: left;
  font-size: 0.9375rem;
}
/* Every body cell gets its right edge + the outer left edge of the whole
   grid, so the borders form one complete set of gridlines — vertical
   dividers between every column, plus a left/right frame around the table
   — instead of just the horizontal rules between rows this used to have.
   Right (not left) is the shared rule so each internal seam is painted
   once, by the cell to its left, rather than doubled by both neighbors;
   the lone left-edge exception below adds the one edge that rule can't
   reach. Deliberately scoped to .pricing-table-cell only, not the head
   row: .pricing-table-head-cell sits on its own solid color block with
   pill CTA buttons, not a gridded row, and a first pass that gave it
   these same borders drew a stroke straight through the buttons' rounded
   outlines and along the "Most Popular" panel's edges — which, per that
   panel's own comment above, is deliberately *not* a table cell, so
   tracing its boundary in the table's own gridline color made it read as
   gridded-in rather than the independent floating card it's meant to be. */
.pricing-table-cell { border-right: 1px solid var(--color-border); }
.pricing-table-cell:first-child { border-left: 1px solid var(--color-border); }
/* display:flex + a shared min-height (rather than trusting padding +
   line-height to land on the same number in every cell) is what actually
   keeps every row the same height: a row's real height is set by its
   *tallest* cell, and without a floor here a value that happens to wrap
   to two lines (e.g. "Dedicated manager" in the narrower Support column)
   would silently stretch just that one row taller than the rest. */
.pricing-table-cell {
  display: flex;
  align-items: center;
  min-height: 52px;
  border-top: 1px solid var(--color-border);
}
.pricing-table-cell:first-child { color: var(--color-ink-soft); }
.pricing-table-head-cell {
  padding-top: 20px;
  padding-bottom: 16px;
  color: var(--color-ink-soft);
  /* Matches .pricing-card-head h3's own size (the plan cards right above
     this table) — same "Free/Starter/Growth/Premium" title, so it reads as
     one consistent size across both. Sentence case too now (platform-wide
     titles rule), not the old all-caps — .pricing-table-head-btn below
     used to need an explicit text-transform:none override just to escape
     this rule; harmless now that this itself no longer transforms, kept
     anyway since the two are allowed to diverge in future. */
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  /* Confirmed via a real screenshot + computed styles: the empty "Feature"
     cell (just an invisible sr-only span, zero line-content) shrank to a
     shorter box than its text-bearing siblings when this used
     align-self:end — align-self shrinks each cell to *its own* content
     height rather than stretching to the shared row height, so the short
     cell left a gap above it with nothing painted, revealing the wrap's
     white background (and its rounded corner) underneath. Fix: every
     header cell now stretches (the grid default) to one uniform row
     height — same height for all of them, always fully painted — and an
     inner flex column bottom-aligns each cell's own content instead. */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 12px;
}
.pricing-table-head-cell:first-child { color: var(--color-ink); }
/* Same CTA as each plan card's own button right above this table (see
   pricing.html's own comment on each one), just switched to the site's
   plain outline pill (.btn-pill-outline, same as the nav's own "Book Demo")
   instead of each card's own solid dark/orange fill, and sized well below
   even that button's own default — a ~200px-wide column has no room for a
   card-sized CTA, so this is smaller than every other .btn on the page.
   This is the site's "sm buttons" size — see DESIGN_GUIDELINES.md "Small
   (sm) 按钮" for the full spec; reuse this class pairing for any future
   small pill CTA rather than hand-rolling another size. */
.pricing-table-head-btn {
  gap: 6px;
  padding: 7px 12px;
  /* Bumped from the original 0.875rem — still the smallest CTA on the page
     (the ~200px-wide column has no room for a card-sized button) but the
     old size read too small next to the rest of the table. */
  font-size: 0.9375rem;
  /* Title Case now (label copy is "Start Free" / "Get Started" / "Talk to
     Sales"), matching the platform-wide button-label standard — not the
     old all-caps treatment, and not the sentence-case variant used
     earlier. Explicit text-transform: none is still required here, not
     just an absent declaration — the three non-Growth buttons sit inside
     .pricing-table-head-cell, which is itself uppercase, so they'd inherit
     that right back without this override. */
  text-transform: none;
  letter-spacing: 0.02em;
  /* .btn-pill-outline's own solid white fill dropped per the user's
     request — true outline now: transparent, just a border, tuned dark
     for the three columns sitting on the light green/cream header. */
  background: transparent;
  border-color: var(--color-ink-soft);
  color: var(--color-ink);
}
/* No hover background-color change, same "sm buttons" rule as every other
   .ai-arrow pill on the site (see DESIGN_GUIDELINES.md "Hero CTA 按钮") —
   hover feedback comes only from the arrow rotating below, not a color
   shift. Needs an explicit override, not just the absence of one: .btn-
   pill-outline:hover (class + pseudo-class, specificity 0,2,0) otherwise
   beats this plain .pricing-table-head-btn background rule (0,1,0)
   regardless of source order, filling these transparent pills with
   var(--color-bg-alt) on hover — visible as an unwanted white/cream fill
   against the green header cells. */
.pricing-table-head-btn:hover,
.pricing-table-head-btn:focus-visible {
  background: transparent;
}
/* Growth's own button sits on the dark cap instead — same transparent
   treatment, flipped to a light border/text so it still reads against
   that dark fill. Also no hover background change, for the same reason. */
.pricing-table-popular-btn {
  border-color: rgba(255, 255, 255, 0.5);
  color: #fff;
}
/* .pricing-table-wrap paints one white background under the whole grid
   (header included), so true transparency here would still just show
   that white through — painted the same solid color as .pricing-compare's
   own backdrop instead, which reads identically to transparent. */
.pricing-table-head-cell:not(.is-popular) { background: #E6EFD3; }
/* The "Most Popular" panel is a genuinely separate component, not a table
   cell — see pricing.html's own comment on it. It used to be a CSS Grid
   item spanning the Growth column (grid-column: 4; grid-row: 1/-1|4/5),
   but that fought the grid in two ways that both proved unfixable in pure
   CSS: (1) as a normal (non-abspos) item it blocked auto-placement from
   ever using column 4 for the *real* per-row cells, shoving Growth's and
   Premium's own values sideways into the wrong columns; (2) switching it
   to position:absolute to dodge that placement fixed (1) but broke the
   sizing — an abspos grid item can only resolve a named line like grid-row:
   1 / -1 against an *explicit* grid-template-rows, and this table only
   declares grid-template-columns (its rows are all implicit), so -1
   collapsed to nothing and the panel shrank to a sliver. Now positioned
   entirely by JS instead (js/main.js, "Pricing table Most Popular panel"):
   plain position:absolute with left/top/width/height set from the real
   Growth head cell's rendered rect and the table's own bottom edge, so it
   tracks the fluid minmax() column width and every row's real height
   without needing to be part of the grid at all. */
.pricing-table-popular-panel {
  position: absolute;
  z-index: 0;
  pointer-events: none;
}
.pricing-table-popular-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Figma "accent-foreground light" — same token as --color-bg-alt
     (#FAF9F8, see index.html's own note on that var), swapped in for the
     original --color-dark-panel per the user's request — for the *body*
     only. The header stays the original dark fill (.pricing-table-popular-
     cap below, painted on top of this for just that zone), per the user's
     follow-up to keep the badge/label area's original color. */
  background: var(--color-bg-alt);
  border-radius: var(--radius-lg);
}
/* The original dark header cap, preserved: covers row 1's own height plus
   the raised badge zone above it (sized by JS to match, same as
   .pricing-table-popular-header just below), painted over ::before's
   light fill for that stretch only — square bottom edge since the light
   body continues flush beneath it, no seam to round there.

   Its own top corners can't just be a plain border-radius, though: cutting
   a rounded corner out of this box reveals whatever's *behind* it, and
   that's ::before's own light accent-foreground-light fill (this cap sits
   directly on top of it) — so the "cut" corner showed pale white/cream,
   not the green page background the top of this table actually sits on
   (same .pricing-compare backdrop the un-popular header cells already
   match flat, no radius, for the same reason). Same radial-gradient
   corner-patch fix as the table's first data row below, just painting
   explicit green in the cutout instead of leaving it to transparency +
   whatever happens to be underneath. */
.pricing-table-popular-cap {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  background-color: var(--color-dark-panel);
  background-image:
    radial-gradient(circle at 20px 20px, transparent 20px, #E6EFD3 21px),
    radial-gradient(circle at calc(100% - 20px) 20px, transparent 20px, #E6EFD3 21px);
  background-size: 20px 20px, 20px 20px;
  background-position: top left, top right;
  background-repeat: no-repeat, no-repeat;
}
/* The visible "Growth" label — the actual Growth header *cell* in the
   table now renders only an sr-only span (see pricing.html), so this is
   the one and only place it's drawn. Sized and positioned by JS to exactly
   overlay that (invisible) real cell's own box — same left/width/height a
   normal header cell would have — so "Growth" lands on precisely the
   baseline Free/Starter/Premium's own text sits on. Used to also hold a
   "Most Popular" pill stacked above the label (dropped per the user's
   request — the star now on the label itself already carries that signal)
   which needed to overflow above this box; kept justify-content/overflow
   here since a single line of content isn't affected by either. */
.pricing-table-popular-header {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-start;
  gap: 12px;
  padding: 20px 20px 16px;
  overflow: visible;
  /* The panel this sits in is pointer-events:none (it's mostly the
     decorative cap/fill) — re-enabled here for the one part that's real,
     selectable content: the "Growth" label and its button. */
  pointer-events: auto;
}
/* .pricing-table-popular-panel (this button's ancestor) is aria-hidden and
   pointer-events:none — purely decorative, since the real Growth column
   data cells are what a screen reader/keyboard sees. This one link is real
   content though, so pointer-events needs re-enabling just for it. */
.pricing-table-popular-btn { pointer-events: auto; }
.pricing-table-popular-label {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #fff;
  /* Matches .pricing-table-head-cell's own size above (which itself
     matches .pricing-card-head h3) so all four titles read as one size —
     sentence case too, same platform-wide titles rule. */
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.03em;
}
/* Same filled-star path as the "Most Popular" badge above it, colored the
   same explicit lime (#D9F20E) rather than inheriting the label's own
   white text color — a deliberate accent, not currentColor. */
.pricing-table-popular-label-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: #D9F20E;
}
/* A real `border-radius` was tried here and initially looked right — but
   pixel-sampled at the actual corner pixel, the "wedge" it reveals is
   plain white (255,255,255), not the green (230,239,211) this needs.
   Reasoning that seemed to justify it — ".pricing-table-wrap's own white
   is *already* clipped to this same radius at its real corners, so the
   two clips align" — only holds at the wrap's actual top/bottom edges;
   this table's first data row sits in the *middle* of the wrap's box
   (row 1, the header, is the real top edge), so there's no wrap-radius
   cutout to align with there, and border-radius just exposes the wrap's
   plain white straight through. (The bottom row *is* the wrap's real
   edge, but sampling it gave the same wrong white — so the two clips
   evidently don't align there either; not relying on that coincidence
   for either corner.) An even earlier version painted the fill correctly
   via a radial-gradient corner-patch (transparent center reveals white,
   opaque #E6EFD3 outside) but couldn't curve the cell's real border to
   match, since a gradient is color, not geometry — that's what produced
   the mismatched-corners bug this replaces.
   This version gets both right by painting the *entire* corner — fill
   and the border's own curve — as color, none of it left to chance about
   what's behind: one radial gradient supplies a transparent disc (reveals
   this cell's own white background-color), a 1px ring in the border color
   at its rim (the curved part of the border), and #E6EFD3 beyond that
   (the fill, self-painted, not revealed by clipping); two linear
   gradients extend a matching 1px straight line the rest of the way along
   the top/left (or bottom/right) edges, picking up exactly where the
   ring's tangent point leaves off. border-top/left (or bottom/right) are
   turned off on these cells since the gradients now draw that entire
   edge themselves. */
.pricing-table-row:nth-child(2) .pricing-table-cell:first-child {
  background-color: #fff;
  background-image:
    radial-gradient(circle at 20px 20px, transparent 19px, var(--color-border) 19px 20px, #E6EFD3 20px),
    linear-gradient(to right, transparent 20px, var(--color-border) 20px),
    linear-gradient(to bottom, transparent 20px, var(--color-border) 20px);
  background-size: 20px 20px, 100% 1px, 1px 100%;
  background-position: top left, top left, top left;
  background-repeat: no-repeat, no-repeat, no-repeat;
  border-top: none;
  border-left: none;
}
.pricing-table-row:nth-child(2) .pricing-table-cell:last-child {
  background-color: #fff;
  background-image:
    radial-gradient(circle at calc(100% - 20px) 20px, transparent 19px, var(--color-border) 19px 20px, #E6EFD3 20px),
    linear-gradient(to left, transparent 20px, var(--color-border) 20px),
    linear-gradient(to bottom, transparent 20px, var(--color-border) 20px);
  background-size: 20px 20px, 100% 1px, 1px 100%;
  background-position: top right, top, top right;
  background-repeat: no-repeat, no-repeat, no-repeat;
  border-top: none;
  border-right: none;
}
/* Same technique for the table's actual bottom-left/right corners
   (Free/Starter/Premium's last row — the popular column's own bottom
   corners are handled by the panel's ::before, not this). border-bottom
   (added here, not just the rounding) is what closes the frame: every
   other edge already gets a stroke (border-top per row, border-right/
   border-left above), this is the one remaining side — the table's
   actual bottom edge; the two corner cells immediately override it with
   their own gradient-drawn version for the same reason their border-left/
   border-right are overridden below. */
.pricing-table-row:last-child .pricing-table-cell {
  border-bottom: 1px solid var(--color-border);
}
.pricing-table-row:last-child .pricing-table-cell:first-child {
  background-color: #fff;
  background-image:
    radial-gradient(circle at 20px calc(100% - 20px), transparent 19px, var(--color-border) 19px 20px, #E6EFD3 20px),
    linear-gradient(to right, transparent 20px, var(--color-border) 20px),
    linear-gradient(to top, transparent 20px, var(--color-border) 20px);
  background-size: 20px 20px, 100% 1px, 1px 100%;
  background-position: bottom left, bottom left, bottom left;
  background-repeat: no-repeat, no-repeat, no-repeat;
  border-bottom: none;
  border-left: none;
}
.pricing-table-row:last-child .pricing-table-cell:last-child {
  background-color: #fff;
  background-image:
    radial-gradient(circle at calc(100% - 20px) calc(100% - 20px), transparent 19px, var(--color-border) 19px 20px, #E6EFD3 20px),
    linear-gradient(to left, transparent 20px, var(--color-border) 20px),
    linear-gradient(to top, transparent 20px, var(--color-border) 20px);
  background-size: 20px 20px, 100% 1px, 1px 100%;
  background-position: bottom right, bottom, bottom right;
  background-repeat: no-repeat, no-repeat, no-repeat;
  border-bottom: none;
  border-right: none;
}
/* The fill itself now lives entirely in .pricing-table-popular-panel's
   ::before (see above) — these stay plain grid cells, just text sitting on
   top of that panel, so the header cell needs no more height, background,
   or corner-radius logic of its own than Free/Starter/Premium have. Text
   color matches the rest of the table (var(--color-ink)) now that the
   panel itself is the light accent-foreground-light fill, not the old
   --color-dark-panel — no more white-on-dark override needed here, or on
   the dash below (its default .pricing-table-dash styling two rules down
   already reads fine against this light background). */
/* Only the *body* cells need the z-index lift — they carry real visible
   text/checkmarks that must paint above the panel sitting behind them.
   The head cell doesn't: it's sr-only-only (see pricing.html), so giving
   it the same z-index:1 gained nothing visible but put an invisible,
   still fully hit-testable box directly on top of the panel's own Get
   Started button for that entire row — every click/hover on it (and on
   the "Growth" text next to it) was silently landing on this empty cell
   instead of passing through to the real content underneath. Left
   unpositioned, it now stays *below* the panel (a positioned element
   always paints over plain static content in the same stacking context),
   so the panel's own pointer-events:none/auto split is what actually
   decides what's clickable there instead. */
.pricing-table-cell.is-popular { position: relative; z-index: 1; }
.pricing-table .pricing-check { color: var(--color-green); }
.pricing-table-dash { color: var(--color-ink-soft); opacity: 0.5; }

/* ---- Mobile plan-compare tabs (<768px only) ----
   Below 768px there's no room to lay out all 4 plan columns at once, so
   the table collapses to 2 columns (Feature + one plan) and this segmented
   control picks which plan fills the second one — same sliding-thumb
   pattern as .pricing-toggle above, just 4 stops instead of 2 (see
   js/main.js, "Pricing table mobile plan tabs"). Defaults to Growth (see
   pricing.html's data-active-plan="growth" + this bar's .is-active tab),
   matching the desktop "Most Popular" emphasis on that same plan. Sized
   noticeably bigger than .pricing-toggle's own buttons per the user's
   request — this is the one and only way to switch plans at this width
   (the table's header row with its own per-column name+CTA is dropped
   below, see that block's comment), so it needs to read as a primary
   control, not a small secondary one. */
.pricing-table-mobile-tabs { display: none; }
.pricing-table-mobile-tabs-thumb {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 0;
  border-radius: var(--radius-pill);
  background: var(--color-ink);
  transition: transform .35s cubic-bezier(.22, 1, .36, 1), width .35s cubic-bezier(.22, 1, .36, 1);
  will-change: transform, width;
}
.pricing-table-mobile-tab {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 13px 6px;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-ink-soft);
  font-weight: 600;
  font-size: 0.9375rem;
  transition: color .2s ease;
}
.pricing-table-mobile-tab.is-active { color: #fff; }

/* Below the breakpoint where the grid's own minmax() columns can't fit,
   this used to just fall back to raw horizontal scroll (overflow-x: auto +
   a wide min-width). Replaced with the tab bar above instead: the grid
   narrows to 2 columns (Feature + whichever plan is active) and every
   other plan column is display:none'd via .pricing-table-wrap's own
   data-active-plan attribute (set by js/main.js on tab click), so there's
   nothing left to scroll at all. */
@media (max-width: 767.98px) {
  .pricing-table-mobile-tabs {
    position: relative;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 2px;
    margin-bottom: 16px;
    padding: 5px;
    background: #fff;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-card);
    /* Left/right drag switches tabs (js/main.js) — pan-y leaves the
       browser's own vertical scroll alone (a plain "none" would block the
       page from scrolling if a drag starts on this bar) while still
       handing horizontal movement to JS instead of the browser trying to
       pan the page sideways with it. */
    touch-action: pan-y;
  }

  /* overflow:hidden (not the old overflow-x:auto) now that there's
     nothing left to scroll — this also lets the wrap's own border-radius
     clip the grid directly, so the per-cell corner-gradient hacks above
     (tuned for the desktop overflow:visible case, where a plain
     border-radius can't clip anything) are switched off just below and
     replaced with plain borders; the wrap's clip draws the same rounded
     corners without needing to know which column happens to be visible. */
  .pricing-table-wrap { overflow: hidden; }
  .pricing-table { grid-template-columns: minmax(132px, 1.25fr) 1fr; min-width: 0; }
  .pricing-table-popular-panel { display: none; }

  /* The whole header row (plan name + its own "Start Free"/"Get Started"
     CTA per column) is dropped at this width — the tab bar above already
     names the selected plan, and its CTA lives in the plan cards higher up
     the page, so repeating both per-column here was redundant (per the
     user's request). The first visible row is now the first *data* row
     (row 2), so its corner-reset just below is what actually meets the
     wrap's top edge now, not just its bottom one. */
  .pricing-table-head-cell { display: none; }

  /* Feature column: light grey, every row — the value column alongside it
     (whichever plan is active) stays plain white per the user's request,
     so this is what actually tells the two columns apart now that Growth
     no longer gets its own distinct tint (removed just below). */
  .pricing-table-cell:first-child { background: var(--color-bg-alt); }

  .pricing-table-row:nth-child(2) .pricing-table-cell:first-child,
  .pricing-table-row:last-child .pricing-table-cell:first-child {
    /* Same selector (and so the same specificity) as the desktop-only
       corner rule above that this is meant to replace at this width — its
       own background-color: #fff otherwise wins the tie by sitting later
       in the file, undoing the grey every other Feature cell gets just
       above. Restating it here is what actually overrides it. */
    background-color: var(--color-bg-alt);
    background-image: none;
    border-right: 1px solid var(--color-border);
  }
  .pricing-table-row:nth-child(2) .pricing-table-cell:last-child,
  .pricing-table-row:last-child .pricing-table-cell:last-child {
    background-image: none;
    background-color: #fff;
    border-left: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
  }
  /* Row 2 now sits at the wrap's actual top edge (the header row above it
     is gone), so nothing above it needs a boundary line — every other row
     (including the corner cells above, which fall back to the plain
     .pricing-table-cell rule's own border-top now that this block no
     longer states one) keeps its usual top border, this is the one
     exception. Split out into its own rule rather than folded into the
     shared corner block above: that block's selector list would apply
     one fixed border-top to all four matched cells alike, not just these
     two. */
  .pricing-table-row:nth-child(2) .pricing-table-cell { border-top: none; }

  /* Show only the Feature column (always nth-child(1)) plus whichever
     plan's own column the active tab points at — every other plan column
     display:none's out of every body row. Grid auto-placement skips
     display:none items entirely, so the one visible plan column always
     flows into the grid's 2nd (and only other) track, landing at the
     wrap's real right edge regardless of which plan it is — which is
     exactly what lets the wrap's own overflow:hidden clip handle that
     corner uniformly above, with no per-plan-column case needed for it. */
  .pricing-table-cell:nth-child(n+2) {
    display: none;
  }
  .pricing-table-wrap[data-active-plan="free"] .pricing-table-cell:nth-child(2) { display: flex; }
  .pricing-table-wrap[data-active-plan="starter"] .pricing-table-cell:nth-child(3) { display: flex; }
  .pricing-table-wrap[data-active-plan="growth"] .pricing-table-cell:nth-child(4) { display: flex; }
  .pricing-table-wrap[data-active-plan="premium"] .pricing-table-cell:nth-child(5) { display: flex; }

  /* Growth used to keep the desktop panel's own light tint here too, but
     per the user's request the value column is now plain white for every
     plan alike (including Growth) — only the Feature column above gets a
     background at this width. */
  .pricing-table-cell.is-popular { background: #fff; }
}

/* ---- Tablet (768–1099px): sticky Feature column + horizontal scroll ----
   The plain desktop 5-column grid below only really has room to breathe
   from ~1100px up — tested across iPad-ish widths its minmax() floors
   squeeze every CTA label ("Start Free" → "Start", "Talk to Sales" →
   "Talk") and the "Most Popular" panel's own label starts overlapping
   Premium's header text; a real screenshot at exactly 1024px (iPad Pro
   12.9" portrait) still clipped "Talk to Sales" by a letter, which is why
   this range runs past that width rather than stopping at it. Rather than
   shrinking text further or dropping to the mobile tab-switcher (there's
   plenty of width here, just not enough for 5 full columns at once), the
   table scrolls horizontally within its own wrap and the Feature column
   freezes in place via position:sticky — same "frozen first column"
   pattern spreadsheets use, so whichever row you're comparing stays
   labeled no matter how far right you've scrolled. The header row (with
   each plan's real name+CTA) and the "Most Popular" panel stay exactly as
   the desktop markup already has them — nothing here is mobile's tab bar
   (still display:none at this width) or its collapsed 2-column grid, just
   the same 5-column table made scrollable. */
@media (min-width: 768px) and (max-width: 1099.98px) {
  .pricing-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .pricing-table {
    /* Each plan column needs enough room for its own CTA on one line
       ("Talk to Sales ↗" is the longest) plus the cell's own 20px side
       padding and the button's own 12px — 150px still clipped that text
       (confirmed by an actual screenshot, not just eyeballing it), 190px
       doesn't. */
    grid-template-columns: minmax(170px, 1.3fr) repeat(4, minmax(190px, 1fr));
    min-width: 940px;
  }

  /* .pricing-table-row is display:contents (see the big comment up top),
     so each cell is its own real grid item and can be sticky on its own —
     no wrapper needed. left:0 pins it to the scrollport's own left edge;
     z-index:2 keeps it painted above both the scrolling body cells (which
     get z-index:1 when they're Growth's own, see .pricing-table-cell.is-
     popular below) and the "Most Popular" panel (z-index:0) as they slide
     underneath it. */
  .pricing-table-head-cell:first-child,
  .pricing-table-cell:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
  }
  /* Needs an explicit opaque background — without one the sticky cell is
     transparent and whichever column is currently scrolled underneath it
     would show straight through. The header cell doesn't need this: it
     already gets #E6EFD3 unconditionally from .pricing-table-head-cell:
     not(.is-popular) above. Same light grey as the mobile breakpoint's own
     Feature column (per the user's request to match it here too, not just
     stay plain white like the desktop grid) — value columns stay
     untouched/white, same left-grey/right-white split as mobile. Row 2/
     last-row's own first-child cells keep their existing corner-gradient
     background instead (a more specific selector elsewhere in this file,
     restated further down to also be this same grey). */
  .pricing-table-cell:first-child { background: var(--color-bg-alt); }
  /* Row 2 (the first data row) is targeted by that same unconditional
     desktop-only corner-gradient rule at equal specificity — its own
     background-color: #fff otherwise wins the tie by sitting later in the
     file, the exact bug already fixed for the mobile breakpoint and for
     this same width's own last row just below. Restating it here is what
     actually overrides it; no border-radius needed on this one, row 2
     isn't a true corner at this width the way it is on mobile — the
     header row above it is still visible here, so row 2 is just a normal
     middle row as far as rounding goes. */
  .pricing-table-row:nth-child(2) .pricing-table-cell:first-child {
    background-color: var(--color-bg-alt);
    background-image: none;
    border-left: 1px solid var(--color-border);
  }
  /* A soft edge so the frozen column visibly sits above what's scrolling
     under it, rather than just relying on the 1px border every cell
     already has. */
  .pricing-table-head-cell:first-child,
  .pricing-table-cell:first-child {
    box-shadow: 4px 0 8px -4px rgba(20, 18, 15, 0.12);
  }

  /* overflow-x:auto above quietly makes overflow-y compute to auto too —
     the same "one axis non-visible forces the other to auto" quirk this
     file's mobile media query already relies on — so .pricing-table-wrap
     now genuinely clips to its own border-radius for the first time at
     this width (the desktop grid just below stays overflow:visible and
     never clips anything, which is exactly why none of its header/footer
     cells have ever needed their own corner radius). A screenshot at this
     width confirmed the bug that leaves unfixed: the header row's plain
     rectangular corners don't match the wrap's curve, so its white
     background peeks through in the sliver the clip cuts away. Each true
     corner cell gets its own matching radius here instead — the two
     bottom ones share a selector with the unconditional desktop-only
     corner-gradient hack elsewhere in this file (tuned for that
     overflow:visible case, where nothing really clips and the gradient
     has to fake the rounded look itself), so background-color is
     restated to actually win the tie, same fix already applied for the
     mobile breakpoint above. */
  .pricing-table-head-cell:first-child { border-top-left-radius: var(--radius-lg); }
  .pricing-table-head-cell:last-child { border-top-right-radius: var(--radius-lg); }
  /* Row 2's own last-child (Premium's first data-row value, at equal
     specificity with that same unconditional corner-gradient rule) isn't
     a true corner here either — same reasoning as row 2's first-child
     above — but its leftover gradient still faintly showed through as a
     shadowed curve in a real screenshot, so it gets the same neutralizing
     treatment: no radius, just a plain cell again. */
  .pricing-table-row:nth-child(2) .pricing-table-cell:last-child {
    background-color: #fff;
    background-image: none;
    border-top: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
  }
  .pricing-table-row:last-child .pricing-table-cell:first-child {
    background-image: none;
    background-color: var(--color-bg-alt);
    border-left: 1px solid var(--color-border);
    border-bottom-left-radius: var(--radius-lg);
  }
  .pricing-table-row:last-child .pricing-table-cell:last-child {
    background-image: none;
    background-color: #fff;
    border-right: 1px solid var(--color-border);
    border-bottom-right-radius: var(--radius-lg);
  }
}

@media (min-width: 768px) {
  .pricing-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .pricing-grid { grid-template-columns: repeat(4, 1fr); }
}

/* ==========================================================================
   AI CREATOR SEARCH (feature page — ai-creator-search.html)
   ========================================================================== */
/* COLOR/IMAGE SPLIT — full-bleed 50/50 panel replacing the old first
   .ai-row ("Already working with creators?"). Same full-bleed-50/50
   technique .cta-inner/.cta-copy/.cta-visual already use (column->row
   flip at 1024px, edge-to-edge via a non-.container inner wrapper) — own
   classes rather than reusing those since there's no copy/buttons/tilt
   here, just a solid color panel and a static image, each exactly half. */
.split-panel { padding-block: 0; overflow: hidden; }
.split-panel-inner {
  display: flex;
  flex-direction: column;
}
.split-panel-color {
  background: #512C00;
  min-height: 240px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 48px 24px;
  /* Stacked (mobile/tablet, <1024px) order: image always on top, copy
     always below — regardless of which half comes first in the markup.
     The first split panel below is copy-then-visual in DOM order (so
     row-layout desktop reads copy-left/image-right), the second is
     visual-then-color (image-left/text-right); without this, stacking
     them by plain DOM order would put the first panel's copy on top and
     the second panel's image on top — inconsistent with each other.
     Reset to the default order at 1024px+ (below) once flex-direction is
     back to row, where DOM order alone should decide left/right again. */
  order: 2;
}
/* Holds the "Every filter you'd actually use" copy moved in from the old
   third .ai-row (its content is a direct match for this panel's own
   Filter-panel image) — reuses .ai-copy/.ai-eyebrow/.ai-copy-text/
   .btn-pill-outline-dark verbatim rather than new classes, and takes
   those classes' own *default* (no nth-child override) colors: lime
   eyebrow, white body copy, lime button — the exact combo the old first
   .ai-row ("Already working with creators?") used, which reads even
   better here against a solid dark-brown ground than it did against that
   row's pale white/cyan wash. */
.split-panel-color .ai-copy {
  width: 100%;
  max-width: 620px;
}
/* .ai-eyebrow normally wraps because it's a flex item on the MAIN axis of
   a row-direction .ai-row (flex-basis/shrink there actually constrains
   its width) — here .ai-copy's parent (.split-panel-color) is a COLUMN
   flex container instead, so .ai-eyebrow sits on the CROSS axis, where
   align-items:flex-start sizes it to its own unwrapped max-content width
   (846px for this heading) rather than clamping to .ai-copy's 480px box.
   max-width alone isn't enough here either: .ai-dot (the only other flex
   child) is display:none, so .ai-eyebrow's own inline-flex/align-items
   is doing nothing useful for it, but still applies its cross-axis
   height logic to the wrapped text — that mis-measures the box to
   roughly one line tall and the second line renders overlapping the
   first instead of below it. display:block sidesteps that: the heading
   is just wrapped text either way, so it lays out (and wraps to as many
   lines as it needs) exactly like the block-level .ai-copy-text below
   it. */
.split-panel-color .ai-eyebrow {
  display: block;
  max-width: 100%;
  text-wrap: balance;
}
/* iPad only (768–1023.98px) — per feedback, center this copy block instead
   of it inheriting .ai-copy's own desktop-style left alignment. Phone
   already reads as centered (the 480px cap roughly equals the viewport's
   own padded width there, so the flush-left block already spans edge to
   edge), and 1024px+ keeps the deliberate left-aligned desktop layout
   (row-flip puts this copy beside the image, where left-aligned is the
   established .split-panel-color intent — see the "Holds the ... copy"
   comment above) — only the stacked tablet width has real empty gutter
   to its right that makes the flush-left block look off-center. */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .split-panel-color .ai-copy { align-items: center; margin-inline: auto; }
  .split-panel-color .ai-copy-bottom { align-items: center; }
  .split-panel-color .ai-eyebrow,
  .split-panel-color .ai-copy-text { text-align: center; }
}
.split-panel-visual {
  display: flex;
  align-items: stretch;
  justify-content: center;
  min-height: 240px;
  order: 1; /* stacked order — see .split-panel-color's own comment */
}
.split-panel-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Below 1024px .split-panel-inner is column-direction (see
   .split-panel-color's own comment above), so .split-panel-visual has no
   row to stretch to. Forcing the image's own native 1800/1608 aspect-ratio
   here means object-fit:cover has nothing left to crop, so the source PNG
   (gradient margin and filter/results card both) renders completely —
   this used to be iPad-only (768–1023.98px) with phone cropped to a 4:3
   card-bleed crop instead, but per feedback phone now gets the same
   "show it completely" treatment as iPad. */
@media (max-width: 1023.98px) {
  .split-panel-visual { aspect-ratio: 1800 / 1608; min-height: 0; }
}
@media (min-width: 1024px) {
  .split-panel-inner {
    flex-direction: row;
    /* Preserve the original 1800x1608 split artwork proportion as the
       viewport grows. Shorter two-line titles must not collapse the panel. */
    min-height: clamp(480px, 44.6667vw, 804px);
  }
  .split-panel-color { flex: 0 0 50%; padding: 64px clamp(24px, 5vw, 64px) 64px clamp(24px, 6vw, 96px); order: 0; }
  .split-panel-visual { flex: 0 0 50%; min-height: auto; order: 0; }
}
/* Keep full-bleed split-panel display titles to at most two lines whenever
   the available desktop column can accommodate them. The global 4rem title
   tier was too large for a half-width panel and produced avoidable third
   lines across the feature pages. */
@media (min-width: 1024px) and (max-width: 1599.98px) {
  .split-panel-color .ai-eyebrow {
    font-size: clamp(2.5rem, 3vw, 3rem);
  }
}
/* Second split panel (below) reuses .split-panel-color for its own
   layout/copy rules but needs a different fill — this modifier just
   swaps the background, same #4d3fe9 purple .ai-icon-badge--video
   already uses elsewhere, added after the base rule so plain source
   order (not specificity) settles it. */
.split-panel-color--violet { background: #4d3fe9; }
/* Image Search split panel reuses the homepage Product Highlight row 3
   palette exactly: pink ground, burgundy display text/button, and dark
   body copy. Kept as a modifier so the other search pages stay violet. */
.split-panel-color--home-pink { background: #f5c3d6; }
.split-panel-color--home-pink .ai-eyebrow { color: #850010; }
.split-panel-color--home-pink .ai-copy-text,
.split-panel-color--home-pink .search-page-split-copy { color: #14120f; }
.split-panel-color--home-pink .btn-pill-outline-dark {
  background: #850010;
  color: #fff;
}
.split-panel-color--home-pink .btn-pill-outline-dark:hover,
.split-panel-color--home-pink .btn-pill-outline-dark:focus-visible { background: #9b1021; }
.split-panel-color--home-pink .btn-pill-outline-dark .ai-arrow { color: #fff; }
.split-panel-color--home-orange { background: #ff9e5c; color: #14120f; }
.split-panel-color--home-orange .ai-eyebrow { color: #6b2d00; }
.split-panel-color--home-orange .ai-copy-text { color: #14120f; }
.split-panel-color--home-orange .btn-pill-outline-dark {
  border-color: #6b2d00;
  background: #6b2d00;
  color: #fff;
}
.split-panel-color--home-orange .btn-pill-outline-dark:hover,
.split-panel-color--home-orange .btn-pill-outline-dark:focus-visible { background: #7d3805; }
.split-panel-color--home-orange .btn-pill-outline-dark .ai-arrow { color: #fff; }
/* Profile-icon badge (this panel's own icon, see the HTML comment above
   it) — #ff984b per Figma node 3311:108459's own background rect, not
   one of the shared .ai-icon-badge--outreach/video/pricing colors above,
   so it gets its own modifier rather than overloading one of those. */
.ai-icon-badge--profile { background: #ff984b; }
/* Image Search split panel's own icon (Figma node 3362:107406) — same
   #4d3fe9 purple .ai-icon-badge--video already uses, but per the
   .ai-icon-badge--profile/--blog precedent above, a coincidentally
   matching color still gets its own modifier rather than overloading
   an unrelated one. */
.ai-icon-badge--image-search { background: #4d3fe9; }
.search-page-split-figma-badge { background: #ffc1d7; }
.search-page-split-figma-badge {
  width: 69.12px;
  height: 69.12px;
  border-radius: 17.28px;
}
.search-page-split-figma-icon { display: block; width: 52.56px; height: 46.08px; }
.product-search-split-figma-badge { background: #d3f300; }
.product-search-split-figma-icon { display: block; width: 42.48px; height: 45.36px; }
/* That second panel's left half holds the Match By screenshot — a small
   floating-dropdown capture on a white ground, not a wide bled photo
   like the first panel's Figma image, so stretching it edge-to-edge via
   the base .split-panel-image's object-fit:cover would crop it
   awkwardly (same reasoning .ai-visual--panel's own comment gives for
   containing screenshots in a white card rather than covering them).
   This variant centers that same white-card treatment on a full-height
   dot-grid ground instead — same dot pattern .cta-copy already uses for
   its own panel background, so the two full-bleed split sections still
   read as one system despite the differing image treatments. */
.split-panel-visual--framed {
  align-items: center;
  padding: 32px;
  background-color: #F5F3F1;
  background-image: radial-gradient(rgba(20, 18, 15, 0.11) 1px, transparent 1px);
  background-size: 16px 16px;
}
.split-panel-image--contain {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 12px;
}
@media (min-width: 1024px) {
  .split-panel-visual--framed { padding: 64px; }
}
/* Same centered badge/h1/sub pattern as .pricing-hero (see that block's own
   comments) — this is the other independent page on the site, so it
   inherits the same "70px below the fixed nav" rule from DESIGN_GUIDELINES.md
   ("非首页顶部留白") rather than .section's generic default. Adds an eyebrow
   line (.pricing-hero doesn't have one) and a hero visual below the CTA row
   (.pricing-hero doesn't have one either, it goes straight into the billing
   toggle) — both new for this page, sized/spaced to match the existing
   rhythm rather than copied wholesale. */
/* Solid-color hero band — per feedback, references linktr.ee's own feature
   page (linktr.ee/features/link-shortener): a full-bleed *solid* color
   panel behind the hero copy, with a plain white screenshot card floating
   on top of it, rather than this site's usual white/dot-grid ground.
   Palette corrected per feedback to match the site's own existing orange
   (not a new brand-orange fill): the exact bg/heading/body/button colors
   already used by index.html's "Automated Outreach" Product Highlight row
   (.ai-row:nth-child(2) — #ff9e5c background, #6b2d00 dark-brown heading/
   button, #14120f near-black body copy), so this reads as the same orange
   the rest of the site already has, not a one-off. Reuses .hero's own
   "pull the fill up behind the fixed header" trick verbatim (margin-top:
   -126px cancels body's 126px nav-clearance padding, padding-top adds it
   back plus this section's own top spacing) so the color reaches the very
   top of the page, not just below the nav pill — see that block's comment
   for why the 126px figure is exact. padding-bottom replaces the old
   flush-0 (previously fine when this section's bg matched the next one)
   with real breathing room so the panel doesn't end flush against the
   screenshot's bottom edge. */
.feature-hero {
  background: #ff9e5c;
  margin-top: -126px;
  padding-top: calc(126px + 70px);
  padding-bottom: 96px;
}
/* Two-column split per feedback — copy on the left, visual on the right,
   same mobile-stacked/desktop-row structure and flex ratio as the
   homepage's own .hero-inner/.hero-copy/.hero-visual (see that block's
   comments), rather than the previous single centered column. */
.feature-hero-inner {
  display: flex;
  flex-direction: column;
  gap: 40px;
}
.feature-hero-copy { text-align: left; }
.feature-hero-badge { margin-bottom: 16px; }
.feature-hero h1 { color: #6b2d00; }
.feature-hero .hero-sub { max-width: 480px; color: #14120f; }
.feature-hero .hero-actions { justify-content: flex-start; margin-top: 32px; }
@media (min-width: 1024px) {
  .feature-hero-inner {
    flex-direction: row;
    align-items: center;
    gap: clamp(40px, 6vw, 80px);
  }
  .feature-hero-copy { min-width: 0; flex: 1 1 62%; }
  .feature-hero-visual { min-width: 0; flex: 1 1 38%; margin-top: 0; }
}
/* Buttons match .ai-row:nth-child(2) .btn-pill-outline-dark's own dark-brown
   pair — the site's usual solid-orange primary CTA would disappear against
   this same-family orange panel, so primary takes that row's dark-brown
   fill/hover instead, and secondary becomes a dark-brown outline. */
.feature-hero .btn-hero-primary { background: #6b2d00; color: #fff; }
.feature-hero .btn-hero-primary:hover { background: #823900; }
.feature-hero .btn-hero-secondary { background: transparent; color: #6b2d00; border-color: #6b2d00; }
.feature-hero .btn-hero-secondary:hover { background: rgba(107, 45, 0, 0.08); }
/* Off at narrow widths for the same reason as .pricing-hero-title-break —
   h1 already wraps "...ideal creator." / "Quacko finds them in seconds."
   into two clean lines on its own below the fixed 1024px+ sizes; forcing
   the manual break on top of that natural wrap would push it to three. */
@media (max-width: 1023.98px) {
  .feature-hero-title-break { display: none; }
}
/* Tablet only (iPad portrait/landscape, before the 1024px 2-column switch
   above) — the phone's 480px .hero-sub cap reads as a narrow, left-hugging
   column with a lot of dead orange to its right once the stacked layout
   has a real tablet-width canvas to fill (.container padding-inline is
   32px here, so 768px alone gives ~700px to play with). Widened so the
   paragraph/button row use more of that width instead of just inheriting
   the phone measure verbatim. */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .feature-hero .hero-sub { max-width: 640px; }
}
/* Decorative photo collage (see the HTML comment above this element) —
   four separately-positioned layers floating directly on the orange
   panel, no card border/shadow/white-fill wrapper (that treatment was for
   the old product-screenshot placeholder, a real UI surface that needed
   to read as its own card; this is loose decoration, same as the
   homepage hero's own uncarded swirl/card art).
   .hero-collage is a 644x383 canvas (Figma's own node size) held at that
   exact aspect ratio so every child below — positioned/sized as a % of
   this canvas, straight from Figma's real coordinates — scales together
   correctly at any width. */
.feature-hero-visual { margin-top: 48px; }
/* Below 1024px, the AI Creator Search hero visual mobile reorder (main.js)
   moves #featureHeroVisual out of its normal spot at the end of
   .feature-hero-inner to sit inside .feature-hero-copy, between the title
   and .hero-sub — same "title -> image -> sub copy" pattern, and the same
   margin numbers, as .hero-copy .hero-visual on the homepage (see that
   rule's own comment for the full rationale). This selector only ever
   matches in that nested state, inert once the same JS moves it back out
   at 1024px+. */
.feature-hero-copy .feature-hero-visual { margin-block: 28px; }
@media (max-width: 1023.98px) {
  .feature-hero-copy .feature-hero-visual { margin-top: 44px; margin-bottom: 64px; }
}
/* Scaled rather than resized, so every child's %-based geometry below
   stays correct. Anchored to the right edge (transform-origin) since that
   edge already sits flush against the container's own padding — growing
   from center or the left would push it past the container; growing from
   the right instead spends the extra size into the gap toward the copy
   column, where the room actually is. 1.2 = the original 1.5x enlargement
   per earlier feedback, then 20% smaller per feedback (1.5 * 0.8). */
.hero-collage {
  position: relative;
  aspect-ratio: 644 / 383;
  transform: scale(1.2);
  transform-origin: 100% 50%;
}
/* Below 1024px the collage is stacked full-width (see the mobile reorder
   in main.js) with no adjacent gap for the right-anchored 1.2x scale to
   grow into, so it was overflowing past the left edge instead — the
   diamond and the painter pill were getting clipped by the viewport.
   Reset to the canvas's own natural size here instead. */
@media (max-width: 1023.98px) {
  .hero-collage { transform: none; }
}
/* Entrance: each of the collage's 4 pieces (diamond+sparkle counts as one)
   rises/scales/fades in on its own, staggered — a plain CSS @keyframes
   animation that just plays on page load, deliberately NOT the JS/
   IntersectionObserver-driven .is-revealed pattern used elsewhere
   ([data-visual-reveal] etc.): this collage is always above the fold, so
   there's nothing to gate on scrolling into view, and a pure-CSS trigger
   can't get stuck permanently invisible if the observer never fires. */
@keyframes heroCollagePieceIn {
  from { opacity: 0; transform: translateY(28px) scale(0.88); }
  to { opacity: 1; transform: none; }
}
.hero-collage-diamond,
.hero-collage-pill--phone,
.hero-collage-pill--painter,
.hero-collage-circle {
  animation: heroCollagePieceIn 0.65s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.hero-collage-diamond { animation-delay: 0ms; }
.hero-collage-pill--phone { animation-delay: 110ms; }
.hero-collage-pill--painter { animation-delay: 220ms; }
.hero-collage-circle { animation-delay: 330ms; }
@media (prefers-reduced-motion: reduce) {
  .hero-collage-diamond,
  .hero-collage-pill--phone,
  .hero-collage-pill--painter,
  .hero-collage-circle {
    animation: none;
  }
}
/* Each photo <img> is its real source image, deliberately larger than its
   pill/circle and offset with a negative left/top — the exact numbers
   needed to reproduce Figma's own crop (photo box position minus pill/
   circle box position, both straight from get_metadata), not object-fit.
   max-width:none overrides the site-wide `img { max-width: 100% }` reset,
   which would otherwise clamp these back down to their container. */
.hero-collage-pill img,
.hero-collage-circle img {
  position: absolute;
  display: block;
  max-width: none;
}
.hero-collage-diamond {
  position: absolute;
  left: 0;
  top: 0;
  width: 31.685%;
  height: 53.276%;
}
.hero-collage-diamond-shape {
  position: absolute;
  display: block;
  left: 14.645%;
  top: 14.645%;
  width: 70.71%;
  height: 70.71%;
  transform: rotate(45deg);
}
.hero-collage-sparkle {
  position: absolute;
  display: block;
  left: 28.19%;
  top: 28.19%;
  width: 43.62%;
  height: 43.62%;
}
.hero-collage-pill {
  position: absolute;
  width: 66.615%;
  height: 44.648%;
  border-radius: 999px;
  overflow: hidden;
}
.hero-collage-pill--phone {
  left: 33.385%;
  top: 4.178%;
  background: #4d3fe9;
}
.hero-collage-pill--phone img {
  left: -1.166%;
  top: -45.614%;
  width: 114.452%;
  height: 191.228%;
}
.hero-collage-pill--painter {
  left: 4.037%;
  top: 55.352%;
}
.hero-collage-pill--painter img {
  left: -15.85%;
  top: -80.702%;
  width: 162.704%;
  height: 299.415%;
}
.hero-collage-circle {
  position: absolute;
  left: 73.447%;
  top: 55.352%;
  width: 26.553%;
  height: 44.648%;
  border-radius: 50%;
  overflow: hidden;
  background: #cde900;
}
.hero-collage-circle img {
  left: -26.883%;
  top: -11.829%;
  width: 146.045%;
  height: 151.279%;
}

/* Both of this page's own section titles ("Ask however comes naturally"
   here and "Three steps, no spreadsheet" further down) were inheriting the
   generic "标准 h2" (Inter/800/28px) default, but per DESIGN_GUIDELINES.md's
   own Display-vs-standard-h2 rule ("凡是页面级、承担视觉锚点作用的大标题...
   用 Display 档") each is a major section's own main heading on this
   independent page — same tier as this site's other already-promoted
   page-section titles (.pricing-compare .section-head h2 "Every plan,
   feature by feature", .cta-copy h2, .faq-heading), not a section-internal
   subheading. Same fixed 2.75rem Tomato Grotesk recipe as those three,
   not the further-scaling Hero/.ai-eyebrow breakpoints. */
.feature-highlights .section-head h2,
.feature-steps .section-head h2 {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
}

/* ---- 3-card feature highlights ---- */
/* Restyled per linktr.ee/features/link-shortener's own "How it works" card
   row (https://linktr.ee/features/link-shortener) — big full-bleed
   saturated-color cards instead of the previous small white bordered
   ones, on user sign-off to match that reference literally rather than
   just scaling up the old white-card recipe.
   - Fill colors reuse the site's own violet/green/blue trio instead of
     inventing new hues: violet is this feature's own AI badge fill
     (#5F2BF5, see the search .ai-icon-badge SVG), green/blue are the
     site's existing "included"/"info" semantic pair (DESIGN_GUIDELINES.md
     "语义色") — same three hues the icon chips already used, just promoted
     from a small chip fill to the whole card's background.
   - 32px radius matches the "We are Quacko" panel (.audience-stats-inner)
     — the only other full-bleed saturated-color card in the system —
     instead of the smaller --radius-lg (20px) plain white cards use.
   - White text on all three: same "pick by actual background lightness"
     call as the Product Highlight sections' dark-vs-white text rule
     (DESIGN_GUIDELINES.md "Product Highlight 排版与配色"); it happens to
     land on white for all three of these particular fills since all three
     are dark/saturated enough, not because white is hardcoded as the
     default for every future card color. */
/* One card per row through phone AND iPad (portrait/landscape); only real
   desktop (1024px+, this site's own established phone/tablet-vs-desktop
   split — see the hero/nav's own min-width:1024px switches) goes back to
   the original 3-across grid, unchanged from before today's edits. */
.feature-card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  margin-top: 48px;
}
@media (min-width: 1024px) {
  .feature-card-grid { grid-template-columns: repeat(3, 1fr); gap: 28px; }
}
.feature-card {
  /* border/box-shadow reset: an older, unused-in-HTML ".feature-card" rule
     earlier in this file (the dead "Features"/.feature-grid section) shares
     this class name and would otherwise leak its 1px border/hover shadow
     through onto these saturated-color cards. */
  border: none;
  box-shadow: none;
  border-radius: 32px;
  /* Padding went 32px -> 42px -> 52px top/bottom across two rounds of
     "make the card taller" feedback, then back down to a flat 32px on
     all four sides per later feedback — the mock should sit exactly
     32px off the top edge and the text exactly 32px off the bottom edge,
     same as the left/right gutter, rather than the wider top/bottom
     margin the taller version had. Still padding rather than a fixed
     min-height, so it scales the same way regardless of breakpoint —
     these cards' own height is otherwise fully organic (driven by the
     mock's aspect-ratio photo, which itself scales with the card's fluid
     width), so a hardcoded px min-height would over/under-shoot at
     different grid widths. */
  padding: 32px;
  color: #fff;
  /* Flex column + margin-top:auto on the heading (below) bottom-aligns
     every card's heading+copy at the same row, instead of it sitting
     right under whatever that card's own mock/icon happens to be — cards
     1/2's real Figma mocks and card 3's still-placeholder icon are very
     different heights, and .feature-card-grid's grid items already
     stretch to the row's tallest card, so without this the three text
     blocks landed at three different heights. margin-top:auto is what
     pushes the freed space into the gap between the mock and the text
     instead of it needing an uneven top/bottom padding split — the same
     mechanism is what keeps the mock pinned to the top and the text
     pinned to the bottom now that the card is 20px taller, rather than
     the extra room just centering everything. */
  display: flex;
  flex-direction: column;
}
.feature-card h3 { margin-top: auto; margin-bottom: 0; font-size: 1.375rem; line-height: 1.25; }
.feature-card p { margin-top: 8px; color: rgba(255, 255, 255, 0.85); font-size: 0.9375rem; line-height: 1.55; }
/* #0C7D8E is Figma's own fill for this card (Quacko Team file, node
   3297:107922) — close to but deliberately distinct from the Product
   Highlight section's teal (#007B91, see "Product Highlight 滚动填充色"),
   so don't merge the two into one token. */
.feature-card--teal { background: #0C7D8E; }
/* Card 1's own copy runs longer than cards 2/3's, so it wraps an extra
   line at wide viewports and (via the shared margin-top:auto bottom-align
   rule above) pushes its heading noticeably higher than the other two —
   trimmed a notch smaller here, scoped to this card only, so its text
   block's height lines back up with its siblings' headings. */
.feature-card--teal p { font-size: 0.8125rem; }
/* #C7B5FB is Figma's own fill for card 2 (node 3297:108028) — the exact
   same lavender this card's old icon-chip already used as its accent
   color, just promoted to the full card background like card 1's teal
   was. Light enough that white text (the card default, see .feature-card
   above) doesn't read — heading/body get a dark-ink override here instead,
   same "pick by actual background lightness" call as the rest of the
   site's Display-text rules. */
.feature-card--lavender { background: #C7B5FB; color: var(--color-ink); }
.feature-card--lavender p { color: var(--color-ink-soft); }
/* Card 3 swapped from the plain --color-blue fill to this lime per
   feedback — renamed --blue -> --lime so the modifier still matches its
   own actual color. Light like card 2's lavender, so it gets the same
   dark-ink text override rather than the saturated-card default white. */
.feature-card--lime { background: #CBF484; color: var(--color-ink); }
.feature-card--lime p { color: var(--color-ink-soft); }
/* Cards 1/2's mocks (search-bar+pills+diamond per Figma node 3297:107922;
   "Search by Image" panel per node 3297:108028) were previously live
   markup, hand-tuned rule by rule to reproduce each Figma frame. Flattened
   to one exported PNG per card at the user's request — internal alignment
   (search bar vs. filter pills vs. the diamond photo chip, in card 1's
   case) kept needing rework across unrelated tweaks, and a raster export
   locks in the approved layout instead of it drifting again. Both PNGs
   are @2x exports of that final live-markup state, so they stay crisp at
   the card's fluid width. Card 3's "Match by" mock (node 3316:108565) had
   no live-markup phase — it replaced the plain icon chip directly with an
   exported SVG, same flattened-asset treatment either way.
   feature-card-mock-flat's border-radius matches each card's own outer
   radius since the flattened image now carries its own rounded corners
   baked in (cards 2/3's panels are rounded, card 1's mock is edge-to-edge
   against the teal so a small radius keeps it from looking clipped
   against the card's own 32px corner). */
/* +20px gap between the mock and the heading below it per feedback
   (20px -> 40px) — margin-bottom on the mock rather than margin-top on
   the heading since the heading's own margin-top:auto (above) already
   does bottom-alignment work; stacking a fixed margin on top of an auto
   one still works; the auto share just shrinks by the same amount so
   this reliably adds exactly 20px, not 0. */
.feature-card-mock { margin-bottom: 40px; }
.feature-card-mock--image { margin-bottom: 40px; }
.feature-card-mock-flat { display: block; width: 100%; height: auto; border-radius: 12px; }
/* Same platform-wide 110px gutter as index.html's/pricing.html's own
   card-grid sections (see DESIGN_GUIDELINES.md "通栏板块左右留白") — this is
   a plain grid of cards with no shared background of its own, so it's the
   margin-inline form (padding-inline:0 to clear .container's own 32px,
   same reasoning as .why-quacko/.quote-stack/.faq-section/.pricing-plans'
   own 1600px+ rules) rather than the padding-inline form full-bleed
   sections use. */
@media (min-width: 1600px) {
  .feature-highlights .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
}

/* ---- Benefit rows ---- */
/* Reuses .ai-rows/.ai-row/.ai-copy/.ai-visual wholesale (see index.html's
   Product Highlight section, same classes, same 3-row violet/orange/pink
   color-cycle keyed off nth-child) rather than a new one-off layout —
   three rows here (not the homepage's spacing for a lone row) so it
   inherits that sequence exactly as designed. */
.feature-benefit-row { padding-block: 64px; }
@media (min-width: 1280px) {
  .feature-benefit-row { padding-block: 96px; }
}
/* Real screenshot rows (Match By, Filter panel) vs. the homepage's demo
   -video rows: a plain white "device-agnostic UI preview" card (border +
   shadow-card, .feature-hero-visual's own recipe) instead of the video
   rows' full-bleed gradient background, since a static screenshot framed
   edge-to-edge under object-fit:cover crops awkwardly at these images'
   own aspect ratio — contain + padding keeps the whole screenshot intact. */
.ai-visual--panel {
  background: #fff;
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-card);
  aspect-ratio: 3 / 2;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
}
.ai-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 10px;
}

/* ---------- Blog ---------- */
/* blog.html only. Layout reference: linktr.ee/blog (category filter pills
   above a featured post, then a card grid + pagination) — adapted to
   Quacko's own IA and component tokens (radius/shadow/color vars), not a
   literal copy. All classes are blog-page-scoped so nothing here touches
   the card/badge/pill patterns other pages already rely on. */
.blog-hero { padding-top: 70px; padding-bottom: 110px; text-align: center; }
.blog-hero-inner { max-width: 720px; margin-inline: auto; }
.blog-hero-inner h1 { margin-top: 12px; }
.blog-hero-inner .hero-sub { max-width: 560px; margin-inline: auto; }

/* Category pills — reuses .btn-pill-outline's shape/border but its own
   smaller scale (blog filters sit dense in a row, not as a single CTA),
   plus an is-active fill so the current filter reads at a glance. */
.blog-filter-section { padding-top: 0; padding-bottom: 0; }
.blog-grid-section { padding-top: 0; }
.blog-filter {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.blog-filter-pill {
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: #fff;
  color: var(--color-ink-soft);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 160ms ease, color 160ms ease, border-color 160ms ease;
}
.blog-filter-pill:hover { border-color: var(--color-ink); color: var(--color-ink); }
.blog-filter-pill.is-active { background: var(--color-ink); border-color: var(--color-ink); color: #fff; }

/* Category tags — one soft-tint/solid-text pair per category, page-scoped
   hex (not global --color-* tokens: these six colors exist only to tell
   blog categories apart, same reasoning .product-mega-featured-media's
   one-off gradient stops above stay inline instead of new tokens). */
/* Reuses the SaaS app's canonical 7-color Tag spec verbatim (quacko_app's
   docs/design-system/tags-and-icons.md — bg/border/text hex per color,
   22px height, 1px border, Inter Medium 12px) instead of this file's old
   one-off palette. Six of the seven colors are in use below; "red" is free
   for a future category. */
.blog-tag {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  border: 1px solid;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.01em;
}
.blog-tag--product-news { background: #dcfcff; border-color: #95e9ff; color: #005cd8; } /* Tag blue */
.blog-tag--guides { background: #c6ffa4; border-color: #b4e1a2; color: #00363c; } /* Tag green */
.blog-tag--success-stories { background: #fff0df; border-color: #ffd386; color: #8b3a09; } /* Tag orange */
.blog-tag--creator-strategies { background: #f0e2ff; border-color: #daacfc; color: #301462; } /* Tag purple */
.blog-tag--best-practices { background: #e1ecec; border-color: #c9d5d6; color: #53596a; } /* Tag grey */
.blog-tag--trends { background: #ffebff; border-color: #ffa9d5; color: #410424; } /* Tag magenta */

.blog-meta { margin-top: 10px; font-size: 0.8125rem; color: var(--color-ink-soft); }

/* Featured post — media left, copy right, one big clickable card; media
   keeps its own fixed-ish column so the copy column has room to breathe
   at any card height. */
.blog-featured {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: center;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 20px;
  box-shadow: none;
  transition: transform 220ms ease;
}
.blog-featured:hover { box-shadow: none; transform: translateY(-2px); }
.blog-featured-media {
  border-radius: var(--radius-md);
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--color-bg-alt);
}
.blog-featured-media img { width: 100%; height: 100%; object-fit: cover; object-position: left top; display: block; }
.blog-featured-title { margin-top: 14px; font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; font-size: 1.75rem; line-height: 1.2; color: var(--color-ink); }
.blog-featured-excerpt { margin-top: 12px; color: var(--color-ink-soft); font-size: 1rem; line-height: 1.5; }
@media (max-width: 767px) {
  .blog-featured { grid-template-columns: 1fr; }
}

/* Card grid — same 3-up responsive breakpoints as .feature-card-grid
   elsewhere on the site, own card treatment since these need an image +
   tag + title + excerpt + meta stack the feature cards don't. */
.blog-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  margin-top: 24px;
}
@media (min-width: 640px) {
  .blog-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .blog-grid { grid-template-columns: repeat(3, 1fr); gap: 28px; }
}
/* display:block, not flex-direction:column — media and body already stack
   correctly with plain block flow, and flex column breaks .blog-card-media's
   aspect-ratio below: in a column flex container, a flex item's main size
   (height) can get resolved from its content's own intrinsic ratio instead
   of the item's specified aspect-ratio, so each card ended up sized to its
   own photo's real proportions (a 1015x797 png vs. a 3072x1714 jpg) instead
   of the uniform 16:10 box every card is supposed to share. */
.blog-card {
  display: block;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: none;
  transition: transform 220ms ease;
}
.blog-card:hover { box-shadow: none; transform: translateY(-2px); }
.blog-card-media { display: block; aspect-ratio: 16 / 10; background: var(--color-bg-alt); }
.blog-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.blog-card-body { padding: 20px; }
.blog-card-title { margin-top: 12px; font-size: 1.125rem; line-height: 1.35; }
.blog-card-title a { color: var(--color-ink); }
.blog-card-title a:hover { color: var(--color-brand-dark); }
.blog-card-excerpt { margin-top: 8px; color: var(--color-ink-soft); font-size: 0.9375rem; line-height: 1.5; }

/* A category with zero matching posts (client-side filter, js/main.js)
   collapses to an empty grid rather than showing a blank gap — this note
   here rather than a real empty-state message since every category
   currently has at least one post. */
.blog-card[hidden], .blog-featured-section[hidden] { display: none; }

/* Same platform-wide 110px gutter as index.html's/pricing.html's own
   card-grid sections (see DESIGN_GUIDELINES.md "通栏板块左右留白") — the post
   grid is a plain grid of cards with no shared background of its own, so
   it's the margin-inline form (padding-inline:0 to clear .container's own
   32px) rather than the padding-inline form full-bleed sections use. */
@media (min-width: 1600px) {
  .blog-grid-section .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
}
/* Same 1600px+ widening as .blog-grid-section's container above, so the
   related-post grid keeps matching the blog index card grid's size at
   every viewport, not just below 1600px. */
@media (min-width: 1600px) {
  .post-related .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
}

/* Pagination — decorative for now (see HTML comment), styled like a
   standard-table's own page controls rather than a bare number list. */
.blog-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 48px;
}
.blog-page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  color: var(--color-ink-soft);
  font-size: 0.875rem;
  font-weight: 600;
}
a.blog-page-btn:hover { border-color: var(--color-ink); color: var(--color-ink); }
.blog-page-btn.is-active { background: var(--color-ink); border-color: var(--color-ink); color: #fff; }
.blog-page-btn.is-disabled { opacity: 0.4; pointer-events: none; }
.blog-page-ellipsis { color: var(--color-ink-soft); font-size: 0.875rem; }

/* ==========================================================================
   Blog post page (single-article template — see blog-creator-brief.html)
   ========================================================================== */
/* Plain white, not the global dot-grid ground (body's own background,
   see body's own comment) — an article's actual reading column (breadcrumb
   through the body copy, shared by both the blog post template and every
   Help Center article) reads as a page of text, not a card floating on
   the canvas texture the rest of the site uses. */
.post { padding-top: 70px; padding-bottom: 64px; background: #fff; }
/* .post-container/.post-media-container/.post-body-container are each
   their own ".container" — margin-inline:auto centers them independently,
   so giving one of them its own max-width re-centers *that one* narrower
   instead of just insetting its content, shifting its left edge off the
   others' (confirmed via getBoundingClientRect, not just a screenshot:
   a max-width:680px .container measured ~260px right of a plain
   max-width:none one at a 2388px viewport — two different centered boxes,
   not one shared left edge). So the 680px reading measure now lives on
   the text elements themselves (below), never on a .container, keeping
   every .post-*-container the same box with the same left edge — back
   link/tag/title/meta, image, and body copy all align. */
.post-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-ink-soft);
}
.post-back:hover { color: var(--color-ink); }
/* Breadcrumb — help articles' own replacement for .post-back's single
   "Back to blog" link (a chain of crumbs makes more sense once a page
   sits under a category, not just one index). Same size/weight/color as
   .post-back so the two read as the same "above the title" component
   family rather than two different styles. */
.help-breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; font-size: 0.875rem; font-weight: 400; }
.help-breadcrumb a { color: var(--color-ink-soft); font-weight: 400; }
.help-breadcrumb a:hover { color: var(--color-ink); }
.help-breadcrumb-sep { color: var(--color-ink-soft); font-size: 1.25em; line-height: 1; }
.help-breadcrumb-current { color: var(--color-ink); font-weight: 500; }
/* Bigger than the plain .blog-tag pill used in the blog grid cards —
   this is the sole tag on a post's own header, next to a large title,
   so it reads as undersized at the card-grid size. Scaled up
   proportionally (~1.3x): 22px→28px height, 10px→14px padding,
   0.75rem→0.875rem type, rather than just bumping one dimension.
   display: flex (not .blog-tag's own inline-flex) + align-items:center —
   flex still centers the text on the cross axis same as inline-flex
   would, but stays block-level/own-line like the original display:block
   did. inline-flex was wrong here: it's an inline-level box, so it could
   share a line with .post-back right above it (also inline-flex) instead
   of wrapping to its own line the way margin-top:28px assumes. */
.post-tag {
  display: flex;
  align-items: center;
  width: fit-content;
  margin-top: 28px;
  height: 28px;
  padding: 0 14px;
  font-size: 0.875rem;
}
/* 900px, not the 680px reading measure below it — at this h1's own
   1280px+ size (4rem, see the h1 rule's own comment) 680px wrapped this
   title to 3 lines ("How to Write a Creator" / "Brief That Actually
   Gets" / "Replies"); confirmed in a real render that 840px is the exact
   point it drops to 2, so 900px clears that with a safety margin and
   lands on an even "How to Write a Creator Brief" / "That Actually Gets
   Replies" break. Only matters as a max — narrower viewports already
   have less room than that and wrap the same as before. */
.post-container h1 { max-width: 900px; margin-top: 16px; }
.post-meta { max-width: 680px; margin-top: 14px; font-size: 0.9375rem; color: var(--color-ink-soft); }

/* Getting Started articles follow the same 1320px desktop guide as the
   floating menu and Help Center banner. Reading measure remains controlled
   by the title/body elements, so wider alignment does not create long lines. */
@media (min-width: 1024px) {
  .help-article-page .post-container,
  .help-article-page .post-media-container,
  .help-article-page .post-body-container {
    max-width: 1320px;
  }

  /* .post-container h1's own 900px cap (above) is tuned for the blog
     post title and wraps "TikTok Shop Cold-start Playbook" to 2 lines
     even though the 1320px container it sits in has room to spare —
     confirmed in a real render that 947px is the exact point this title
     drops to 1 line, so 1010px clears that with the same ~60px safety
     margin the blog title's own 840→900 cap uses. Scoped to
     .help-article-page only, so the blog post h1's tuned 900px/2-line
     break is untouched. */
  .help-article-page .post-container h1 { max-width: 1010px; }
}

/* Media has no max-width of its own (just the standard .container it's
   already in, same as the text column above/below it), so it reads as a
   full-width hero image rather than the narrower 680px reading measure. */
.post-media-container { margin-top: 40px; }
.post-media { border-radius: var(--radius-lg); overflow: hidden; aspect-ratio: 16 / 9; background: var(--color-bg-alt); }
.post-media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Same "no max-width of its own" reasoning as .post-media-container right
   above — the card fills the full .container width so it reads as the
   same width as the hero image, not the narrower 680px reading measure
   the Contents/body layout below it uses.
   Card shell reuses .post-callout's own rounded/bg-alt look (see that
   rule further down) for the surface, but its own gradient-ring border
   below instead of that rule's plain solid one. The sparkle icon/gradient
   is the exact live-rotating-conic-gradient SVG already used for
   Quacko's real AI search bar elsewhere on this site
   (.outreach-search-chip-icon in index.html) — reused verbatim (own
   gradient id to avoid colliding with that instance) so "AI" reads as
   the same product feature, not a new one-off icon. */
.post-ai-summary-container { margin-top: 32px; }
/* Gradient ring border, same masked-::before technique as
   .outreach-search-chip-card::before in index.html (a plain `border`
   snaps to whole device pixels in Blink — see that rule's own comment)
   but this card's actual colors: pink→orange, matching the real app
   search bar's own border (confirmed against a screenshot of it) and
   the same two stop-colors already used by the sparkle icon's rotating
   gradient above, not that other card's unrelated orange→grey pair. */
.post-ai-summary {
  position: relative;
  padding: 28px 32px;
  border-radius: var(--radius-lg);
  background: var(--color-bg-alt);
}
.post-ai-summary::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 1.5px;
  border-radius: inherit;
  background: linear-gradient(90deg, #FF2D92, #FF7A00);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.post-ai-summary-label {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-weight: 600;
  font-size: 1.125rem;
  color: var(--color-ink);
}
.post-ai-summary-icon { width: 22px; height: 22px; flex-shrink: 0; }
.post-ai-summary-list {
  margin-top: 18px;
  padding-left: 20px;
  list-style: disc;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.post-ai-summary-list li { font-size: 1.0625rem; line-height: 1.6; color: var(--color-ink-soft); }

/* Contents sidebar + copy, side by side — same grid/sticky shape as the
   FAQ section's own aside layout (.faq-layout/.faq-aside: minmax(220px,
   320px) 1fr grid, clamp(32px, 5vw, 64px) gap, sticky aside pinned
   110px below the fixed nav), reused here instead of inventing a new
   sidebar pattern. Collapses to a single static column below 1024px,
   same breakpoint the FAQ layout already uses. */
.post-layout {
  display: grid;
  grid-template-columns: minmax(220px, 320px) 1fr;
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}
.post-toc { position: sticky; top: 110px; }
.post-toc-heading {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-weight: 600;
  font-size: 1.125rem;
  color: var(--color-ink);
  margin-bottom: 16px;
}
.post-toc-nav {
  display: flex;
  flex-direction: column;
  gap: 20px;
  border-left: 3px solid #E3E1DE;
}
.post-toc-link {
  position: relative;
  padding-left: 28px;
  font-size: 1.1rem;
  font-weight: 500;
  line-height: 1.4;
  color: var(--color-ink-soft);
  text-decoration: none;
  transition: color 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.post-toc-link::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -3px;
  width: 3px;
  border-radius: 999px;
  background: var(--color-brand-dark);
  opacity: 0;
  transform: scaleY(0.35);
  transform-origin: center;
  transition: opacity 260ms ease, transform 360ms cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity, transform;
}
.post-toc-link:hover { color: var(--color-ink); }
/* Active section (scroll-spy, js/main.js) uses the site's existing
   brand-accent color (same var as .post-callout-label's own accent
   above) rather than introducing a new one-off color. */
.post-toc-link.is-active { color: var(--color-brand-dark); }
.post-toc-link.is-active::before { opacity: 1; transform: scaleY(1); }
@media (max-width: 1023px) {
  .post-layout { grid-template-columns: 1fr; gap: 32px; }
  .post-toc { position: static; }
}

.post-body { max-width: 680px; }
.post-body-container { margin-top: 48px; }
.post-body p { font-size: 1.0625rem; line-height: 1.7; color: var(--color-ink-soft); }
.post-body p + p,
.post-body p + h3 { margin-top: 24px; }
.post-body h3 { margin-top: 40px; color: var(--color-ink); }
.post-body h3 + p { margin-top: 12px; }
.post-callout {
  margin-top: 24px;
  padding: 20px 24px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg-alt);
}
.post-callout-heading { display: flex; align-items: center; gap: 10px; }
.post-callout-icon {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  color: #5F2BF5;
}
.post-callout-label {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 1.125rem;
  font-weight: 700;
  font-style: normal;
  line-height: inherit;
  text-transform: none;
  letter-spacing: normal;
  color: var(--color-brand-dark);
}
.post-callout p { font-style: italic; font-size: 0.9375rem; }
.post-callout .post-callout-label {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 1.125rem;
  font-weight: 700;
  font-style: normal;
}
.post-callout-heading + p { margin-top: 12px; }
.post-callout p + p { margin-top: 12px; }
.post-body .post-next-guide { margin-top: 32px; }
.post-next-guide-link {
  display: inline;
  color: #0000EE;
  font-weight: 500;
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 4px;
  transition: color 160ms ease, text-decoration-thickness 160ms ease;
}
.post-next-guide-link:visited { color: #0000EE; }
.post-next-guide-link:hover,
.post-next-guide-link:focus-visible {
  color: #0000CC;
  text-decoration-thickness: 2px;
}
.post-article-pagination {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--color-border);
}
.post-article-pagination-link {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  color: var(--color-ink);
}
.post-article-pagination-link--next { grid-column: 2; text-align: right; }
.post-article-pagination-direction {
  color: #0000EE;
  font-size: 0.875rem;
  font-weight: 500;
}
.post-article-pagination-title {
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
}
.post-article-pagination-link:hover .post-article-pagination-title,
.post-article-pagination-link:focus-visible .post-article-pagination-title { color: #0000EE; }
@media (max-width: 639.98px) {
  .post-article-pagination { grid-template-columns: 1fr; gap: 24px; }
  .post-article-pagination-link--next { grid-column: 1; text-align: left; }
}

/* "You may also like" related-reads strip — see the HTML comment above it
   for the reference and the display-font caveat. Solid peach band (own
   color, not a --color-* token — this section's alone) per feedback,
   same "full-bleed color section" idea as .creator-marquee-section. Light
   background this time, so text stays the platform's normal dark ink
   (not the white-on-dark pass a darker band would need). */
.post-related.section { background: #FF9E5D; }
.post-related-heading {
  margin-bottom: 48px;
  text-align: center;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.12;
  /* Sentence case, not all-caps — platform-wide rule: every real title
     reads as authored (just its own first letter capitalized), all-caps
     stays reserved for small eyebrow/badge/label chrome. */
  color: var(--color-ink);
  /* Exact same fluid scale + desktop breakpoints as the platform's own h1
     (see the h1 rule and its two @media overrides) — "same size as the
     blog page" heading, not a new one-off size. */
  font-size: clamp(2.25rem, -0.25rem + 12.5vw, 4rem);
}
@media (min-width: 1024px) {
  .post-related-heading { font-size: 3rem; }
}
@media (min-width: 1280px) {
  .post-related-heading { font-size: 4rem; }
}
/* Cards are the blog index's own .blog-card component (see the HTML
   comment above the section) — .post-related-grid itself no longer needs
   its own grid rules since .blog-grid on the same element already
   supplies them; this class now only exists so the 1600px+ container
   widening selector above (".post-related .container") still targets
   this section. Borderless here per feedback — scoped to this section's
   own cards (".post-related .blog-card") rather than touching the base
   .blog-card rule, which stays bordered on the actual blog index grid. */
.post-related .blog-card { border: none; box-shadow: none; }
.post-related .blog-card:hover { box-shadow: none; }

.help-related.post-related.section { padding-block: 56px 72px; background: #fff; }
.help-related.post-related .help-related-container {
  width: 100%;
  max-width: 1320px;
  margin-inline: auto;
  padding-inline: 16px;
}
.help-related .post-related-heading {
  margin-bottom: 32px;
  text-align: left;
  font-size: 2.75rem;
}
.help-related .help-article-list { margin-top: 0; padding: 24px; box-shadow: none; }
.help-related .help-article-row { padding-inline: 0; }
.help-related .help-article-row-arrow { font-size: 1.5rem; line-height: 1; }
.help-related-list {
  padding: 18px 32px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: #fff;
}
.help-related-list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  min-height: 72px;
  color: var(--color-ink);
  font-size: clamp(1.0625rem, 1.6vw, 1.35rem);
  line-height: 1.35;
}
.help-related-list a span:last-child {
  flex: 0 0 auto;
  color: #59247D;
  font-size: 2rem;
  line-height: 1;
  transition: transform 160ms ease;
}
.help-related-list a:hover span:first-child { color: var(--color-brand-dark); }
.help-related-list a:hover span:last-child { transform: translateX(4px); }
.help-article-feedback {
  margin-top: 48px;
  padding: 44px 24px 36px;
  border-radius: var(--radius-lg);
  background: var(--color-bg-alt);
  text-align: center;
}
.help-article-feedback > p {
  font-family: "Tomato Grotesk Medium", Arial, sans-serif;
  color: var(--color-ink);
  font-size: clamp(1.35rem, 2.5vw, 2rem);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.01em;
}
.help-feedback-actions { display: flex; justify-content: center; gap: 22px; margin-top: 28px; }
.help-feedback-actions button {
  padding: 4px;
  border: 0;
  background: transparent;
  font-size: 2.5rem;
  line-height: 1;
  cursor: pointer;
  transition: transform 160ms ease, opacity 160ms ease;
}
.help-feedback-actions button:hover { transform: translateY(-3px) scale(1.08); }
.help-feedback-actions button[aria-pressed="true"] { transform: scale(1.14); }
.help-feedback-actions:has(button[aria-pressed="true"]) button:not([aria-pressed="true"]) { opacity: .4; }
.help-feedback-thanks { display: block; min-height: 20px; margin-top: 16px; color: var(--color-ink-soft); font-size: .875rem; }
@media (max-width: 767px) {
  /* Help article mobile layout: keep the long dynamic article titles and
     breadcrumb readable without clipping, then tighten the desktop-scale
     list/card padding to the same 16px page gutter used by the hero. */
  .help-article-page .post {
    padding-top: 40px;
    padding-bottom: 48px;
  }
  .help-article-page .post-container h1 {
    max-width: 100%;
    margin-top: 14px;
    font-size: clamp(2.25rem, 10.5vw, 2.75rem);
    line-height: 1.08;
    overflow-wrap: anywhere;
  }
  .help-article-page .post-meta {
    margin-top: 16px;
    font-size: 0.875rem;
  }
  .help-article-page .help-breadcrumb {
    gap: 6px 8px;
    font-size: 0.8125rem;
    line-height: 1.45;
  }
  .help-article-page .help-breadcrumb-sep { font-size: 1rem; }
  .help-article-page .post-body-container { margin-top: 36px; }
  .help-article-page .post-layout,
  .blog-article-page .post-layout { gap: 0; }
  .help-article-page .post-toc,
  .blog-article-page .post-toc { display: none; }
  .help-article-page .post-toc-nav { gap: 14px; }
  .help-article-page .post-toc-link {
    padding-left: 18px;
    font-size: 0.9375rem;
  }
  .help-article-page .post-body { width: 100%; max-width: none; }
  .help-article-page .post-body p {
    font-size: 1rem;
    line-height: 1.65;
  }
  .help-article-page .post-body h3 { margin-top: 32px; }
  .help-article-page .post-callout { padding: 18px; }
  .help-related.post-related.section { padding-block: 40px 48px; }
  .help-related .post-related-heading {
    margin-bottom: 24px;
    font-size: 2.25rem;
    line-height: 1.08;
  }
  .help-related .help-article-list { padding: 24px; }
  .help-related-list a {
    min-height: 68px;
    gap: 14px;
    font-size: 0.9375rem;
  }
  .help-related-list a span:last-child { font-size: 1.5rem; }
  .help-article-feedback {
    margin-top: 32px;
    padding: 32px 18px 24px;
  }
  .help-feedback-actions { gap: 18px; margin-top: 22px; }
  .help-feedback-actions button { font-size: 2rem; }
}

/* ==========================================================================
   Help Center (help-center.html + help-getting-started-ai-search.html)
   ========================================================================== */
/* Full-bleed banner, per linktr.ee/help's own hero (topbar + big
   left-aligned h1 + wide search bar) — a deliberate departure from this
   site's usual centered .blog-hero shape. Section itself carries the
   fill edge-to-edge; .help-hero-inner is a normal .container so the
   content column still respects the site's standard gutters. Same blue
   (#4b3fe0) as .cta-visual, since that's the ground the Growth Playbook
   illustration below was designed against. body's own 126px padding-top
   (clears the fixed floating nav pill) would otherwise leave a gap of its
   own dot-grid ground above this section — negative margin-top pulls the
   fill up to cover that too, with matching extra padding-top so the
   topbar/h1 still land at the same visual position as before. 70px is
   this site's standard non-home top clearance (see .blog-hero); the
   additional padding beyond 126px+70px is the topbar's own height. */
.help-hero {
  margin-top: -126px;
  padding-top: calc(126px + 32px);
  padding-bottom: 12px;
  background: #197E8C;
  color: #fff;
}
/* .help-hero-inner used to cap at 720px on its own (single-column copy
   only); now it's the site's standard .container width so
   .help-hero-row has room to lay the illustration out to the right of
   that same 720px-ish copy measure. */
.help-hero-inner { text-align: left; }
@media (min-width: 1024px) {
  /* Match the desktop nav pill's own 1320px container so the blue hero's
     left copy and right illustration share its horizontal guide lines. */
  .help-hero-inner { max-width: 1320px; }
  /* Compact desktop banner: the fixed menu already establishes the top
     clearance, so the hero does not need the much taller page default. */
  .help-hero { padding-top: 110px; padding-bottom: 0; }
  .help-hero-topbar { padding-top: 0; }
  .help-hero-inner h1,
  .help-hero-title { font-size: 3.5rem; }
  .help-hero .help-search { padding-block: 6px; }
}
.help-hero-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 24px;
  margin-bottom: 4px;
}
.help-hero-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 40px;
}
@media (min-width: 900px) {
  .help-hero-row { flex-direction: row; align-items: flex-start; justify-content: space-between; gap: 24px; }
}
.help-hero-copy { width: 100%; max-width: 620px; }
.help-hero-brand {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 1.0625rem;
  font-weight: 600;
  color: #D9F20E;
}
/* The Getting Started / cold-start pages render this as a real link back
   to help-center.html (help-center.html itself keeps it a plain span —
   no point linking a page to itself); it works today but with no hover
   feedback at all it reads as static text, same complaint the plain
   .help-hero-brand span would raise if it were clickable. Scoped to the
   `a` tag only, same as .footer-col a:hover / .help-breadcrumb a:hover
   elsewhere in this file, and reusing .footer-col a:hover's own
   soft-color→white-on-dark-background treatment (this sits on the same
   #197E8C dark hero fill) rather than inventing a new hover language. */
a.help-hero-brand:hover { color: #fff; }
.help-hero-inner h1,
.help-hero-title {
  margin: 0;
  color: #fff;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.02em;
}
@media (max-width: 899px) {
  .help-hero-topbar,
  .help-hero-copy { transform: translateY(-40px); }
  .help-hero-inner h1,
  .help-hero-title {
    font-size: clamp(2rem, 9vw, 2.5rem);
    white-space: nowrap;
  }
  .help-hero-copy { max-width: none; }
  .help-hero .help-search {
    width: 100%;
    max-width: none;
    margin-inline: auto;
    padding: 16px 22px;
  }
  .help-hero .help-search-icon {
    width: 21px;
    height: 21px;
  }
  .help-hero .help-search-input { font-size: 1.0625rem; }
}
.help-hero-inner .hero-sub { max-width: 480px; margin-top: 14px; color: rgba(255, 255, 255, 0.72); }
.help-search {
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 600px;
  margin-top: 16px;
  padding: 14px 20px;
  /* Frosted-glass pill on the dark fill, standing in for Linktree's own
     translucent search bar over their illustration — backdrop-filter
     softens whatever sits behind it (just the flat --color-ink fill
     here) instead of needing a real image to blur. */
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius-pill);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.help-search-icon { flex-shrink: 0; width: 18px; height: 18px; color: rgba(255, 255, 255, 0.75); }
.help-search-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  font-family: inherit;
  font-size: 0.9375rem;
  color: #fff;
}
.help-search-input::placeholder { color: rgba(255, 255, 255, 0.6); }

/* Growth Playbook illustration — the exact same artwork as index.html's
   .cta-section's .cta-illustration, copied verbatim rather than redrawn
   (its own "Want to learn more?" button stays index.html-only; this
   placement is just the graphic). Deliberately does NOT reuse
   .cta-visual's own wrapper class — this placement keeps the hero's own
   background instead of picking up .cta-visual's separate min-height/
   padding meant for a full CTA panel. Still needs .cta-visual's
   position:relative + perspective so the [data-tilt] pointer effect (see
   js/main.js — it queries every [data-tilt] element on the page, not just
   .cta-visual) has the same 3D vanishing point to tilt against. On mobile
   the row stacks, placing this artwork directly below the search field. */
.help-hero-illustration {
  display: block;
  position: relative;
  perspective: 900px;
  width: min(100%, 360px);
  align-self: center;
}
@media (max-width: 899px) {
  .help-hero-row { gap: 20px; }
  .help-hero-illustration {
    width: min(92vw, 360px);
    margin-bottom: -12px;
  }
}
@media (min-width: 900px) {
  .help-hero-illustration {
    width: 360px;
    flex-shrink: 0;
    margin-bottom: -36px;
    transform: translate(8px, -36px);
  }
}
.help-hero-banner-image { display: block; width: 100%; height: auto; }
/* Legacy inline artwork is retained in the document only to keep this
   change isolated; the exported Figma node above is the rendered banner. */
.help-hero-illustration .cta-illustration { display: none; }

/* Category grid — 4 icon-chip colors cycle across the 8 cards, reusing
   the site's existing semantic bg/fg token pairs (brand-tint/brand-dark,
   green-bg/green, blue-bg/blue, orange-bg/brand-dark) instead of new
   one-off hexes. */
@media (min-width: 1024px) {
  /* Use the same desktop width as the nav pill and Help hero. The four
     equal grid columns absorb the extra width, so every card gets wider
     while both outer edges land on the menu's guide lines. */
  .help-categories > .container { max-width: 1320px; }
}
.help-category-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
.help-category-section-heading {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.02em;
}
.help-category-section-heading--features { margin-top: 56px; }
.help-category-section-copy { max-width: 720px; margin: 10px 0 28px; color: var(--color-ink-soft); font-size: 1.0625rem; }
@media (min-width: 640px) {
  .help-category-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .help-category-grid { grid-template-columns: repeat(4, 1fr); gap: 24px; }
  .help-category-grid--start { grid-template-columns: repeat(2, 1fr); }
  .help-category-grid--features { grid-template-columns: repeat(2, 1fr); }
}
.help-category-card {
  display: block;
  padding: 24px;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: none;
  transition: box-shadow 220ms ease, transform 220ms ease;
}
.help-category-card:hover { box-shadow: none; transform: translateY(-2px); }
.help-category-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-lg);
}
.help-category-icon--orange { background: var(--color-brand-tint); color: var(--color-brand-dark); }
.help-category-icon--green { background: var(--color-green-bg); color: var(--color-green); }
.help-category-icon--blue { background: var(--color-blue-bg); color: var(--color-blue); }
.help-category-icon--warm { background: var(--color-orange-bg); color: var(--color-brand-dark); }
.help-category-card h3 { margin-top: 16px; font-size: 1.0625rem; }
.help-category-card p { margin-top: 6px; color: var(--color-ink-soft); font-size: 0.9375rem; line-height: 1.5; }
.help-category-count { display: block; margin-top: 14px; font-size: 0.8125rem; font-weight: 600; color: var(--color-ink-soft); }

/* Help collection page — a focused index between the Help Center and an
   individual article. Its narrow reading measure follows the reference
   collection pattern while retaining Quacko's own cards and typography. */
.help-child-bar {
  background: #197E8C;
  color: #fff;
}
.help-child-bar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  padding-block: 22px;
}
.help-child-bar-brand {
  flex-shrink: 0;
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 1.25rem;
  font-weight: 600;
  color: #D9F20E;
}
.help-child-search {
  display: flex;
  align-items: center;
  gap: 10px;
  width: min(440px, 100%);
  padding: 10px 18px;
  color: rgba(255, 255, 255, 0.78);
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: var(--radius-pill);
}
.help-child-search svg { flex-shrink: 0; }
.help-child-search input {
  width: 100%;
  min-width: 0;
  border: 0;
  outline: 0;
  color: #fff;
  background: transparent;
}
.help-child-search input::placeholder { color: rgba(255, 255, 255, 0.65); }
@media (min-width: 1024px) {
  .help-child-bar-inner { max-width: 1320px; }
}
@media (max-width: 639.98px) {
  .help-child-bar-inner { align-items: stretch; flex-direction: column; gap: 14px; }
  .help-child-search { width: 100%; }
}
.help-collection { padding-block: 56px 96px; }
.help-collection-inner { max-width: 1320px; }
.help-breadcrumb {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--color-ink-soft);
  font-size: 0.9375rem;
  font-weight: 600;
}
.help-breadcrumb:hover { color: var(--color-brand-dark); }
.help-collection-head { margin-top: 36px; }
.help-collection-badge { position: relative; overflow: hidden; margin-bottom: 16px; }
.help-collection-badge-icon-frame {
  position: relative;
  left: 6px;
  top: -4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56.77px;
  height: 56.77px;
}
.help-collection-badge img {
  display: block;
  width: 37.52px;
  height: 42.77px;
  max-width: none;
  transform: rotate(45deg);
}
.help-collection-head h1 { font-size: clamp(2.25rem, 6vw, 4rem); line-height: 1.05; }
.help-collection-head p { max-width: 1000px; margin-top: 14px; color: var(--color-ink-soft); font-size: 1.0625rem; }
.help-collection-count { display: block; margin-top: 12px; color: var(--color-ink-soft); font-size: 0.875rem; font-weight: 600; }
.help-article-list {
  margin-top: 40px;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.help-article-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 22px 24px;
  border-bottom: 1px solid var(--color-border);
  transition: background 160ms ease;
}
.help-article-link:last-child { border-bottom: 0; }
.help-article-link:hover { background: transparent; }
.help-article-link h2 {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.35;
}
.help-article-link p { margin-top: 4px; color: var(--color-ink-soft); font-size: 0.875rem; line-height: 1.45; }
.help-article-arrow {
  flex-shrink: 0;
  color: var(--color-ink-soft);
  font-size: 0;
  transition: transform 220ms ease, color 220ms ease;
}
.help-article-arrow::before { content: "›"; font-size: 1.5rem; line-height: 1; }
.help-article-link:hover h2 { color: var(--color-brand-dark); }
.help-article-link:hover .help-article-arrow { color: var(--color-brand-dark); transform: translateX(4px); }
.help-collection .help-article-list { padding: 24px; box-shadow: none; }
.help-collection .help-article-link { padding: 20px 0; }
@media (max-width: 639.98px) {
  .help-collection { padding-block: 40px 72px; }
  .help-collection .help-article-list { padding: 24px; }
  .help-article-link { padding-block: 18px; }
}

/* Popular articles — plain link rows (see the HTML comment above the
   section for why, vs. this site's usual .blog-card grid), same reading
   width as an article body (680px) since it's a text list, not a
   photo-card grid needing the full container. */
.help-popular-container { max-width: 1320px; margin-inline: auto; }
.help-article-page .help-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  font-weight: 400;
}
.help-article-page .help-breadcrumb a { color: var(--color-ink-soft); font-weight: 400; }
.help-article-page .help-breadcrumb-current { color: var(--color-ink); font-weight: 500; }
.help-categories { padding-bottom: 24px; }
.help-popular { padding-block: 24px; }
.help-popular-heading {
  font-family: "Tomato Grotesk SemiBold", Arial, sans-serif;
  font-size: 2.75rem;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.02em;
  margin-bottom: 32px;
}
.help-popular .help-article-list { margin-top: 0; padding: 24px; box-shadow: none; }
.help-popular .help-article-row { padding-inline: 0; }
.help-article-list { margin-top: 24px; padding: 24px; display: flex; flex-direction: column; }
.help-article-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 4px;
  border-bottom: 1px solid var(--color-border);
}
.help-article-list .help-article-row:first-child { border-top: 1px solid var(--color-border); }
.help-article-list .help-article-row:last-child { border-bottom: 0; }

/* Compact search-product pages reuse the AI Creator Search layout system. */
.search-page-hero .eyebrow { margin: 20px 0 14px; }
@media (min-width: 1024px) {
  /* Match AI Creator Search's original desktop banner dimensions.
     Flexible inner spacing accommodates longer copy on sibling pages. */
  .feature-hero {
    min-height: 782px;
    padding-block: 126px 22px;
    display: grid;
    align-items: center;
  }
}
@media (min-width: 1280px) {
  .feature-hero { min-height: 736px; }
}

.image-search-page .feature-hero { background: #C4B6F6; }
.image-search-page .feature-hero h1 { color: #1a1714; }
.image-search-page .feature-hero .hero-sub { color: #514b47; }
.image-search-page .feature-hero .feature-hero-badge { background: transparent; }
.product-search-page .feature-hero .feature-hero-badge { background: transparent; }
.lookalike-search-page .feature-hero .feature-hero-badge {
  width: 69.12px;
  height: 69.12px;
  border-radius: 17.28px;
  background: #c7b5fb;
}
.lookalike-search-page .feature-hero .search-page-figma-icon {
  width: 50.4px;
  height: 45.36px;
}
.image-search-title-mobile { display: none; }
.search-page-figma-icon { display: block; width: 69.12px; height: 69.12px; }
.image-search-page .feature-hero .btn-hero-primary { background: #5F2BF5; color: #fff; }
.image-search-page .feature-hero .btn-hero-primary:hover { background: #4c20d2; }
.image-search-page .feature-hero .btn-hero-secondary { color: #26145f; border-color: #26145f; }
.image-search-page .feature-hero .btn-hero-secondary:hover { background: rgba(95, 43, 245, .1); }
/* Lookalike Search hero uses the homepage AI Creator Search palette:
   teal band, acid-lime display text/primary CTA, and white supporting
   copy. This changes color only; the shared search-page layout stays
   identical across Image, Lookalike, and Product Search. */
.lookalike-search-page .feature-hero { background: #007b91; }
.lookalike-search-page .feature-hero h1 { color: #d9f20f; }
.lookalike-search-page .feature-hero .hero-sub { color: #fff; }
.lookalike-search-page .feature-hero .btn-hero-primary {
  background: #d9f20f;
  color: #14120f;
}
.lookalike-search-page .feature-hero .btn-hero-primary:hover,
.lookalike-search-page .feature-hero .btn-hero-primary:focus-visible { background: #e5fa34; }
.lookalike-search-page .feature-hero .btn-hero-secondary {
  border-color: #fff;
  color: #fff;
}
.lookalike-search-page .feature-hero .btn-hero-secondary:hover,
.lookalike-search-page .feature-hero .btn-hero-secondary:focus-visible {
  background: rgba(255, 255, 255, .12);
}
/* DM Agent keeps the AI Creator Search hero composition unchanged and only
   swaps its palette to the site's established violet/lime treatment. */
.dm-agent-page .feature-hero { background: #4d3fe9; }
.dm-agent-page .feature-hero h1 { color: #d9f20f; }
.dm-agent-page .feature-hero .hero-sub { color: #fff; }
.dm-agent-page .feature-hero .feature-hero-badge { background: #ff984b; }
.dm-agent-hero-icon {
  display: block;
  width: 46.8px;
  height: 39.6px;
}
.dm-agent-page .feature-hero .btn-hero-primary {
  background: #d9f20f;
  color: #14120f;
}
.dm-agent-page .feature-hero .btn-hero-primary:hover,
.dm-agent-page .feature-hero .btn-hero-primary:focus-visible { background: #e5fa34; }
.dm-agent-page .feature-hero .btn-hero-secondary {
  border-color: #fff;
  color: #fff;
}
.dm-agent-page .feature-hero .btn-hero-secondary:hover,
.dm-agent-page .feature-hero .btn-hero-secondary:focus-visible {
  background: rgba(255, 255, 255, .12);
}
/* Detail section headings use the available container width before wrapping. */
:is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page, .email-outreach-page, .smart-reply-page, .smart-flow-page) .section-head { max-width: none; }
:is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page, .email-outreach-page, .smart-reply-page, .smart-flow-page) .section-head h2 {
  white-space: normal;
  text-wrap: wrap;
  overflow-wrap: anywhere;
}
@media (max-width: 767.98px) {
  :is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page, .email-outreach-page, .smart-reply-page, .smart-flow-page) .section-head h2 {
    font-size: clamp(1.75rem, 7vw, 2.5rem);
    line-height: 1.18;
  }
}

/* DM Agent cards use the three Product Highlight colors documented in
   DESIGN_GUIDELINES.md, in teal -> orange -> pink order. */
.dm-agent-page .feature-card--teal {
  background: #FF9E5C;
  color: #6B2D00;
}
.dm-agent-page .feature-card--teal p {
  color: #14120F;
  font-size: 0.9375rem;
}
.dm-agent-page .feature-card--lavender {
  background: #CBF484;
  color: #14120F;
}
.dm-agent-page .feature-card--lavender p { color: #514C46; }
.dm-agent-page .feature-card--lime {
  background: #F5C3D6;
  color: #850010;
}
.dm-agent-page .feature-card--lime p { color: #14120F; }
.dm-agent-page .dm-agent-split-color--teal { background: #007B91; }
.dm-agent-choose-icon-badge {
  display: block;
  width: 69.12px;
  height: 69.12px;
}
.dm-agent-choose-icon-badge img {
  display: block;
  width: 100%;
  height: 100%;
}
.dm-agent-page .dm-title-line { white-space: nowrap; }
@media (min-width: 1024px) {
  /* Swap both halves of all three DM Agent split sections while preserving
     their alternating rhythm: image/text, text/image, image/text. */
  .dm-agent-page .split-panel-inner { flex-direction: row-reverse; }
}
@media (max-width: 767.98px) {
  /* Product detail split-panel titles share the mobile scale;
     hero headings and other page types retain their own typography. */
  :is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page, .email-outreach-page, .smart-reply-page, .smart-flow-page) .split-panel-color .ai-eyebrow {
    font-size: clamp(1.75rem, 7vw, 2.5rem);
    line-height: 1.18;
    min-width: 0;
    max-width: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .dm-agent-page .section-head h2,
  .dm-agent-page .faq-heading,
  .dm-agent-page .cta-copy h2 {
    font-size: clamp(1.75rem, 7vw, 2.5rem);
    line-height: 1.18;
  }
  .dm-agent-page .feature-highlights .section-head h2,
  .dm-agent-page .dm-title-line { white-space: normal; }
  .dm-agent-page :is(h2, h3, .ai-eyebrow) {
    max-width: 100%;
    min-width: 0;
    overflow-wrap: anywhere;
  }
  .dm-agent-page :is(.feature-card, .step) {
    min-width: 0;
  }
  .dm-agent-page :is(.feature-card, .step) h3 {
    font-size: 1.125rem;
    line-height: 1.35;
  }
  .dm-card-copy-break { display: none; }
}
/* The outreach artwork is exported as one SVG, so two temporary trailing
   copies create a layered arrival before the crisp front copy settles. */
.dm-agent-hero-art {
  position: relative;
  width: 100%;
  height: 383px;
  aspect-ratio: auto;
  margin-top: 0;
  transform: translateX(-32px) scale(1.33875);
  transform-origin: center;
}
.dm-agent-hero-object {
  display: block;
  width: 100%;
  height: 100%;
}
@media (max-width: 1023.98px) {
  .dm-agent-hero-art {
    width: min(88%, 360px);
    height: 388px;
    margin-inline: auto;
    transform: translateX(-10px) scale(1.08);
  }
}
@media (prefers-reduced-motion: reduce) {
  .dm-agent-hero-object { animation: none; }
}
/* Product Search hero follows DESIGN_GUIDELINES.md's pink Product
   Highlight palette: pink field, deep-red display/primary action, and
   neutral dark supporting copy. */
.product-search-page .feature-hero { background: #f5c3d6; }
.product-search-page .feature-hero h1 { color: #850010; }
.product-search-page .feature-hero .hero-sub { color: #14120f; }
.product-search-page .feature-hero .btn-hero-primary {
  background: #850010;
  color: #fff;
}
.product-search-page .feature-hero .btn-hero-primary:hover,
.product-search-page .feature-hero .btn-hero-primary:focus-visible { background: #9b1021; }
.product-search-page .feature-hero .btn-hero-secondary {
  border-color: #850010;
  color: #850010;
}
.product-search-page .feature-hero .btn-hero-secondary:hover,
.product-search-page .feature-hero .btn-hero-secondary:focus-visible {
  background: rgba(133, 0, 16, .08);
}
.image-search-page .search-page-hero-visual--lavender {
  height: 383px;
  min-height: 0;
  overflow: visible;
  padding: 0;
  background: transparent;
}
.search-page-hero-visual {
  display: grid;
  place-items: center;
  aspect-ratio: 644 / 383;
  border-radius: 32px;
  overflow: hidden;
  padding: clamp(28px, 4vw, 64px);
}
.search-page-hero-visual--lavender { background: #cbb6ff; }
.search-page-hero-visual--lime { background: #ccea01; }
.search-page-hero-visual--teal { background: #197e8c; }
.search-page-hero-visual img { width: 100%; max-height: 100%; object-fit: contain; }
/* Email Outreach hero art — per feedback, no boxed/colored card behind it
   (not .search-page-hero-visual, which is what the earlier version of
   this used and is why a teal rounded box was showing up behind the
   illustration) — the SVG floats directly on the hero's own background,
   same unboxed treatment as .dm-agent-hero-art/.dm-agent-hero-object. The
   asset already draws its own background gradient card/shadow, so it
   doesn't need a card behind it either. Plain width:100%/height:auto
   (not a fixed-height/object-fit box like .dm-agent-hero-art's) since
   this illustration's own ~512:475 canvas is close to square, not an
   ultra-wide collage that needs containing. */
.email-outreach-hero-art { width: 100%; position: relative; top: -15px; }
.email-outreach-hero-object { display: block; width: 100%; height: auto; }
@media (max-width: 1023.98px) {
  .email-outreach-hero-art { width: min(88%, 420px); margin-inline: auto; }
}
.lookalike-search-page .search-page-hero-visual--lime {
  height: 383px;
  min-height: 0;
  overflow: visible;
  padding: 0;
  background: transparent;
}
.lookalike-search-page .lookalike-search-banner-svg {
  display: block;
  position: relative;
  top: -55px;
  left: -80px;
  width: 576.58px !important;
  max-width: none;
  height: auto;
  max-height: none;
  filter: drop-shadow(0 24px 34px rgba(0, 38, 48, .18));
  animation: lookalike-banner-enter 900ms cubic-bezier(.16, 1, .3, 1) both;
}
.product-search-page .search-page-hero-visual--teal {
  position: relative;
  top: -115px;
  height: 383px;
  min-height: 0;
  overflow: visible;
  padding: 0;
  background: transparent;
}
.product-search-page .product-search-banner-svg {
  display: block;
  position: relative;
  top: 0;
  left: -60px;
  width: 600.88px;
  max-width: none;
  height: auto;
  max-height: none;
  animation: product-banner-enter 920ms cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes lookalike-banner-enter {
  from { opacity: 0; transform: translate3d(42px, 30px, 0) scale(.94) rotate(1.5deg); }
  to { opacity: 1; transform: translate3d(0, 0, 0) scale(1) rotate(0); }
}
@keyframes product-banner-enter {
  from { opacity: 0; transform: translate3d(42px, 34px, 0) scale(.93) rotate(1.2deg); }
  to { opacity: 1; transform: translate3d(0, 0, 0) scale(1) rotate(0); }
}
.image-search-page .search-page-hero-visual .image-search-banner-svg {
  display: block;
  width: 582px !important; /* 520px + 12% */
  height: auto !important;
  max-width: none;
  max-height: none;
  object-fit: contain;
  transform: translate(-80px, -90px);
}
/* Platform-wide 110px gutter (DESIGN_GUIDELINES.md "通栏板块左右留白") only
   turns on at 1600px+ everywhere else; on this page the user wants every
   section's edge inset to read as a consistent 110px starting from a normal
   desktop width, not just ultra-wide screens. Page-scoped (not a site-wide
   breakpoint change) — same 110px value the shared rules already use at
   1600px+, just pulled down to 1024px+ for image-search.html's own
   .feature-hero/.feature-steps/.faq-section/.site-footer containers.
   Higher specificity than the shared 1600px-only rules (2 classes vs 1) so
   it wins at every width from 1024px up, including past 1600px where both
   would otherwise agree anyway. .feature-hero is included here too (unlike
   the sitewide docs' hero exception, which centers hero at its own
   max-width:1600px instead of the 110px edge inset) — on this page that
   left the hero copy's left edge drifting out of alignment with the cards/
   FAQ/footer below it at any width under 1600px, which is exactly the
   misalignment being fixed; every section on this page now shares one
   edge. split-panel/cta-inner stay full-bleed by design — neither uses a
   .container gutter, so there's nothing to unify there.
   dm-agent-page has the exact same shape (feature-hero/feature-steps/
   faq-section/site-footer, same split-panel/cta-inner exception) and the
   same reported symptom — hero copy's left edge sitting at the 1600px-tier
   centered inset (64px at 1728px wide) instead of lining up with
   .feature-steps' cards below it, which already had the 110px gutter — so
   it's folded into the same fix rather than given a separate rule.
   ai-creator-search-page is the page DESIGN_GUIDELINES.md's "通栏板块左右
   留白" section originally named as the hero-110px exception (the other
   pages here all copied its max-width:1600px pattern) — same 96px-at-
   1728px/growing-with-viewport symptom as the rest once measured, so it's
   no longer actually exempt; folded in here too and the doc updated to
   match. */
@media (min-width: 1024px) {
  :is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page) .feature-hero .container,
  :is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page) .feature-steps .container,
  :is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page) .faq-section .container,
  .connect-detail-page .feature-hero .container,
  .connect-detail-page .feature-steps .container,
  .connect-detail-page .faq-section .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
  :is(.ai-creator-search-page, .image-search-page, .lookalike-search-page, .product-search-page, .dm-agent-page) .site-footer .container,
  .connect-detail-page .site-footer .container {
    width: 100%;
    max-width: none;
    padding-inline: 110px;
  }
}
.search-page-card-grid .feature-card { min-height: 460px; justify-content: flex-end; }
.search-page-card-number {
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  margin-bottom: auto;
  border: 2px solid currentColor;
  border-radius: 20px;
  font-size: 1.15rem;
  font-weight: 700;
  transition: transform 420ms cubic-bezier(.2,.75,.25,1);
}
.search-page-card-grid .feature-card:hover .search-page-card-number { transform: translateY(-7px) rotate(-3deg); }
/* Same copy scale as ai-creator-search.html's .ai-copy-text: the short
   lead and supporting paragraph are peers, so both stay at 18px rather
   than the paragraph becoming the visually dominant line. */
.search-page-split-copy { max-width: 620px; margin: 22px 0 30px; font-size: 1.125rem; line-height: 1.55; }
.search-page-cta-visual { display: grid; place-items: center; padding: clamp(35px, 6vw, 90px); }
.search-page-cta-visual img { width: min(100%, 560px); max-height: 440px; object-fit: contain; }

@media (max-width: 1023px) {
  .search-page-hero-visual { width: 100%; margin-top: 44px; border-radius: 26px; }
  .search-page-card-grid .feature-card { min-height: 390px; }
  .search-page-cta-visual { min-height: 420px; }
  /* Match ai-creator-search.html's centered stacked split-panel treatment:
     every element, including this page's extra paragraph, shares one
     centered axis on phone/tablet. Desktop remains intentionally left. */
  :is(.image-search-page, .lookalike-search-page, .product-search-page) .split-panel-color .ai-copy {
    width: 100%;
    margin-inline: auto;
    align-items: center;
  }
  .connect-detail-page .split-panel-color .ai-copy {
    width: 100%;
    margin-inline: auto;
    align-items: center;
  }
  :is(.image-search-page, .lookalike-search-page, .product-search-page) .split-panel-color .ai-copy-bottom { align-items: center; }
  .connect-detail-page .split-panel-color .ai-copy-bottom { align-items: center; }
  :is(.image-search-page, .lookalike-search-page, .product-search-page) .split-panel-color .ai-eyebrow,
  :is(.image-search-page, .lookalike-search-page, .product-search-page) .split-panel-color .ai-copy-text,
  :is(.image-search-page, .lookalike-search-page, .product-search-page) .split-panel-color .search-page-split-copy {
    text-align: center;
    margin-inline: auto;
  }
  .connect-detail-page .split-panel-color .ai-eyebrow,
  .connect-detail-page .split-panel-color .ai-copy-text,
  .connect-detail-page .split-panel-color .search-page-split-copy {
    text-align: center;
    margin-inline: auto;
  }
  /* Below 1024px main.js reorders #featureHeroVisual to sit between the h1
     and .hero-sub (see .feature-hero-visual's own comment above) — so this
     card's box needs to actually contain its image instead of overflowing
     into the paragraph that now follows right after it in the DOM. The
     desktop rules above size this variant for the enlarged (582px,
     translated -80/-90) banner deliberately bigger than its box with
     overflow:visible; at these widths the banner switches to a plain
     100%-wide, untransformed image instead (both reset right below), so
     the box needs height:auto — and, since aspect-ratio:644/383 would
     otherwise still force a height from the box's own width even with
     height:auto, aspect-ratio:auto too — to size to that image's real
     rendered height rather than the old fixed desktop ratio. */
  .image-search-page .search-page-hero-visual--lavender { height: auto; aspect-ratio: auto; }
  .image-search-page .search-page-hero-visual .image-search-banner-svg {
    width: min(100%, 560px) !important;
    height: auto;
    max-width: 100%;
    transform: none;
  }
  .lookalike-search-page .search-page-hero-visual--lime { height: auto; aspect-ratio: auto; }
  .lookalike-search-page .lookalike-search-banner-svg {
    top: -20px;
    left: 0;
    width: min(100%, 560px);
    height: auto;
    max-width: 100%;
  }
  .product-search-page .search-page-hero-visual--teal {
    top: 0;
    height: auto;
    aspect-ratio: auto;
    margin-top: 32px;
  }
  .product-search-page .product-search-banner-svg {
    top: 0;
    left: 0;
    width: min(100%, 550px);
    height: auto;
    max-width: 100%;
    margin-inline: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .lookalike-search-page .lookalike-search-banner-svg { animation: none; }
  .product-search-page .product-search-banner-svg { animation: none; }
}

@media (max-width: 639.98px) {
  .image-search-title-desktop { display: none; }
  .image-search-title-mobile { display: inline; }
}

@media (max-width: 767px) {
  .search-page-hero-visual { padding: 24px; border-radius: 22px; }
  .search-page-card-grid .feature-card { min-height: 340px; }
  .search-page-card-number { width: 62px; height: 62px; border-radius: 17px; }
}
.help-article-row-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.help-article-row-title { font-size: 1.0625rem; font-weight: 600; color: var(--color-ink); }
.help-article-row-category { font-size: 0.875rem; color: var(--color-ink-soft); }
.help-article-row-arrow { flex-shrink: 0; color: var(--color-ink-soft); transition: transform 220ms ease, color 220ms ease; }
.help-article-row:hover .help-article-row-title { color: var(--color-brand-dark); }
.help-article-row:hover .help-article-row-arrow { color: var(--color-brand-dark); transform: translateX(4px); }

/* Still need help — .post-callout's bordered/rounded card shell, sized up
   into a two-column split (copy left, actions right) instead of a plain
   text block. */
.help-contact { padding-top: 24px; }
@media (min-width: 1024px) {
  .help-contact > .container { max-width: 1320px; }
}
.help-contact-card {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 40px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg-alt);
}
@media (min-width: 768px) {
  .help-contact-card { flex-direction: row; align-items: center; justify-content: space-between; padding: 48px 56px; }
}
.help-contact-copy h2 { font-size: 1.75rem; }
.help-contact-copy p { margin-top: 8px; color: var(--color-ink-soft); }
.help-contact-actions { display: flex; flex-direction: column; gap: 12px; flex-shrink: 0; }
@media (min-width: 480px) {
  .help-contact-actions { flex-direction: row; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition-duration: 0.001ms !important; animation-duration: 0.001ms !important; }
}
/* ==========================================================================
   About Quacko
   ========================================================================== */
.about-page main { overflow: hidden; }
.about-hero { padding: 58px 0 104px; }
.about-hero-inner { display: grid; grid-template-columns: minmax(0, .9fr) minmax(420px, 1.1fr); gap: 110px; align-items: center; }
.about-hero-copy h1 { max-width: 720px; margin: 18px 0 24px; }
.about-hero-lead { max-width: 650px; color: var(--color-ink-soft); font-size: 1.125rem; line-height: 1.55; }
.about-hero-art { width: 80%; justify-self: end; display: grid; place-items: center; overflow: hidden; border-radius: 48px; }
.about-hero-art img { display: block; width: 100%; height: auto; }
.about-story { background: #fff; }
.about-story-inner { display: grid; grid-template-columns: 1fr 2.25fr; gap: 72px; max-width: 1180px; }
.about-story-kicker { color: #f04b18; font-size: .8125rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.about-story-heading h2 { margin-top: 18px; font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; font-size: 2.75rem; font-weight: 600; letter-spacing: -.02em; line-height: 1.12; }
.about-story-copy { display: grid; gap: 30px; }
.about-story-copy p { font-size: 1.125rem; line-height: 1.55; }
.about-faq h2 { margin-top: 12px; font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; font-size: 2.75rem; font-weight: 600; letter-spacing: -.02em; line-height: 1.12; }
.about-belief { color: #fff; background: #197E8C; }
.about-belief .eyebrow { color: #ccea01; }
.about-belief h2 { max-width: 900px; margin: 18px 0 58px; font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; font-size: 2.75rem; font-weight: 600; letter-spacing: -.02em; line-height: 1.12; }
.about-belief-grid { display: grid; grid-template-columns: repeat(3, 1fr); }
.about-belief-grid article { padding: 40px 36px 12px 0; }
.about-belief-grid article + article { padding-left: 36px; border-left: 1px solid rgba(255,255,255,.3); }
.about-belief-icon { display: grid; place-items: center; width: 69.12px; height: 69.12px; overflow: hidden; border-radius: 17.28px; }
.about-belief-icon img { display: block; object-fit: contain; }
.about-belief-icon--fit img { width: 69.12px; height: 69.12px; }
.about-belief-icon--context img { width: 43.2px; height: auto; }
.about-belief-icon--relationships img { width: 43.2px; height: auto; }
.about-belief-icon--context {
  background: #FFC1D7;
}
.about-belief-icon--relationships { background: #FF984B; }
.about-belief-grid h3 { margin: 32px 0 16px; font-size: 1.125rem; }
.about-belief-grid p { color: rgba(255,255,255,.8); line-height: 1.6; }
.about-faq { background: #572077; }
.about-faq-inner { display: grid; grid-template-columns: minmax(260px, .75fr) minmax(0, 1.25fr); gap: 80px; }
.about-faq-list article { padding: 28px 0; border-bottom: 1px solid var(--color-border); }
.about-faq-list article:first-child { padding-top: 0; }
.about-faq-list article:last-child { border-bottom: 0; }
.about-faq-list h3 { margin-bottom: 10px; font-size: 1.125rem; }
.about-faq-list p { color: var(--color-ink-soft); line-height: 1.6; }
@media (min-width: 1024px) {
  .about-page main > section > .container {
    width: auto;
    max-width: none;
    margin-inline: 110px;
    padding-inline: 0;
  }
  .about-story-inner { max-width: none; }
}
@media (max-width: 900px) {
  .about-hero { padding: 126px 0 72px; }
  .about-hero-inner, .about-story-inner, .about-faq-inner { grid-template-columns: 1fr; gap: 44px; }
  .about-hero-inner { gap: 0; }
  .about-hero-copy { display: contents; }
  .about-hero-copy .eyebrow { order: 1; }
  .about-hero-copy h1 { order: 2; }
  .about-hero-art {
    order: 3;
    width: calc(100vw - 32px);
    max-width: none;
    margin-inline: calc(50% - 50vw + 16px);
    justify-self: center;
    border-radius: 32px;
  }
  .about-hero-lead { order: 4; margin-top: 24px; }
  .about-belief-grid { grid-template-columns: 1fr; }
  .about-belief-grid article { padding: 30px 0; }
  .about-belief-grid article + article { padding-left: 0; border-top: 1px solid rgba(255,255,255,.3); border-left: 0; }
  .about-belief-grid h3 { margin-top: 24px; }
}
@media (max-width: 639.98px) {
  /* All mobile page banners use the homepage's header-to-hero rhythm:
     body's 126px fixed-nav clearance, followed by 32px of visible space. */
  .pricing-hero,
  .blog-hero,
  .about-hero { padding-top: 32px; }
  .feature-hero { padding-top: calc(126px + 32px); }
  .about-hero-art { border-radius: 32px; }
  .about-work-media { min-height: 320px; }
}

/* Email Outreach follows the DM Agent detail layout with email UI previews. */
.email-outreach-page .email-card--purple { background: #552173; color: #fff; }
.email-outreach-page .email-card--green { background: #cbf484; color: #14120f; }
.email-outreach-page .email-card--pink { background: #ffc1d7; color: #14120f; }
.email-outreach-page .feature-card p { color: inherit; }
.email-outreach-page .email-card-preview { width: 100%; margin-bottom: 28px; }
/* Per feedback, all 3 "How it works" card mocks should read as the same
   size — same idea as dm-agent.html's own 3 .feature-card-mock cards,
   which land there for free since their 3 exported assets already share
   close to the same aspect ratio (443:245, 425:215, 422:211). Ours don't
   (card 1's asset is 395:251, card 2's is a much shorter 419:158, and
   card 3 is a live 2x2 stat grid with no fixed aspect at all), so an
   aspect-ratio box on the shared wrapper is needed to make them agree.
   aspect-ratio (not a hardcoded min-height) per .feature-card's own
   comment above ("a hardcoded px min-height would over/under-shoot at
   different grid widths") — this scales with the card the same fluid
   way. 395/251 matches card 1's own asset, the closest of the three to
   dm-agent's own ~1.8-2:1 range. */
.email-outreach-page .email-card-media {
  aspect-ratio: 395 / 251;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 28px;
}
.email-outreach-page .email-card-media .feature-card-mock-flat {
  width: 100%;
  height: 100%;
  object-fit: contain;
  margin-bottom: 0;
}
/* Per feedback: this asset's own aspect ratio exactly matches
   .email-card-media's box (both 395:251), so object-fit:contain fills it
   edge-to-edge with no breathing room at all — shrunk to 90% so the pink
   card shows through around it, same as the visible margin the other two
   cards' shorter/wider assets already get for free from being
   letterboxed inside the same box. */
.email-outreach-page .email-card-media .email-connect-sender-image { width: 90%; height: 90%; }
/* Per feedback, the 4 tiles read as too tall (height:100% stretched them
   to fill the whole aspect-ratio box) — sized to their own content
   instead and vertically centered in the box via .email-card-media's own
   flex align-items:center, leaving even white space above/below instead
   of stretching. */
.email-outreach-page .email-card-media .email-stat-grid { width: 100%; }
.email-outreach-page .email-preview {
  width: 100%; min-width: 0; padding: 24px; border: 1px solid #e9e6e2;
  border-radius: 18px; background: #fff; color: #14120f;
  box-shadow: 0 18px 48px #24180c14; font-size: 13px; text-align: left;
}
.email-outreach-page .email-preview-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-weight: 700; font-size: 16px; margin-bottom: 24px; }
.email-outreach-page .email-preview-heading > span { font-size: 11px; background: #f2f1ee; padding: 6px 10px; border-radius: 8px; white-space: nowrap; }
.email-outreach-page .email-preview-row { display: flex; justify-content: space-between; gap: 12px; padding: 16px 0; border-top: 1px solid #efede9; align-items: center; }
.email-outreach-page .email-preview-row b { text-align: right; overflow-wrap: anywhere; }
.email-outreach-page .email-preview-status { color: #27754b; background: #edf8ef; padding: 5px 9px; border-radius: 20px; font-size: 11px; }
.email-outreach-page .email-preview-message { background: #faf9f7; padding: 16px; line-height: 1.6; border-radius: 8px; margin-bottom: 12px; overflow-wrap: anywhere; }
.email-outreach-page .email-preview-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.email-outreach-page .email-preview-metrics strong { display: block; font-size: clamp(20px, 2.5vw, 32px); }
.email-outreach-page .email-preview-metrics span { font-size: 11px; color: #706b65; }
.email-outreach-page .email-preview-bars { height: 130px; display: flex; align-items: end; gap: 12px; margin-top: 28px; border-bottom: 1px solid #ece9e4; }
.email-outreach-page .email-preview-bars i { flex: 1; height: var(--bar); border-radius: 6px 6px 0 0; background: #cbf484; }
/* "Launch and Track Results" card — 2x2 stat-tile grid redrawn from the
   real /email-agent dashboard's own StatCard (see the HTML comment above
   this markup): label on top, big value below, icon pinned top-right,
   white rounded-card tile with a border — var(--radius-md) is the same
   14px "rounded-card" token that component uses. */
.email-outreach-page .email-stat-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}
.email-outreach-page .email-stat-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 22px;
  border: 1px solid #e9e6e2;
  border-radius: var(--radius-md);
  background: #fff;
}
/* Per feedback: moved from top-right (small, next to the label) to
   bottom-right and sized up — the tile's own aspect-ratio-driven height
   (see .email-card-media above) already leaves clear space below the
   value for it to sit in without overlapping either line. */
.email-outreach-page .email-stat-icon {
  position: absolute;
  bottom: 14px;
  right: 14px;
  width: 28px;
  height: 28px;
  color: var(--color-ink-soft);
}
.email-outreach-page .email-stat-label { font-size: 13px; color: var(--color-ink-soft); }
.email-outreach-page .email-stat-value { font-size: 1.375rem; line-height: 1; font-weight: 600; color: #14120f; }
.email-outreach-page .email-split-visual { align-items: center; justify-content: center; padding: clamp(24px, 5vw, 80px); }
.email-outreach-page .email-split-visual .email-preview { max-width: 560px; }
.email-outreach-page .email-split-visual--0 { background: #cce9ed; }
.email-outreach-page .email-split-visual--1 { background: #f9dbe6; }
.email-outreach-page .email-split-visual--2 { background: #e5edbe; }
.email-outreach-page .email-split-color--0 { background: #007b91; }
.email-outreach-page .email-split-color--1 { background: #c7b5fb; color: #14120f; }
.email-outreach-page .email-split-color--1 .ai-eyebrow { color: #552173; }
.email-outreach-page .email-split-color--1 .ai-copy-text { color: #14120f; }
.email-outreach-page .email-split-color--1 .btn-pill-outline-dark { background: #552173; border-color: #552173; color: #fff; }
.email-outreach-page .email-split-color--1 .btn-pill-outline-dark .ai-arrow { color: #fff; }
.email-outreach-page .email-split-color--1 .btn-pill-outline-dark:hover,
.email-outreach-page .email-split-color--1 .btn-pill-outline-dark:focus-visible { background: #42195b; border-color: #42195b; }
.email-outreach-page .email-split-color--2 { background: #512c00; }
@media (max-width: 767.98px) {
  .email-outreach-page .email-preview { padding: 16px; }
  .email-outreach-page .feature-card { min-width: 0; }
  .email-outreach-page .email-split-visual { aspect-ratio: auto; min-height: 350px; padding-block: 40px; }
}

/* Email banner badge — Figma 3417:49312, scaled with the shared 69.12px badge. */
.email-outreach-page .feature-hero-badge { background: #d3f300; }
.email-hero-icon { display: block; width: 45.307px; height: 35.577px; }

/* SaaS email agent type picker, used in the first Email detail panel. */
.email-agent-picker .email-preview-heading { margin-bottom: 20px; }
.email-agent-option { display: flex; align-items: center; gap: 14px; padding: 18px 14px; border: 1px solid #e9e6e2; border-radius: 12px; }
.email-agent-option + .email-agent-option { margin-top: 12px; }
.email-agent-option img { width: 48px; height: 48px; object-fit: contain; flex-shrink: 0; }
.email-agent-option > div { flex: 1; min-width: 0; }
.email-agent-option h4 { margin: 0; font-size: 15px; line-height: 1.4; }
.email-agent-option p { margin: 5px 0 0; color: #737373; font-size: 12px; line-height: 1.5; }
@media (max-width: 480px) {
  .email-agent-picker { padding: 18px !important; }
  .email-agent-option { padding: 12px 10px; gap: 10px; }
  .email-agent-option img { width: 36px; height: 36px; }
}

/* Fill the entire panel with the supplied artwork, including its background. */
.email-outreach-page .email-split-visual--0,
.email-outreach-page .email-split-visual--1 {
  position: relative;
  padding: 0;
  overflow: hidden;
}
.email-agent-types-image,
.email-performance-image {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
}
@media (max-width: 1023.98px) {
  .email-outreach-page .email-split-visual--0 {
    aspect-ratio: 1905 / 1688;
    min-height: 0;
  }
}

@media (max-width: 1023.98px) {
  .email-outreach-page .email-split-visual--1 {
    aspect-ratio: 1905 / 1688;
    min-height: 0;
  }
}

/* Creator outreach use case — DESIGN_GUIDELINES.md typography and color system. */
.creator-outreach-use-case h2 { font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; font-size: 2.75rem; font-weight: 600; line-height: 1.12; letter-spacing: -.02em; }
.creator-outreach-use-case .feature-hero-copy { max-width: 760px; }
.creator-outreach-use-case .feature-hero-copy > .eyebrow { margin-bottom: 24px; }
.creator-outreach-use-case .uc-heading { max-width: 850px; margin: 0 auto 48px; text-align: center; }
.creator-outreach-use-case .section-sub { margin-top: 20px; }
.creator-outreach-use-case .uc-grid { display: grid; gap: 24px; }
.creator-outreach-use-case .uc-card { min-width: 0; padding: 32px; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-bg); }
.creator-outreach-use-case .uc-card p { margin-top: 12px; color: var(--color-ink-soft); }
.creator-outreach-use-case .uc-number { display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; border-radius: 12px; background: #D9F20F; font-weight: 700; margin-bottom: 28px; }
.creator-outreach-use-case .uc-pain { background: #F0F0F0; }
.creator-outreach-use-case .uc-pain-grid { display: grid; gap: 16px; }
.creator-outreach-use-case .uc-note { display: flex; align-items: center; gap: 20px; padding: 28px; background: white; border: 1px solid var(--color-border); border-radius: var(--radius-lg); }
.creator-outreach-use-case .uc-note > span { color: var(--color-brand-dark); font-size: 13px; font-weight: 600; }
.creator-outreach-use-case .uc-note p { font-weight: 500; }
.creator-outreach-use-case .uc-compare { display: grid; gap: 24px; }
.creator-outreach-use-case .uc-compare-card { padding: 40px; border-radius: var(--radius-lg); }
.creator-outreach-use-case .uc-compare-card h3 { font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; font-size: 32px; font-weight: 600; line-height: 1.3; letter-spacing: -.01em; }
.creator-outreach-use-case #manual-vs-agent { background: #FF9E5C; color: #14120F; }
.creator-outreach-use-case #manual-vs-agent :is(h2, h3, .eyebrow) { color: #6B2D00; }
.creator-outreach-use-case #manual-vs-agent :is(.section-sub, .uc-comparison-note) { color: #14120F; }
.creator-outreach-use-case .uc-before { background: #FFF1E6; border: 1px solid #6B2D0026; }
.creator-outreach-use-case .uc-after { background: #6B2D00; color: #FFF1E6; }
.creator-outreach-use-case #manual-vs-agent .uc-after h3 { color: #FFF1E6; }
.creator-outreach-use-case .uc-compare ul { margin-top: 28px; padding: 0; list-style: none; }
.creator-outreach-use-case .uc-compare li { padding: 16px 0; border-bottom: 1px solid #14120f1a; display: flex; gap: 14px; }
.creator-outreach-use-case .uc-after li { border-bottom-color: #FFF1E633; }
.creator-outreach-use-case .uc-compare li:last-child { border-bottom: 0; }
.creator-outreach-use-case .uc-before li::before { content: '−'; }
.creator-outreach-use-case .uc-after li::before { content: '✓'; color: #FF9E5C; }
.creator-outreach-use-case .uc-workflow { background: var(--color-bg-alt); }
.creator-outreach-use-case .uc-split-inner { display: grid; align-items: center; gap: 40px; }
.creator-outreach-use-case .uc-split figure { margin: 0; min-width: 0; }
.creator-outreach-use-case .uc-split img { display: block; width: 100%; height: auto; max-height: 540px; object-fit: contain; border-radius: var(--radius-lg); }
.creator-outreach-use-case .uc-split .hero-actions { justify-content: flex-start; margin-top: 28px; }
.creator-outreach-use-case .uc-teal { background: #007B91; color: white; }
.creator-outreach-use-case .uc-teal h2, .creator-outreach-use-case .uc-teal .eyebrow { color: #D9F20F; }
.creator-outreach-use-case .uc-teal .section-sub { color: white; }
.creator-outreach-use-case .uc-teal .btn { background: #D9F20F; color: var(--color-ink); border-color: transparent; }
.creator-outreach-use-case .uc-orange { background: #FF9E5C; }
.creator-outreach-use-case .uc-orange h2, .creator-outreach-use-case .uc-orange .eyebrow { color: #6B2D00; }
.creator-outreach-use-case .uc-pink { background: #F5C3D6; }
.creator-outreach-use-case .uc-pink h2, .creator-outreach-use-case .uc-pink .eyebrow { color: #850010; }
.creator-outreach-use-case .uc-fit { background: #E6EFD3; }
.creator-outreach-use-case .uc-fit-list { list-style: none; padding: 0; margin: 0; }
.creator-outreach-use-case .uc-fit-list li { padding: 20px 0; border-bottom: 1px solid #14120f26; display: flex; gap: 16px; }
.creator-outreach-use-case .uc-fit-list li::before { content: '✓'; color: var(--color-green); }
.creator-outreach-use-case .uc-final { text-align: center; }
.creator-outreach-use-case .uc-final h2 { max-width: 780px; margin: 16px auto 0; }
.creator-outreach-use-case .uc-final .hero-actions { justify-content: center; margin-top: 32px; }
.creator-outreach-use-case .uc-related { display: flex; flex-direction: column; text-decoration: none; color: var(--color-ink); transition: box-shadow 220ms ease; }
.creator-outreach-use-case .uc-related > span { margin-top: auto; padding-top: 28px; font-weight: 600; color: var(--color-brand-dark); }
.creator-outreach-use-case .uc-related:hover { box-shadow: var(--shadow-card-hover); }
.creator-outreach-use-case .uc-related:focus-visible { outline: 3px solid var(--color-brand); outline-offset: 4px; }
@media (min-width: 640px) {
  .creator-outreach-use-case .uc-pain-grid, .creator-outreach-use-case .uc-three { grid-template-columns: repeat(2,minmax(0,1fr)); }
}
@media (min-width: 1024px) {
  /* Match the documented 115px horizontal gutter across every use-case section. */
  .creator-outreach-use-case main > .section > .container { width: 100%; max-width: none; margin-inline: 0; padding-inline: 115px; }
  .creator-outreach-use-case .uc-three, .creator-outreach-use-case .uc-pain-grid { grid-template-columns: repeat(3,minmax(0,1fr)); }
  .creator-outreach-use-case .uc-two, .creator-outreach-use-case .uc-compare { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .creator-outreach-use-case .uc-split-inner { grid-template-columns: repeat(2,minmax(0,1fr)); gap: 64px; }
}
@media (max-width: 639.98px) {
  .creator-outreach-use-case .feature-hero { margin-top: -126px; padding-top: calc(126px + 32px); }
  .creator-outreach-use-case .uc-card, .creator-outreach-use-case .uc-compare-card { padding: 24px; }
  .creator-outreach-use-case .uc-split { padding-block: 32px; }
  .creator-outreach-use-case .uc-split h2 { font-size: clamp(1.75rem,7vw,2.5rem); line-height: 1.18; }
}
@media (prefers-reduced-motion: reduce) {
  .creator-outreach-use-case .uc-related { transition: none; }
}

.creator-outreach-use-case .feature-hero-copy > .eyebrow { color: #D9F20F; }

/* Detailed creator outreach tutorial. */
.creator-outreach-use-case .uc-guide { scroll-margin-top: 150px; }
.creator-outreach-use-case .uc-guide-body { max-width: 880px; margin-inline: auto; line-height: 1.75; overflow-wrap: anywhere; }
.creator-outreach-use-case .uc-guide-body h3 { margin: 32px 0 12px; font-size: 24px; line-height: 1.3; }
.creator-outreach-use-case .uc-guide-body p + p { margin-top: 16px; }
.creator-outreach-use-case .uc-guide-body ul,
.creator-outreach-use-case .uc-guide-body ol { padding-left: 24px; margin-block: 24px; }
.creator-outreach-use-case .uc-guide-body li + li { margin-top: 14px; }
.creator-outreach-use-case .uc-guide-body a { text-decoration: underline; text-underline-offset: 4px; }
.creator-outreach-use-case .uc-guide-toc { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 12px; margin-block: 28px; }
.creator-outreach-use-case .uc-guide-toc a { padding: 16px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-bg); }
.creator-outreach-use-case .uc-message { margin: 20px 0; padding: 28px; border-left: 4px solid #6b2d00; border-radius: 0 16px 16px 0; background: #fff8f0; color: #14120f; }
.creator-outreach-use-case .uc-guide-body figure { margin: 36px 0 0; }
.creator-outreach-use-case .uc-guide-body figure img { display: block; width: 100%; max-height: 480px; object-fit: contain; }
.creator-outreach-use-case .uc-table-wrap { overflow-x: auto; margin-block: 28px; }
.creator-outreach-use-case .uc-table-wrap table { width: 100%; border-collapse: collapse; text-align: left; font-size: 15px; }
.creator-outreach-use-case .uc-table-wrap caption { text-align: left; font-weight: 600; margin-bottom: 12px; }
.creator-outreach-use-case .uc-table-wrap th,
.creator-outreach-use-case .uc-table-wrap td { padding: 16px; border: 1px solid var(--color-border); vertical-align: top; min-width: 140px; }
.creator-outreach-use-case .uc-table-wrap thead { background: #e6efd3; }
@media (max-width: 639.98px) {
  .creator-outreach-use-case .uc-guide-toc { grid-template-columns: minmax(0,1fr); }
  .creator-outreach-use-case .uc-message { padding: 20px; }
}

/* Practical tips within the creator outreach tutorial. */
.creator-outreach-use-case .uc-tip { margin-top: 32px; padding: 24px 28px; border: 1px solid #b6c887; border-left: 4px solid #607b27; border-radius: var(--radius-md); background: #f2f7e8; color: #14120f; }
.creator-outreach-use-case .uc-tip .uc-tip-label { margin: 0; color: #465d19; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; }
.creator-outreach-use-case .uc-tip h3 { margin: 8px 0 12px; font-size: 20px; }
.creator-outreach-use-case .uc-tip p { margin: 0; }
@media (max-width: 639.98px) {
  .creator-outreach-use-case .uc-tip { padding: 20px; }
}

/* Short tutorial steps use the same full-width, two-column composition as product pages. */
.creator-outreach-use-case .uc-guide-split .uc-guide-intro { min-width: 0; }
.creator-outreach-use-case .uc-guide-split .uc-guide-intro > p { margin-top: 24px; line-height: 1.75; }
.creator-outreach-use-case .uc-guide-split .uc-guide-intro figure { margin: 32px 0 0; }
.creator-outreach-use-case .uc-guide-split .uc-guide-intro figure img { width: 100%; max-height: 320px; object-fit: contain; }
.creator-outreach-use-case .uc-guide-split .uc-guide-body { max-width: none; margin: 0; min-width: 0; }
.creator-outreach-use-case .uc-guide-split .uc-guide-body > :first-child { margin-top: 0; }
.creator-outreach-use-case .uc-guide-split .uc-guide-body > ol,
.creator-outreach-use-case #launch-agent .uc-guide-body > ul { list-style: none; padding: 0; display: grid; gap: 16px; counter-reset: guide-step; }
.creator-outreach-use-case .uc-guide-split .uc-guide-body > ol > li,
.creator-outreach-use-case #launch-agent .uc-guide-body > ul > li { margin: 0; padding: 24px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-bg); }
.creator-outreach-use-case .uc-guide-split .uc-guide-body > ol > li::before { counter-increment: guide-step; content: counter(guide-step, decimal-leading-zero); display: block; color: var(--color-brand-dark); font-weight: 600; margin-bottom: 10px; }
.creator-outreach-use-case #launch-agent .uc-guide-body > ul strong { display: block; margin-bottom: 8px; }
@media (min-width: 1024px) {
  .creator-outreach-use-case .uc-guide-split > .container { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: clamp(40px, 5vw, 80px); align-items: start; }
  .creator-outreach-use-case #launch-agent .uc-guide-body > ul { grid-template-columns: repeat(2,minmax(0,1fr)); }
}
@media (max-width: 1023.98px) {
  .creator-outreach-use-case .uc-guide-split .uc-guide-body { margin-top: 32px; }
}

/* Message tutorial: writing guidance beside an immediately readable example. */
.creator-outreach-use-case .uc-message-guide { scroll-margin-top: 150px; }
.creator-outreach-use-case .uc-message-columns { display: grid; gap: 40px; align-items: start; }
.creator-outreach-use-case .uc-message-columns > div { min-width: 0; }
.creator-outreach-use-case .uc-message-instructions h2 { margin-top: 16px; }
.creator-outreach-use-case .uc-message-guide p { line-height: 1.7; }
.creator-outreach-use-case .uc-message-guide h3 { font-size: 22px; line-height: 1.3; margin-bottom: 10px; }
.creator-outreach-use-case .uc-writing-steps { list-style: none; padding: 0; margin: 32px 0 0; counter-reset: writing; }
.creator-outreach-use-case .uc-writing-steps li { position: relative; padding: 22px 0 22px 56px; border-top: 1px solid #6b2d0033; }
.creator-outreach-use-case .uc-writing-steps li::before { counter-increment: writing; content: counter(writing, decimal-leading-zero); position: absolute; left: 0; top: 22px; font-weight: 600; color: #6b2d00; }
.creator-outreach-use-case .uc-dm-example { padding: clamp(24px,3vw,40px); background: #fff8f0; border-radius: var(--radius-lg); border: 1px solid #6b2d0026; }
.creator-outreach-use-case .uc-dm-example > h3 { margin-top: 16px; font-size: 28px; }
.creator-outreach-use-case .uc-dm-example .uc-message { padding: 24px; margin: 24px 0 16px; background: white; border: 1px solid #e9dacb; border-radius: 16px; overflow-wrap: anywhere; }
.creator-outreach-use-case .uc-dm-example .uc-message p + p { margin-top: 18px; }
.creator-outreach-use-case .uc-example-context,
.creator-outreach-use-case .uc-example-note { font-size: 14px; color: #66513e; }
.creator-outreach-use-case .uc-offer-detail { margin-top: 24px; padding-top: 24px; border-top: 1px solid #6b2d0026; }
.creator-outreach-use-case .uc-offer-detail blockquote { margin: 14px 0; font-weight: 500; }
.creator-outreach-use-case .uc-message-tools { display: grid; gap: 24px; margin-top: 48px; }
.creator-outreach-use-case .uc-message-tools article { min-width: 0; padding: 28px; border: 1px solid #6b2d0033; border-radius: var(--radius-lg); }
.creator-outreach-use-case .uc-tool-number { display: block; color: #6b2d00; font-weight: 600; margin-bottom: 18px; }
.creator-outreach-use-case .uc-message-tools code { overflow-wrap: anywhere; }
@media (min-width: 1024px) {
  .creator-outreach-use-case .uc-message-columns { grid-template-columns: repeat(2,minmax(0,1fr)); gap: clamp(40px,5vw,80px); }
  .creator-outreach-use-case .uc-message-tools { grid-template-columns: repeat(3,minmax(0,1fr)); }
}

/* Follow-up tutorial uses a sequence timeline beside the setup instructions. */
.creator-outreach-use-case .uc-followup-guide .uc-dm-example { background: #fff7fa; border-color: #85001026; }
.creator-outreach-use-case .uc-followup-guide .uc-writing-steps li,
.creator-outreach-use-case .uc-followup-guide .uc-message-tools article { border-color: #85001026; }
.creator-outreach-use-case .uc-followup-guide .uc-writing-steps li::before,
.creator-outreach-use-case .uc-followup-guide .uc-tool-number { color: #850010; }
.creator-outreach-use-case .uc-followup-timeline { list-style: none; padding: 0; margin: 28px 0 20px; }
.creator-outreach-use-case .uc-followup-timeline > li { position: relative; padding: 0 0 28px 26px; border-left: 2px solid #d9a2b7; }
.creator-outreach-use-case .uc-followup-timeline > li:last-child { padding-bottom: 0; border-left-color: transparent; }
.creator-outreach-use-case .uc-followup-timeline > li::before { content: ''; position: absolute; left: -7px; top: 5px; width: 12px; height: 12px; border-radius: 50%; background: #850010; }
.creator-outreach-use-case .uc-sequence-label { display: block; margin-bottom: 10px; color: #850010; font-size: 13px; font-weight: 600; }
.creator-outreach-use-case .uc-followup-timeline .uc-message { margin-top: 16px; border-color: #ead6df; }
.creator-outreach-use-case .uc-followup-link { margin-top: 24px; }
.creator-outreach-use-case .uc-followup-link a { text-decoration: underline; text-underline-offset: 4px; }

/* Tracking tutorial: dashboard reading steps, metric definitions, and next actions. */
.creator-outreach-use-case .uc-tracking-panel { background: #fff; border-color: var(--color-border); }
.creator-outreach-use-case .uc-metric-list { margin: 24px 0 0; }
.creator-outreach-use-case .uc-metric-list > div { padding: 20px 0; border-top: 1px solid var(--color-border); }
.creator-outreach-use-case .uc-metric-list dt { font-size: 18px; font-weight: 600; margin-bottom: 8px; }
.creator-outreach-use-case .uc-metric-list dd { margin: 0; line-height: 1.7; color: var(--color-ink-soft); }
.creator-outreach-use-case .uc-response-example { margin-top: 20px; padding: 24px; background: #e6efd3; border-radius: var(--radius-md); }
.creator-outreach-use-case .uc-response-example h3 { margin-top: 12px; }
.creator-outreach-use-case .uc-response-formula { margin: 18px 0; font-size: 20px; }
.creator-outreach-use-case .uc-response-example .uc-example-note { margin-top: 16px; color: var(--color-ink-soft); }
.creator-outreach-use-case .uc-tracking-outcomes { margin-top: 48px; padding-top: 40px; border-top: 1px solid var(--color-border); }
.creator-outreach-use-case .uc-tracking-outcomes .uc-message-tools { margin-top: 24px; }
.creator-outreach-use-case .uc-tracking-outcomes article { background: #fff; border-color: var(--color-border); }
@media (min-width: 1024px) {
  .creator-outreach-use-case .uc-tracking-outcomes .uc-message-tools { grid-template-columns: repeat(2,minmax(0,1fr)); }
}

/* Simplified use-case tutorial: one explanation and one action block per step. */
.creator-outreach-use-case .uc-quick-start { padding-block: 32px; border-bottom: 1px solid var(--color-border); }
.creator-outreach-use-case .uc-simple-nav { display: flex; flex-wrap: wrap; gap: 12px 24px; margin-top: 20px; }
.creator-outreach-use-case .uc-simple-nav a { text-decoration: underline; text-underline-offset: 4px; }
.creator-outreach-use-case .uc-simple-step { scroll-margin-top: 150px; }
.creator-outreach-use-case .uc-simple-step > .container { display: grid; gap: 32px; }
.creator-outreach-use-case .uc-simple-intro, .creator-outreach-use-case .uc-simple-content { min-width: 0; }
.creator-outreach-use-case .uc-simple-intro h2 { margin: 16px 0 20px; }
.creator-outreach-use-case .uc-simple-step p,
.creator-outreach-use-case .uc-simple-step li,
.creator-outreach-use-case .uc-simple-step dd { line-height: 1.7; }
.creator-outreach-use-case .uc-simple-content h3 { font-size: 22px; line-height: 1.3; margin-bottom: 16px; }
.creator-outreach-use-case .uc-simple-content p + p { margin-top: 16px; }
.creator-outreach-use-case .uc-simple-content ul,
.creator-outreach-use-case .uc-simple-content ol { padding-left: 22px; margin: 0 0 24px; }
.creator-outreach-use-case .uc-simple-content li + li { margin-top: 12px; }
.creator-outreach-use-case .uc-simple-tip { margin-top: 24px; padding-top: 20px; border-top: 1px solid #14120f26; }
.creator-outreach-use-case .uc-simple-content .uc-message { margin: 0 0 20px; overflow-wrap: anywhere; }
.creator-outreach-use-case .uc-simple-metrics { margin: 0; }
.creator-outreach-use-case .uc-simple-metrics > div + div { margin-top: 24px; }
.creator-outreach-use-case .uc-simple-metrics dt { font-weight: 600; margin-bottom: 8px; }
.creator-outreach-use-case .uc-simple-metrics dd { margin: 0; }
@media (min-width: 1024px) {
  .creator-outreach-use-case .uc-simple-step > .container { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: clamp(40px, 5vw, 80px); }
}

/* A compact tutorial with enough examples and product context to act on. */
.creator-outreach-use-case .uc-balanced-visual { margin: 28px 0 0; }
.creator-outreach-use-case .uc-balanced-visual img { display: block; width: 100%; max-height: 290px; object-fit: contain; }
.creator-outreach-use-case .uc-inline-example { margin-top: 28px; padding-top: 24px; border-top: 1px solid #14120f26; }
.creator-outreach-use-case .uc-inline-example h3 { font-size: 22px; line-height: 1.3; margin-bottom: 12px; }
.creator-outreach-use-case .uc-inline-example p + p { margin-top: 12px; }
.creator-outreach-use-case .uc-balanced-list { padding-left: 22px; margin: 28px 0 0; }
.creator-outreach-use-case .uc-balanced-list li + li { margin-top: 16px; }
.creator-outreach-use-case .uc-simple-content p + h3 { margin-top: 28px; }
.creator-outreach-use-case .uc-simple-content a { text-decoration: underline; text-underline-offset: 4px; }

.creator-outreach-use-case .uc-comparison-note { max-width: 850px; margin: 24px auto 0; text-align: center; line-height: 1.7; color: var(--color-ink-soft); }

.creator-outreach-use-case .uc-quick-start .uc-heading { margin-bottom: 28px; }
.creator-outreach-use-case .uc-quick-start .uc-simple-nav { justify-content: center; }
.creator-outreach-use-case #outreach-scenarios .uc-card h3,
.creator-outreach-use-case #features .uc-card h3 { font-size: 24px; line-height: 1.3; }

/* Inherit the same responsive H1 scale as AI Creator Search. */
.creator-outreach-use-case .uc-hero-title > span { display: block; }

/* Creator outreach hero — shared blush and burgundy brand palette. */
.creator-outreach-use-case .feature-hero { background: #350d20; color: #f5c3d6; }
.creator-outreach-use-case .feature-hero h1 { color: #f5c3d6; }
.creator-outreach-use-case .feature-hero .hero-sub { color: #f5c3d6; }
.creator-outreach-use-case .feature-hero .feature-hero-badge { background: #ffe4ee; }
.creator-outreach-use-case .feature-hero .btn-hero-primary { background: #f5c3d6; border-color: #f5c3d6; color: #350d20; }
.creator-outreach-use-case .feature-hero .btn-hero-primary:hover,
.creator-outreach-use-case .feature-hero .btn-hero-primary:focus-visible { background: #ffe4ee; border-color: #ffe4ee; }
.creator-outreach-use-case .feature-hero .btn-hero-secondary { background: transparent; border-color: #f5c3d6; color: #f5c3d6; }
.creator-outreach-use-case .feature-hero .btn-hero-secondary:hover,
.creator-outreach-use-case .feature-hero .btn-hero-secondary:focus-visible { background: #f5c3d61a; }
.creator-outreach-use-case .feature-hero .btn .ai-arrow { color: inherit; }

/* Give the complete opening phrase its own desktop line without shrinking H1. */
@media (min-width: 1024px) {
  .creator-outreach-use-case .feature-hero-copy { flex: 0 0 auto; width: max-content; max-width: none; }
  .creator-outreach-use-case .uc-hero-title > span { white-space: nowrap; }
  .creator-outreach-use-case .feature-hero-visual { flex: 1 1 0; }
}

/* Three illustrated pain cards, matching the creator-search feature cards. */
.creator-outreach-use-case .uc-pain-cards .feature-card--teal { background: #0C7D8E; color: #fff; }
.creator-outreach-use-case .uc-pain-cards .feature-card--teal p { color: rgba(255, 255, 255, 0.85); }
.creator-outreach-use-case .uc-pain-cards .feature-card--lavender { background: #C7B5FB; color: var(--color-ink); }
.creator-outreach-use-case .uc-pain-cards .feature-card--lime { background: #CBF484; color: var(--color-ink); }
.creator-outreach-use-case .uc-pain-cards :is(.feature-card--lavender, .feature-card--lime) p { color: var(--color-ink-soft); }
.creator-outreach-use-case .uc-pain-cards .feature-card p { font-size: 15px; }
/* Scale the entire illustration from a 410px canvas, including type and stickers. */
.creator-outreach-use-case .uc-pain-art { min-width: 0; container-type: inline-size; margin-bottom: 36px; padding-top: 8px; line-height: 1.5; }
.creator-outreach-use-case .uc-mini-window { background: #fff; color: #242424; border-radius: 3.90244cqw; padding: 4.39024cqw; min-height: 56.09756cqw; box-shadow: 0 2.43902cqw 5.85366cqw #0000000c; }
.creator-outreach-use-case .uc-mini-title { display: flex; justify-content: space-between; gap: 2.43902cqw; font-size: 3.41463cqw; font-weight: 600; padding-bottom: 3.90244cqw; border-bottom: 0.2439cqw solid #eee; }
.creator-outreach-use-case .uc-mini-row { display: flex; align-items: center; gap: 1.95122cqw; padding-top: 3.90244cqw; font-size: 2.92683cqw; flex-wrap: nowrap; }
.creator-outreach-use-case .uc-mini-avatar { display: block; flex: 0 0 7.80488cqw; width: 7.80488cqw; height: 7.80488cqw; max-height: none; border-radius: 50%; object-fit: cover; }
.creator-outreach-use-case .uc-mini-tag { margin-left: auto; border-radius: 4.87805cqw; background: #fff0dc; color: #7d4900; padding: 1.21951cqw 1.95122cqw; font-size: 2.43902cqw; }
.creator-outreach-use-case .uc-art-sticker { display: inline-block; position: relative; margin: -0.97561cqw 1.95122cqw 0; padding: 2.43902cqw 3.41463cqw; border-radius: 2.43902cqw; background: #ffe4ee; color: #850010; font-size: 2.92683cqw; font-weight: 600; transform: rotate(-3deg); white-space: nowrap; }
.creator-outreach-use-case .uc-sticker-emoji { display: inline-block; font-size: 5.85366cqw; line-height: 1; vertical-align: middle; margin-right: 0.97561cqw; }
.creator-outreach-use-case .uc-reminder { display: flex; gap: 2.43902cqw; align-items: flex-start; margin-top: 3.65854cqw; font-size: 2.92683cqw; }
.creator-outreach-use-case .uc-reminder-bell { display: block; flex: 0 0 4.87805cqw; width: 4.87805cqw; height: 4.87805cqw; object-fit: contain; }
.creator-outreach-use-case .uc-reminder small { display: block; margin-top: 0.97561cqw; color: #777; font-size: 2.43902cqw; }
.creator-outreach-use-case .uc-campaign-images { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.92683cqw; margin-top: 4.87805cqw; }
.creator-outreach-use-case .uc-campaign-images img { display: block; width: 100%; height: auto; aspect-ratio: 815 / 401; object-fit: cover; border-radius: 2.43902cqw; }
.creator-outreach-use-case .uc-campaign-stat { position: relative; overflow: hidden; border-radius: 2.43902cqw; }
.creator-outreach-use-case .uc-campaign-stat::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(0,0,0,.38), transparent); pointer-events: none; }
.creator-outreach-use-case .uc-campaign-stat-copy { position: absolute; inset: 0; z-index: 1; padding: 2.92683cqw; display: flex; flex-direction: column; justify-content: center; gap: 0.97561cqw; color: #fff; }
.creator-outreach-use-case .uc-campaign-stat-copy span { font-size: 3.17073cqw; line-height: 1.2; color: #fff; }
.creator-outreach-use-case .uc-campaign-stat-copy strong { font-size: 7.80488cqw; line-height: 1; color: #fff; }
.creator-outreach-use-case .uc-mini-sources { display: flex; gap: 1.95122cqw; flex-wrap: nowrap; margin-top: 4.39024cqw; font-size: 2.68293cqw; color: #666; }
.creator-outreach-use-case .uc-mini-sources span { padding: 0.97561cqw 1.95122cqw; border: 0.2439cqw dashed #ccc; border-radius: 1.46341cqw; }
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .creator-outreach-use-case .uc-pain-cards .feature-card { padding: 22px; }
}

/* Auto-playing CardSwap for the static-site hero (60px / 70px stack offsets). */
.creator-outreach-use-case .uc-card-swap { position: relative; height: 600px; min-width: 0; perspective: 1400px; overflow: clip; }
.creator-outreach-use-case .uc-swap-stage { position: absolute; inset: 0; }
.creator-outreach-use-case .uc-swap-card { position: absolute; left: 8px; top: 175px; width: calc(100% - 136px); height: 340px; margin: 0; border: 1px solid #85001033; border-radius: 20px; overflow: hidden; background: #fff8fb; box-shadow: 0 16px 35px #65000c1a; transform-origin: center; transition: transform 850ms cubic-bezier(.22,.8,.25,1), opacity 450ms ease; }
.creator-outreach-use-case .uc-swap-card[data-slot="0"] { z-index: 3; transform: translate3d(0,0,0) rotate(-4deg); }
.creator-outreach-use-case .uc-swap-card[data-slot="1"] { z-index: 2; transform: translate3d(60px,-70px,-45px) rotate(0deg); background: #eee7ff; }
.creator-outreach-use-case .uc-swap-card[data-slot="2"] { z-index: 1; transform: translate3d(120px,-140px,-90px) rotate(4deg); background: #ecf8d9; }
.creator-outreach-use-case .uc-swap-card.is-leaving { transform: translate3d(0,240px,40px) rotate(-10deg); opacity: 0; }
.creator-outreach-use-case .uc-swap-card figcaption { display: flex; align-items: center; gap: 10px; padding: 16px; border-bottom: 1px solid #8500101a; font-size: 14px; font-weight: 600; color: #850010; }
.creator-outreach-use-case .uc-swap-card figcaption span { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.creator-outreach-use-case .uc-swap-card img { display: block; width: 100%; height: calc(100% - 54px); padding: 16px; object-fit: contain; }
@media (min-width: 1024px) and (max-width: 1599.98px) {
  .creator-outreach-use-case .uc-card-swap { overflow: visible; }
  .creator-outreach-use-case .uc-swap-card { width: calc(100% - 48px); height: 300px; top: 160px; }
  .creator-outreach-use-case .uc-swap-card[data-slot="1"] { transform: translate3d(24px,-50px,-45px); }
  .creator-outreach-use-case .uc-swap-card[data-slot="2"] { transform: translate3d(48px,-100px,-90px) rotate(4deg); }
}
@media (max-width: 1023.98px) {
  .creator-outreach-use-case .uc-card-swap { width: min(100%,560px); height: 460px; margin-inline: auto; flex: none; }
  .creator-outreach-use-case .uc-swap-card { width: calc(100% - 76px); height: 280px; top: 135px; }
  .creator-outreach-use-case .uc-swap-card[data-slot="1"] { transform: translate3d(30px,-50px,-45px); }
  .creator-outreach-use-case .uc-swap-card[data-slot="2"] { transform: translate3d(60px,-100px,-90px) rotate(4deg); }
}
@media (prefers-reduced-motion: reduce) {
  .creator-outreach-use-case .uc-swap-card { transition: none; }
}
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .creator-outreach-use-case .feature-hero-inner { flex-direction: column; align-items: flex-start; gap: 24px; }
  .creator-outreach-use-case .uc-card-swap { width: 560px; max-width: 100%; height: 500px; flex: none; align-self: center; }
}

/* DM Agent artwork: a soft entrance followed by a restrained float. */
.creator-outreach-use-case .uc-banner-art {
  min-width: 0;
  margin-top: 0;
  padding: 20px 0;
  animation: uc-banner-enter 900ms cubic-bezier(.22, 1, .36, 1) both;
}
.creator-outreach-use-case .uc-banner-image {
  display: block;
  width: 100%;
  height: auto;
  object-fit: contain;
  animation: uc-banner-float 7s ease-in-out 900ms infinite;
}
@keyframes uc-banner-enter {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes uc-banner-float {
  0%, 100% { transform: translateY(0) rotate(-.4deg); }
  50% { transform: translateY(-12px) rotate(.4deg); }
}
@media (max-width: 1279.98px) {
  .creator-outreach-use-case .uc-banner-art { width: min(100%, 609px); flex: none; align-self: center; margin-inline: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .creator-outreach-use-case .uc-banner-art,
  .creator-outreach-use-case .uc-banner-image { animation: none; }
}

.creator-outreach-use-case .uc-layered-banner { min-width: 0; margin: 0; padding: 16px 0; }
.creator-outreach-use-case .uc-banner-deck { position: relative; width: 100%; aspect-ratio: 609 / 500; isolation: isolate; transform: scale(.9); transform-origin: center; }
.creator-outreach-use-case .uc-deck-card { position: absolute; width: 68.35%; height: auto; left: 0; top: 44.57%; filter: drop-shadow(0 12px 14px #16040b30); transition: transform 550ms cubic-bezier(.22,.8,.25,1), opacity 250ms ease; }
.creator-outreach-use-case .uc-deck-card[data-position="0"] { z-index: 1; transform: translate(0,0); }
.creator-outreach-use-case .uc-deck-card[data-position="1"] { z-index: 2; transform: translate(23.08%,-32.14%); }
.creator-outreach-use-case .uc-deck-card[data-position="2"] { z-index: 3; transform: translate(46.15%,-64.29%); }
.creator-outreach-use-case .uc-deck-card.is-exiting { transform: translate(50%,-84%) rotate(4deg); opacity: 0; }
.creator-outreach-use-case .uc-agent-badge { position: absolute; z-index: 5; left: 46.14%; top: 0; width: 30.54%; height: auto; }
@media (max-width: 1279.98px) {
  .creator-outreach-use-case .uc-layered-banner { width: min(100%,609px); flex: none; align-self: center; margin-inline: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .creator-outreach-use-case .uc-deck-card { transition: none; }
}

/* Match the fixed Agent badge to the pink card when it reaches the front. */
.creator-outreach-use-case .uc-agent-badge { background-color: #0C7D8E; border-radius: 999px; transition: background-color 350ms ease; }
.creator-outreach-use-case .uc-banner-deck:has(.uc-deck-card[src*="dm-banner-card-2.svg"][data-position="2"]) .uc-agent-badge { background-color: #4D3FE9; }
.creator-outreach-use-case .uc-banner-deck:has(.uc-deck-card[src*="dm-banner-card-1.svg"][data-position="2"]) .uc-agent-badge { background-color: #572077; }
@media (prefers-reduced-motion: reduce) {
  .creator-outreach-use-case .uc-agent-badge { transition: none; }
}

/* Workflow: a text-led step menu paired with a single product visual. */
.creator-outreach-use-case .uc-workflow-showcase { background: var(--workflow-background, #FFE4EE); }
.uc-workflow-layout { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: 64px; align-items: start; }
.uc-workflow-menu { min-width: 0; }
.uc-workflow-item { position: static; padding: 56px 0; border-bottom: 1px solid #14120f26; scroll-margin-top: 150px; }
.uc-workflow-item:first-child { padding-top: 0; }
.uc-workflow-item h3 { margin: 0; }
 .uc-workflow-title { display: flex; align-items: baseline; gap: 16px; padding: 0 0 24px; color: #850010; font: 500 32px/1.2 'Tomato Grotesk', sans-serif; }
.uc-workflow-number { font: 500 12px/1.5 Inter, sans-serif; }
.uc-workflow-copy { padding: 0 0 26px 32px; font-size: 16px; line-height: 1.65; }
.uc-workflow-copy[hidden] { display: none; }
.uc-workflow-copy p { margin: 0 0 16px; }
.uc-workflow-copy .uc-simple-tip { margin-top: 16px; padding-top: 0; border: 0; font-size: 14px; color: #514C46; }

.uc-workflow-more > div { padding-top: 20px; }
.uc-workflow-more h3 { font: 600 18px/1.4 Inter, sans-serif; margin: 20px 0 12px; }
.uc-workflow-more :is(ul,ol) { padding-left: 22px; }
.uc-workflow-more li + li { margin-top: 8px; }
.uc-workflow-media { position: sticky; top: 140px; min-width: 0; }
.uc-workflow-image { margin: 0; padding: 0; overflow: hidden; background: #F5C3D6; border-radius: 28px; animation: uc-workflow-reveal 350ms ease both; }
.uc-workflow-image[hidden] { display: none; }
.uc-workflow-image img { display: block; width: 100%; height: auto; aspect-ratio: 2017 / 1548; object-fit: cover; }
.uc-workflow-image figcaption { display: flex; align-items: center; gap: 20px; border-top: 1px solid #85001026; padding-top: 20px; margin-top: 24px; color: #850010; font-size: 14px; font-weight: 600; }
.uc-workflow-image figcaption span { font-size: 12px; white-space: nowrap; }
.uc-workflow-image[data-workflow-image="1"], .uc-workflow-image[data-workflow-image="5"] { background: #CBF484; }
.uc-workflow-image[data-workflow-image="2"], .uc-workflow-image[data-workflow-image="4"] { background: #C7B5FB; }
.uc-workflow-image[data-workflow-image="3"] { background: #FF9E5C; }
@keyframes uc-workflow-reveal { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
@media (max-width: 1023.98px) {
 .uc-workflow-layout { grid-template-columns: 1fr; gap: 0; }
 .uc-workflow-media { display: none; }
 .uc-workflow-title { font-size: 26px; }
 .uc-workflow-copy { padding-left: 0; }
 .uc-workflow-image { margin-top: 24px; border-radius: 20px; }

}
@media (prefers-reduced-motion: reduce) { .uc-workflow-image { animation: none; } }

/* Matching Figma photos anchor both comparison cards. */
.creator-outreach-use-case .uc-compare-card { display: flex; flex-direction: column; border-radius: 40px; }
.creator-outreach-use-case .uc-compare-photo { margin-top: auto; padding-top: 28px; }
.creator-outreach-use-case .uc-compare-photo img { display: block; width: 100%; height: auto; aspect-ratio: 667 / 337; object-fit: cover; border-radius: 48px; }

.creator-outreach-use-case .uc-workflow-label { margin: 0 0 16px; }
.creator-outreach-use-case .uc-workflow-copy { padding-left: 0; }



/* Step tips use the shared Lucide Lightbulb outline. */
.creator-outreach-use-case .uc-tip-box { display: flex; align-items: flex-start; gap: 12px; margin: 24px 0; padding: 20px; border: 0; border-radius: 16px; background: rgba(255,255,255,.65); color: #514C46; }
.creator-outreach-use-case .uc-tip-icon { flex: 0 0 24px; color: #850010; margin-top: 1px; }
.creator-outreach-use-case .uc-tip-box p { margin: 0; font-size: 14px; line-height: 1.65; }
.creator-outreach-use-case .uc-tip-box strong { color: #850010; }

/* Step heading icons from @hugeicons/core-free-icons. */
.creator-outreach-use-case .uc-workflow-title { align-items: flex-start; gap: 12px; }
.creator-outreach-use-case .uc-workflow-title-icon { display: block; flex: 0 0 1em; width: 1em; height: 1em; margin-top: .1em; color: inherit; }
.creator-outreach-use-case .uc-workflow-title > span { min-width: 0; }

.creator-outreach-use-case .feature-hero .dm-agent-hero-icon-badge { background: transparent; padding: 0; }
.creator-outreach-use-case .dm-agent-hero-icon-badge img { display: block; width: 100%; height: 100%; object-fit: contain; }

/* Outreach scenarios: editorial rows with example messages. */
.creator-outreach-use-case .uc-scenario-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(32px, 5vw, 80px); align-items: center; padding: 48px 0; border-top: 1px solid #14120f20; }
.creator-outreach-use-case .uc-scenario-row:last-child { padding-bottom: 0; }
.creator-outreach-use-case .uc-scenario-copy h3 { font: 500 clamp(26px, 2.5vw, 36px)/1.2 'Tomato Grotesk', sans-serif; margin: 12px 0 20px; }
.creator-outreach-use-case .uc-scenario-copy > p:not(.eyebrow) { font-size: 16px; line-height: 1.65; max-width: 470px; }
.creator-outreach-use-case .uc-scenario-audience { margin-top: 24px; color: #514c46; }
.creator-outreach-use-case .uc-scenario-audience strong { display: block; color: #14120f; margin-bottom: 4px; }
@media (max-width: 767.98px) {
 .creator-outreach-use-case .uc-scenario-row { grid-template-columns: minmax(0, 1fr); gap: 24px; padding: 32px 0; }
 }

.creator-outreach-use-case .uc-scenario-visual { min-width: 0; overflow: hidden; border-radius: 28px; }
.creator-outreach-use-case .uc-scenario-visual img { display: block; width: 100%; height: auto; aspect-ratio: 2017 / 1548; object-fit: cover; }

/* Full-bleed scenario panels reuse the AI Search split component. */
.creator-outreach-use-case .uc-scenario-split .split-panel-color { background: #0C7D8E; }
.creator-outreach-use-case .uc-scenario-split .split-panel-inner:nth-child(2) .split-panel-color { background: #C7B5FB; }
.creator-outreach-use-case .uc-scenario-split .uc-split-label { margin: 0 0 24px; color: #F5C3D6; font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.creator-outreach-use-case .uc-scenario-split .ai-eyebrow { margin: 0 0 24px; }
.creator-outreach-use-case .uc-scenario-split .ai-copy-text { margin: 0; }
.creator-outreach-use-case .uc-scenario-split .btn { width: auto; max-width: 100%; min-height: 56px; height: auto; padding: 16px 24px; gap: 24px; margin-top: 8px; align-self: flex-start; white-space: nowrap; }

/* Coordinated teal/lime and lavender/plum scenario palettes. */
.creator-outreach-use-case .uc-scenario-split .uc-split-label,
.creator-outreach-use-case .uc-scenario-split .ai-eyebrow { color: #ECF8D9; }
.creator-outreach-use-case .uc-scenario-split .ai-copy-text { color: #FFFFFF; }
.creator-outreach-use-case .uc-scenario-split .btn { background: #CBF484; color: #143D36; }
.creator-outreach-use-case .uc-scenario-split .btn .ai-arrow { color: inherit; }
.creator-outreach-use-case .uc-scenario-split .btn:hover { background: #DEFAA9; }
.creator-outreach-use-case .uc-scenario-split .split-panel-inner:nth-child(2) :is(.uc-split-label, .ai-eyebrow) { color: #572077; }
.creator-outreach-use-case .uc-scenario-split .split-panel-inner:nth-child(2) .ai-copy-text { color: #352442; }
.creator-outreach-use-case .uc-scenario-split .split-panel-inner:nth-child(2) .btn { background: #572077; color: #F5E8FF; }
.creator-outreach-use-case .uc-scenario-split .split-panel-inner:nth-child(2) .btn:hover { background: #43165E; }

/* Match the reference panel's icon, display heading and pill hierarchy. */
.creator-outreach-use-case .uc-scenario-split .ai-copy { gap: 24px; }
.creator-outreach-use-case .uc-scenario-split .ai-icon-badge { background: #FFC1D7; margin: 0; }
.creator-outreach-use-case .uc-scenario-split .ai-eyebrow { margin: 0; font-family: "Tomato Grotesk SemiBold", Arial, sans-serif; font-size: clamp(36px, 3.6vw, 64px); line-height: 1.12; letter-spacing: -.02em; }
.creator-outreach-use-case .uc-scenario-split .ai-copy-bottom { gap: 28px; }
.creator-outreach-use-case .uc-scenario-split .btn { margin-top: 0; padding: 16px 28px; min-height: 59px; border-radius: 60px; }
@media (max-width: 767.98px) { .creator-outreach-use-case .uc-scenario-split .ai-eyebrow { font-size: 36px; } }

.creator-outreach-use-case .uc-final .section-sub { max-width: 860px; margin-left: auto; margin-right: auto; }

@media (max-width: 1023.98px) {
  .creator-outreach-use-case .uc-workflow-item > .uc-workflow-image { margin: 0 0 24px; }
}

/* Keep the longer setup-guide CTA comfortable on narrow screens. */
@media (max-width: 639.98px) {
  .creator-outreach-use-case .uc-final .hero-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    width: 100%;
    max-width: 360px;
    margin-inline: auto;
  }
  .creator-outreach-use-case .uc-final .hero-actions .btn {
    flex: none;
    width: 100%;
    min-height: 52px;
    padding: 14px 22px;
    gap: 16px;
    justify-content: center;
    white-space: normal;
    text-align: center;
  }
  .creator-outreach-use-case .uc-final .hero-actions .ai-arrow { flex-shrink: 0; }
}

.creator-outreach-use-case .uc-scenario-split .uc-affiliate-icon { background: #FFC1D7; padding: 0; overflow: hidden; }
.creator-outreach-use-case .uc-affiliate-icon img { display: block; width: 100%; height: 100%; object-fit: contain; border-radius: inherit; }

@media (max-width: 767.98px) {
  .creator-outreach-use-case .feature-hero-copy > .uc-layered-banner {
    width: 100%;
    margin: 24px auto;
  }
  .creator-outreach-use-case .feature-hero .hero-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    width: 100%;
  }
  .creator-outreach-use-case .feature-hero .hero-actions .btn {
    flex: none;
    width: 100%;
    min-height: 52px;
    padding: 14px 22px;
    gap: 16px;
    white-space: normal;
    text-align: center;
    justify-content: center;
  }
  .creator-outreach-use-case .feature-hero .hero-actions .ai-arrow { flex-shrink: 0; }
}

.creator-outreach-use-case .uc-scenario-split .uc-reconnect-icon { background: transparent; padding: 0; overflow: hidden; }
.creator-outreach-use-case .uc-reconnect-icon img { display: block; width: 100%; height: 100%; object-fit: contain; border-radius: inherit; }

.creator-outreach-use-case .uc-workflow-copy .uc-inline-example { border-top: 0; margin-top: 24px; padding-top: 0; }
.creator-outreach-use-case .uc-workflow-more > div { padding-top: 0; }
.creator-outreach-use-case .uc-workflow-more > div > :first-child { margin-top: 0; }
.creator-outreach-use-case .uc-workflow-copy .uc-inline-example > h3:first-child { margin-top: 0; }
