/* =============================================================================
   GUIDANCE AGENT HERO
   Every class is ga-hero-*, every attribute is data-ga-hero-*.
   Fonts: DM Sans (500/600) + DM Mono (400) — already loaded by the site.
   ========================================================================== */


/* --- root + tokens ---------------------------------------------------------- */
.ga-hero {
  --ga-hero-header: 80px;
  --ga-hero-avail: calc(100svh - var(--ga-hero-header));
  --ga-hero-ease: cubic-bezier(.2, .7, .2, 1);
  --ga-hero-blur: blur(30px) saturate(165%);
  --ga-hero-ink: #24242A;
  --ga-hero-accent: #205AE3;
  /* height of the collapsed "Done" row + its gap. The hint-2 card is parked
     this far down while the Done row is on screen and translates up by exactly
     this much when it leaves, so the shift is never a layout change. */
  --ga-hero-done-h: 56px;
  --ga-hero-done-gap: 8px;
  display: block;
  padding-top: var(--ga-hero-header);
  color: var(--ga-hero-ink);
  font-family: 'DM Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  overflow-x: clip;
}
.ga-hero *, .ga-hero *::before, .ga-hero *::after { box-sizing: border-box; }
/* base link colour only applies to unclassed links, so it never swallows a
   prefixed link component the way a bare `.ga-hero a` would */
.ga-hero a:not([class]) { color: var(--ga-hero-ink); text-decoration: none; }
.ga-hero a:not([class]):hover, .ga-hero a:not([class]):focus { color: var(--ga-hero-accent); }

/* dark glass (default) */
.ga-hero[data-ga-hero-theme="dark"] {
  --ga-hero-fill: rgba(26, 28, 38, 0.30);
  --ga-hero-border: rgba(255, 255, 255, 0.17);
  --ga-hero-inset: rgba(255, 255, 255, 0.11);
  --ga-hero-shadow: 0 22px 48px rgba(6, 6, 12, 0.26);
  --ga-hero-btn: rgba(30, 32, 42, 0.34);
  --ga-hero-btn-border: rgba(255, 255, 255, 0.19);
  --ga-hero-text: #EDEFF4;
  --ga-hero-strong: #FFFFFF;
  --ga-hero-label: #C2C9D6;
  --ga-hero-muted: #D9DCE4;
  --ga-hero-tile: rgba(255, 255, 255, 0.10);
  --ga-hero-tile-border: rgba(255, 255, 255, 0.12);
  --ga-hero-chip: rgba(255, 255, 255, 0.15);
  --ga-hero-chip-text: #E9ECF2;
  --ga-hero-key: rgba(147, 222, 253, 0.20);
  --ga-hero-glow: rgba(147, 222, 253, 0.52);
  --ga-hero-hl: rgba(147, 222, 253, 0.16);
  --ga-hero-hl-border: rgba(147, 222, 253, 0.55);
  --ga-hero-ok: rgba(126, 214, 168, 0.24);
  --ga-hero-ok-text: #CBEEDB;
  --ga-hero-ok-border: rgba(126, 214, 168, 0.55);
  --ga-hero-mark: none;
  --ga-hero-tl-label: rgba(255, 255, 255, 0.5);
  --ga-hero-tl-strong: #FFFFFF;
  --ga-hero-tl-border: rgba(255, 255, 255, 0.30);
  --ga-hero-tl-track: rgba(255, 255, 255, 0.18);
}
/* white glass */
.ga-hero[data-ga-hero-theme="light"] {
  --ga-hero-fill: rgba(255, 255, 255, 0.90);
  --ga-hero-border: rgba(255, 255, 255, 0.92);
  --ga-hero-inset: rgba(255, 255, 255, 0.95);
  --ga-hero-shadow: 0 22px 48px rgba(10, 26, 72, 0.24);
  --ga-hero-btn: rgba(255, 255, 255, 0.90);
  --ga-hero-btn-border: rgba(255, 255, 255, 0.92);
  --ga-hero-text: #1E1F27;
  --ga-hero-strong: #0E0E13;
  --ga-hero-label: #5A616C;
  --ga-hero-muted: #3C3C47;
  --ga-hero-tile: rgba(36, 36, 42, 0.04);
  --ga-hero-tile-border: rgba(36, 36, 42, 0.10);
  --ga-hero-chip: rgba(36, 36, 42, 0.07);
  --ga-hero-chip-text: #3C3C47;
  --ga-hero-key: rgba(32, 90, 227, 0.12);
  --ga-hero-glow: rgba(32, 89, 227, 0.72);
  --ga-hero-hl: rgba(32, 90, 227, 0.09);
  --ga-hero-hl-border: rgba(32, 90, 227, 0.45);
  --ga-hero-ok: rgba(46, 160, 100, 0.14);
  --ga-hero-ok-text: #155B39;
  --ga-hero-ok-border: rgba(46, 160, 100, 0.45);
  --ga-hero-mark: invert(1) brightness(0.18);
  --ga-hero-tl-label: rgba(36, 36, 42, 0.5);
  --ga-hero-tl-strong: #20212B;
  --ga-hero-tl-border: rgba(36, 36, 42, 0.32);
  --ga-hero-tl-track: rgba(36, 36, 42, 0.16);
}

/* --- intro ------------------------------------------------------------------
   The intro (eyebrow, h1, CTA, lede) is deliberately NOT in this stylesheet or
   the markup below. It is the page's own Hero Component - the block the live
   page already renders directly above this section - so shipping it inside the
   CDN bundle would duplicate the copy. It would also put variable-height content
   inside the mount, and the mount's reserve has to be exact to avoid a layout
   shift; the intro's height changes with the h1's wrap, so it cannot be
   expressed as a calc(). With it gone the track is the first thing in the
   section and the root's only top offset is the nav height.
   -------------------------------------------------------------------------- */

/* --- scroll-morph stage -----------------------------------------------------
   The stage is authored at its full-bleed size and sized down by paintMorph()
   on mount. The morph writes width/height/left/radius/gap on this element and
   the faces column follows - it is a layout resize, not a transform scale.
   -------------------------------------------------------------------------- */
/* These two heights are the no-JS fallback. layout() writes the real one inline
   from the branch it actually took, because these are media queries and that
   branch is picked from the sticky element's clientWidth. The values below mirror
   layout()'s formula exactly - 1 + (dist - lead) + hold + dist - so a no-JS render
   gets the same runway the script would have given it. */
.ga-hero-track { position: relative; margin-top: 0; margin-bottom: 80px; height: calc(var(--ga-hero-avail) * 1.95); }
.ga-hero-sticky { position: sticky; top: var(--ga-hero-header); height: var(--ga-hero-avail); overflow: hidden; }
.ga-hero-stage {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  display: flex;
  gap: 2px;
  border-radius: 0;
  overflow: hidden;
}
/* No background. The gap between the tiles and the panel, and the corner
   notches at rest, show the page through - which is what the live page does.
   Painting the stage dark turns both into black lines. The panel carries its
   own fill; only the gutters were ever meant to be the page. */
/* These are the FINISHED values, which is what reduced motion and no-JS both
   get. The rest state is written by paintMorph() on mount, so the stage is a
   real layout box that resizes - never a fixed box that gets scaled. The
   scaled version had to over-bleed the sticky by its corner radius on every
   side for the corners to sit outside the clip at full bleed, and that offset
   is what pushed the pill and the face labels off the viewport edge at peak
   morph.

   No will-change on the stage, and none on anything else that rides the scroll.
   Promoting the stage mid-scroll costs twice: WebKit recomposites the promoted
   layer on every geometry write of the morph, and the promotion isolates the
   grain layer, whose mix-blend-mode: overlay then blends against the new group
   instead of the wall behind it. Both effects ride the scroll attribute, so
   both appeared mid-scroll and reverted on settle - which reads as a rendering
   glitch rather than as a CSS rule. */

/* --- face tiles -------------------------------------------------------------
   Column width comes from its own aspect-ratio, so two near-square tiles fall
   out of the stage height with no measurement.
   -------------------------------------------------------------------------- */
/* The aspect-ratio is the no-JS and reduced-motion fallback only. paintMorph()
   writes an explicit width, and an explicit width plus height beats
   aspect-ratio, so the ratio never fights the morph. Without it the column is
   flex: none with no width, its only children are absolutely positioned, and
   it collapses to zero - a hero with no faces. */
/* gap and radius are the FULL-BLEED values, matching the stage's own. The rest
   values (24 / 20) are written by paintMorph(), so the stylesheet has to carry
   the other end - otherwise reduced motion and no-JS render a full-bleed stage
   with rest-state gutters and rounded corners, and the page shows through both. */
.ga-hero-faces { flex: none; height: 100%; aspect-ratio: 0.5; display: flex; flex-direction: column; gap: 2px; }
.ga-hero-face { position: relative; flex: 1 1 0; min-height: 0; overflow: hidden; border-radius: 0; background: #D5DCE3; }
/* the crop is object-fit/object-position on the <video> itself, so the poster
   is cropped by the same rule — posters ship as the full uncropped frame */
/* Twice the tile in each axis, offset per face, so the crop is a tight
   head-and-shoulders rather than the full frame shrunk to fit. A 100%/100%
   cover of a 16:9 source in a 1.56:1 tile barely crops at all, which is what
   made the faces read as distant. */
.ga-hero-clip { position: absolute; width: 200%; height: 200%; object-fit: cover; display: block; }
.ga-hero-face-agent .ga-hero-clip { left: -16.1%; top: -12.0%; object-position: 33% 22%; }
.ga-hero-face-customer .ga-hero-clip { left: -76.0%; top: -20.0%; object-position: 63% 25%; }
/* graduated frost under the name plate */
.ga-hero-frost { position: absolute; left: 0; bottom: 0; width: 52%; height: 28%; pointer-events: none; }
.ga-hero-frost span { position: absolute; inset: 0; }
.ga-hero-frost span:nth-child(1) { -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); -webkit-mask-image: radial-gradient(ellipse 100% 100% at 0% 100%, #000 52%, transparent 84%); mask-image: radial-gradient(ellipse 100% 100% at 0% 100%, #000 52%, transparent 84%); }
.ga-hero-frost span:nth-child(2) { -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); -webkit-mask-image: radial-gradient(ellipse 100% 100% at 0% 100%, #000 22%, transparent 48%); mask-image: radial-gradient(ellipse 100% 100% at 0% 100%, #000 22%, transparent 48%); }
.ga-hero-frost span:nth-child(3) { background: radial-gradient(ellipse 100% 100% at 0% 100%, rgba(8, 8, 12, 0.52) 0%, rgba(8, 8, 12, 0.28) 34%, rgba(8, 8, 12, 0) 68%); }
.ga-hero-name { position: absolute; left: 16px; right: 16px; bottom: 14px; display: flex; align-items: center; gap: 7px; }
.ga-hero-name span { flex: none; font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: #FFFFFF; text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); }
.ga-hero-dot { flex: none; width: 10px; height: 10px; border-radius: 999px; }
.ga-hero-face-agent .ga-hero-dot { background: linear-gradient(140deg, #6E95F0, #AE95DD); }
.ga-hero-face-customer .ga-hero-dot { background: linear-gradient(140deg, #FCB56C, #F87563); }
.ga-hero-pill { position: absolute; left: 14px; top: 14px; display: flex; align-items: center; gap: 10px; padding: 7px 16px 7px 7px; border-radius: 999px; background: rgba(20, 20, 26, 0.55); border: 1px solid rgba(255, 255, 255, 0.14); -webkit-backdrop-filter: blur(14px) saturate(160%); backdrop-filter: blur(14px) saturate(160%); }
.ga-hero-pill-av { position: relative; flex: none; display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 999px; background: #F2F1EE; font-size: 14px; font-weight: 600; color: #3C3C47; }
.ga-hero-live { position: absolute; right: 1px; bottom: 0; width: 8px; height: 8px; border-radius: 999px; background: #4ADE80; box-shadow: 0 0 0 1.5px rgba(20, 20, 26, 0.55); }
.ga-hero-live::after { content: ''; position: absolute; inset: 0; border-radius: 999px; background: #4ADE80; animation: ga-hero-ping 1800ms var(--ga-hero-ease) infinite; }
.ga-hero-pill-text { display: flex; flex-direction: column; gap: 1px; }
.ga-hero-pill-text b { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; color: #FFFFFF; white-space: nowrap; }
.ga-hero-pill-text i { font-size: 12px; font-weight: 500; font-style: normal; color: #FFFFFF; }

/* --- product panel ---------------------------------------------------------- */
.ga-hero-panel { position: relative; flex: 1 1 auto; min-width: 0; overflow: hidden; border-radius: 0; background: #0E0E13; }
.ga-hero-wall { position: absolute; inset: 0; background-size: cover; background-position: center; transition: opacity 460ms var(--ga-hero-ease); }
.ga-hero[data-ga-hero-theme="light"] .ga-hero-wall-dark { opacity: 0; }
.ga-hero[data-ga-hero-theme="dark"] .ga-hero-wall-light { opacity: 0; }
/* grain is a baked 160px tile, not a filtered SVG */
.ga-hero-grain { position: absolute; inset: 0; opacity: 0.055; mix-blend-mode: overlay; background-repeat: repeat; background-size: 160px 160px; pointer-events: none; }
.ga-hero-chrome { position: absolute; top: 0; left: 0; right: 0; z-index: 6; height: 38px; display: flex; align-items: center; justify-content: space-between; padding: 0 26px 0 20px; background: rgba(12, 14, 22, 0.42); border-bottom: 1px solid rgba(255, 255, 255, 0.09); -webkit-backdrop-filter: blur(20px) saturate(150%); backdrop-filter: blur(20px) saturate(150%); color: rgba(255, 255, 255, 0.9); font-size: 14px; font-weight: 500; }
.ga-hero[data-ga-hero-theme="light"] .ga-hero-chrome { background: rgba(255, 255, 255, 0.62); border-bottom-color: rgba(36, 36, 42, 0.10); color: rgba(20, 20, 26, 0.86); }
.ga-hero-chrome-left { display: flex; align-items: center; gap: 9px; }
.ga-hero-chrome-right { display: flex; align-items: center; gap: 15px; opacity: 0.78; }
.ga-hero-chrome img { width: 15px; height: 15px; object-fit: contain; display: block; filter: var(--ga-hero-mark); }
.ga-hero-clock { font-variant-numeric: tabular-nums; }

/* --- cresta cluster ---------------------------------------------------------
   Authored at a fixed 720px design width: 420 main column + 20 gap + 280
   snapshot column. Scaled into the panel by one transform.
   -------------------------------------------------------------------------- */
.ga-hero-cluster { position: absolute; left: 0; top: 0; z-index: 3; display: flex; align-items: flex-start; gap: 20px; width: 720px; transform-origin: 0 0; }
.ga-hero-col-main { flex: none; width: 420px; }
.ga-hero-col-side { display: flex; flex-direction: column; gap: 8px; flex: none; width: 280px; padding-top: 48px; } /* lines the snapshot up with the transcript card, past the toolbar */

.ga-hero-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.ga-hero-grip { display: flex; flex: none; align-items: center; justify-content: center; gap: 9px; width: 104px; height: 40px; border-radius: 999px; background: var(--ga-hero-btn); border: 1px solid var(--ga-hero-btn-border); box-shadow: inset 0 1px 0 var(--ga-hero-inset); -webkit-backdrop-filter: blur(22px) saturate(165%); backdrop-filter: blur(22px) saturate(165%); cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
.ga-hero-grip img { width: 17px; height: 17px; object-fit: contain; display: block; filter: var(--ga-hero-mark); }
.ga-hero-timer { width: 44px; font-size: 14px; font-weight: 500; color: var(--ga-hero-text); font-variant-numeric: tabular-nums; }
.ga-hero-tool { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 999px; background: var(--ga-hero-btn); border: 1px solid var(--ga-hero-btn-border); box-shadow: inset 0 1px 0 var(--ga-hero-inset); color: var(--ga-hero-text); -webkit-backdrop-filter: blur(22px) saturate(165%); backdrop-filter: blur(22px) saturate(165%); }
/* active keeps the same backdrop blur — no layer churn */
.ga-hero-tool-on { background: #6B4BD6; border-color: rgba(255, 255, 255, 0.16); color: #FFFFFF; }

.ga-hero-card { position: relative; display: flex; flex-direction: column; gap: 12px; padding: 15px 16px 13px; border-radius: 12px; background: var(--ga-hero-fill); border: 1px solid var(--ga-hero-border); box-shadow: inset 0 1px 0 var(--ga-hero-inset), var(--ga-hero-shadow); -webkit-backdrop-filter: var(--ga-hero-blur); backdrop-filter: var(--ga-hero-blur); }
.ga-hero-card-title { display: flex; align-items: center; gap: 9px; color: var(--ga-hero-text); font-size: 14px; font-weight: 500; }
.ga-hero-card-title svg { flex: none; }
.ga-hero-win-glow { position: absolute; inset: 0; border-radius: 12px; pointer-events: none; opacity: 0; visibility: hidden; }
.ga-hero-quote { padding-left: 13px; border-left: 2px solid var(--ga-hero-tile-border); }
.ga-hero-said { margin: 0; font-size: 14px; line-height: 1.5; letter-spacing: -0.005em; color: var(--ga-hero-text); text-wrap: pretty; }
.ga-hero-said strong { font-weight: 600; color: var(--ga-hero-strong); }

/* --- transcript -------------------------------------------------------------
   Six lines is more than the card can hold, so the transcript is a fixed 180px
   window with the stack pinned so the newest line sits on its bottom edge.
   Like the export (max-height + justify-content:flex-end), the box starts at
   the first line's height and grows with each line until it hits 180px, then
   the stack scrolls. Height and offset are both written from the clock; the
   180px here is only the no-JS fallback. The top fade only comes in once a
   line has actually scrolled under it (--ga-hero-tfade, alpha of the fade's
   transparent end), so an unscrolled box has no faded first line.

   The transform is driven from the story clock, NOT by a CSS animation. It was
   a `ga-hero-scroll` keyframe animation, and that cannot be made to stay in
   step: the animation runs on its own wall clock the moment it is applied,
   while `t` only advances while the hero is on screen, the tab is visible and
   the story is not paused. Leave the hero, come back, and the transcript is
   wherever its own clock got to while `t` was frozen - the stack sits at an
   offset the conversation has not reached yet. A clock-driven write cannot
   desync, because the position is a function of `t` and nothing else. That is
   also how the delta does it: it renders `v.lines` filtered by time, so its
   transcript is a pure function of the clock too.
   -------------------------------------------------------------------------- */
.ga-hero-transcript { --ga-hero-tfade: 0; position: relative; height: 180px; max-height: 180px; overflow: hidden; -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,var(--ga-hero-tfade)) 0, #000 38px); mask-image: linear-gradient(180deg, rgba(0,0,0,var(--ga-hero-tfade)) 0, #000 38px); }
.ga-hero-tstack { position: absolute; left: 0; top: 0; right: 0; display: flex; flex-direction: column; gap: 14px; }
.ga-hero-line { flex: none; display: flex; gap: 11px; align-items: flex-start; }
.ga-hero-av { flex: none; width: 24px; height: 24px; border-radius: 999px; border: 1px solid rgba(255, 255, 255, 0.18); background-size: cover; background-position: center; }
.ga-hero-av-cust { background-image: linear-gradient(140deg, #FCB56C, #F87563); }
.ga-hero-line-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.ga-hero-who { font-size: 12px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; color: var(--ga-hero-label); opacity: 0.62; }
/* word-by-word typing: the script marks untyped words off from the story clock
   (paintCarets), the same clock and the same index that place the caret. A CSS
   stagger would run on the wall clock while the caret follows the story clock,
   which the tick clamps and freezes, so words would type ahead of the caret.
   Untyped is the marked state so no-JS and reduced motion show every word.
   Going off is instant (loop wrap, seek back); only typing in transitions. */
.ga-hero-w { display: inline-block; transition: opacity 300ms var(--ga-hero-ease), transform 300ms var(--ga-hero-ease); }
.ga-hero-w[data-ga-hero-off] { opacity: 0; transform: translate3d(0, 0.3em, 0); transition: none; }
.ga-hero[data-ga-hero-snap] .ga-hero-w { transition: none; }
.ga-hero-caret { display: inline-block; width: 2px; height: 1em; margin-left: 2px; margin-right: -4px; vertical-align: -0.12em; border-radius: 1px; background: currentColor; visibility: hidden; animation: ga-hero-blink 760ms steps(2, jump-none) infinite; }
.ga-hero-keyword { position: relative; border-radius: 4px; padding: 0 2px; margin: 0 -2px; font-weight: 600; color: var(--ga-hero-strong); background: var(--ga-hero-key); }
.ga-hero-keyflash { position: absolute; inset: -1px; border-radius: 5px; opacity: 0; visibility: hidden; box-shadow: 0 0 16px 2px var(--ga-hero-glow); background: var(--ga-hero-key); }

/* --- guidance hints ---------------------------------------------------------
   One fixed-height slot holds both hints. Hint 1 "collapsing" into the Done
   row is a cross-fade between two separately authored cards pinned to the top
   of that slot — the slot itself never changes height, so nothing below it
   moves and no layout property is ever animated.
   -------------------------------------------------------------------------- */
.ga-hero-hints { position: relative; margin-top: 8px; }
.ga-hero-hint { position: absolute; left: 0; top: 0; width: 100%; }
.ga-hero-hint-body { display: flex; flex-direction: column; gap: 12px; }
.ga-hero-hint-title { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; color: var(--ga-hero-strong); }
/* the collapsed row: same card chrome, one line tall */
.ga-hero-done { flex-direction: row; align-items: center; gap: 9px; height: var(--ga-hero-done-h); padding: 0 16px; }
.ga-hero-done-title { flex: 1; min-width: 0; font-size: 14px; font-weight: 500; letter-spacing: -0.01em; color: var(--ga-hero-strong); white-space: nowrap; }
.ga-hero-done-flag { display: flex; align-items: center; gap: 6px; flex: none; font-size: 12px; font-weight: 600; color: var(--ga-hero-ok-text); }
/* hint 2 is parked below the Done row and translates up when the row leaves */
.ga-hero-hint-shift { animation: ga-hero-unpark 420ms var(--ga-hero-ease) both; }

/* --- customer snapshot ------------------------------------------------------
   The card is authored at its FINAL height and simply fades up at its cue; the
   loading row and the ready state cross-fade inside a reserved slot. Nothing
   here grows, so the card's box never animates. Cost: the lower part of the
   card is empty between 2.8s and 8.0s, where the source grew the card.
   -------------------------------------------------------------------------- */
.ga-hero-snap-slot { position: relative; min-height: 110px; }
.ga-hero-snap-layer { position: absolute; left: 0; top: 0; width: 100%; }
.ga-hero-loading { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 10px; background: var(--ga-hero-tile); border: 1px solid var(--ga-hero-tile-border); }
.ga-hero-loading-mark { flex: none; width: 18px; height: 18px; object-fit: contain; display: block; filter: var(--ga-hero-mark); }
.ga-hero-loading span { font-size: 14px; font-weight: 500; color: var(--ga-hero-muted); }
.ga-hero-spin { animation: ga-hero-spin 1100ms linear infinite; }
.ga-hero-ready { display: flex; flex-direction: column; gap: 12px; }
.ga-hero-who-row { display: flex; align-items: center; gap: 11px; }
.ga-hero-who-av { display: flex; flex: none; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 999px; background: linear-gradient(140deg, #FCB56C, #F87563); color: #3A2410; font-size: 14px; font-weight: 600; }
.ga-hero-who-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ga-hero-who-name { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; color: var(--ga-hero-strong); }
.ga-hero-who-sub { font-size: 12.5px; color: var(--ga-hero-muted); }
.ga-hero-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ga-hero-chip { padding: 4px 10px; border-radius: 8px; background: var(--ga-hero-chip); color: var(--ga-hero-chip-text); font-size: 12.5px; font-weight: 500; }
.ga-hero-offer-in { display: flex; flex-direction: column; gap: 12px; } /* carries the entrance cue, so the card's own opacity stays free */
.ga-hero-offer-body { display: flex; flex-direction: column; gap: 8px; }
/* Sits under the chips and is divided off by a rule, so the snapshot reads as
   the profile on top and the call history underneath it. */
.ga-hero-history { display: flex; flex-direction: column; gap: 9px; padding-top: 11px; border-top: 1px solid var(--ga-hero-tile-border); }
.ga-hero-history-title { font-size: 12.5px; font-weight: 600; color: var(--ga-hero-strong); }
.ga-hero-history-row { display: flex; gap: 9px; }
.ga-hero-history-row svg { flex: none; margin-top: 2px; color: var(--ga-hero-muted); }
.ga-hero-history-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ga-hero-history-when { font-size: 11.5px; color: var(--ga-hero-muted); }
.ga-hero-history-what { font-size: 12.5px; line-height: 1.4; color: var(--ga-hero-text); text-wrap: pretty; }
.ga-hero-history-more { font-size: 12px; color: var(--ga-hero-muted); }
.ga-hero-offer-label { font-size: 13px; font-weight: 600; color: var(--ga-hero-strong); }
.ga-hero-offer-tile { position: relative; display: flex; flex-direction: column; gap: 8px; padding: 12px 13px; border-radius: 10px; background: var(--ga-hero-tile); border: 1px solid var(--ga-hero-tile-border); }
.ga-hero-offer-name { position: relative; font-size: 14px; font-weight: 600; color: var(--ga-hero-strong); }
.ga-hero-offer-sub { position: relative; font-size: 12.5px; color: var(--ga-hero-muted); }
.ga-hero-ring-wrap { position: absolute; inset: 0; pointer-events: none; }
.ga-hero-ring { position: absolute; inset: -1px; border-radius: 10px; pointer-events: none; background: var(--ga-hero-hl); border: 1px solid var(--ga-hero-hl-border); }
.ga-hero-ring-glow { position: absolute; inset: -1px; border-radius: 10px; pointer-events: none; opacity: 0; visibility: hidden; background: var(--ga-hero-hl); box-shadow: 0 0 22px var(--ga-hero-glow); }
.ga-hero-ring-ok { position: absolute; inset: -1px; border-radius: 10px; pointer-events: none; background: var(--ga-hero-ok); border: 1px solid var(--ga-hero-ok-border); }
.ga-hero-accepted { position: relative; display: flex; align-items: center; gap: 6px; width: fit-content; padding: 4px 10px; border-radius: 999px; background: var(--ga-hero-ok); color: var(--ga-hero-ok-text); font-size: 12px; font-weight: 600; }

/* --- the side column is a slot, not a stack ---------------------------------
   The snapshot holds the column from 1.2s and hands it to the offer at 9s, and
   the offer clears it at 26s when the outcome beat takes over. Both cards are
   gated on the story clock rather than on a cue: each has to leave again, and a
   cue fires once and cannot be seeked out of.
   The display toggle is what puts the offer in the snapshot's place - left in
   flow, the snapshot's own box would push it down the column even while it is
   invisible, which is what put an empty offer card at the bottom right.
   The un-attributed state is the no-JS and reduced-motion one, and it is the
   design's last frame: the offer accepted. Both of those stop the clock before
   it ever runs, so they need the end state in CSS rather than a write.
   -------------------------------------------------------------------------- */
.ga-hero-snap-in { display: flex; flex-direction: column; gap: 12px; } /* carries the entrance cue, so the card's own opacity stays free */
.ga-hero-snap-card { display: none; transition: opacity 560ms var(--ga-hero-ease), transform 560ms var(--ga-hero-ease); }
.ga-hero-offer { display: flex; transition: opacity 600ms var(--ga-hero-ease); }
.ga-hero[data-ga-hero-side="pre"] .ga-hero-snap-card,
.ga-hero[data-ga-hero-side="pre"] .ga-hero-offer { display: none; }
.ga-hero[data-ga-hero-side="snap"] .ga-hero-snap-card,
.ga-hero[data-ga-hero-side="snapout"] .ga-hero-snap-card { display: flex; }
.ga-hero[data-ga-hero-side="snap"] .ga-hero-offer,
.ga-hero[data-ga-hero-side="snapout"] .ga-hero-offer { display: none; }
.ga-hero[data-ga-hero-side="snapout"] .ga-hero-snap-card { opacity: 0; transform: translateY(-12px) scale(0.98); }
.ga-hero[data-ga-hero-side="end"] .ga-hero-offer { opacity: 0; }

/* --- transport -------------------------------------------------------------- */
.ga-hero-controls { position: absolute; left: 20px; right: 110px; bottom: 18px; z-index: 8; display: flex; justify-content: center; }
.ga-hero-controls-row { display: flex; align-items: center; gap: 10px; min-width: 0; max-width: 100%; }
.ga-hero-scenario { flex: none; margin-right: 4px; font-size: 14px; font-weight: 600; letter-spacing: 0.02em; color: var(--ga-hero-tl-strong); opacity: 0.85; white-space: nowrap; }
.ga-hero-tbtn { font: inherit; flex: none; display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; padding: 0; border: 1px solid var(--ga-hero-tl-border); border-radius: 999px; background: var(--ga-hero-btn); color: var(--ga-hero-tl-strong); -webkit-backdrop-filter: blur(18px) saturate(160%); backdrop-filter: blur(18px) saturate(160%); cursor: pointer; transition: --ga-hero-hov 180ms ease; }
.ga-hero-tbtn:hover { --ga-hero-hov: 1.35; } /* brightness rides the glass filter - see the glass block */
.ga-hero-steps { display: flex; align-items: stretch; gap: 14px; margin-left: 8px; min-width: 0; flex: none; }
.ga-hero-step { font: inherit; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; flex: none; min-width: 64px; padding: 6px 0 4px; border: 0; background: transparent; cursor: pointer; text-align: left; }
.ga-hero-step-label { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; letter-spacing: 0.055em; text-transform: uppercase; white-space: nowrap; color: var(--ga-hero-tl-label); transition: color 220ms ease; }
.ga-hero-step[data-ga-hero-cur="1"] .ga-hero-step-label { color: var(--ga-hero-tl-strong); }
.ga-hero-step-star { flex: none; }
.ga-hero-step-outcome[data-ga-hero-cur="1"] .ga-hero-step-text { background: linear-gradient(90deg, #6E95F0, #AE95DD, #C98FD6); -webkit-background-clip: text; background-clip: text; color: transparent; }
.ga-hero-rail { position: relative; display: block; width: 100%; height: 2px; border-radius: 2px; overflow: hidden; background: var(--ga-hero-tl-track); }
.ga-hero-fill { position: absolute; inset: 0; transform: scaleX(0); transform-origin: 0 50%; background: var(--ga-hero-tl-strong); }
.ga-hero-theme { font: inherit; position: absolute; right: 20px; bottom: 20px; z-index: 8; display: flex; align-items: center; width: 66px; height: 40px; padding: 3px; border-radius: 999px; background: rgba(18, 18, 24, 0.42); border: 1px solid rgba(255, 255, 255, 0.14); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); box-shadow: 0 8px 22px rgba(8, 8, 14, 0.3); cursor: pointer; }
.ga-hero-knob { display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 999px; background: #FFFFFF; color: #24242A; transition: transform 300ms var(--ga-hero-ease); }
.ga-hero[data-ga-hero-theme="light"] .ga-hero-knob { transform: translate3d(26px, 0, 0); }
.ga-hero[data-ga-hero-theme="light"] .ga-hero-moon { display: none; }
.ga-hero[data-ga-hero-theme="dark"] .ga-hero-sun { display: none; }
.ga-hero[data-ga-hero-paused="1"] .ga-hero-ico-pause, .ga-hero[data-ga-hero-paused="0"] .ga-hero-ico-play { display: none; }

/* --- outcome beat -----------------------------------------------------------
   The revenue-lift card lands over the call UI at the tail of the story. It
   outranks the transport and the theme toggle (both z 8) and the chrome (z 6)
   because the export orders it before the cluster and lifts it with z-index, so
   it is deliberately the last thing on screen - the call is over by then.

   It is the one staged element gated on TWO states, and ga-hero-fade would only
   give it the first of them. The reveal opens at page load, so on its own it
   left the card on screen - reading $0.0M, over a live call that still had 26
   seconds to run - for the whole first act. The outcome beat is the other half,
   and it is the half that makes this an outcome rather than a fixture. Both are
   required, and neither writer can be ga-hero-fade: that class is one rule for
   one property, and this needs a different condition.

   No transition on purpose. This element is a bare flex container with no fill
   of its own, and the card inside carries ga-hero-cue-in (the export's ka-in blur entrance) at 26.6 - the
   delta's ka-in - with a both fill, so the cue is the entire entrance and an
   outer fade would only dilute it. Nothing shows in the 600ms between the
   outcome attribute at 26 and the cue at 26.6, because the cue's own fill hides
   the card until its delay elapses.
   -------------------------------------------------------------------------- */
.ga-hero-kpi { position: absolute; inset: 38px 0 0; z-index: 9; display: flex; align-items: center; justify-content: center; padding: 0 16px 60px; pointer-events: none; opacity: 0; visibility: hidden; }
.ga-hero[data-ga-hero-reveal="1"][data-ga-hero-outcome="1"] .ga-hero-kpi { opacity: 1; visibility: visible; }
.ga-hero-kpi-card { position: relative; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 14px; width: 100%; max-width: 400px; padding: 36px 32px 32px; border-radius: 20px; background: var(--ga-hero-fill); border: 1px solid var(--ga-hero-border); box-shadow: inset 0 1px 0 var(--ga-hero-inset), var(--ga-hero-shadow); -webkit-backdrop-filter: var(--ga-hero-blur); backdrop-filter: var(--ga-hero-blur); }
.ga-hero-kpi-hl { position: absolute; inset: -1px; border-radius: 20px; border: 1px solid var(--ga-hero-hl-border); pointer-events: none; opacity: 0; }
.ga-hero-kpi-head { display: flex; align-items: center; gap: 9px; color: var(--ga-hero-text); font-size: 14px; font-weight: 500; }
.ga-hero-kpi-head svg { flex: none; }
.ga-hero-kpi-body { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.ga-hero-kpi-title { font-size: 17px; font-weight: 500; letter-spacing: -0.01em; color: var(--ga-hero-text); }
.ga-hero-kpi-num { font-size: 84px; font-weight: 500; line-height: 1; letter-spacing: -0.045em; color: var(--ga-hero-strong); font-variant-numeric: tabular-nums; }
.ga-hero-kpi[data-ga-hero-kpi-landed="1"] .ga-hero-kpi-num { animation: ga-hero-num-land 620ms var(--ga-hero-ease) both; }
.ga-hero-kpi-bar { display: block; width: 160px; height: 3px; border-radius: 3px; overflow: hidden; background: var(--ga-hero-tl-track); }
.ga-hero-kpi-fill { display: block; height: 100%; border-radius: 3px; background: linear-gradient(90deg, #93DEFD, #AE95DD 45%, #FCB56C); transform-origin: 0 50%; transform: scaleX(0); transition: transform 120ms linear; }
@keyframes ga-hero-num-land { from { transform: scale(1.08); filter: blur(8px); opacity: 0.6; } to { transform: scale(1); filter: blur(0); opacity: 1; } }

/* The call UI clears the way. Three of the four the export gates on mainO: the
   toolbar, the transcript card and the hint. The fourth is the offer card, whose
   opacity the side column drives instead - the same attribute that hands the
   column from the snapshot to the offer is what clears it again here, and one
   writer per property is the only way the fade stays seekable.
   The snapshot is not on this list because it is already gone by 9s, long before
   the outcome beat - that is the side column's doing, not this rule's. */
/* The transcript card is itself glass, so its own opacity is safe. The toolbar
   and the hint slot only CONTAIN glass, so they fade through --ga-hero-vis-o -
   see the glass block below for why a container never writes opacity. */
.ga-hero-col-main > .ga-hero-card { transition: opacity 600ms var(--ga-hero-ease); }
.ga-hero-toolbar, .ga-hero-hints { transition: --ga-hero-vis-o 600ms var(--ga-hero-ease); }
.ga-hero[data-ga-hero-outcome="1"] .ga-hero-col-main > .ga-hero-card { opacity: 0; }
.ga-hero[data-ga-hero-outcome="1"] .ga-hero-toolbar,
.ga-hero[data-ga-hero-outcome="1"] .ga-hero-hints { --ga-hero-vis-o: 0; }

/* --- reveal ----------------------------------------------------------------
   Product UI is hidden until the morph is most of the way open.
   -------------------------------------------------------------------------- */
.ga-hero-fade { --ga-hero-vis-r: 0; visibility: hidden; transition: --ga-hero-vis-r 300ms var(--ga-hero-ease), visibility 0s linear 300ms; }
.ga-hero[data-ga-hero-reveal="1"] .ga-hero-fade { --ga-hero-vis-r: 1; visibility: visible; transition: --ga-hero-vis-r 300ms var(--ga-hero-ease), visibility 0s; }
.ga-hero[data-ga-hero-reveal="0"] .ga-hero-fade { pointer-events: none; }
/* Backdrop blur is NOT suspended while scrolling. The glass fills are around 30%
   alpha and read as panels only because the blur is there to carry them; with it
   off the wall's grain shows straight through the tint and the hero looks like it
   lost its background. The suspension also rode the scroll attribute, so the
   panels snapped back the moment the scroll settled - a rendering glitch, not a
   deliberate state. The blur is expensive, but it is the design. */

/* --- glass fades -----------------------------------------------------------
   A container that holds glass must never fade with opacity. Opacity below 1
   (like filter, mask or clip-path) makes that ancestor the backdrop root, and a
   backdrop-filter only sees what is painted inside its backdrop root - which,
   for a card in the cluster, is nothing. So for the whole 300ms reveal, the
   600ms outcome fade and every hint sink, the cards lost their blur and the
   wall's grain showed straight through their 30% fills: the panels went clear
   mid-fade and snapped back to frosted when the fade ended.
   Containers write a registered custom property instead - a custom property
   paints nothing, so it can never make a backdrop root - and each glass leaf
   multiplies the three into a filter on itself. A filter on the glass element
   applies after its own backdrop blur, so the panel stays frosted all the way
   down. One property per writer keeps the reveal, the outcome and the sink from
   ever fighting over one value. Visibility stays on the containers: it is not a
   backdrop root, and it is what keeps a faded-out cue unclickable. */
@property --ga-hero-vis-r { syntax: '<number>'; inherits: true; initial-value: 1; }
@property --ga-hero-vis-o { syntax: '<number>'; inherits: true; initial-value: 1; }
@property --ga-hero-vis-s { syntax: '<number>'; inherits: true; initial-value: 1; }
@property --ga-hero-hov { syntax: '<number>'; inherits: false; initial-value: 1; }
.ga-hero-chrome, .ga-hero-theme, .ga-hero-grip, .ga-hero-tool, .ga-hero-card,
.ga-hero-scenario, .ga-hero-steps { filter: opacity(calc(var(--ga-hero-vis-r) * var(--ga-hero-vis-o) * var(--ga-hero-vis-s))); }
.ga-hero-tbtn { filter: opacity(var(--ga-hero-vis-r)) brightness(var(--ga-hero-hov)); }
/* The hint wrappers are the only cue-outs with glass inside; the snapshot's
   loading row and the offer's ring sit in a card, so their opacity sink is safe. */
.ga-hero-hint.ga-hero-cue-out { animation-name: ga-hero-sink-glass; }
@keyframes ga-hero-sink-glass { 0% { --ga-hero-vis-s: 1; } 99% { visibility: visible; } 100% { --ga-hero-vis-s: 0; visibility: hidden; transform: translate3d(0, -6px, 0); } }

/* --- story cues ------------------------------------------------------------
   Every staged element carries data-ga-hero-cue="<seconds>". The script only
   writes animation-delay, so the staging itself runs on the compositor and
   survives a busy main thread. An element that has to leave again is wrapped
   in a .ga-hero-cue-out, so no element ever carries two animations that both
   touch opacity (a back-filling second animation would cancel the first).
   Un-animated, every cue element sits in its finished state — which is what
   reduced motion and no-JS both get.
   -------------------------------------------------------------------------- */
.ga-hero-cue-rise { animation: ga-hero-rise 420ms var(--ga-hero-ease) both; }
.ga-hero-cue-slide { animation: ga-hero-slide 520ms var(--ga-hero-ease) both; }
.ga-hero-cue-in { animation: ga-hero-in 620ms var(--ga-hero-ease) both; }
.ga-hero-cue-flash { animation: ga-hero-flash 1500ms var(--ga-hero-ease) both; }
/* The landed highlight is not a flash: the export's field-glow ends at opacity 1
   with the glow spent, so the border stays on the card once the number lands.
   ga-hero-cue-flash would take it away again at 100%, which is the one thing
   this must not do. */
.ga-hero-cue-glow { animation: ga-hero-field-glow 1800ms var(--ga-hero-ease) both; }
.ga-hero-cue-out { animation: ga-hero-sink 320ms var(--ga-hero-ease) both; }
.ga-hero-cue-on { animation: ga-hero-on 260ms var(--ga-hero-ease) both; }
.ga-hero-cue-key { animation: ga-hero-keyon 300ms var(--ga-hero-ease) both; }
.ga-hero-cue-fill { animation: ga-hero-fill 1000ms linear both; }
.ga-hero[data-ga-hero-paused="1"] [data-ga-hero-cue],
.ga-hero[data-ga-hero-paused="1"] .ga-hero-caret,
.ga-hero[data-ga-hero-paused="1"] .ga-hero-spin { animation-play-state: paused; }

/* every cue starts fully hidden, not merely transparent */
@keyframes ga-hero-rise { 0% { opacity: 0; visibility: hidden; transform: translate3d(0, 14px, 0) scale(0.97); } 1% { visibility: visible; } 100% { opacity: 1; visibility: visible; transform: none; } }
@keyframes ga-hero-in { 0% { opacity: 0; visibility: hidden; transform: translate3d(0, 14px, 0) scale(0.965); filter: blur(12px); } 1% { visibility: visible; } 45% { opacity: 0.92; filter: blur(2px); } 100% { opacity: 1; visibility: visible; transform: none; filter: blur(0); } }
@keyframes ga-hero-slide { 0% { opacity: 0; visibility: hidden; transform: translate3d(-8px, 0, 0); } 1% { visibility: visible; } 100% { opacity: 1; visibility: visible; transform: none; } }
@keyframes ga-hero-sink { 0% { opacity: 1; } 99% { visibility: visible; } 100% { opacity: 0; visibility: hidden; transform: translate3d(0, -6px, 0); } }
@keyframes ga-hero-flash { 0% { opacity: 0; visibility: hidden; } 1% { visibility: visible; } 22% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
/* The card glow is a bloom, not a flash. The delta's win-glow holds the halo at
   nothing, grows it to 18px at 18% and 26px at 42%, then spends it again by 100%
   - 3.2s of travel where ga-hero-flash puts the full 26px up in 330ms and takes
   it away. Same peak either way; the weight is entirely in the ramp.
   The delta carries two glow colours (0.32 then 0.52 alpha); the same two-step is
   written here as opacity over one themed colour, so the dark theme keeps working
   - a literal rgba would not follow --ga-hero-glow. 0.32/0.52 = 0.62. */
/* No delay in the shorthand: the delta's 320ms is measured from the card mounting,
   and in this build the card is always mounted, so the cue time carries it - 1.5,
   9.3, 9.3 and 19.5 are already the delta's mount + 320ms. A delay here would be a
   second writer for the same thing, and applyCues overwrites animationDelay anyway. */
.ga-hero-cue-bloom { animation: ga-hero-win-bloom 3200ms cubic-bezier(.33, 0, .2, 1) both; }
@keyframes ga-hero-win-bloom { 0% { opacity: 0; visibility: hidden; box-shadow: 0 0 0 0 var(--ga-hero-glow); } 1% { visibility: visible; } 18% { opacity: 0.62; box-shadow: 0 0 18px 1px var(--ga-hero-glow); } 42% { opacity: 1; box-shadow: 0 0 26px 3px var(--ga-hero-glow); } 100% { opacity: 1; box-shadow: 0 0 0 0 var(--ga-hero-glow); } }
@keyframes ga-hero-field-glow { 0% { opacity: 0; box-shadow: 0 0 0 var(--ga-hero-glow); } 35% { opacity: 1; box-shadow: 0 0 22px var(--ga-hero-glow); } 100% { opacity: 1; box-shadow: 0 0 0 var(--ga-hero-glow); } }
@keyframes ga-hero-keyon { from { background-color: transparent; } }
@keyframes ga-hero-on { to { background-color: #6B4BD6; border-color: rgba(255, 255, 255, 0.16); color: #FFFFFF; } }
@keyframes ga-hero-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes ga-hero-unpark { from { transform: translate3d(0, calc(var(--ga-hero-done-h) + var(--ga-hero-done-gap)), 0); } to { transform: none; } }
@keyframes ga-hero-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0.2; } }
@keyframes ga-hero-spin { to { transform: rotate(360deg); } }
@keyframes ga-hero-ping { 0% { transform: scale(1); opacity: 0.55; } 75%, 100% { transform: scale(2.2); opacity: 0; } }

/* --- responsive ------------------------------------------------------------- */
@media (max-width: 1240px) { .ga-hero-scenario { display: none; } }
@media (max-width: 860px) {
  .ga-hero-track { height: calc(var(--ga-hero-avail) * 1.5175); }
  .ga-hero-stage { flex-direction: column; }
  .ga-hero-faces { flex-direction: row; width: 100%; height: auto; aspect-ratio: 2; }
  .ga-hero-controls { left: 12px; right: 90px; }
  .ga-hero-steps { flex: 1 1 auto; gap: 8px; margin-left: 2px; }
  .ga-hero-step { flex: 0 0 22px; min-width: 0; }
  .ga-hero-step[data-ga-hero-cur="1"] { flex: 1 1 auto; }
  .ga-hero-step-label { visibility: hidden; font-size: 11px; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .ga-hero-step[data-ga-hero-cur="1"] .ga-hero-step-label { visibility: visible; }
  .ga-hero-grip { cursor: default; }
}
@media (max-width: 600px) { .ga-hero-pill { transform: scale(0.74); transform-origin: 0 0; left: 10px; top: 10px; } }

/* --- reduced motion ---------------------------------------------------------
   No morph, no staging, no playback: the hero renders at full bleed with the
   story on its last frame — every line up, hint 1 gone, hint 2 in place, the
   offer accepted. That is the state that reads correctly as a still.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ga-hero-track { height: var(--ga-hero-avail); }
  .ga-hero-stage { transform: none; }
  .ga-hero-fade { --ga-hero-vis-r: 1; visibility: visible; transition: none; }
  .ga-hero [data-ga-hero-cue], .ga-hero-w, .ga-hero-spin, .ga-hero-knob, .ga-hero-wall, .ga-hero-tstack, .ga-hero-hint-shift { animation: none; transition: none; }
  .ga-hero-hint-shift { transform: none; }
  .ga-hero-caret { display: none; }
  .ga-hero-live::after { display: none; }
  .ga-hero-transient { display: none; }
  .ga-hero-fill { transform: scaleX(1); }
  .ga-hero-win-glow, .ga-hero-keyflash, .ga-hero-ring-glow { display: none; }
}
