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


/* --- root + tokens ---------------------------------------------------------- */
.ka-hero {
  --ka-hero-header: 80px;
  --ka-hero-avail: calc(100svh - var(--ka-hero-header));
  --ka-hero-ease: cubic-bezier(.2, .7, .2, 1);
  --ka-hero-blur: blur(30px) saturate(165%);
  --ka-hero-ink: #24242A;
  --ka-hero-accent: #205AE3;
  display: block;
  padding-top: var(--ka-hero-header);
  color: var(--ka-hero-ink);
  font-family: 'DM Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  overflow-x: clip;
}
.ka-hero *, .ka-hero *::before, .ka-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 `.ka-hero a` would */
.ka-hero a:not([class]) { color: var(--ka-hero-ink); text-decoration: none; }
.ka-hero a:not([class]):hover, .ka-hero a:not([class]):focus { color: var(--ka-hero-accent); }
.ka-hero button { font: inherit; }

/* Fades never use opacity on a container: opacity below 1 makes that ancestor the
   backdrop root, and every backdrop-filter inside it then samples nothing - the
   glass cards read as having lost their background mid-fade, and snap back when
   the fade ends. Containers write a registered custom property instead (it
   transitions because it is registered), and each glass leaf multiplies the
   relevant ones into a filter on itself - filter applies after the backdrop
   blur, so the blur survives the fade.
   --ka-hero-vis-r  the reveal (chrome, CRM, impact, transport, theme toggle)
   --ka-hero-vis-o  the ending (cluster cards + toolbar, and the CRM)
   --ka-hero-vis-s  the narrow CRM sheet (phone only) */
@property --ka-hero-vis-r { syntax: '<number>'; inherits: true; initial-value: 1; }
@property --ka-hero-vis-o { syntax: '<number>'; inherits: true; initial-value: 1; }
@property --ka-hero-vis-s { syntax: '<number>'; inherits: true; initial-value: 1; }
.ka-hero {
  --ka-hero-vis-r: 0;
  --ka-hero-vis-o: 1;
  --ka-hero-vis-s: 1;
  transition: --ka-hero-vis-r 300ms var(--ka-hero-ease), --ka-hero-vis-o 600ms var(--ka-hero-ease), --ka-hero-vis-s 480ms var(--ka-hero-ease);
}
.ka-hero[data-ka-hero-reveal="1"] { --ka-hero-vis-r: 1; }
.ka-hero[data-ka-hero-outcome="1"] { --ka-hero-vis-o: 0; }
/* The glass leaves each fade themselves. Anything holding a backdrop-filter, and
   any plain-text sibling that has to fade with it, takes the filter here. */
.ka-hero-chrome, .ka-hero-tbtn, .ka-hero-scenario, .ka-hero-steps, .ka-hero-theme, .ka-hero-impact-card {
  filter: opacity(var(--ka-hero-vis-r));
}
.ka-hero-crm-win { filter: opacity(calc(var(--ka-hero-vis-r) * var(--ka-hero-vis-o) * var(--ka-hero-vis-s))); }
.ka-hero-card, .ka-hero-grip, .ka-hero-tool { filter: opacity(var(--ka-hero-vis-o)); }
/* The cards' entrance cue animates `filter` (blur), which would override the
   fade above for the whole animation and - with fill both - forever after. The
   keyframes multiply this in at every frame, so the two compose. */
.ka-hero-card { --ka-hero-vis-f: var(--ka-hero-vis-o); }
.ka-hero-impact-card { --ka-hero-vis-f: var(--ka-hero-vis-r); }

/* dark glass (default) */
.ka-hero[data-ka-hero-theme="dark"] {
  --ka-hero-fill: rgba(26, 28, 38, 0.30);
  --ka-hero-crm-fill: linear-gradient(to bottom left, rgba(25, 34, 65, 0.92), rgba(25, 34, 65, 0) 45%), rgba(12, 18, 45, 0.92);
  --ka-hero-border: rgba(255, 255, 255, 0.17);
  --ka-hero-inset: rgba(255, 255, 255, 0.11);
  --ka-hero-shadow: 0 22px 48px rgba(6, 6, 12, 0.26);
  --ka-hero-btn: rgba(30, 32, 42, 0.34);
  --ka-hero-btn-border: rgba(255, 255, 255, 0.19);
  --ka-hero-text: #EDEFF4;
  --ka-hero-strong: #FFFFFF;
  --ka-hero-label: #C2C9D6;
  --ka-hero-muted: #D9DCE4;
  --ka-hero-tile: rgba(255, 255, 255, 0.10);
  --ka-hero-tile-border: rgba(255, 255, 255, 0.12);
  --ka-hero-chip: rgba(255, 255, 255, 0.15);
  --ka-hero-chip-text: #E9ECF2;
  --ka-hero-key: rgba(147, 222, 253, 0.24);
  --ka-hero-glow: rgba(147, 222, 253, 0.52);
  --ka-hero-glow-soft: rgba(147, 222, 253, 0.32);
  --ka-hero-hl: rgba(147, 222, 253, 0.16);
  --ka-hero-hl-border: rgba(147, 222, 253, 0.55);
  --ka-hero-mark: none;
  --ka-hero-tl-label: rgba(255, 255, 255, 0.5);
  --ka-hero-tl-strong: #FFFFFF;
  --ka-hero-tl-border: rgba(255, 255, 255, 0.30);
  --ka-hero-tl-track: rgba(255, 255, 255, 0.18);
}
/* white glass */
.ka-hero[data-ka-hero-theme="light"] {
  --ka-hero-fill: rgba(255, 255, 255, 0.90);
  --ka-hero-crm-fill: linear-gradient(transparent, transparent), rgba(253, 253, 255, 0.96);
  --ka-hero-border: rgba(255, 255, 255, 0.92);
  --ka-hero-inset: rgba(255, 255, 255, 0.95);
  --ka-hero-shadow: 0 22px 48px rgba(10, 26, 72, 0.24);
  --ka-hero-btn: rgba(255, 255, 255, 0.90);
  --ka-hero-btn-border: rgba(255, 255, 255, 0.92);
  --ka-hero-text: #1E1F27;
  --ka-hero-strong: #0E0E13;
  --ka-hero-label: #5A616C;
  --ka-hero-muted: #3C3C47;
  --ka-hero-tile: rgba(36, 36, 42, 0.04);
  --ka-hero-tile-border: rgba(36, 36, 42, 0.10);
  --ka-hero-chip: rgba(36, 36, 42, 0.07);
  --ka-hero-chip-text: #3C3C47;
  --ka-hero-key: rgba(32, 90, 227, 0.12);
  --ka-hero-glow: rgba(32, 89, 227, 0.72);
  --ka-hero-glow-soft: rgba(32, 89, 227, 0.44);
  --ka-hero-hl: rgba(32, 90, 227, 0.09);
  --ka-hero-hl-border: rgba(32, 90, 227, 0.45);
  --ka-hero-mark: invert(1) brightness(0.18);
  --ka-hero-tl-label: rgba(36, 36, 42, 0.5);
  --ka-hero-tl-strong: #20212B;
  --ka-hero-tl-border: rgba(36, 36, 42, 0.32);
  --ka-hero-tl-track: rgba(36, 36, 42, 0.16);
}

/* The page is the hero and nothing else: the eyebrow, headline, CTA and lede that
   used to sit above it are gone, so the track is the first thing in the section. */

/* --- scroll-morph stage -----------------------------------------------------
   The morph is the live agent-assist model, not a transform: the stage's own
   width, height, left, radius and gap are lerped, and so is the face column's
   width. At rest that gives a 1568x962 card with a 746px video column; at full
   bleed a 1920x1000 frame with a 499px column. A uniform scale cannot produce
   that, because the rest composition is not a scaled copy of the open one - it
   is a proportionally taller card with a much wider video column.

   Writes are quantized (4px geometry, 1px radius/gap) so sub-bucket frames are
   skipped, which is what keeps this inside the 120Hz budget.
   -------------------------------------------------------------------------- */
/* The track is the first thing in the section now, so it takes the top of the
   hero rather than sitting 120px under the intro it used to follow. */
.ka-hero-track { position: relative; margin-top: 0; margin-bottom: 80px; height: calc(var(--ka-hero-avail) * 3.5); }
.ka-hero-sticky { position: sticky; top: var(--ka-hero-header); height: var(--ka-hero-avail); overflow: hidden; }
.ka-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 morph animates layout properties, so the hint only pays while the page is
   actually scrolling. Left on, it keeps the stage a compositing and containing
   block for the whole visit, which is a cost the rest state never earns. */
/* No will-change on the stage. The live page drops it explicitly under WebKit -
   "WebKit recomposites the will-change layer on every geometry write of the
   morph" - and promoting the stage mid-scroll also isolates the grain layer,
   whose mix-blend-mode: overlay then blends against the group instead of the
   wall. Both effects ride the scroll attribute, so both reverted on settle. */

/* --- face tiles -------------------------------------------------------------
   The column is flex:none with its width written by the morph, so the two tiles
   fall out of the stage height. At rest that is 746x469 landscape; at full
   bleed 499x499 square.

   The values here are the FINISHED state, which is what reduced motion and
   no-JS both get - the morph writes the rest state over them on mount.
   -------------------------------------------------------------------------- */
.ka-hero-faces { flex: none; width: 26%; height: 100%; display: flex; flex-direction: column; gap: 2px; }
.ka-hero-face { position: relative; flex: 1 1 0; min-height: 0; overflow: hidden; background: #D5DCE3; }
/* The clip is oversized 2:1 and offset, and the tile clips it. A square source
   in a square box gives object-fit nothing to crop, so the zoom has to come
   from the element being larger than its window. The offsets below reproduce
   the live agent-assist framing: the visible window starts at 8%/6% of the
   source for the agent and 38%/10% for the customer. The poster rides the same
   box, so it is cropped identically and ships as the full uncropped frame. */
/* The clip box is 2x the tile, so it is only square when the tile is. At rest
   the tile is wide, cover crops the square master vertically, and object-position
   is what picks the band - the live page's values, constant across the morph. */
.ka-hero-clip { position: absolute; width: 200%; height: 200%; object-fit: cover; display: block; }
.ka-hero-face-agent .ka-hero-clip { left: -16.1%; top: -12.0%; object-position: 33% 22%; }
.ka-hero-face-customer .ka-hero-clip { left: -76.0%; top: -20.0%; object-position: 63% 25%; }
/* graduated frost under the name plate */
.ka-hero-frost { position: absolute; left: 0; bottom: 0; width: 52%; height: 28%; pointer-events: none; }
.ka-hero-frost span { position: absolute; inset: 0; }
.ka-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%); }
.ka-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%); }
.ka-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%); }
.ka-hero-name { position: absolute; left: 16px; right: 16px; bottom: 14px; display: flex; align-items: center; gap: 7px; }
.ka-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); }
.ka-hero-dot { flex: none; width: 10px; height: 10px; border-radius: 999px; }
.ka-hero-face-agent .ka-hero-dot { background: linear-gradient(140deg, #6E95F0, #AE95DD); }
.ka-hero-face-customer .ka-hero-dot { background: linear-gradient(140deg, #FCB56C, #F87563); }
.ka-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.42); border: 1px solid rgba(255, 255, 255, 0.14); -webkit-backdrop-filter: blur(14px) saturate(160%); backdrop-filter: blur(14px) saturate(160%); }
.ka-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; }
.ka-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); }
.ka-hero-live::after { content: ''; position: absolute; inset: 0; border-radius: 999px; background: #4ADE80; animation: ka-hero-ping 1800ms var(--ka-hero-ease) infinite; }
.ka-hero-pill-text { display: flex; flex-direction: column; gap: 1px; }
.ka-hero-pill-text b { font-size: 14px; font-weight: 600; line-height: 1.5; letter-spacing: -0.01em; color: #FFFFFF; white-space: nowrap; }
.ka-hero-pill-text i { font-size: 12px; font-weight: 500; font-style: normal; line-height: 1.5; color: rgba(255, 255, 255, 0.7); }

/* --- product panel ---------------------------------------------------------- */
.ka-hero-panel { position: relative; flex: 1 1 auto; min-width: 0; overflow: hidden; border-radius: 0; background: #0E0E13; }
/* Wall = flat surface colour, a 264px grain tile repeating over it, and the
   gradient painting cover underneath. Same stack the live page runs. */
.ka-hero-wall { position: absolute; inset: 0; background-size: 264px 264px, cover; background-position: 0 0, 50% 50%; background-repeat: repeat, no-repeat; transition: opacity 460ms var(--ka-hero-ease); }
.ka-hero-wall-dark { background-color: #050A1F; background-size: 88px 88px, cover; }
.ka-hero-wall-light { background-color: #F8F9FC; }
.ka-hero[data-ka-hero-theme="light"] .ka-hero-wall-dark { opacity: 0; }
.ka-hero[data-ka-hero-theme="dark"] .ka-hero-wall-light { opacity: 0; }
/* The live page paints this noise from an inline feTurbulence SVG on a layer
   that resizes every frame, which re-rasterises the filter per frame. This is
   Chrome's own rasterisation of that exact filter, baked at its intrinsic 160px
   and tiled - same pixels, no per-frame filter. background-size pins the tile to
   the asset's intrinsic size; without it the browser stretches the one tile
   across the whole layer and the grain smears into large blotches. */
.ka-hero-grain { position: absolute; inset: 0; opacity: 0.055; mix-blend-mode: overlay; background-repeat: repeat; background-size: 160px 160px; pointer-events: none; }
.ka-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; }
.ka-hero[data-ka-hero-theme="light"] .ka-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); }
.ka-hero-chrome-left { display: flex; align-items: center; gap: 9px; }
.ka-hero-chrome-right { display: flex; align-items: center; gap: 15px; opacity: 0.78; }
.ka-hero-chrome img { width: 15px; height: 15px; object-fit: contain; display: block; filter: var(--ka-hero-mark); }
.ka-hero-clock { font-variant-numeric: tabular-nums; }

/* --- cresta cluster --------------------------------------------------------- */
.ka-hero-cluster { position: absolute; left: 0; top: 0; z-index: 3; width: 420px; transform-origin: 0 0; }
.ka-hero-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.ka-hero-grip { display: flex; flex: none; align-items: center; justify-content: center; gap: 9px; width: 104px; height: 40px; border-radius: 999px; background: var(--ka-hero-btn); border: 1px solid var(--ka-hero-btn-border); box-shadow: inset 0 1px 0 var(--ka-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; }
.ka-hero-grip img { width: 17px; height: 17px; object-fit: contain; display: block; filter: var(--ka-hero-mark); }
.ka-hero-timer { width: 44px; font-size: 14px; font-weight: 500; color: var(--ka-hero-text); font-variant-numeric: tabular-nums; }
.ka-hero-tool { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 999px; background: var(--ka-hero-btn); border: 1px solid var(--ka-hero-btn-border); box-shadow: inset 0 1px 0 var(--ka-hero-inset); color: var(--ka-hero-text); -webkit-backdrop-filter: blur(22px) saturate(165%); backdrop-filter: blur(22px) saturate(165%); }
/* active keeps the same backdrop blur — no layer churn */
.ka-hero-tool-on { background: #6B4BD6; border-color: rgba(255, 255, 255, 0.16); color: #FFFFFF; }

.ka-hero-card { position: relative; display: flex; flex-direction: column; gap: 12px; padding: 15px 16px 13px; border-radius: 12px; background: var(--ka-hero-fill); border: 1px solid var(--ka-hero-border); box-shadow: inset 0 1px 0 var(--ka-hero-inset), var(--ka-hero-shadow); -webkit-backdrop-filter: var(--ka-hero-blur); backdrop-filter: var(--ka-hero-blur); }
.ka-hero-card + .ka-hero-card { margin-top: 8px; }
/* The card carries all three agent states at once and shows one at a time, the way live
   does. The two that are not showing are taken out of flow, so the card is exactly as tall
   as the agent on screen instead of the tallest of the three; the state named on the card
   is the one in flow and sets the height. Left and right are 16px because an absolutely
   positioned box resolves against the padding box, which is where the in-flow content
   already starts. */
/* The card keeps the entrance cue, so the ending's fade goes to the cluster it sits
   in rather than to the card itself. A cue-rise fills `both`, so a fade written on
   the card would be outranked by the animation's own `opacity: 1` and the card would
   stay lit through the ending - the trap the Guidance hero hit with its offer card.
   This wrapper is here for the beats inside the card that land on their own cues. */
.ka-hero-card-in { display: flex; flex-direction: column; gap: 12px; }
.ka-hero-card-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
/* Live sets the head at 14px on a 21px line, and the card's height is measured
   off that: leaving the line-height to the font's own 1.15 put the whole card 4px
   short of live's 228. */
/* The agent card is the one live pads to 14 at the foot rather than 13 - its
   transcript card is `15px 16px 13px` and its agent card `15px 16px 14px`. */
.ka-hero-card-agent { padding-bottom: 14px; }
.ka-hero-card-title { display: flex; align-items: center; gap: 9px; color: var(--ka-hero-text); font-size: 14px; font-weight: 500; line-height: 21px; }
/* No `opacity`/`visibility` here, unlike the other glow layers: the export animates
   this one's box-shadow rather than its opacity, so hiding it in the base rule would
   hide it for the whole animation. The resting state is the animation's own 0% and
   100% - no shadow - which is also what no-JS gets. */
.ka-hero-win-glow { position: absolute; inset: 0; border-radius: 12px; pointer-events: none; box-shadow: 0 0 0 0 rgba(147, 222, 253, 0); }

/* --- the ending -------------------------------------------------------------
   The story finishes on the outcome rather than on the answer, so the last beat
   takes the panel: the toolbar, both cards and the CRM fade together and the
   impact card lands in the middle of what is now empty. It is a panel-level
   overlay, not a card in the column, so the cluster's own layout is untouched by
   it - and it sits at z9 because the chrome (z6) and the controls (z8) are both
   above the cluster and would otherwise cut through it.
   `transient` takes it out under reduced motion, where every cue renders in its
   finished state at once and an outcome card on top of a full cluster is noise.
   -------------------------------------------------------------------------- */
.ka-hero-impact { position: absolute; inset: 38px 0 0; z-index: 9; display: flex; align-items: center; justify-content: center; padding: 0 16px 60px; pointer-events: none; }
/* The export sets this card at a fixed 440 with fixed type, which is fine for a demo
   at one size and wrong on a narrow panel: the box shrinks with the panel and the type
   does not, so the 64px word runs out over the tiles and off the frame. It is held at
   its design width instead and `fit` scales the whole card to the panel - the same
   treatment the cluster and the CRM already get. The scale rides the `scale` property
   rather than `transform`, because the entrance animates `transform`. */
.ka-hero-impact-card { position: relative; display: flex; flex-direction: column; align-items: center; gap: 18px; width: 440px; padding: 40px 36px 36px; border-radius: 20px; text-align: center; background: var(--ka-hero-fill); border: 1px solid var(--ka-hero-border); box-shadow: inset 0 1px 0 var(--ka-hero-inset), var(--ka-hero-shadow); -webkit-backdrop-filter: var(--ka-hero-blur); backdrop-filter: var(--ka-hero-blur); }
.ka-hero-impact-hl { position: absolute; inset: -1px; border-radius: 20px; border: 1px solid var(--ka-hero-hl-border); pointer-events: none; opacity: 0; }
.ka-hero-impact-head { display: flex; align-items: center; gap: 9px; color: var(--ka-hero-text); font-size: 14px; font-weight: 500; }
.ka-hero-impact-head svg { flex: none; }
.ka-hero-impact-body { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.ka-hero-impact-title { font-size: 30px; font-weight: 500; line-height: 1.15; letter-spacing: -0.025em; color: var(--ka-hero-text); }
/* The number and its tick share a row, and the row holds its box from load: the
   cue hides it with visibility, not display, so the card is the same height
   before and after the word lands and the beat does not shove the card up. */
.ka-hero-impact-row { display: flex; align-items: center; justify-content: center; gap: 14px; }
.ka-hero-impact-word { font-size: 64px; font-weight: 500; line-height: 1.05; letter-spacing: -0.04em; color: var(--ka-hero-strong); }
.ka-hero-impact-check { display: flex; flex: none; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 50%; background: linear-gradient(135deg, #93DEFD, #AE95DD 45%, #FCB56C); box-shadow: 0 6px 18px rgba(147, 222, 253, 0.28); }
.ka-hero-impact-bar { display: block; width: 72px; height: 3px; border-radius: 3px; background: linear-gradient(135deg, #93DEFD, #AE95DD 45%, #FCB56C); transform-origin: 50% 50%; }
/* Four things leave for the ending - the toolbar, both cards and the CRM - and
   they leave on the same attribute so the panel empties as one movement. The
   attribute flips --ka-hero-vis-o on the root (see the top of the sheet); the
   glass leaves fade themselves, so no container ever carries an opacity that
   would make it the backdrop root for the blur inside it. */

/* transcript: a 190px window anchored to the bottom, so the card stops growing
   once the call has three lines in it and the oldest line scrolls out under a
   top fade. The fade is not static: it is latched on by the script only once
   the content truly overflows (measured from layout, not mid-animation), so the
   opening exchange at rest reads clean. */
.ka-hero-transcript { display: flex; flex-direction: column; justify-content: flex-end; gap: 14px; max-height: 190px; overflow: hidden; }
/* The live page shows its two opening lines at rest and holds everything after
   them until the visitor scrolls - its timer reads 00:00 and the transcript does
   not grow. The opening lines are un-cued so they render on load; every other
   cue is paused here until the call starts. */
.ka-hero[data-ka-hero-call="0"] [data-ka-hero-cue],
.ka-hero[data-ka-hero-call="0"] [data-ka-hero-cue] * { animation-play-state: paused; }
.ka-hero-line { flex: none; display: flex; gap: 11px; align-items: flex-start; }
/* The source cut-out is cropped tight enough that the top of his head meets the
   image's top edge. At cover the image is exactly the box, so that flat cut lands
   just inside the circle and reads as a skin-coloured blob sitting on a blue disc
   rather than a face. 8% of zoom, bottom-anchored, pushes the flat edge above the
   circle's top so the circle is what crops him. */
.ka-hero-av { flex: none; width: 24px; height: 24px; border-radius: 999px; border: 1px solid rgba(255, 255, 255, 0.18); background-size: 108%; background-position: 50% 100%; }
.ka-hero-av-cust { background-image: linear-gradient(140deg, #FCB56C, #F87563); }
.ka-hero-line-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.ka-hero-who { font-size: 12px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; color: var(--ka-hero-label); opacity: 0.62; }
.ka-hero-said { margin: 0; font-size: 14px; line-height: 1.5; letter-spacing: -0.005em; color: var(--ka-hero-text); text-wrap: pretty; }
/* word-by-word typing: one span per word, staggered purely in CSS off the
   line's own cue offset (--ka-hero-at, written by the script on seek) so the
   whole run is a compositor animation. --ka-hero-n is the line's word count,
   which is all the caret needs to know when to leave. */
.ka-hero-w { display: inline-block; animation: ka-hero-word 300ms var(--ka-hero-ease) both; animation-delay: calc(var(--ka-hero-at, 0) * 1ms + var(--ka-hero-i, 0) * 70ms); }
.ka-hero-caret { display: inline-block; width: 2px; height: 1em; margin-left: 2px; vertical-align: -0.12em; border-radius: 1px; background: currentColor; animation: ka-hero-caret-life calc(var(--ka-hero-n, 0) * 70ms + 300ms) step-end both, ka-hero-blink 760ms steps(2, jump-none) infinite; animation-delay: calc(var(--ka-hero-at, 0) * 1ms), calc(var(--ka-hero-at, 0) * 1ms); }
.ka-hero-spin { animation: ka-hero-spin 1100ms linear infinite; }
.ka-hero-keyword { position: relative; font-weight: 600; color: var(--ka-hero-strong); background: var(--ka-hero-key); }
.ka-hero-keyword .ka-hero-keyflash { position: absolute; inset: -1px; border-radius: 5px; opacity: 0; visibility: hidden; box-shadow: 0 0 16px 2px var(--ka-hero-glow); background: var(--ka-hero-key); }

/* knowledge agent answer */
.ka-hero-badge { display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 9px; border-radius: 999px; background: linear-gradient(100deg, #4A6FA8, #786CA5 45%, #8A6A63); color: #FFFFFF; font-size: 11.5px; font-weight: 600; white-space: nowrap; }
/* Live's answer box carries a hairline border, the question line sits above it rather
   than inside it, and its source chips carry a document glyph. */
/* Live draws this box's hairline with an outline, which costs no layout, so the
   border is an inset shadow here for the same reason - a real 1px border put the
   card 2px over live's 307. */
.ka-hero-answer { position: relative; display: flex; flex-direction: column; gap: 10px; padding: 12px 13px; border-radius: 12px; background: var(--ka-hero-tile); box-shadow: inset 0 0 0 1px var(--ka-hero-tile-border); }
.ka-hero-answer::before { content: ''; position: absolute; inset: 0; border-radius: 12px; padding: 1px; background: linear-gradient(135deg, #93DEFD, #AE95DD 45%, #FCB56C); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; pointer-events: none; }
.ka-hero-thinking { display: flex; align-items: center; gap: 10px; }
.ka-hero-thinking img { flex: none; width: 18px; height: 18px; object-fit: contain; display: block; filter: var(--ka-hero-mark); }
.ka-hero-thinking span { font-size: 14px; font-weight: 500; color: var(--ka-hero-muted); }
.ka-hero-answer-body { display: flex; flex-direction: column; gap: 11px; }
.ka-hero-source { display: flex; align-items: center; gap: 8px; }
.ka-hero-meta { font-size: 12.5px; font-weight: 500; color: var(--ka-hero-label); }
.ka-hero-doc { display: flex; align-items: center; gap: 5px; padding: 3px 8px; border-radius: 8px; background: var(--ka-hero-chip); color: var(--ka-hero-chip-text); font-size: 11.5px; font-weight: 500; }
.ka-hero-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-height: 28px; }
.ka-hero-chip { display: flex; align-items: center; gap: 5px; padding: 5px 8px; line-height: 1.45; border-radius: 999px; background: var(--ka-hero-chip); border: 1px solid var(--ka-hero-tile-border); color: var(--ka-hero-chip-text); font-size: 11.5px; font-weight: 500; white-space: nowrap; flex: none; }

/* --- connected app (the CRM) -------------------------------------------------
   The export draws the CRM as a connected app rather than a browser window: a
   gradient initial, the account and the record set it exposes, a live
   "connected" pill, then the case and its four fields. It wears the same glass
   as the rest of the panel, which is the point - the CRM reads as another
   surface the agent already has open, not as a screenshot pasted in.
   The two fields the agent actually reads out of it - the reason and the rate -
   carry the highlight ring; the reservation and the loyalty tier do not.
   -------------------------------------------------------------------------- */
.ka-hero-crm { position: absolute; left: 0; top: 0; z-index: 2; width: 400px; transform-origin: 0 0; }
.ka-hero-crm-win { display: flex; flex-direction: column; overflow: hidden; border-radius: 12px; background: var(--ka-hero-crm-fill); border: 1px solid var(--ka-hero-border); box-shadow: inset 0 1px 0 var(--ka-hero-inset), var(--ka-hero-shadow); transition: background 460ms var(--ka-hero-ease); }
.ka-hero-crm-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--ka-hero-tile-border); cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
.ka-hero-crm-org { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ka-hero-crm-mark { display: flex; flex: none; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 8px; background: linear-gradient(140deg, #F0A86E, #DD7E95); color: #FFFFFF; font-size: 13px; font-weight: 700; }
.ka-hero-crm-org-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ka-hero-crm-org-name { font-size: 14px; font-weight: 600; letter-spacing: -0.005em; color: var(--ka-hero-strong); white-space: nowrap; }
.ka-hero-crm-org-sub { font-size: 12px; color: var(--ka-hero-label); white-space: nowrap; }
.ka-hero-crm-live { display: flex; flex: none; align-items: center; gap: 7px; padding: 4px 10px 4px 8px; border-radius: 999px; background: var(--ka-hero-tile); border: 1px solid var(--ka-hero-tile-border); font-size: 11.5px; font-weight: 600; color: var(--ka-hero-text); white-space: nowrap; }
.ka-hero-crm-live img { display: block; width: 12px; height: 12px; object-fit: contain; filter: var(--ka-hero-mark); }
.ka-hero-crm-live i { flex: none; width: 7px; height: 7px; border-radius: 999px; background: #4ADE80; }
.ka-hero-crm-head { display: flex; flex-direction: column; gap: 3px; padding: 14px 16px 4px; }
.ka-hero-crm-case { font-size: 12px; color: var(--ka-hero-label); }
.ka-hero-crm-name { font-size: 18px; font-weight: 600; letter-spacing: -0.01em; color: var(--ka-hero-strong); }
.ka-hero-crm-body { display: flex; flex-direction: column; gap: 8px; padding: 10px 16px 16px; }
.ka-hero-field { position: relative; display: flex; flex-direction: column; gap: 5px; padding: 11px 13px; border-radius: 10px; border: 1px solid var(--ka-hero-tile-border); background: var(--ka-hero-tile); }
.ka-hero-field-label { position: relative; font-size: 12px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; color: var(--ka-hero-label); opacity: 0.62; }
.ka-hero-field-value { position: relative; font-size: 14px; font-weight: 500; line-height: 1.45; letter-spacing: -0.005em; color: var(--ka-hero-strong); }
.ka-hero-ring { position: absolute; inset: -1px; border-radius: 10px; padding: 1px; pointer-events: none; background: linear-gradient(135deg, #93DEFD, #AE95DD 45%, #FCB56C); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
/* No `visibility: hidden` here, unlike the other glows: this one is paired with
   cue-glow rather than cue-flash, and field-glow never writes visibility - so
   hiding it would leave the wash invisible after the animation had landed at
   opacity 1, which is the opposite of what the beat is for. */
.ka-hero-ring-glow { position: absolute; inset: -1px; border-radius: 10px; pointer-events: none; opacity: 0; background: var(--ka-hero-hl); box-shadow: 0 0 22px var(--ka-hero-glow); }

/* --- transport -------------------------------------------------------------- */
.ka-hero-controls { position: absolute; left: 20px; right: 110px; bottom: 18px; z-index: 8; display: flex; justify-content: center; }
.ka-hero-controls-row { display: flex; align-items: center; gap: 10px; min-width: 0; max-width: 100%; }
.ka-hero-scenario { flex: none; margin-right: 4px; font-size: 14px; font-weight: 600; letter-spacing: 0.02em; color: var(--ka-hero-tl-strong); opacity: 0.85; white-space: nowrap; }
.ka-hero-tbtn { flex: none; display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; padding: 0; border: 1px solid var(--ka-hero-tl-border); border-radius: 999px; background: var(--ka-hero-btn); color: var(--ka-hero-tl-strong); -webkit-backdrop-filter: blur(18px) saturate(160%); backdrop-filter: blur(18px) saturate(160%); cursor: pointer; transition: filter 180ms ease; }
.ka-hero-tbtn:hover { filter: brightness(1.35); }
.ka-hero-steps { display: flex; align-items: stretch; gap: 14px; margin-left: 8px; min-width: 0; flex: none; }
.ka-hero-step { 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; }
.ka-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(--ka-hero-tl-label); transition: color 220ms ease; }
.ka-hero-step[data-ka-hero-cur="1"] .ka-hero-step-label { color: var(--ka-hero-tl-strong); }
.ka-hero-step-star { flex: none; }
/* The Impact step is not a beat of the call, so it is starred and takes the brand
   gradient rather than the white the other three fill to. The gradient rides the
   label's background and is only clipped to the glyphs while the step is current -
   the export leaves the background on in every state and lets the inherited colour
   win when it is not, so an inactive outcome step reads as muted text, not a
   half-painted gradient. */
.ka-hero-step-outcome .ka-hero-step-text { background: linear-gradient(90deg, #6E95F0, #AE95DD, #C98FD6); -webkit-background-clip: text; background-clip: text; }
.ka-hero-step-outcome[data-ka-hero-cur="1"] .ka-hero-step-text { color: transparent; }
.ka-hero-rail { position: relative; display: block; width: 100%; height: 2px; border-radius: 2px; overflow: hidden; background: var(--ka-hero-tl-track); }
.ka-hero-fill { position: absolute; inset: 0; transform: scaleX(0); transform-origin: 0 50%; background: var(--ka-hero-tl-strong); }
.ka-hero-step-outcome .ka-hero-fill { background: linear-gradient(90deg, #6E95F0, #AE95DD, #C98FD6); }
.ka-hero-theme { 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; }
.ka-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(--ka-hero-ease); }
.ka-hero[data-ka-hero-theme="light"] .ka-hero-knob { transform: translate3d(26px, 0, 0); }
.ka-hero[data-ka-hero-theme="light"] .ka-hero-moon { display: none; }
.ka-hero[data-ka-hero-theme="dark"] .ka-hero-sun { display: none; }
.ka-hero[data-ka-hero-paused="1"] .ka-hero-ico-pause, .ka-hero[data-ka-hero-paused="0"] .ka-hero-ico-play { display: none; }

/* --- reveal ----------------------------------------------------------------
   Product UI is hidden until the morph is most of the way open. The fade itself
   is the custom-property work at the top of the sheet; these rules are only the
   parts opacity cannot do: taking the hidden UI out of the a11y tree and off
   the pointer.
   -------------------------------------------------------------------------- */
.ka-hero-fade { visibility: hidden; transition: visibility 0s linear 300ms; }
.ka-hero[data-ka-hero-reveal="1"] .ka-hero-fade { visibility: visible; transition: visibility 0s; }
.ka-hero[data-ka-hero-reveal="0"] .ka-hero-fade { pointer-events: none; }
/* The glass is NOT suspended while scrolling. Doing so dropped the cards to
   their 30% fill with nothing behind it, so the wall's grain tile showed
   through sharp and the panel read as having lost its background - and it
   snapped back the moment the scroll settled. The live page keeps its glass
   through the morph and only drops the blur on a pill that has become a solid
   accent fill, where the blur showed as a pale ring inside it. */

/* --- story cues ------------------------------------------------------------
   Every staged element carries data-ka-hero-cue="<seconds>" (and optionally
   data-ka-hero-cue-out). The script only writes animation-delay, so the
   staging itself runs on the compositor and survives a busy main thread.
   Un-animated, each cue element sits in its finished state — which is what
   reduced motion and no-JS both get.
   -------------------------------------------------------------------------- */
/* One keyframe, four speeds. The export applies `ka-in` at 620ms to the cards,
   380ms to transcript lines, chips and the CRM, 300ms to the answer's source row
   and 320ms to the CRM field rings - so the duration is a per-element choice
   rather than a property of the entrance, and the default is the most common. */
.ka-hero-cue-rise { animation: ka-hero-rise 380ms var(--ka-hero-ease) both; }
.ka-hero-cue-rise-620 { animation-duration: 620ms; }
.ka-hero-cue-rise-320 { animation-duration: 320ms; }
.ka-hero-cue-rise-300 { animation-duration: 300ms; }
/* The out animation fills forwards, never backwards. With `both` its 0% state - fully
   visible - applies for the whole delay before the out, and it wins over the in
   animation's hidden 0%, so an element that fades out later sits visible from load. */
.ka-hero-cue-rise[data-ka-hero-cue-out] { animation: ka-hero-rise 380ms var(--ka-hero-ease) both, ka-hero-sink 320ms var(--ka-hero-ease) forwards; }
.ka-hero-cue-flash { animation: ka-hero-flash 1500ms var(--ka-hero-ease) both; }
/* The card's waking glow and the keyword's flash. Both were on cue-flash, which is
   the hero's generic 1.5s opacity blink; the export gives each its own shape - the
   card a 3.2s box-shadow swell, the keyword a 1.6s highlight peaking at 35%. */
.ka-hero-cue-winglow { animation: ka-hero-win-glow 3200ms cubic-bezier(.33, 0, .2, 1) both; }
.ka-hero-cue-kw { animation: ka-hero-kw-glow 1600ms var(--ka-hero-ease) both; }
.ka-hero-cue-on { animation: ka-hero-on 260ms var(--ka-hero-ease) both; }
.ka-hero-cue-fill { animation: ka-hero-fill 1000ms linear both; }
/* The read-out highlight, and the one cue that must not end where flash does:
   the CRM fields the agent has just read keep their wash, so the glow animation
   lands at opacity 1 with the shadow spent rather than taking the whole thing
   away again. */
.ka-hero-cue-glow { animation: ka-hero-field-glow 1400ms var(--ka-hero-ease) both; }
.ka-hero-cue-glow-slow { animation-duration: 1800ms; }
.ka-hero-cue-stream { animation: ka-hero-stream 700ms var(--ka-hero-ease) 60ms both; }
.ka-hero-cue-word { animation: ka-hero-word-land 720ms var(--ka-hero-ease) both; }
.ka-hero-cue-kpop { animation: ka-hero-kcheck-pop 520ms var(--ka-hero-ease) both; }
.ka-hero-cue-kdraw { animation: ka-hero-kcheck-draw 420ms var(--ka-hero-ease) 260ms both; }
.ka-hero-cue-bar { animation: ka-hero-bar-in 700ms var(--ka-hero-ease) 300ms both; }
.ka-hero[data-ka-hero-paused="1"] [data-ka-hero-cue],
.ka-hero[data-ka-hero-paused="1"] .ka-hero-w,
.ka-hero[data-ka-hero-paused="1"] .ka-hero-caret,
.ka-hero[data-ka-hero-paused="1"] .ka-hero-spin,
.ka-hero[data-ka-hero-offscreen="1"] [data-ka-hero-cue],
.ka-hero[data-ka-hero-offscreen="1"] .ka-hero-w,
.ka-hero[data-ka-hero-offscreen="1"] .ka-hero-caret,
.ka-hero[data-ka-hero-offscreen="1"] .ka-hero-spin { animation-play-state: paused; }

/* every cue starts fully hidden, not merely transparent */
/* The export's `ka-in`, used for every entrance in the hero. The 45% stop is not
   decoration: it is where opacity has already reached 0.92 while the blur is
   still clearing, which is what stops the rise reading as a slide. */
@keyframes ka-hero-rise { 0% { opacity: 0; visibility: hidden; transform: translate3d(0, 14px, 0) scale(0.965); filter: blur(12px) opacity(var(--ka-hero-vis-f, 1)); } 1% { visibility: visible; } 45% { opacity: 0.92; filter: blur(2px) opacity(var(--ka-hero-vis-f, 1)); } 100% { opacity: 1; visibility: visible; transform: none; filter: blur(0) opacity(var(--ka-hero-vis-f, 1)); } }
@keyframes ka-hero-sink { from { opacity: 1; transform: none; } to { opacity: 0; transform: translate3d(0, -6px, 0); visibility: hidden; } }
/* The transient beats are the only cue-out elements that sit in flow, so they are
   the only ones whose box has to go when they do. Fading one to `visibility:
   hidden` left it holding 18px plus the card's gap - live's Summary card is 307
   and the leftover beat put this one 28 over before the other metrics were read. */
@keyframes ka-hero-sink-beat { from { opacity: 1; transform: none; } to { opacity: 0; transform: translate3d(0, -6px, 0); display: none; } }
.ka-hero-transient[data-ka-hero-cue-out] { animation: ka-hero-rise 380ms var(--ka-hero-ease) both, ka-hero-sink-beat 320ms var(--ka-hero-ease) forwards; }
@keyframes ka-hero-flash { 0% { opacity: 0; visibility: hidden; } 1% { visibility: visible; } 22% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
@keyframes ka-hero-kw-glow { 0% { opacity: 0; visibility: hidden; } 1% { visibility: visible; } 35% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
@keyframes ka-hero-on { to { background-color: #6B4BD6; border-color: rgba(255, 255, 255, 0.16); color: #FFFFFF; } }
@keyframes ka-hero-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes ka-hero-word { 0% { opacity: 0; transform: translate3d(0, 0.3em, 0); } 100% { opacity: 1; transform: none; } }
@keyframes ka-hero-caret-life { from { visibility: visible; } to { visibility: hidden; } }
@keyframes ka-hero-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0.2; } }
@keyframes ka-hero-spin { to { transform: rotate(360deg); } }
@keyframes ka-hero-ping { 0% { transform: scale(1); opacity: 0.55; } 75%, 100% { transform: scale(2.2); opacity: 0; } }

/* The impact beat's own animations, and the one field highlight that has to outlive
   its own glow. field-glow ends at opacity 1 with the shadow spent, which is the
   whole point: the export keeps the landed field's highlight border on screen, where
   cue-flash takes the border away again at 100%. Only the keyframes the export
   actually applies are ported - it defines seventeen and uses nine. */
@keyframes ka-hero-field-glow { 0% { opacity: 0; box-shadow: 0 0 0 rgba(147, 222, 253, 0); } 35% { opacity: 1; box-shadow: 0 0 22px var(--ka-hero-glow); } 100% { opacity: 1; box-shadow: 0 0 0 rgba(147, 222, 253, 0); } }
@keyframes ka-hero-stream { from { opacity: 0; clip-path: inset(0 100% 0 0); filter: blur(4px); } 45% { opacity: 1; filter: blur(0); } to { opacity: 1; clip-path: inset(0 0 0 0); filter: blur(0); } }
@keyframes ka-hero-word-land { from { opacity: 0; transform: translateY(18px) scale(1.06); filter: blur(10px); } 60% { opacity: 1; filter: blur(0); } to { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); } }
@keyframes ka-hero-kcheck-pop { from { opacity: 0; transform: scale(0.6); } 55% { transform: scale(1.08); } to { opacity: 1; transform: scale(1); } }
@keyframes ka-hero-kcheck-draw { from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; } }
@keyframes ka-hero-bar-in { from { transform: scaleX(0); opacity: 0; } to { transform: scaleX(1); opacity: 1; } }
/* A swell, not a flash: the glow passes through a soft 18px/1px pass at 18% before
   its 26px/3px peak at 42%, then falls away. 3.2s and an ease-out-quint curve, so
   the card reads as waking up rather than blinking. */
@keyframes ka-hero-win-glow { 0% { box-shadow: 0 0 0 0 rgba(147, 222, 253, 0); } 18% { box-shadow: 0 0 18px 1px var(--ka-hero-glow-soft); } 42% { box-shadow: 0 0 26px 3px var(--ka-hero-glow); } 100% { box-shadow: 0 0 0 0 rgba(147, 222, 253, 0); } }

/* --- responsive ------------------------------------------------------------- */
@media (max-width: 1240px) { .ka-hero-scenario { display: none; } }
@media (max-width: 860px) {
  .ka-hero-track { height: calc(var(--ka-hero-avail) * 1.52); }
  .ka-hero-stage { flex-direction: column; }
  .ka-hero-chips { display: none; }
  .ka-hero-faces { flex: 0 0 200px; flex-direction: row; width: 100%; }
  .ka-hero-panel { min-height: 0; }
  .ka-hero-controls { left: 12px; right: 90px; }
  .ka-hero-steps { flex: 1 1 auto; gap: 8px; margin-left: 2px; }
  .ka-hero-step { flex: 0 0 22px; min-width: 0; }
  .ka-hero-step[data-ka-hero-cur="1"] { flex: 1 1 auto; }
  .ka-hero-step-label { visibility: hidden; font-size: 11px; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .ka-hero-step[data-ka-hero-cur="1"] .ka-hero-step-label { visibility: visible; }
  .ka-hero-grip, .ka-hero-crm-top { cursor: default; }
  /* There is no room for a 420px cluster and a 400px CRM side by side, so on a
     narrow panel the CRM becomes a bottom sheet: it rides up over the panel for
     the stretch of the call where the agent reads it (2.4s-6.6s, written by the
     script as data-ka-hero-sheet), then drops away. Only the two fields the
     answer leans on come up with it; the reservation and the tier stay hidden.
     The slide is a transform on the container (safe for the blur inside); the
     opacity half of the fade is --ka-hero-vis-s on the glass, per the top of
     the sheet. */
  .ka-hero-crm { left: 12px; right: 12px; top: auto; bottom: 76px; width: auto; z-index: 7; transform: translate3d(0, calc(100% + 76px), 0); transition: transform 720ms var(--ka-hero-ease); pointer-events: none; }
  .ka-hero[data-ka-hero-sheet="1"] .ka-hero-crm { transform: none; pointer-events: auto; }
  .ka-hero-crm-win { box-shadow: inset 0 1px 0 var(--ka-hero-inset), 0 -18px 48px rgba(6, 6, 12, 0.32); }
  .ka-hero-crm-x { display: none; }
}
@media (max-width: 600px) { .ka-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, which is the state that reads correctly as a still.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ka-hero { --ka-hero-vis-r: 1; transition: none; }
  .ka-hero-track { height: var(--ka-hero-avail); }
  .ka-hero-fade { visibility: visible; transition: none; }
  .ka-hero-transcript { -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 38px); mask-image: linear-gradient(180deg, transparent 0, #000 38px); }
  .ka-hero [data-ka-hero-cue], .ka-hero-w, .ka-hero-spin, .ka-hero-knob, .ka-hero-wall, .ka-hero-crm-win { animation: none; transition: none; }
  .ka-hero-caret { display: none; }
  .ka-hero-live::after { display: none; }
  .ka-hero-transient { display: none; }
  .ka-hero-fill { transform: scaleX(1); }
  .ka-hero-win-glow, .ka-hero-ring-glow, .ka-hero-keyflash { display: none; }
}
