/* ─────────────────────────────────────────────────────────────
   Crew Tower - live hero demo
   A macOS session drawn in the DOM: wallpaper, menu bar, notch,
   windows and dock. No video, no canvas, no build step, no images
   beyond our own sprite strips.

   ── how it is driven ──────────────────────────────────────────
   Two attributes on the root do everything:

       <div class="ct-stage" data-state="approval" data-autoplay="true">

   Every visual difference below is a selector off [data-state].
   Choreography inside a scene (the pointer walking to Allow, the
   tab handing over) is CSS animation whose duration is var(--ct-dwell),
   which hero-demo.js sets from the same per-scene dwell map that
   drives autoplay - retune a dwell and its choreography rescales.
   Scenes restart their animations on entry for free, because the
   selector stops matching and starts matching again.

   A third attribute, data-size, is set by the script from the
   mount's width: "full" is the desktop scene, "compact" (under
   700px) is a different composition, not the same one shrunk.

   The only visuals the script owns are --ct-scale (fit to the box)
   and --ct-h (the notch's height): CSS cannot interpolate a height
   to `auto`, and the notch must grow to exactly the card it shows.

   ── how it is sized ───────────────────────────────────────────
   .ct-screen is a canvas authored in fixed "design pixels", sized
   to 100%/scale and scaled back down, so it holds exactly as many
   design pixels as the container can fit: an ultrawide box gets a
   wider desktop, a portrait box a taller one. Nothing is
   letterboxed, nothing is cropped. Everything inside is placed
   from the centre line or from an edge, never from a canvas size.

   Geometry is the app's own, in points, multiplied by 1.5 so the
   pixel-art mascots land on whole-number scales:
     notch bar     32pt -> 48px      centre gap  190pt -> 285px
     wing         128pt -> 192px     collapsed   446pt -> 669px
     expanded     760pt -> 1140px
   669/2270 is the real 29.5% of a MacBook's width, which is what
   the canvas works out to on a full-bleed hero.

   Everything is namespaced ct- to stay out of styles.css's way.
   ───────────────────────────────────────────────────────────── */

.ct-demo, .ct-demo *, .ct-demo *::before, .ct-demo *::after,
.ct-stage, .ct-stage *, .ct-stage *::before, .ct-stage *::after {
  box-sizing: border-box;
}

/* The app's palette. Declared at the document root rather than on the stage
   because the notch fragments in the features section reuse the same brand
   marks, pips and rings outside any stage, and a second copy of these values
   would be free to drift from the app's. Everything below inherits it. */
:root {
  --ct-working:   var(--working,   #4ec97f);
  --ct-attention: var(--attention, #ff9f2e);
  --ct-asking:    var(--asking,    #59a6ff);
  --ct-done:      var(--done,      #6ee7c0);
  --ct-ok:        var(--ctx-ok,    #5cd18c);
  /* Pip palette, after the app gave green to "done" and let "working" go
     quiet. Working is announced by its spinning mark, so it needs no hue;
     idle is that same neutral turned down. The achromatic axis reads as
     "nothing for you here", which frees green to mean finished well. */
  --ct-pip-working: #e8e8e8;
  --ct-pip-done:    #30d158;
  --ct-pip-idle:    #5c5c5c;
  --ct-high:      var(--ctx-high,  #ffb847);
  --ct-crit:      var(--ctx-crit,  #ff6b47);
}

.ct-stage {
  --ct-sans:    var(--sans, ui-sans-serif, -apple-system, "Segoe UI", sans-serif);
  --ct-mono:    var(--mono, ui-monospace, "SF Mono", Menlo, monospace);
  --ct-display: var(--font-display, "Inter", ui-sans-serif, system-ui, sans-serif);

  --ct-scale: 1;        /* design px -> css px, set by hero-demo.js */
  --ct-dwell: 6000ms;   /* how long the current scene lasts */
  --ct-collapsed: 669px;/* notch body width, collapsed */
  --ct-br-collapsed: 18px;
  --ct-br-open: 45px;
  --ct-w: var(--ct-collapsed);
  --ct-h: 48px;         /* notch body height, bar + whatever card is open */
  --ct-br: var(--ct-br-collapsed);
  --ct-open: 0;         /* 0 collapsed, 1 expanded */
  --ct-panel: 1140px;   /* expanded panel width */

  /* The collapsed bar's geometry, so every part of it scales together
     rather than being pinned in px. The app's own numbers x1.5:
       collapsed 446pt -> 669px, gap 190pt -> 285px, bar 32pt -> 48px,
     which is an aspect of 669/48 = 13.94 : 1.
     hero-demo.js recomputes these for the phone composition from
     --ct-notch-ratio so the notch stays the same share of the screen as
     it is on a Mac. Raise the ratio if you want the character legible on
     a phone; it is the one knob that trades the two off. */
  --ct-notch-ratio: .295;
  --ct-bar: 48px;       /* bar height */
  --ct-gap: 285px;      /* the camera housing: always empty */
  --ct-mascot-k: 1;     /* mascot scale inside the bar */

  position: relative;
  width: 100%;
  height: 100%;         /* the mount must be sized; the fold decides how */
  min-height: 320px;    /* so it can never collapse to nothing */
  overflow: hidden;
  isolation: isolate;
}

/* Wrapped in .ct-demo the stage takes the room the
   control strip does not, so both fit whatever height the wrapper is given. */
.ct-demo { display: flex; flex-direction: column; height: 100%; }
.ct-demo > .ct-stage { height: auto; flex: 1 1 0; }

/* The panel is open in exactly these scenes. */
.ct-stage[data-state="approval"],
.ct-stage[data-state="ask"],
.ct-stage[data-state="jump"] {
  --ct-w: var(--ct-panel);
  --ct-br: var(--ct-br-open);
  --ct-open: 1;
}

.ct-screen {
  position: absolute;
  top: 0;
  left: 0;
  width: calc(100% / var(--ct-scale));
  height: calc(100% / var(--ct-scale));
  transform: scale(var(--ct-scale));
  transform-origin: top left;
  overflow: hidden;
  font-family: var(--ct-sans);
  color: #fff;
  -webkit-font-smoothing: antialiased;
}

/* ── wallpaper ───────────────────────────────────────────────── */
/* Ours, not Apple's: six layered radial fields at different radii and
   opacities over a deep vertical bed, a vignette to weight the edges,
   and a grain layer - a big flat gradient bands without one, and the
   banding is what makes these look cheap. */

/* A bright Sequoia-style desktop: cool blue on the left sweeping through
   violet into warm amber on the right, with soft rays fanning from the lower
   right. The wallpaper has to be LIGHT - the whole point of the composition is
   a dark Crew Tower panel sitting on a bright desktop. A dark wallpaper makes
   the product disappear into its own background. */
/* Published at the document root, not on the stage, because the page paints
   a blurred copy of this same wallpaper behind the screen to make it glow.
   Custom properties only inherit downwards, and that glow lives on .product,
   which is the stage's parent. Every stop here is a percentage, so the one
   declaration works at both sizes. */
:root {
  --ct-wallpaper:
    /* the rays */
    repeating-conic-gradient(from 196deg at 78% 116%,
      rgba(255, 255, 255, .10) 0deg 4deg,
      rgba(255, 255, 255, 0)   4deg 11deg),
    /* colour fields */
    radial-gradient(72% 84% at 96% 88%, rgba(255, 186, 92, .95), transparent 62%),
    radial-gradient(58% 72% at 74% 96%, rgba(255, 148, 96, .70), transparent 66%),
    radial-gradient(64% 78% at 40% 92%, rgba(168, 122, 240, .62), transparent 68%),
    radial-gradient(72% 86% at 6%  74%, rgba(58, 118, 232, .88), transparent 66%),
    radial-gradient(80% 60% at 30% 4%,  rgba(126, 176, 255, .70), transparent 70%),
    linear-gradient(160deg, #5b8de6 0%, #7b7ae0 34%, #b884d8 58%, #f0a271 82%, #ffc98a 100%);
}

.ct-wall {
  position: absolute;
  inset: 0;
  background: var(--ct-wallpaper);
}

/* Three slow fields drifting over the bed. Transform and opacity only,
   so they live on the compositor; 40s+ periods so it reads as light
   moving rather than an animation playing. */
.ct-aurora {
  position: absolute;
  border-radius: 50%;
  will-change: transform;
  pointer-events: none;
}
.ct-aurora-1 {
  left: 2%; top: -34%;
  width: 62%; height: 108%;
  background: radial-gradient(closest-side, rgba(86, 170, 255, .34), transparent 72%);
  filter: blur(70px);
  animation: ct-drift-a 46s ease-in-out infinite;
}
.ct-aurora-2 {
  right: -6%; top: -26%;
  width: 58%; height: 96%;
  background: radial-gradient(closest-side, rgba(30, 200, 190, .26), transparent 74%);
  filter: blur(80px);
  animation: ct-drift-b 61s ease-in-out infinite;
}
.ct-aurora-3 {
  left: 28%; top: 6%;
  width: 52%; height: 78%;
  background: radial-gradient(closest-side, rgba(126, 108, 255, .20), transparent 76%);
  filter: blur(90px);
  animation: ct-drift-c 53s ease-in-out infinite;
}
@keyframes ct-drift-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(6%, 4%, 0) scale(1.12); }
}
@keyframes ct-drift-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1.08); }
  50%      { transform: translate3d(-7%, 3%, 0) scale(1); }
}
@keyframes ct-drift-c {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: .9; }
  50%      { transform: translate3d(4%, -3%, 0) scale(1.1); opacity: .6; }
}

/* Grain. Not optional: it is what stops the gradients banding. */
.ct-grain {
  position: absolute;
  inset: -10%;
  opacity: .055;
  pointer-events: none;
  background-repeat: repeat;
  background-size: 200px 200px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
}

.ct-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(128% 92% at 50% 26%, transparent 40%, rgba(0, 0, 0, .30) 74%, rgba(0, 0, 0, .62) 100%),
    linear-gradient(to bottom, rgba(0, 0, 0, .28), transparent 22%);
}

/* ── windows ─────────────────────────────────────────────────── */
/* Held to the right of the centre line so the left of the scene stays
   clear for the statement, at every canvas width. */

/* A real macOS terminal: translucent dark glass over the desktop, a hairline
   light edge along the top where the window catches the light, and a deep
   soft shadow. The desktop showing faintly through is what makes it read as
   a window rather than a black rectangle. */
.ct-win {
  position: absolute;
  left: 50%;
  border-radius: 12px;
  overflow: hidden;
  background: rgba(22, 20, 30, .74);
  backdrop-filter: blur(28px) saturate(150%);
  -webkit-backdrop-filter: blur(28px) saturate(150%);
  box-shadow:
    0 34px 80px rgba(24, 14, 44, .46),
    0 8px 22px rgba(24, 14, 44, .28),
    inset 0 1px 0 rgba(255, 255, 255, .14),
    0 0 0 .5px rgba(0, 0, 0, .45);
  will-change: transform;
}

/* Both windows have to finish inside the canvas. The canvas is 1700 design
   px wide, so anything past +850 is sliced off by the frame and the window
   ends on a hard square edge instead of its own rounded corner - which is
   exactly what made these read as broken rectangles. Right edges now stop at
   +780 and the pair sits closer to the middle. */
.ct-win-term   { margin-left: -300px; width: 900px; height: 318px; top: max(486px, 50%); z-index: 3; }
.ct-win-editor { margin-left: 60px;   width: 720px; height: 300px; top: max(452px, 46%); z-index: 2; }

/* The window you are not in sits back a touch and loses its contrast. */
.ct-win-editor { transform: scale(.985) translate3d(0, 6px, 0); filter: brightness(.7) saturate(.85); }

.ct-win-title {
  height: 42px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
  /* the title bar is part of the same glass, just a shade lighter */
  background: linear-gradient(rgba(255, 255, 255, .07), rgba(255, 255, 255, .03));
  border-bottom: 1px solid rgba(0, 0, 0, .28);
}
.ct-lights { display: flex; gap: 8px; }
.ct-light { width: 13px; height: 13px; border-radius: 50%; background: #3a3f48; }
.ct-win-term .ct-light-r { background: #ff5f57; }
.ct-win-term .ct-light-y { background: #febc2e; }
.ct-win-term .ct-light-g { background: #28c840; }
/* macOS centres the window title over the whole bar, not beside the lights */
.ct-win-name {
  position: absolute;
  left: 0; right: 0;
  text-align: center;
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -.01em;
  color: rgba(255, 255, 255, .68);
  pointer-events: none;
}
.ct-win-title { position: relative; }

.ct-editor-body {
  height: calc(100% - 42px);
  background: #0e1116;
  padding: 18px 22px;
  font-family: var(--ct-mono);
  font-size: 14px;
  line-height: 1.75;
  color: rgba(255, 255, 255, .30);
}
.ct-editor-body span { color: rgba(120, 170, 255, .45); }
.ct-editor-body em { color: rgba(255, 184, 71, .45); font-style: normal; }

/* ── terminal tabs ───────────────────────────────────────────── */

.ct-tabs {
  display: flex;
  gap: 2px;
  height: 41px;
  padding: 6px 8px 0;
  background: rgba(0, 0, 0, .18);
  border-bottom: 1px solid rgba(0, 0, 0, .3);
}
.ct-tab {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 0 10px;
  border-radius: 8px 8px 0 0;
  font-size: 14px;
  color: rgba(255, 255, 255, .45);
  background: transparent;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ct-tab i {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ct-working);
  flex: none;
}
.ct-tab::after {
  content: "";
  position: absolute;
  left: 10px; right: 10px; bottom: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--ct-high);
  opacity: 0;
}
.ct-tab-0 { background: #1d2129; color: rgba(255, 255, 255, .95); }
.ct-tab-0::after { opacity: 1; }

/* jump: the tab hands over partway through the scene */
.ct-stage[data-state="jump"] .ct-tab-0        { animation: ct-tab-off var(--ct-dwell) both; }
.ct-stage[data-state="jump"] .ct-tab-0::after { animation: ct-under-off var(--ct-dwell) both; }
.ct-stage[data-state="jump"] .ct-tab-1        { animation: ct-tab-on var(--ct-dwell) both; }
.ct-stage[data-state="jump"] .ct-tab-1::after { animation: ct-under-on var(--ct-dwell) both; }
@keyframes ct-tab-off {
  0%, 44%   { background: #1d2129; color: rgba(255, 255, 255, .95); }
  56%, 100% { background: rgba(29, 33, 41, 0); color: rgba(255, 255, 255, .45); }
}
@keyframes ct-tab-on {
  0%, 44%   { background: rgba(29, 33, 41, 0); color: rgba(255, 255, 255, .45); }
  56%, 100% { background: #1d2129; color: rgba(255, 255, 255, .95); }
}
@keyframes ct-under-off { 0%, 44% { opacity: 1; } 56%, 100% { opacity: 0; } }
@keyframes ct-under-on  { 0%, 44% { opacity: 0; } 56%, 100% { opacity: 1; } }

.ct-term-body {
  position: relative;
  height: calc(100% - 42px - 41px);
  background: #0d1015;
  overflow: hidden;
}
.ct-pane {
  position: absolute;
  inset: 0;
  padding: 16px 20px;
  font-family: var(--ct-mono);
  font-size: 15px;
  line-height: 1.62;
  color: rgba(238, 235, 250, .9);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
  opacity: 0;
}
.ct-pane-0 { opacity: 1; }
.ct-stage[data-state="jump"] .ct-pane-0 { animation: ct-pane-off var(--ct-dwell) both; }
.ct-stage[data-state="jump"] .ct-pane-1 { animation: ct-pane-on  var(--ct-dwell) both; }
@keyframes ct-pane-off { 0%, 44% { opacity: 1; } 52%, 100% { opacity: 0; } }
@keyframes ct-pane-on  { 0%, 48% { opacity: 0; } 56%, 100% { opacity: 1; } }

.ct-pane b { color: var(--ct-ok); font-weight: 500; }
.ct-pane i { color: rgba(255, 255, 255, .38); font-style: normal; }
.ct-pane u { color: var(--ct-high); text-decoration: none; }
.ct-line {
  height: 24px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Output that only exists once the request has been made and answered.
   Hidden lines take no room, so the caret rides up with them. */
.ct-line-ask, .ct-line-wait, .ct-line-done { height: 0; opacity: 0; }
.ct-stage[data-state="approval"] .ct-line-ask  { animation: ct-line-in var(--ct-dwell) both; }
.ct-stage[data-state="approval"] .ct-line-wait { animation: ct-line-in var(--ct-dwell) both; animation-delay: calc(var(--ct-dwell) * .05); }
.ct-stage[data-state="approval"] .ct-line-done { animation: ct-line-late var(--ct-dwell) both; }
@keyframes ct-line-in   { 0% { height: 0; opacity: 0; } 6%, 100% { height: 24px; opacity: 1; } }
@keyframes ct-line-late { 0%, 62% { height: 0; opacity: 0; } 70%, 100% { height: 24px; opacity: 1; } }

.ct-caret {
  display: inline-block;
  width: 9px;
  height: 17px;
  vertical-align: -3px;
  background: rgba(255, 255, 255, .75);
  animation: ct-blink 1.1s steps(1) infinite;
}
@keyframes ct-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* jump also brings the terminal forward */
.ct-stage[data-state="jump"] .ct-win-term   { animation: ct-raise var(--ct-dwell) both; }
.ct-stage[data-state="jump"] .ct-win-editor { animation: ct-lower var(--ct-dwell) both; }
@keyframes ct-raise {
  0%, 40%   { transform: scale(.985) translate3d(0, 6px, 0); filter: brightness(.72) saturate(.85); }
  52%       { transform: scale(1.012) translate3d(0, -2px, 0); filter: none; }
  60%, 100% { transform: none; filter: none; }
}
@keyframes ct-lower {
  0%, 40%   { transform: none; filter: none; }
  60%, 100% { transform: scale(.985) translate3d(0, 6px, 0); filter: brightness(.7) saturate(.85); }
}

/* ── menu bar ────────────────────────────────────────────────── */

.ct-menubar {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: var(--ct-bar);
  display: flex;
  align-items: center;
  padding: 0 22px;
  /* No fill. A real Mac menu bar is fully transparent - the wallpaper runs
     straight under it and the labels carry their own legibility. The tinted
     strip we had was the single most obvious tell that this was a drawing. */
  background: none;
  font-size: 18px;
  z-index: 10;
}
.ct-mb-l, .ct-mb-r { display: flex; align-items: center; gap: 22px; }
.ct-mb-r { margin-left: auto; }
/* with nothing behind them, the labels need their own shadow */
.ct-menubar span,
.ct-menubar i { text-shadow: 0 1px 3px rgba(8, 12, 28, .45); }
.ct-mb-app { font-weight: 600; color: #fff; }
.ct-mb-l span:not(.ct-mb-app) { color: rgba(255, 255, 255, .8); }
.ct-mb-clock { color: rgba(255, 255, 255, .92); font-variant-numeric: tabular-nums; }

/* ── the notch ───────────────────────────────────────────────── */
/* Flush with the very top edge of the container: square above the
   flare, rounded only below. */

.ct-notch {
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--ct-w);
  margin-left: calc(var(--ct-w) / -2);
  z-index: 30;
  transition: width .46s cubic-bezier(.22, 1, .28, 1), margin-left .46s cubic-bezier(.22, 1, .28, 1);
}
/* Opening the panel is an animation; being resized is not. Without this
   the notch spends half a second sliding to its new geometry every time
   the container changes, which reads as lag. */
.ct-stage.is-fitting .ct-notch,
.ct-stage.is-fitting .ct-notch-body,
.ct-stage.is-fitting .ct-statements { transition: none !important; }
.ct-notch-body {
  position: relative;
  height: var(--ct-h);
  background: #000;
  border-radius: 0 0 var(--ct-br) var(--ct-br);
  overflow: hidden;
  transition: height .46s cubic-bezier(.22, 1, .28, 1), border-radius .46s ease;
}
.ct-notch::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 0 0 var(--ct-br) var(--ct-br);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .62);
  opacity: var(--ct-open);
  transition: opacity .4s ease;
  pointer-events: none;
  z-index: -1;
}

/* NotchShape's outward flare: a quarter circle carved out of a black
   square sitting beside the body. */
.ct-flare { position: absolute; top: 0; width: 16px; height: 16px; }
.ct-flare-l { right: 100%; background: radial-gradient(circle at 0 100%, transparent 15.5px, #000 16px); }
.ct-flare-r { left: 100%;  background: radial-gradient(circle at 100% 100%, transparent 15.5px, #000 16px); }

/* Everything in the bar is a fraction of the bar's height, so the wings,
   the gap, the dots and the character all scale with the notch. */
.ct-bar {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: var(--ct-bar);
  display: flex;
  align-items: center;
}
.ct-wing { display: flex; align-items: center; height: var(--ct-bar); }
.ct-wing-l {
  flex: 1;
  gap: calc(var(--ct-bar) * .167);
  padding-left: calc(var(--ct-bar) * .44);
  opacity: calc(1 - var(--ct-open));
  transition: opacity .3s ease;
}
.ct-gap { width: var(--ct-gap); flex: none; }
.ct-wing-r { flex: 1; justify-content: flex-end; padding-right: calc(var(--ct-bar) * .44); }

/* ── agent brand marks ───────────────────────────────────────── */
/* The app retired the status dot. Every session now wears its agent's own
   logo, and a coloured pip on the corner says what that session wants.
   Ported from AgentIcon / AgentBadge / StatusPip at the scene's 1.5x scale,
   so the app's 20pt mark reads as 30 design px here.

   The logos are template-rendered in SwiftUI, which is a mask: the asset's
   alpha is the shape and the tint is painted through it. A CSS mask does
   exactly the same thing, so the shipped SVGs are reused untouched.

   The mark carries the mask, never the wrapper: a masked element clips its
   own children, and the badge and pip deliberately hang off the corner. */

/* The app draws these at 20pt in a 32pt bar, which is 62.5% of its height
   and leaves 6pt of air. That ratio is right at 1x on a real Mac; here the
   whole scene is scaled to about 0.6, so the same 6pt lands as ~5 css px and
   the bar reads cramped. The mark comes down to 54% of the bar, which is the
   one dimension that can flex: the notch's own height and width are
   calibrated against the implied screen and must not move. */
.ct-agent {
  --ct-agent-size: 26px;
  position: relative;
  flex: none;
  display: block;
  width: var(--ct-agent-size);
  height: var(--ct-agent-size);
}
.ct-agent-mark {
  position: absolute;
  inset: 0;
  background-color: var(--ct-tint, #fff);
  -webkit-mask: var(--ct-src) center / contain no-repeat;
          mask: var(--ct-src) center / contain no-repeat;
}
/* Rotation means "this session is working" and nothing else. A session
   blocked on your answer is not working, so it holds still. */
.ct-agent.is-spinning .ct-agent-mark { animation: ct-agent-spin 4s linear infinite; }
@keyframes ct-agent-spin { to { transform: rotate(360deg); } }

/* The roster, matching AgentIconStyle exactly. Three of the six are white,
   which is what keeps the strip from turning into a fruit salad. */
.ct-agent-claude   { --ct-tint: #d97757; --ct-src: url("assets/brand/claude.svg"); }
.ct-agent-codex    { --ct-tint: #ffffff; --ct-src: url("assets/brand/codex.svg"); }
.ct-agent-cursor   { --ct-tint: #ffffff; --ct-src: url("assets/brand/cursor.svg"); }
.ct-agent-gemini   { --ct-tint: #3186ff; --ct-src: url("assets/brand/gemini.svg"); }
.ct-agent-qwen     { --ct-tint: #6f69f7; --ct-src: url("assets/brand/qwen.svg"); }
.ct-agent-opencode { --ct-tint: #ffffff; --ct-src: url("assets/brand/opencode.svg"); }
.ct-agent-unknown  { --ct-tint: #9e9e9e; --ct-src: url("assets/brand/unknown.svg"); }
/* our own pixel art, so it must not be smoothed */
.ct-agent-unknown .ct-agent-mark { -webkit-mask-size: contain; mask-size: contain; image-rendering: pixelated; }

/* The pip: what this one session is doing, worn on the mark's corner.
   Colours are SessionSignalColors after the app gave green to "done" and
   let "working" go quiet, so only the three states that want something
   from you carry a hue. */
/* Badge and pip are sized off the mark, not in fixed px, so the app's
   proportions survive any change to the mark's size. */
.ct-agent-pip {
  position: absolute;
  top: calc(var(--ct-agent-size) * -.15);
  right: calc(var(--ct-agent-size) * -.2);
  width: calc(var(--ct-agent-size) * .4);
  height: calc(var(--ct-agent-size) * .4);
  border-radius: 50%;
  background: var(--ct-pip, var(--ct-pip-working));
  /* a dark collar, so the pip reads as an object sitting on the mark */
  box-shadow: 0 0 0 2.25px #000;
}
.ct-pip-working { --ct-pip: var(--ct-pip-working); }
.ct-pip-perm    { --ct-pip: var(--ct-attention); }
.ct-pip-ask     { --ct-pip: var(--ct-asking); }
.ct-pip-done    { --ct-pip: var(--ct-pip-done); }
.ct-pip-idle    { --ct-pip: var(--ct-pip-idle); }

/* The badge: how many of this brand's sessions want you. Never drawn at
   zero, so a busy-but-not-blocked brand stays quiet. */
.ct-badge {
  position: absolute;
  top: calc(var(--ct-agent-size) * -.2);
  right: calc(var(--ct-agent-size) * -.25);
  min-width: calc(var(--ct-agent-size) * .55);
  height: calc(var(--ct-agent-size) * .55);
  padding: 0 calc(var(--ct-agent-size) * .1);
  border-radius: 999px;
  font-size: calc(var(--ct-agent-size) * .4);
  font-weight: 700;
  line-height: 1;
  background: #ff5f57;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 2.25px #000;
  opacity: 0;
  transform: scale(.6);
}
.ct-stage[data-state="approval"] .ct-badge-claude,
.ct-stage[data-state="ask"] .ct-badge-cursor { animation: ct-badge var(--ct-dwell) both; }
@keyframes ct-badge {
  0%        { opacity: 0; transform: scale(.6); }
  4%, 62%   { opacity: 1; transform: scale(1); }
  70%, 100% { opacity: 0; transform: scale(.6); }
}

/* The blocked session stops spinning and its pip takes the state's colour,
   for exactly as long as the badge is up. */
.ct-stage[data-state="approval"] .ct-card-permission .ct-agent-pip,
.ct-stage[data-state="approval"] .ct-agent-claude .ct-agent-pip {
  animation: ct-pip-perm var(--ct-dwell) both;
}
.ct-stage[data-state="ask"] .ct-card-question .ct-agent-pip,
.ct-stage[data-state="ask"] .ct-agent-cursor .ct-agent-pip {
  animation: ct-pip-ask var(--ct-dwell) both;
}
@keyframes ct-pip-perm {
  0%, 62%   { background: var(--ct-attention); }
  70%, 100% { background: var(--ct-pip-done); }
}
@keyframes ct-pip-ask {
  0%, 62%   { background: var(--ct-asking); }
  70%, 100% { background: var(--ct-pip-done); }
}

/* ── mascots ─────────────────────────────────────────────────── */
/* Mood follows the crew, exactly as CharacterDirector does in the app:
     any session working        -> active
     a session needs attention  -> alert (resting if that character has
                                   no alert strip, which is the app's
                                   own fallback)
     a request just answered    -> celebrate, briefly
     nothing running            -> resting
   Since each scene *is* a crew state, the moods below are that mapping:
     overview  every session working          -> active
     approval  one blocked, then answered     -> alert, then celebrate
     ask       one asking, then answered      -> alert, then celebrate
     jump      every session working          -> active
     character every session working          -> active, cast rotating

   Each strip is cropped to its alpha content box, unioned across that
   character's moods so it cannot jump when the mood changes - the same
   thing CharacterFrames.contentBox does - and drawn at 1x, a whole
   number, never a fraction. Measured off the shipped PNGs:
     cat 58x46 @(0,18)   bunny 32x26 @(0,6)    ghost 25x29 @(1,3)
     mushroom 31x35 @(24,29)                   samurai 69x48 @(27,21)
   The samurai carries scale 1.3 in its manifest and is duly the
   tallest of the five at 48px. */

.ct-mascot {
  position: relative;
  width: 69px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: scale(var(--ct-mascot-k));
  transform-origin: center right;
}
.ct-sprite {
  position: absolute;
  display: block;
  image-rendering: pixelated;
  background-repeat: no-repeat;
  opacity: 0;
  will-change: background-position;
}

/* cat - 64x64 cell */
.ct-cat { width: 58px; height: 46px; background-position: 0 -18px; }
.ct-cat-idle  { background-image: url("assets/characters/cat-idle.png");    background-size: 384px 64px; animation: ct-cat-6 1s    steps(6) infinite; }
.ct-cat-run   { background-image: url("assets/characters/cat-running.png"); background-size: 384px 64px; animation: ct-cat-6 .5s   steps(6) infinite; }
.ct-cat-alert { background-image: url("assets/characters/cat-alert.png");   background-size: 256px 64px; animation: ct-cat-4 .5s   steps(4) infinite; }
.ct-cat-happy { background-image: url("assets/characters/cat-happy.png");   background-size: 640px 64px; animation: ct-cat-10 .833s steps(10) infinite; }
@keyframes ct-cat-6  { from { background-position-x: 0; } to { background-position-x: -384px; } }
@keyframes ct-cat-4  { from { background-position-x: 0; } to { background-position-x: -256px; } }
@keyframes ct-cat-10 { from { background-position-x: 0; } to { background-position-x: -640px; } }

/* bunny - 32x32 cell, no alert strip: falls back to resting */
.ct-bunny { width: 32px; height: 26px; background-position: 0 -6px; }
.ct-bunny-idle { background-image: url("assets/characters/bunny-idle.png");    background-size: 384px 32px; animation: ct-bun-12 1.5s  steps(12) infinite; }
.ct-bunny-run  { background-image: url("assets/characters/bunny-running.png"); background-size: 256px 32px; animation: ct-bun-8  .667s steps(8)  infinite; }
@keyframes ct-bun-12 { from { background-position-x: 0; } to { background-position-x: -384px; } }
@keyframes ct-bun-8  { from { background-position-x: 0; } to { background-position-x: -256px; } }

/* ghost - 32x32 cell */
.ct-ghost { width: 25px; height: 29px; background-position: -1px -3px; }
.ct-ghost-idle  { background-image: url("assets/characters/ghost-idle.png");    background-size: 256px 32px; animation: ct-gh-8  1.333s steps(8)  infinite; }
.ct-ghost-run   { background-image: url("assets/characters/ghost-running.png"); background-size: 256px 32px; animation: ct-gh-8  .8s    steps(8)  infinite; }
.ct-ghost-alert { background-image: url("assets/characters/ghost-alert.png");   background-size: 480px 32px; animation: ct-gh-15 1.5s   steps(15) infinite; }
@keyframes ct-gh-8  { from { background-position-x: -1px; } to { background-position-x: -257px; } }
@keyframes ct-gh-15 { from { background-position-x: -1px; } to { background-position-x: -481px; } }

/* mushroom - 80x64 cell, no alert strip */
.ct-mushroom { width: 31px; height: 35px; background-position: -24px -29px; }
.ct-mushroom-idle { background-image: url("assets/characters/mushroom-idle.png");    background-size: 560px 64px; animation: ct-mu-7 1s  steps(7) infinite; }
.ct-mushroom-run  { background-image: url("assets/characters/mushroom-running.png"); background-size: 640px 64px; animation: ct-mu-8 .8s steps(8) infinite; }
@keyframes ct-mu-7 { from { background-position-x: -24px; } to { background-position-x: -584px; } }
@keyframes ct-mu-8 { from { background-position-x: -24px; } to { background-position-x: -664px; } }

/* samurai - 96x96 cell */
.ct-samurai { width: 69px; height: 48px; background-position: -27px -21px; }
.ct-samurai-idle  { background-image: url("assets/characters/samurai-idle.png");    background-size: 480px 96px; animation: ct-sa-5 .833s steps(5) infinite; }
.ct-samurai-run   { background-image: url("assets/characters/samurai-running.png"); background-size: 768px 96px; animation: ct-sa-8 .667s steps(8) infinite; }
.ct-samurai-alert { background-image: url("assets/characters/samurai-alert.png");   background-size: 576px 96px; animation: ct-sa-6 .429s steps(6) infinite; }
@keyframes ct-sa-5 { from { background-position-x: -27px; } to { background-position-x: -507px; } }
@keyframes ct-sa-8 { from { background-position-x: -27px; } to { background-position-x: -795px; } }
@keyframes ct-sa-6 { from { background-position-x: -27px; } to { background-position-x: -603px; } }

/* The crew is working: the cat runs. */
.ct-stage[data-state="overview"] .ct-cat-run,
.ct-stage[data-state="jump"]     .ct-cat-run { opacity: 1; }

/* One is blocked: alert, then delighted the moment it is answered. */
.ct-stage[data-state="approval"] .ct-cat-alert,
.ct-stage[data-state="ask"]      .ct-cat-alert {
  animation: ct-cat-4 .5s steps(4) infinite, ct-mood-out var(--ct-dwell) both;
}
.ct-stage[data-state="approval"] .ct-cat-happy,
.ct-stage[data-state="ask"]      .ct-cat-happy {
  animation: ct-cat-10 .833s steps(10) infinite, ct-mood-in var(--ct-dwell) both;
}
@keyframes ct-mood-out { 0%, 62% { opacity: 1; } 68%, 100% { opacity: 0; } }
@keyframes ct-mood-in  { 0%, 62% { opacity: 0; } 68%, 100% { opacity: 1; } }

/* The character scene hands the wing round the whole cast - all of them
   in their active mood, because the crew is working the whole time. */
.ct-stage[data-state="character"] .ct-cat-run      { animation: ct-cat-6 .5s steps(6) infinite,   ct-char-1 var(--ct-dwell) both; }
.ct-stage[data-state="character"] .ct-bunny-run    { animation: ct-bun-8 .667s steps(8) infinite, ct-char-2 var(--ct-dwell) both; }
.ct-stage[data-state="character"] .ct-mushroom-run { animation: ct-mu-8 .8s steps(8) infinite,    ct-char-3 var(--ct-dwell) both; }
.ct-stage[data-state="character"] .ct-ghost-run    { animation: ct-gh-8 .8s steps(8) infinite,    ct-char-4 var(--ct-dwell) both; }
.ct-stage[data-state="character"] .ct-samurai-run  { animation: ct-sa-8 .667s steps(8) infinite,  ct-char-5 var(--ct-dwell) both; }
@keyframes ct-char-1 { 0%, 12% { opacity: 1; } 17%, 82% { opacity: 0; } 87%, 100% { opacity: 1; } }
@keyframes ct-char-2 { 0%, 12% { opacity: 0; } 17%, 29% { opacity: 1; } 34%, 100% { opacity: 0; } }
@keyframes ct-char-3 { 0%, 29% { opacity: 0; } 34%, 46% { opacity: 1; } 51%, 100% { opacity: 0; } }
@keyframes ct-char-4 { 0%, 46% { opacity: 0; } 51%, 63% { opacity: 1; } 68%, 100% { opacity: 0; } }
@keyframes ct-char-5 { 0%, 63% { opacity: 0; } 68%, 82% { opacity: 1; } 87%, 100% { opacity: 0; } }

/* ── the panel ───────────────────────────────────────────────── */

.ct-panel {
  position: absolute;
  top: var(--ct-bar); left: 0;
  width: var(--ct-panel);
  opacity: 0;
  transition: opacity .26s ease;
}
.ct-stage[data-state="approval"] .ct-panel,
.ct-stage[data-state="ask"] .ct-panel,
.ct-stage[data-state="jump"] .ct-panel { opacity: 1; transition-delay: .12s; }

.ct-card {
  position: absolute;
  top: 0; left: 0;
  width: var(--ct-panel);
  padding: 9px 18px 18px;
  opacity: 0;
  transition: opacity .26s ease;
}
.ct-stage[data-state="approval"] .ct-card-permission,
.ct-stage[data-state="ask"] .ct-card-question,
.ct-stage[data-state="jump"] .ct-card-list { opacity: 1; }

.ct-row {
  padding: 12px 18px;
  border-radius: 21px;
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 0 0 1px transparent;
}
.ct-row + .ct-row { margin-top: 9px; }

.ct-stage[data-state="approval"] .ct-card-permission .ct-row { animation: ct-edge-warm var(--ct-dwell) both; }
.ct-stage[data-state="ask"] .ct-card-question .ct-row { animation: ct-edge-ask var(--ct-dwell) both; }
@keyframes ct-edge-warm {
  0%, 62%   { box-shadow: inset 0 0 0 1px rgba(255, 159, 46, .55); }
  70%, 100% { box-shadow: inset 0 0 0 1px rgba(78, 201, 127, .5); }
}
@keyframes ct-edge-ask {
  0%, 62%   { box-shadow: inset 0 0 0 1px rgba(89, 166, 255, .55); }
  70%, 100% { box-shadow: inset 0 0 0 1px rgba(78, 201, 127, .5); }
}

.ct-stage[data-state="jump"] .ct-row-jump { animation: ct-row-hover var(--ct-dwell) both; }
@keyframes ct-row-hover {
  0%, 26%   { background: rgba(255, 255, 255, .06); }
  32%, 48%  { background: rgba(255, 255, 255, .12); }
  56%, 100% { background: rgba(255, 255, 255, .06); }
}

.ct-row-head { display: flex; align-items: flex-start; gap: 15px; }
.ct-row-head > .ct-agent { margin-top: 3px; }
.ct-row-main { flex: 1; min-width: 0; }

.ct-row-top { height: 24px; display: flex; align-items: center; gap: 9px; }
.ct-project { font-size: 20px; font-weight: 600; letter-spacing: -.01em; color: #fff; }
.ct-row-top .ct-spacer { flex: 1; }
.ct-chip {
  font-size: 13px;
  font-weight: 500;
  color: rgba(255, 255, 255, .7);
  background: rgba(255, 255, 255, .1);
  border-radius: 6px;
  padding: 3px 8px;
  white-space: nowrap;
}
.ct-elapsed { font-size: 15px; color: rgba(255, 255, 255, .45); white-space: nowrap; }

.ct-status {
  height: 21px;
  margin-top: 5px;
  font-family: var(--ct-mono);
  font-size: 16px;
  color: rgba(255, 255, 255, .55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ct-status-attention { color: var(--ct-attention); }
.ct-status-asking    { color: var(--ct-asking); }
.ct-stage[data-state="approval"] .ct-status-attention { animation: ct-cool-warm var(--ct-dwell) both; }
.ct-stage[data-state="ask"] .ct-status-asking { animation: ct-cool-ask var(--ct-dwell) both; }
@keyframes ct-cool-warm { 0%, 62% { color: var(--ct-attention); } 70%, 100% { color: rgba(255, 255, 255, .55); } }
@keyframes ct-cool-ask  { 0%, 62% { color: var(--ct-asking);    } 70%, 100% { color: rgba(255, 255, 255, .55); } }


.ct-detail { padding-left: 26px; margin-top: 9px; }
.ct-risk {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 20px;
  font-size: 15px;
  font-weight: 500;
  color: var(--ct-high);
}
.ct-stage[data-state="approval"] .ct-risk { animation: ct-risk-out var(--ct-dwell) both; }
@keyframes ct-risk-out { 0%, 62% { opacity: 1; } 70%, 100% { opacity: 0; } }

.ct-code {
  margin-top: 9px;
  padding: 12px;
  border-radius: 9px;
  background: rgba(255, 255, 255, .07);
  font-family: var(--ct-mono);
  font-size: 15px;
  color: rgba(255, 255, 255, .85);
}

.ct-actions {
  position: relative;
  height: 33px;
  margin-top: 9px;
  display: flex;
  align-items: center;
}
.ct-buttons { display: flex; align-items: center; gap: 9px; }
.ct-btn {
  height: 33px;
  padding: 0 15px;
  border-radius: 17px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 15px;
  font-weight: 600;
  color: rgba(255, 255, 255, .85);
  background: rgba(255, 255, 255, .12);
  white-space: nowrap;
}
.ct-btn-allow { color: rgba(0, 0, 0, .9); background: var(--ct-attention); padding-right: 0; }
.ct-btn-allow .ct-btn-split { width: 1px; height: 20px; margin-left: 12px; background: rgba(0, 0, 0, .2); }
.ct-btn-allow .ct-btn-chev { padding: 0 12px; display: flex; align-items: center; }

.ct-resolved {
  position: absolute;
  left: 0; top: 0;
  height: 33px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 16px;
  color: rgba(255, 255, 255, .72);
  opacity: 0;
}
.ct-stage[data-state="approval"] .ct-card-permission .ct-buttons,
.ct-stage[data-state="ask"] .ct-card-question .ct-buttons { animation: ct-btns-out var(--ct-dwell) both; }
.ct-stage[data-state="approval"] .ct-card-permission .ct-resolved,
.ct-stage[data-state="ask"] .ct-card-question .ct-resolved { animation: ct-resolved-in var(--ct-dwell) both; }
@keyframes ct-btns-out    { 0%, 60% { opacity: 1; } 66%, 100% { opacity: 0; } }
@keyframes ct-resolved-in { 0%, 66% { opacity: 0; } 72%, 100% { opacity: 1; } }

.ct-stage[data-state="approval"] .ct-btn-allow { animation: ct-press var(--ct-dwell) both; }
@keyframes ct-press {
  0%, 54%   { transform: none; box-shadow: none; }
  58%       { transform: scale(.94); box-shadow: 0 0 0 5px rgba(255, 159, 46, .3); }
  62%, 100% { transform: none; box-shadow: none; }
}

.ct-opt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 11px;
  background: rgba(255, 255, 255, .10);
  box-shadow: inset 0 0 0 1px transparent;
}
.ct-opt + .ct-opt { margin-top: 7px; }
.ct-opt-no {
  width: 20px; height: 20px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .15);
  color: rgba(255, 255, 255, .8);
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.ct-opt-label { font-size: 16px; color: rgba(255, 255, 255, .9); }
.ct-opt-detail { font-size: 15px; color: rgba(255, 255, 255, .5); margin-left: auto; }

.ct-stage[data-state="ask"] .ct-opt-2 { animation: ct-pick var(--ct-dwell) both; }
.ct-stage[data-state="ask"] .ct-opt-2 .ct-opt-no { animation: ct-pick-no var(--ct-dwell) both; }
@keyframes ct-pick {
  0%, 52%   { background: rgba(255, 255, 255, .10); box-shadow: inset 0 0 0 1px rgba(89, 166, 255, 0); }
  58%, 100% { background: rgba(89, 166, 255, .16);  box-shadow: inset 0 0 0 1px rgba(89, 166, 255, .6); }
}
@keyframes ct-pick-no {
  0%, 52%   { background: rgba(255, 255, 255, .15); color: rgba(255, 255, 255, .8); }
  58%, 100% { background: var(--ct-asking); color: rgba(0, 0, 0, .85); }
}

.ct-ring { display: flex; align-items: center; gap: 6px; }
.ct-ring svg { width: 17px; height: 17px; }
.ct-ring circle { fill: none; stroke-width: 3; }
.ct-ring .ct-ring-bg { stroke: rgba(255, 255, 255, .14); }
.ct-ring .ct-ring-fg { stroke: var(--ct-ok); stroke-linecap: round; }
.ct-ring-num { font-size: 15px; font-weight: 500; color: rgba(255, 255, 255, .5); font-variant-numeric: tabular-nums; }
.ct-ring-high .ct-ring-fg { stroke: var(--ct-high); }
.ct-ring-high .ct-ring-num { color: var(--ct-high); }

/* The Allow scope menu is a real menu: it floats over the panel, and it
   is positioned from the centre line like everything else in the card. */
.ct-scopemenu {
  position: absolute;
  left: 50%;
  top: 250px;
  margin-left: -307px;
  width: 258px;
  padding: 6px;
  border-radius: 14px;
  background: rgba(38, 38, 42, .98);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55), inset 0 0 0 1px rgba(255, 255, 255, .09);
  opacity: 0;
  transform: translate3d(0, -8px, 0) scale(.97);
  transform-origin: top left;
  pointer-events: none;
  z-index: 40;
}
.ct-scopemenu div {
  height: 32px;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 12px;
  border-radius: 9px;
  font-size: 16px;
  color: rgba(255, 255, 255, .9);
}
.ct-scopemenu i { width: 6px; height: 6px; border-radius: 50%; background: var(--ct-attention); }
.ct-stage[data-state="approval"] .ct-scopemenu { animation: ct-scope var(--ct-dwell) both; }
@keyframes ct-scope {
  0%, 18%   { opacity: 0; transform: translate3d(0, -8px, 0) scale(.97); }
  24%, 40%  { opacity: 1; transform: none; }
  46%, 100% { opacity: 0; transform: translate3d(0, -8px, 0) scale(.97); }
}

/* ── pointer ─────────────────────────────────────────────────── */
/* Everything it aims at sits inside the centred panel, so every target
   is a fixed offset from the canvas centre line at any aspect ratio.
   Keep these in step with the card layout above. */

.ct-cursor {
  position: absolute;
  left: 50%; top: 0;
  width: 24px; height: 34px;
  opacity: 0;
  z-index: 50;
  pointer-events: none;
  transform: translate3d(340px, 560px, 0);
  will-change: transform;
}
.ct-cursor svg { filter: drop-shadow(0 2px 5px rgba(0, 0, 0, .6)); }

.ct-stage[data-state="approval"] .ct-cursor { animation: ct-path-approve var(--ct-dwell) both; }
@keyframes ct-path-approve {
  0%        { opacity: 0; transform: translate3d(340px, 560px, 0); }
  8%        { opacity: 1; transform: translate3d(340px, 560px, 0); }
  20%, 46%  { opacity: 1; transform: translate3d(-295px, 224px, 0); }  /* the chevron */
  56%, 76%  { opacity: 1; transform: translate3d(-360px, 224px, 0); }  /* Allow */
  90%, 100% { opacity: 0; transform: translate3d(-344px, 250px, 0); }
}

.ct-stage[data-state="ask"] .ct-cursor { animation: ct-path-ask var(--ct-dwell) both; }
@keyframes ct-path-ask {
  0%        { opacity: 0; transform: translate3d(340px, 560px, 0); }
  10%       { opacity: 1; transform: translate3d(340px, 560px, 0); }
  50%, 76%  { opacity: 1; transform: translate3d(-400px, 192px, 0); }  /* option 2 */
  90%, 100% { opacity: 0; transform: translate3d(-384px, 218px, 0); }
}

.ct-stage[data-state="jump"] .ct-cursor { animation: ct-path-jump var(--ct-dwell) both; }
@keyframes ct-path-jump {
  0%        { opacity: 0; transform: translate3d(340px, 560px, 0); }
  8%        { opacity: 1; transform: translate3d(340px, 560px, 0); }
  30%, 52%  { opacity: 1; transform: translate3d(-400px, 177px, 0); }  /* the api-gateway row */
  70%, 100% { opacity: 0; transform: translate3d(-384px, 203px, 0); }
}

/* ── the statement ───────────────────────────────────────────── */
/* The loudest thing in the scene, and the reason the windows are held
   to the right of the centre line: this column of wallpaper is kept
   clear for it at every canvas width. */

.ct-statements {
  position: absolute;
  left: max(56px, calc(50% - 840px));
  top: 452px;
  width: min(660px, calc(50% - 130px));
  z-index: 20;
  pointer-events: none;
}
.ct-statement {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity .45s ease, transform .45s cubic-bezier(.22, 1, .28, 1);
}
.ct-statement-head {
  margin: 0;
  font-family: var(--ct-display);
  font-weight: 500;
  font-size: 50px;
  letter-spacing: -.026em;
  line-height: 1.06;
  letter-spacing: -.015em;
  color: #fff;
  text-wrap: balance;
  text-shadow: 0 2px 30px rgba(0, 0, 0, .45);
}
.ct-statement-sub {
  margin: 16px 0 0;
  font-family: var(--ct-sans);
  font-weight: 400;
  font-size: 18px;
  line-height: 1.5;
  color: rgba(255, 255, 255, .62);
  max-width: 34em;
  text-shadow: 0 1px 16px rgba(0, 0, 0, .5);
}
.ct-stage[data-state="overview"]  .ct-statement[data-scene="overview"],
.ct-stage[data-state="approval"]  .ct-statement[data-scene="approval"],
.ct-stage[data-state="ask"]       .ct-statement[data-scene="ask"],
.ct-stage[data-state="jump"]      .ct-statement[data-scene="jump"],
.ct-stage[data-state="character"] .ct-statement[data-scene="character"] {
  opacity: 1;
  transform: none;
  transition-delay: .16s;
}

/* ── compact: a crop, not a smaller scene ────────────────────── */
/* Under 700px - or wherever the desktop stops being legible - the frame
   becomes a zoomed view of the top-centre of a Mac display. The implied
   screen runs off both edges, so the menu bar continues past the frame
   the way a crop does; the notch keeps its authored 669x48 geometry and
   its sprites keep drawing at 1x, and the card is sized to the frame.
   hero-demo.js sets --ct-screen-w, --ct-panel and --ct-gutter here. */

.ct-stage[data-size="compact"] .ct-win-editor,
.ct-stage[data-size="compact"] .ct-scopemenu,
.ct-stage[data-size="compact"] .ct-cursor { display: none; }

/* the menu bar belongs to the implied screen, not to the frame */
.ct-stage[data-size="compact"] .ct-menubar {
  width: var(--ct-screen-w, 2230px);
  left: 50%;
  margin-left: calc(var(--ct-screen-w, 2230px) / -2);
  padding: 0 40px;
}

/* The terminal stays, but only as a suggestion along the bottom edge -
   enough to keep the tab bar visible, which is the whole point of the
   jump scene, without asking anyone to read a terminal on a phone. */
.ct-stage[data-size="compact"] .ct-win-term {
  width: 94%;
  margin-left: -47%;
  height: 300px;
  top: auto;
  bottom: -186px;
  animation: none !important;
  transform: none !important;
  filter: none !important;
}
.ct-stage[data-size="compact"] .ct-tab-3 { display: none; }
.ct-stage[data-size="compact"] .ct-tab { font-size: 21px; }
.ct-stage[data-size="compact"] .ct-tabs { height: 56px; padding: 8px 10px 0; }
.ct-stage[data-size="compact"] .ct-tab { height: 48px; }
.ct-stage[data-size="compact"] .ct-win-title { height: 56px; }
.ct-stage[data-size="compact"] .ct-win-name { font-size: 21px; }
.ct-stage[data-size="compact"] .ct-light { width: 17px; height: 17px; }
.ct-stage[data-size="compact"] .ct-term-body { height: calc(100% - 56px - 56px); }
.ct-stage[data-size="compact"] .ct-pane { font-size: 22px; padding: 18px 20px; line-height: 1.55; }
.ct-stage[data-size="compact"] .ct-line { height: 34px; }
@keyframes ct-line-in-compact   { 0% { height: 0; opacity: 0; } 6%, 100% { height: 34px; opacity: 1; } }
@keyframes ct-line-late-compact { 0%, 62% { height: 0; opacity: 0; } 70%, 100% { height: 34px; opacity: 1; } }
.ct-stage[data-size="compact"][data-state="approval"] .ct-line-ask,
.ct-stage[data-size="compact"][data-state="approval"] .ct-line-wait { animation-name: ct-line-in-compact; }
.ct-stage[data-size="compact"][data-state="approval"] .ct-line-done { animation-name: ct-line-late-compact; }

/* cards: fewer ornaments, and type sized for the crop's scale */
.ct-stage[data-size="compact"] .ct-card { padding: 12px 14px 20px; }
.ct-stage[data-size="compact"] .ct-row { padding: 18px 20px; border-radius: 26px; }
.ct-stage[data-size="compact"] .ct-row + .ct-row { margin-top: 12px; }
.ct-stage[data-size="compact"] .ct-row-head { gap: 16px; }
.ct-stage[data-size="compact"] .ct-row-head > .ct-agent { --ct-agent-size: 35px; margin-top: 2px; }
.ct-stage[data-size="compact"] .ct-chip,
.ct-stage[data-size="compact"] .ct-elapsed { display: none; }
.ct-stage[data-size="compact"] .ct-row-top { height: 34px; }
.ct-stage[data-size="compact"] .ct-project { font-size: 30px; }
.ct-stage[data-size="compact"] .ct-status { font-size: 24px; height: 32px; margin-top: 7px; }
.ct-stage[data-size="compact"] .ct-detail { padding-left: 0; margin-top: 14px; }
.ct-stage[data-size="compact"] .ct-risk { font-size: 22px; height: 30px; gap: 10px; }
.ct-stage[data-size="compact"] .ct-risk svg { width: 22px; height: 22px; }
.ct-stage[data-size="compact"] .ct-code { font-size: 24px; padding: 18px; border-radius: 13px; margin-top: 14px; }
.ct-stage[data-size="compact"] .ct-actions { height: 52px; margin-top: 14px; }
.ct-stage[data-size="compact"] .ct-buttons { gap: 13px; }
.ct-stage[data-size="compact"] .ct-btn { font-size: 23px; height: 52px; padding: 0 22px; border-radius: 26px; gap: 9px; }
.ct-stage[data-size="compact"] .ct-btn svg { width: 20px; height: 20px; }
.ct-stage[data-size="compact"] .ct-btn-allow { padding-right: 0; }
.ct-stage[data-size="compact"] .ct-btn-allow .ct-btn-split { height: 30px; margin-left: 17px; }
.ct-stage[data-size="compact"] .ct-btn-allow .ct-btn-chev { padding: 0 17px; }
.ct-stage[data-size="compact"] .ct-resolved { height: 52px; font-size: 23px; gap: 11px; }
.ct-stage[data-size="compact"] .ct-opt { padding: 14px 17px; border-radius: 16px; gap: 14px; }
.ct-stage[data-size="compact"] .ct-opt + .ct-opt { margin-top: 10px; }
.ct-stage[data-size="compact"] .ct-opt-no { width: 29px; height: 29px; font-size: 19px; }
.ct-stage[data-size="compact"] .ct-opt-label { font-size: 24px; }
.ct-stage[data-size="compact"] .ct-opt-detail { display: none; }
.ct-stage[data-size="compact"] .ct-ring svg { width: 25px; height: 25px; }
.ct-stage[data-size="compact"] .ct-ring-num { font-size: 23px; }
.ct-stage[data-size="compact"] .ct-ring { gap: 9px; }
/* four rows do not fit a phone; the two that carry the point do */
.ct-stage[data-size="compact"] .ct-card-list .ct-row:nth-child(n+3) { display: none; }

/* The statement moves below the panel and stays the loudest text. The
   gutter has to live on the statement itself: an absolutely positioned
   child is laid out against its ancestor's padding box, so padding on
   .ct-statements would do nothing. */
.ct-stage[data-size="compact"] .ct-statements {
  left: 0;
  width: 100%;
  padding: 0;
  top: calc(var(--ct-h) + 48px);
  transition: top .46s cubic-bezier(.22, 1, .28, 1);
}
.ct-stage[data-size="compact"] .ct-statement {
  left: var(--ct-gutter, 24px);
  right: var(--ct-gutter, 24px);
  width: auto;
  max-width: none;
}
.ct-stage[data-size="compact"] .ct-statement-head { font-size: 62px; }
.ct-stage[data-size="compact"] .ct-statement-sub { font-size: 26px; margin-top: 18px; max-width: none; }

/* ── the scene control strip ─────────────────────────────────── */
/* Real controls: buttons, focusable, labelled, current one marked. */

.ct-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px 12px 0;
}
.ct-chip-btn {
  font: inherit;
  font-size: .8125rem;
  font-weight: 500;
  line-height: 1;
  color: var(--text-dim, #9aa0a9);
  background: rgba(255, 255, 255, .045);
  border: 1px solid var(--hair, rgba(255, 255, 255, .09));
  border-radius: 999px;
  /* 44px is the smallest comfortable touch target */
  min-height: 44px;
  padding: 12px 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: color .2s ease, background .2s ease, border-color .2s ease;
}
.ct-chip-btn:hover { color: var(--text, #e9e6e1); background: rgba(255, 255, 255, .09); }
.ct-chip-btn:focus-visible { outline: 2px solid var(--ctx-high, #ffb847); outline-offset: 3px; }
.ct-chip-btn[aria-current="true"] {
  color: #0d0f13;
  background: var(--ctx-high, #ffb847);
  border-color: transparent;
}
/* The autoplay control is a real toggle that is always present and always
   the same size - Pause while it is playing, Resume once you have taken
   over. A control that hides itself measures 0x0, which is
   indistinguishable from a broken one. */
.ct-chip-play { gap: 9px; }
.ct-chip-play::before {
  content: "";
  width: 9px; height: 11px;
  border-left: 3px solid currentColor;
  border-right: 3px solid currentColor;
}
.ct-chips.is-manual .ct-chip-play::before {
  width: 0; height: 0;
  border-left: 9px solid currentColor;
  border-right: 0;
  border-top: 5.5px solid transparent;
  border-bottom: 5.5px solid transparent;
}
.ct-chips.is-manual .ct-chip-play {
  color: #0d0f13;
  background: var(--ctx-high, #ffb847);
  border-color: transparent;
}

/* ── paused / reduced motion ─────────────────────────────────── */

.ct-stage.is-paused *,
.ct-stage.is-paused *::before,
.ct-stage.is-paused *::after { animation-play-state: paused !important; }

/* Held on one scene - a chip was clicked, or the page took control -
   the scene keeps demonstrating itself instead of freezing. */
.ct-stage[data-autoplay="false"] .ct-screen *,
.ct-stage[data-autoplay="false"] .ct-screen *::before,
.ct-stage[data-autoplay="false"] .ct-screen *::after { animation-iteration-count: infinite; }

/* There is no video behind this any more, so the still frame has to
   carry the product on its own: the permission card, mid-decision,
   pointer on Allow, statement in place. */
@media (prefers-reduced-motion: reduce) {
  .ct-stage *,
  .ct-stage *::before,
  .ct-stage *::after {
    animation: none !important;
    transition: none !important;
  }
  .ct-stage .ct-cat-alert { opacity: 1; }
  .ct-stage .ct-badge { opacity: 1; transform: none; }
  .ct-stage .ct-line-ask, .ct-stage .ct-line-wait { height: 24px; opacity: 1; }
  .ct-stage .ct-card-permission .ct-row { box-shadow: inset 0 0 0 1px rgba(255, 159, 46, .55); }
  .ct-stage .ct-card-permission .ct-agent-pip { background: var(--ct-attention); }
  /* nothing turns: rotation is the one motion the brand marks carry */
  .ct-stage .ct-agent.is-spinning .ct-agent-mark { animation: none; }
  .ct-stage .ct-cursor { opacity: 1; transform: translate3d(-360px, 224px, 0); }
  .ct-stage .ct-scopemenu { opacity: 0; }
  .ct-stage .ct-chips { display: none; }
}
