/* =============================================================================
 * Kivi V19, Dusk to Daylight
 * tokens -> base -> nav -> bands -> components -> scenes -> responsive
 * ========================================================================== */

@font-face {
  font-family: "Fraunces";
  src: url("/assets/fonts/Fraunces-Variable.woff2") format("woff2");
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}

@font-face {
  font-family: "Fraunces";
  src: url("/assets/fonts/Fraunces-Italic-Variable.woff2") format("woff2");
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
}

@font-face {
  font-family: "Switzer";
  src: url("/assets/fonts/Switzer-Variable.woff2") format("woff2");
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Variable.woff2") format("woff2");
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}

:root {
  /* one continuous stage: the page ground never changes register */
  --stage: #0b0e0a;
  --dusk-0: #0b0e0a;
  --dusk-1: #121710;
  --dusk-2: #1a2016;
  --dusk-ink: #f4f1e6;
  --dusk-ink-2: #b9b6a8;
  --dusk-ink-3: #7d7b6f;
  --hairline-dusk: rgba(255, 255, 255, 0.08);
  --hairline-dusk-strong: rgba(255, 255, 255, 0.12);

  --paper-0: #f6f4ec;
  --paper-1: #fbfaf5;
  --paper-2: #efece0;
  --ink-0: #1b1d18;
  --ink-1: #4c4f46;
  --ink-2: #8a8c80;
  --hairline-paper: rgba(27, 29, 24, 0.1);
  --hairline-paper-strong: rgba(27, 29, 24, 0.16);

  --green-glow: #a8e063;
  --green-deep: #256b2f;
  --green-tint: rgba(37, 107, 47, 0.12);
  --amber: #c77e28;
  --sel-blue: #b3d7ff;
  --proc-blue: #0a66ff;

  --shadow-low: 0 2px 4px rgba(10, 12, 8, 0.12);
  --shadow-mid: 0 4px 24px rgba(10, 12, 8, 0.16);
  --shadow-float: 0 24px 64px -16px rgba(10, 12, 8, 0.35);
  --window-shadow-dusk:
    0 0 0 1px rgba(255, 255, 255, 0.1),
    0 0 0 1.5px rgba(6, 10, 5, 0.1),
    0 2.8px 2.2px rgba(6, 10, 5, 0.034),
    0 6.7px 5.3px rgba(6, 10, 5, 0.048),
    0 12.5px 10px rgba(6, 10, 5, 0.06),
    0 22.3px 17.9px rgba(6, 10, 5, 0.072),
    0 41.8px 33.4px rgba(6, 10, 5, 0.086),
    0 100px 80px rgba(6, 10, 5, 0.12);
  --window-shadow-paper:
    0 0 0 1px rgba(255, 255, 255, 0.72),
    0 0 0 1.5px rgba(43, 36, 26, 0.1),
    0 2.8px 2.2px rgba(43, 36, 26, 0.034),
    0 6.7px 5.3px rgba(43, 36, 26, 0.048),
    0 12.5px 10px rgba(43, 36, 26, 0.06),
    0 22.3px 17.9px rgba(43, 36, 26, 0.072),
    0 41.8px 33.4px rgba(43, 36, 26, 0.086),
    0 100px 80px rgba(43, 36, 26, 0.12);

  --radius-1: 4px;
  --radius-2: 8px;
  --radius-3: 12px;
  --radius-4: 20px;
  --radius-pill: 999px;

  /* floating paper sheets (chapters 2-4) */
  --sheet-radius: clamp(28px, 3.2vw, 44px);
  --sheet-inset: clamp(16px, 4vw, 56px);
  --sheet-pad: clamp(48px, 7vw, 112px);
  --sheet-gap: clamp(96px, 9vw, 128px);
  --sheet-shadow: 0 32px 80px -28px rgba(6, 10, 5, 0.55);

  --dur-micro: 150ms;
  --dur-small: 250ms;
  --dur-panel: 450ms;
  --dur-scene: 700ms;
  --ease-enter: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-exit: cubic-bezier(0.3, 0, 0.8, 0.15);
  --ease-move: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);

  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Switzer", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-host: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif;

  --grain-tile: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256' viewBox='0 0 256 256'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='256' height='256' filter='url(%23n)' opacity='.32'/%3E%3C/svg%3E");
}

@supports (color: color(display-p3 0 1 0)) {
  :root {
    --green-glow: color(display-p3 0.62 0.9 0.32);
    --green-deep: color(display-p3 0.11 0.43 0.15);
  }
}

/* ---------------------------------------------------------------------- base */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; background: var(--stage); }
body {
  margin: 0;
  min-width: 320px;
  overflow-x: clip;
  background: var(--stage);
  color: var(--dusk-ink);
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 460;
  line-height: 1.6;
  letter-spacing: -0.011em;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
[hidden] { display: none !important; }
button, a { font: inherit; }
a { color: inherit; text-decoration: none; }
button { color: inherit; }
h1, h2, p, figure { margin: 0; }

:focus-visible { outline: 2px solid var(--green-glow); outline-offset: 4px; }

.skip-link {
  position: fixed;
  z-index: 1000;
  top: 8px;
  left: 8px;
  padding: 10px 14px;
  border-radius: var(--radius-2);
  background: var(--paper-1);
  color: var(--ink-0);
  transform: translateY(-150%);
  transition: transform var(--dur-small) var(--ease-enter);
}
.skip-link:focus { transform: translateY(0); }

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

.v19-site { position: relative; isolation: isolate; overflow: clip; }
.v19-top-sentinel { position: absolute; top: 0; left: 0; width: 1px; height: 24px; }
.v19-grid {
  position: relative;
  z-index: 2;
  width: min(calc(100% - 64px), 1160px);
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: 32px;
}

/* ------------------------------------------------------------------- stage */
/* One near-black ground from top to bottom. Every colour on it comes from
 * composed glow layers, never from the ground itself, so no two chapters can
 * ever blend into mud. */
.v19-stage {
  position: fixed;
  z-index: 0;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  background: var(--stage);
}
/* the glows are always lit — JS and reduced motion only decide whether they
 * drift, never whether the stage has light on it */
.v19-stage__aurora {
  position: absolute;
  inset: 0;
  will-change: transform;
}
/* layer one: green stage light thrown from high behind the hero window, plus a
 * deep green pool on the floor. Centres sit off canvas so only the long
 * falloff is ever on screen, and every falloff is multi-stop so the grain has
 * no banding left to hide. */
.v19-stage__aurora--green {
  background:
    radial-gradient(74vw 66vw at 79% -9%,
      rgba(168, 224, 99, 0.1) 0%,
      rgba(156, 212, 90, 0.064) 15%,
      rgba(132, 190, 74, 0.034) 31%,
      rgba(96, 152, 55, 0.014) 49%,
      rgba(64, 112, 38, 0.004) 62%,
      transparent 74%),
    radial-gradient(84vw 54vw at 44% 118%,
      rgba(37, 107, 47, 0.075) 0%,
      rgba(36, 101, 45, 0.047) 18%,
      rgba(32, 88, 40, 0.023) 36%,
      rgba(26, 70, 32, 0.009) 54%,
      rgba(20, 52, 25, 0.003) 66%,
      transparent 78%);
  animation: stage-drift-green 24s ease-in-out infinite alternate;
  animation-play-state: paused;
}
/* layer two: warm gold from low left, the only warm source on the stage */
.v19-stage__aurora--gold {
  background:
    radial-gradient(72vw 58vw at -7% 81%,
      rgba(206, 154, 78, 0.062) 0%,
      rgba(196, 143, 70, 0.04) 17%,
      rgba(172, 122, 58, 0.02) 35%,
      rgba(132, 92, 43, 0.008) 52%,
      rgba(96, 66, 31, 0.003) 64%,
      transparent 76%);
  animation: stage-drift-gold 36s ease-in-out infinite alternate;
  animation-play-state: paused;
}
.v19-stage.is-stage-lit .v19-stage__aurora { animation-play-state: running; }
/* the vignette only closes the far edges: it must never eat the glows */
.v19-stage__vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(132% 98% at 50% 42%,
    transparent 40%,
    rgba(2, 5, 2, 0.1) 66%,
    rgba(2, 5, 2, 0.24) 84%,
    rgba(2, 5, 2, 0.4) 100%);
}
@keyframes stage-drift-green { to { transform: translate3d(-1.8%, 1.4%, 0) scale(1.06); } }
@keyframes stage-drift-gold { to { transform: translate3d(2.4%, -1.8%, 0) scale(1.05); } }

.v19-grain {
  position: fixed;
  z-index: 20;
  inset: 0;
  pointer-events: none;
  background-image: var(--grain-tile);
  background-size: 256px 256px;
  mix-blend-mode: overlay;
  opacity: 0.46;
}

/* V22: the soft white cursor glow is DEAD. The pointer's only light on this
 * page is the dither lens (see "the vibrancy lens" below) — a stepped, punched
 * aperture with no smooth halo anywhere. */

.v19-footprints {
  position: fixed;
  z-index: 4;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
}

/* =============================================================================
 * V22 · THE DITHERED ART LAYER AND THE VIBRANCY LENS
 * Binding spec: V22-DITHER-SPEC.md §0 (broadly visible at rest, lens brings
 * clarity), §1 (seam bands), §4 (lens mechanics, easter egg, document entry).
 *
 * V22.2 (founder, 2026-08-11) REVERSES V22.1's hidden rest: "completely hidden
 * is bad. Keep it at like some low opacity so you can broadly see it, but on
 * hovering your mouse over it, it becomes clear." So the art is part of the
 * page at rest — 12% (`--art-rest`, from the manifest), enough that you can
 * tell there is artwork and make out the moon, the flower banks, the trail —
 * and the lens is an UPGRADE to full vibrancy rather than the only appearance.
 * Which means the art is no longer a pointer-only secret, and everyone gets it:
 * touch, reduced motion and JS-off all render the art at rest and simply never
 * arm the aperture.
 *
 * Every value that belongs to a SCENE arrives as an inline custom property from
 * art-manifest.json (see DitherArt.astro). Nothing below names a file, a size,
 * a framing or a mask: this block only says how a scene behaves, never which
 * scene it is. That is the whole reason a new artwork costs a pipeline run and
 * zero edits here.
 * ========================================================================== */
.dither-art {
  position: absolute;
  z-index: 0;
  left: 0;
  right: 0;
  display: block;
  pointer-events: none;
}
/* THE FALLBACK, and at V22.2 it is a SUBTRACTION OF THE LENS, not of the art
 * (§0). The art is visible at rest now, so it is part of the page rather than a
 * pointer-only secret, and a visitor without a pointer is missing an
 * enhancement, not the artwork. So the host renders everywhere — touch,
 * reduced motion, script off — carrying the rest layer at `--art-rest`, and
 * only the moving aperture drops out (see the two blocks at the bottom of this
 * file, plus the JS gate). Nothing is needed here to make that happen: the
 * host and the rest layer are unconditional, which is also why the JS-off page
 * is identical to the pointer-parked page rather than a second design. */
/* a region reaches BEYOND its section by the overlaps the manifest declared, so
 * its dissolve band and the neighbouring scene's dissolve band land in the SAME
 * strip and interleave dot by dot rather than abutting */
.dither-art--fill {
  top: calc(-1 * var(--art-overlap-top, 0px));
  bottom: calc(-1 * var(--art-overlap-bottom, 0px));
}
/* a band as tall as its artwork, pinned to one end of the section: this is how
 * a landscape plate dresses the gutters of a document thousands of pixels tall
 * without being stretched or cropped to nothing */
.dither-art--aspect { aspect-ratio: var(--art-aspect); }
.dither-art--top { top: calc(-1 * var(--art-overlap-top, 0px)); }
.dither-art--bottom { bottom: calc(-1 * var(--art-overlap-bottom, 0px)); }
/* a scene pinned to the viewport for the length of a sticky section: the plate
 * keeps the framing it was drawn with instead of being scaled up to fill a
 * region several screens tall. The painted box runs one seam past the viewport
 * at BOTH ends, so while it is pinned its dissolve bands are off screen and the
 * only thing on screen is art. */
.dither-art--sticky {
  top: calc(-1 * var(--art-overlap-top, 0px));
  bottom: calc(-1 * var(--art-overlap-bottom, 0px));
}
.dither-art--sticky > .dither-art__seam {
  position: sticky;
  inset: auto;
  top: calc(-1 * var(--seam-lead, 0px));
  height: calc(100svh + var(--seam-lead, 0px) * 2);
}
.dither-art__seam,
.dither-art__rest,
.dither-art__vivid {
  position: absolute;
  inset: 0;
  display: block;
}
/* the scene-to-scene dissolves, carried once for both art layers: one repeating
 * dither tile per dissolving edge plus an opaque block covering everything
 * between them. Mask layers composite with the initial `add`, so the union is
 * "solid art thinning to nothing across each band" — no gradient anywhere. */
.dither-art__seam {
  -webkit-mask-image: var(--seam-image, linear-gradient(#000, #000));
  mask-image: var(--seam-image, linear-gradient(#000, #000));
  -webkit-mask-size: var(--seam-size, 100% 100%);
  mask-size: var(--seam-size, 100% 100%);
  -webkit-mask-position: var(--seam-position, top left);
  mask-position: var(--seam-position, top left);
  -webkit-mask-repeat: var(--seam-repeat, no-repeat);
  mask-repeat: var(--seam-repeat, no-repeat);
}
/* both art layers paint the SAME bitmap: rest and reveal differ only in how
 * much of the stage is left showing through, which is what makes the lens read
 * as vibrancy rather than as a second picture */
.dither-art__rest,
.dither-art__vivid {
  background-image: var(--art-src);
  background-repeat: no-repeat;
  background-size: var(--art-fit, cover);
  background-position: var(--art-position, 50% 50%);
  /* the dither is authored at art resolution; the browser must never resample
   * it with a smoothing filter or the dot structure turns to soup */
  image-rendering: pixelated;
}
/* THE REST LAYER: the whole artwork, held at `--art-rest` (V22.2: 0.12 from the
 * manifest) over the near-black stage. That is the LOWEST rung of a
 * screenshotted ladder at which the moon reads as a moon and the flower banks
 * read as flowers — broadly visible, never competing — and the number is the
 * pipeline's, not this file's, because it is the same number the quantiser's
 * contrast audit measured every text rect against. It is also the PAINTED BOX
 * the lens measures itself against: the JS reads this element's rect, which for
 * a pinned scene is not the host's. No filter: 12% of a plate whose ground IS
 * the stage ink cannot go milky, and anything that reshapes it here would
 * silently diverge from what the audit measured. */
.dither-art__rest { opacity: var(--art-rest, 0.12); }
/* full vibrancy, punched out by the lens aperture — an organic sprawl whose
 * strength falls linearly from the pointer to a noise-displaced boundary. The
 * shape, the size and the falloff are all in the mask bitmap, which is why
 * there is not a single number about the lens in this file. Hidden until the
 * JS proves a fine pointer exists, so JS-off, touch and reduced-motion never
 * see it. */
.dither-art__vivid {
  display: none;
  -webkit-mask-image: var(--lens-src);
  mask-image: var(--lens-src);
  -webkit-mask-size: var(--lens-size);
  mask-size: var(--lens-size);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: calc(var(--lens-x, -9999px) - var(--lens-radius)) calc(var(--lens-y, -9999px) - var(--lens-radius));
  mask-position: calc(var(--lens-x, -9999px) - var(--lens-radius)) calc(var(--lens-y, -9999px) - var(--lens-radius));
  /* the damage rect is one aperture, and it stays one aperture */
  contain: paint;
}
html.v19-lens .dither-art__vivid { display: block; }

/* the easter egg pulse (§4): an aperture reused as a stamp, inked kivi green.
 * It sits OUTSIDE the seam band so a bird found near a seam still gets its
 * whole pulse. V22.3: its own tile at its own footprint (`--egg-*`), because
 * the aperture that finds the bird is now four times the area a wink wants. */
.dither-art__egg {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  display: block;
  width: calc(var(--egg-radius) * 2);
  height: calc(var(--egg-radius) * 2);
  opacity: 0;
  background: var(--green-glow, #a8e063);
  -webkit-mask-image: var(--egg-src);
  mask-image: var(--egg-src);
  -webkit-mask-size: var(--egg-size);
  mask-size: var(--egg-size);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  transform: translate3d(calc(var(--egg-x, 0px) - var(--egg-radius)), calc(var(--egg-y, 0px) - var(--egg-radius)), 0);
  mix-blend-mode: screen;
  pointer-events: none;
}
.dither-art.is-egg-lit .dither-art__egg { animation: dither-egg-pulse 900ms steps(1, end) 1; }
/* stepped, not eased: a halftone does not fade, it changes how many dots it
 * spends. Four states and out. */
@keyframes dither-egg-pulse {
  0% { opacity: 0; transform: translate3d(calc(var(--egg-x) - var(--egg-radius)), calc(var(--egg-y) - var(--egg-radius)), 0) scale(0.72); }
  14% { opacity: 0.5; transform: translate3d(calc(var(--egg-x) - var(--egg-radius)), calc(var(--egg-y) - var(--egg-radius)), 0) scale(0.94); }
  38% { opacity: 0.22; transform: translate3d(calc(var(--egg-x) - var(--egg-radius)), calc(var(--egg-y) - var(--egg-radius)), 0) scale(1.06); }
  58% { opacity: 0.44; transform: translate3d(calc(var(--egg-x) - var(--egg-radius)), calc(var(--egg-y) - var(--egg-radius)), 0) scale(1.14); }
  100% { opacity: 0; transform: translate3d(calc(var(--egg-x) - var(--egg-radius)), calc(var(--egg-y) - var(--egg-radius)), 0) scale(1.2); }
}

/* ----------------------------------------------------------------------- nav */
.v19-nav {
  position: fixed;
  z-index: 100;
  inset: 0 0 auto;
  height: 72px;
  color: var(--dusk-ink);
  border-bottom: 1px solid transparent;
  transition: color var(--dur-small) ease, background-color var(--dur-small) ease, border-color var(--dur-small) ease;
}
/* Dark glass once scrolled, everywhere: the nav crosses stage and sheets alike.
 * Glass is the DEFAULT so that with JS off — where nothing can ever tell the
 * nav it has been scrolled — cream ink never lands on a cream sheet. */
.v19-nav,
html.v19-js .v19-nav.is-scrolled {
  border-bottom-color: var(--hairline-dusk);
  background: rgba(11, 14, 10, 0.78);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  backdrop-filter: blur(22px) saturate(150%);
}
html.v19-js .v19-nav {
  border-bottom-color: transparent;
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.v19-nav__progress {
  position: absolute;
  inset: auto 0 -1px;
  height: 1px;
  color: inherit;
  pointer-events: none;
}
.v19-nav__progress::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0.16;
}
.v19-nav__progress > i {
  position: absolute;
  z-index: 2;
  left: 0;
  bottom: 0;
  width: 2px;
  height: 1em;
  background: currentColor;
  transform: translate3d(0, 0, 0);
  transform-origin: 50% 100%;
}
/* four ticks, one per chapter: the chapter numbers index something real.
 * They stay invisible until JS has measured where the chapters actually are. */
.v19-nav__progress > b {
  position: absolute;
  z-index: 1;
  left: 0;
  bottom: 0;
  width: 1px;
  height: 5px;
  background: currentColor;
  opacity: 0;
  transform: translate3d(0, 0, 0);
  transition: opacity var(--dur-panel) ease;
}
.v19-nav__progress.has-ticks > b { opacity: 0.3; }
.v19-nav__progress.is-idle > i { animation: nav-progress-caret 920ms steps(1, end) infinite; }
@keyframes nav-progress-caret { 0%, 48% { opacity: 1; } 49%, 100% { opacity: 0; } }
.v19-nav__inner { height: 100%; align-items: center; }
.v19-nav__mark { position: relative; grid-column: 1 / span 2; width: 88px; height: 30px; }
.v19-nav__logo { position: absolute; inset: 0; }
.v19-nav__tittle {
  position: absolute;
  z-index: 2;
  top: 5%;
  width: 6.85%;
  height: 19.5%;
  border-radius: 50%;
  background: #90c46a;
  pointer-events: none;
}
.v19-nav__tittle--first { left: 40.9%; }
.v19-nav__tittle--second { left: 74%; }
.v19-nav__links {
  grid-column: 7 / -1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(18px, 2.6vw, 34px);
  font-size: 14px;
  font-weight: 540;
}
.v19-nav__links > a:not(.v19-button) { opacity: 0.72; transition: opacity var(--dur-micro) ease; }

.v19-button {
  min-height: 48px;
  width: fit-content;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 12px 18px;
  border: 1px solid transparent;
  border-radius: var(--radius-2);
  background: var(--green-glow);
  color: var(--dusk-0);
  font-size: 15px;
  font-weight: 620;
  line-height: 1;
  box-shadow: 0 1px 0 rgba(244, 241, 230, 0.24) inset, var(--shadow-low);
  transition: transform var(--dur-micro) var(--ease-enter), background-color var(--dur-micro) ease, box-shadow var(--dur-micro) ease;
}
.v19-button:active { transform: translateY(1px) scale(0.97); box-shadow: var(--shadow-low); }
/* V21 §1: one big, playful button. Bigger type, far more air, a rounder shape,
 * and a light that sits under it — the only object in the hero besides the
 * sentence, so it is allowed to be the loudest thing on the stage. */
.v19-button--hero {
  min-height: 66px;
  gap: 13px;
  padding: 20px 38px;
  border-radius: 18px;
  font-size: 20px;
  font-weight: 640;
  letter-spacing: -0.014em;
  box-shadow:
    0 1px 0 rgba(244, 241, 230, 0.32) inset,
    0 2px 6px rgba(6, 10, 5, 0.34),
    0 18px 44px -14px rgba(133, 196, 68, 0.42);
}
.v19-button--hero svg { width: 21px; height: 26px; }
.v19-button--hero .v19-listening-bars { width: 11px; height: 16px; }
.v19-button--hero .v19-listening-bars i { height: 14px; }
/* the wobble rides the `rotate` property, not `transform`, so it composes with
 * the magnetic offset instead of fighting it */
@keyframes hero-wobble {
  0%, 100% { rotate: 0deg; }
  28% { rotate: -1.8deg; }
  62% { rotate: 1.4deg; }
  84% { rotate: -0.5deg; }
}
.v19-listening-bars {
  width: 9px;
  height: 13px;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  flex: none;
}
.v19-listening-bars i {
  width: 1px;
  height: 11px;
  border-radius: 1px;
  background: currentColor;
  transform: scaleY(0.38);
  transform-origin: center;
}
.v19-listening-bars i:nth-child(2) { transform: scaleY(0.72); }
.v19-listening-bars i:nth-child(3) { transform: scaleY(0.52); }
@keyframes cta-listen {
  0%, 100% { transform: scaleY(0.34); }
  45% { transform: scaleY(0.96); }
  72% { transform: scaleY(0.56); }
}

/* ------------------------------------------------- bands, chapters, sheets */
/* Hero and closing sit directly on the stage. Chapters 2-4 are paper sheets
 * floating on it, with the dark visible in the gutters and between them. */
.v19-band { position: relative; color: var(--dusk-ink); }
.v19-band--hero {
  min-height: max(900px, 100svh);
  display: grid;
  align-items: center;
  padding: clamp(132px, 13vh, 172px) 0 clamp(120px, 13vh, 176px);
  overflow: hidden;
}

/* THE DOCUMENT (V21 §5). The three chapter sheets are one continuous paper
 * surface now: the dark hands over to paper exactly once, here, and takes it
 * back only at the closing. Inside, chapters are separated by whitespace and a
 * single hairline glyph — no new card edges anywhere. */
/* V22: the document now opens ONE sheet gap below the collage rather than
 * abutting it. The gap is not decoration — it is the strip the S2 and S3
 * dissolve bands share, and the air the paper's own dissolve condenses out of.
 * Without it the document (z-index 20) would hand its art a band that lands on
 * top of the collage cards (z-index 2), which is exactly backwards. */
.v19-document { position: relative; z-index: 20; margin-top: var(--sheet-gap); margin-bottom: var(--sheet-gap); }
.v19-doc {
  position: relative;
  z-index: 21;
  margin-inline: var(--sheet-inset);
  padding: clamp(72px, 8vw, 132px) var(--sheet-pad) var(--sheet-pad);
  border-radius: var(--doc-top-radius, var(--sheet-radius)) var(--doc-top-radius, var(--sheet-radius)) var(--sheet-radius) var(--sheet-radius);
  color: var(--ink-0);
  /* coated stock: paper ground, two faint light pockets, one top light line */
  background-color: var(--paper-0);
  background-image:
    radial-gradient(58% 34% at 74% 0%, rgba(214, 178, 96, 0.06), rgba(214, 178, 96, 0.022) 42%, transparent 72%),
    radial-gradient(52% 30% at 8% 98%, rgba(37, 107, 47, 0.03), rgba(37, 107, 47, 0.011) 44%, transparent 74%);
  /* V22: the inset top light line is gone. It was the highlight on a sheet's
   * cut edge, and this sheet no longer has one — it condenses out of the
   * dither. A 1px cream rule across the full width would have been the only
   * hard line left in a transition built entirely out of dots. */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06);
}
/* V22 §4, the document entry: the paper does not START, it CONDENSES. This
 * strip stands directly above the sheet's top edge and paints the same stock
 * through the dither tile — opaque where it meets the paper, thinning to
 * nothing as it climbs into S3's meadow. The mask tile and its height come from
 * the manifest (see DOC_STYLE in index.astro), so re-cutting it is a pipeline
 * run. Squared top corners are the price and the point: a sheet that dissolves
 * has no corner to round. */
.v19-doc::before {
  content: "";
  position: absolute;
  z-index: 2;
  bottom: 100%;
  left: 0;
  right: 0;
  height: var(--paper-top-height, 0px);
  background-color: var(--paper-0);
  -webkit-mask-image: var(--paper-top-src);
  mask-image: var(--paper-top-src);
  -webkit-mask-size: var(--paper-top-size);
  mask-size: var(--paper-top-size);
  -webkit-mask-repeat: repeat-x;
  mask-repeat: repeat-x;
  pointer-events: none;
}
.v19-document { --doc-top-radius: 0px; }

/* the cast shadow rides its own layer so the rise can soften it without
 * repainting the document's own fill */
.v19-doc__lift {
  position: absolute;
  z-index: -1;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--sheet-shadow);
  pointer-events: none;
}
/* paper grain: soft-light, quieter than the stage grain above it */
.v19-doc::after {
  content: "";
  position: absolute;
  z-index: 3;
  inset: 0;
  border-radius: inherit;
  background-image: var(--grain-tile);
  background-size: 256px 256px;
  mix-blend-mode: soft-light;
  opacity: 0.25;
  pointer-events: none;
}
.v19-doc .v19-grid { width: min(100%, 1160px); }
.doc-chapter { position: relative; z-index: 4; }
/* generous air between chapters, and one quiet mark to say a new one starts */
.doc-divider {
  position: relative;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  width: min(100%, 1160px);
  margin: clamp(96px, 10vw, 148px) auto;
}
.doc-divider i { width: clamp(48px, 8vw, 104px); height: 1px; background: rgba(27, 29, 24, 0.14); }
.doc-divider b { width: 5px; height: 5px; border-radius: 1px; background: rgba(27, 29, 24, 0.26); transform: rotate(45deg); }
/* V22: the wave sits above the collage scene's dissolve band, which reaches up
 * into the bottom of the hero to interleave with the hero scene */
.hero-proximity-wave {
  position: absolute;
  z-index: 3;
  right: clamp(20px, 4vw, 64px);
  bottom: clamp(22px, 4vh, 42px);
  left: clamp(20px, 4vw, 64px);
  height: 44px;
  display: grid;
  grid-template-columns: repeat(48, minmax(1px, 1fr));
  align-items: end;
  gap: clamp(2px, 0.42vw, 7px);
  pointer-events: none;
  opacity: 0.54;
}
.hero-proximity-wave > span {
  width: 2px;
  height: 40px;
  justify-self: center;
  border-radius: 2px;
  background: rgba(125, 123, 111, 0.66);
  transform: scaleY(var(--wave-height));
  transform-origin: 50% 100%;
}
.hero-proximity-wave > span:nth-child(4n + 1),
.hero-proximity-wave > span:nth-child(7n + 3) { background: rgba(168, 224, 99, 0.46); }
.hero-proximity-wave.is-proximity-active > span { will-change: transform; }

/* V21 phase one: one sentence and one button. The kicker, the subline, the
 * alpha line and the edge-peek cards are all gone — the collage below carries
 * the proof, so nothing up here competes with the idea.
 *
 * The media slot is an empty, full-band layer waiting for the founder's
 * background asset. Everything in front of it is positioned independently, so
 * dropping a video or an image in here moves nothing. */
.hero-media-slot {
  position: absolute;
  z-index: 1;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.hero-grid { align-items: center; justify-items: center; row-gap: 64px; }
.hero-copy { grid-column: 2 / span 10; position: relative; z-index: 3; text-align: center; }
.hero-copy h1,
.chapter-header h2,
.closing-grid h2 {
  font-family: var(--font-display);
  font-weight: 460;
  text-wrap: balance;
}
.hero-copy h1 {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
  color: var(--dusk-ink);
  font-size: clamp(56px, 7.6vw, 116px);
  line-height: 0.94;
  letter-spacing: -0.028em;
  font-variation-settings: "opsz" 120, "SOFT" 70, "WONK" 0;
}
.hero-headline-line {
  display: block;
  height: 0.94em;
  white-space: nowrap;
}
.hero-headline-line > [data-hero-word] { display: inline-block; }
/* the italic pivot is dead (V21 §1): both lines are Fraunces roman, the closing
 * heading's own cut. Only the colour changes, and only on the second line. */
.hero-headline-line--kivi { color: var(--green-glow); }
.hero-headline-caret-anchor { display: inline-block; width: 1px; height: 0.76em; vertical-align: -0.02em; }
.hero-dictation-wave {
  position: absolute;
  top: -21px;
  left: 0;
  width: 23px;
  height: 15px;
  display: grid;
  place-items: center;
  color: var(--green-glow);
  pointer-events: none;
  transform-origin: 0 50%;
}
.hero-dictation-wave__bars {
  width: 23px;
  height: 15px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.hero-dictation-wave__bars i {
  width: 2px;
  height: 13px;
  border-radius: 2px;
  background: currentColor;
  transform: scaleY(var(--bar-rest));
  transform-origin: center;
}
.hero-dictation-wave__caret {
  position: absolute;
  width: 2px;
  height: 0.78em;
  background: currentColor;
  opacity: 0;
}
html.v19-js:not(.v19-hero-dictated) [data-hero-word] {
  opacity: 0;
  transform: translate3d(0, 5px, 0);
}
.devanagari { color: var(--dusk-ink); font-family: "Kohinoor Devanagari", "Noto Sans Devanagari", system-ui, sans-serif; font-weight: 560; }
.hero-actions { display: flex; align-items: center; justify-content: center; margin-top: clamp(40px, 5vh, 58px); }
.closing-note i { font-style: normal; opacity: 0.5; padding-inline: 2px; }

[data-hero-load] {
  transition:
    opacity 560ms var(--ease-enter),
    transform 560ms var(--ease-enter),
    filter 480ms var(--ease-enter);
}
.hero-actions[data-hero-load] { transition-delay: 1040ms; }
html.v19-js:not(.v19-hero-loaded) [data-hero-load] { opacity: 0; filter: blur(4px); transform: translate3d(0, 12px, 0); }

.chapter-grid { align-items: center; row-gap: 64px; }
.chapter-grid > .chapter-header { grid-column: 1 / span 5; }
.chapter-grid > figure { grid-column: 6 / -1; }
.chapter-grid--reverse > .chapter-header { grid-column: 8 / -1; grid-row: 1; }
.chapter-grid--reverse > figure { grid-column: 1 / span 7; grid-row: 1; }
.chapter-header { max-width: 440px; }
.chapter-header h2 { max-width: 14ch; font-size: clamp(36px, 4.2vw, 56px); line-height: 1.04; letter-spacing: -0.022em; font-variation-settings: "opsz" 72, "SOFT" 64, "WONK" 0; }
.chapter-header--centred { max-width: 640px; margin-inline: auto; text-align: center; }
.chapter-header--centred h2 { max-width: none; margin-inline: auto; }
.chapter-header--centred .chapter-subline { margin-inline: auto; }
/* One measure for every sentence that sits under a heading, anywhere on the
 * page: 18px / 1.55 / 34ch at ink-1. Heading-adjacent copy that is smaller than
 * this reads as a caption, and none of these are captions. */
.chapter-subline { max-width: 34ch; margin-top: 22px; color: var(--ink-1); font-size: 18px; font-weight: 440; line-height: 1.55; letter-spacing: -0.008em; text-wrap: pretty; }

/* scroll-scrubbed headings: words start dim, never absent */
[data-chapter-headline] .v19-word { display: inline-block; will-change: transform, opacity, filter; }

.v19-band--closing {
  min-height: 720px;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding: clamp(120px, 12vw, 176px) 0;
}
.closing-grid { justify-items: center; text-align: center; }
.closing-perch,
.closing-grid h2,
.closing-grid > .v19-button,
.closing-grid > p { grid-column: 1 / -1; }
.closing-perch { position: relative; width: 240px; height: 184px; margin-bottom: 24px; display: grid; place-items: end center; }
.closing-bird { position: relative; z-index: 2; width: 128px; height: 128px; transform-origin: 50% 100%; }
.closing-bird__motion,
.closing-bird__breath { width: 128px; height: 128px; display: block; transform-origin: 50% 100%; }
.closing-bird__breath { animation: bird-breathe 4.6s ease-in-out infinite alternate; animation-play-state: paused; }
.closing-bird__breath.is-idle-visible { animation-play-state: running; }
.closing-perch.is-walking .closing-bird,
.closing-perch.is-walking .closing-bird__motion,
.closing-perch.is-walking .closing-ground { will-change: transform, opacity; }
.closing-bird img { width: 128px; height: 128px; }
.closing-ground { position: absolute; z-index: 0; bottom: 7px; width: 154px; height: 24px; border-radius: 50%; background: radial-gradient(ellipse, rgba(168, 224, 99, 0.24), transparent 70%); filter: blur(7px); }
.closing-grid h2 { max-width: 10ch; color: var(--dusk-ink); font-size: clamp(48px, 6vw, 78px); line-height: 0.98; letter-spacing: -0.024em; }
.closing-grid > .v19-button { margin-top: 36px; }
.closing-note {
  margin-top: 18px;
  color: rgba(244, 241, 230, 0.6);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 400;
  font-variant-numeric: tabular-nums slashed-zero;
}
.closing-note a { color: var(--green-glow); }

/* --------------------------------------------------------------- components */
.mac-window {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--hairline-paper-strong);
  border-radius: 10px;
  background: var(--paper-1);
  color: var(--ink-0);
  font-family: var(--font-host);
  box-shadow: var(--window-shadow-paper);
}
.mac-window--dark { border-color: var(--hairline-dusk-strong); background: #181a18; color: #ecece8; box-shadow: var(--window-shadow-dusk); }
.mac-window__toolbar {
  min-height: 52px;
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr) 88px;
  align-items: center;
  padding-inline: 19px;
  border-bottom: 1px solid var(--hairline-paper);
  background: rgba(251, 250, 245, 0.72);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
}
.mac-window--dark .mac-window__toolbar { border-color: var(--hairline-dusk); background: rgba(28, 30, 27, 0.78); }
.mac-window__lights { display: flex; gap: 8px; }
.mac-window__lights i { width: 12px; height: 12px; border-radius: 50%; box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); }
.mac-window__lights i:nth-child(1) { background: #ff5f57; }
.mac-window__lights i:nth-child(2) { background: #febc2e; }
.mac-window__lights i:nth-child(3) { background: #28c840; }
.mac-window__identity { min-width: 0; display: flex; align-items: center; justify-content: center; gap: 8px; text-align: left; }
.mac-window__identity > img { width: 24px; height: 24px; flex: none; object-fit: contain; }
.mac-window__identity > span { min-width: 0; display: grid; line-height: 1.15; }
.mac-window__identity strong { overflow: hidden; color: currentColor; font-size: 13px; font-weight: 590; text-overflow: ellipsis; white-space: nowrap; }
.mac-window__identity small { color: var(--ink-2); font-size: 11px; font-weight: 400; white-space: nowrap; }
.mac-window--dark .mac-window__identity small { color: rgba(236, 236, 232, 0.48); }
.mac-window__content { min-height: 0; }

/* ------------------------------------------------------- shared host caret */
.v19-caret { display: inline-block; width: 2px; height: 1em; margin-left: 2px; background: var(--green-deep); vertical-align: -0.1em; animation: caret-blink 1.1s steps(1, end) infinite; }
.is-streaming .v19-caret,
.is-streaming.v19-caret { animation: none; opacity: 1; }
@keyframes caret-blink { 0%, 44% { opacity: 1; } 45%, 100% { opacity: 0; } }

/* ------------------------------------------------------------ receipt chip */
/* The product's receipt: the bird that heard it, what kivi did, and the thing
 * it did it to. One signal per part (law 1) — the label is the only run at full
 * ink, the detail steps down at the same size (law 11), and the confirmation is
 * a MARK that draws itself rather than a colour, so it survives colour blindness
 * (law: colour = why, mark = what).
 *
 * The check has a `stroke-dasharray` because the memory scene DRAWS it: the
 * timeline tweens `stroke-dashoffset` 14 -> 0. Without these declarations the
 * bare `<path>` falls back to `fill: black`, which is what put a black triangle
 * in the compose field. Every chip on the page depends on this block. */
.receipt-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 34px;
  padding: 6px 10px;
  border: 1px solid rgba(27, 29, 24, 0.1);
  border-radius: var(--radius-2);
  background: var(--paper-1);
  font-family: var(--font-body);
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: 0 1px 2px rgba(43, 36, 26, 0.06), 0 10px 24px -12px rgba(43, 36, 26, 0.3);
}
.receipt-chip__bird { width: 20px; height: 20px; flex: none; display: block; transform-origin: 50% 100%; }
.receipt-chip__bird img { width: 20px; height: 20px; }
.receipt-chip__copy { min-width: 0; display: flex; align-items: baseline; gap: 7px; }
.receipt-chip__copy strong { color: var(--ink-0); font-size: 12px; font-weight: 500; letter-spacing: -0.005em; }
.receipt-chip__copy small { color: rgba(27, 29, 24, 0.6); font-size: 12px; font-weight: 400; letter-spacing: -0.005em; }
.receipt-chip__check { width: 14px; height: 14px; flex: none; display: block; }
.receipt-chip__check svg { width: 14px; height: 14px; display: block; overflow: visible; }
.receipt-chip__check path {
  fill: none;
  stroke: var(--green-deep);
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 14;
}
/* the check-only variant carries no drawing beat of its own, so it arrives
 * already ticked */
.receipt-chip--check { gap: 7px; }
.receipt-chip--check .receipt-chip__check { width: 13px; height: 13px; }
.receipt-chip--check .receipt-chip__check svg { width: 13px; height: 13px; }
.receipt-chip--check .receipt-chip__check path { stroke-dashoffset: 0; }

/* =============================================================================
 * THE COLLAGE — "talk with kivi anywhere"
 *
 * Six cards on the dark stage, dressed as the apps you actually type in. The
 * cards are paper because their hosts are paper; the stage stays velvet behind
 * them, so the whole thing reads as a desk lit from above rather than a grid.
 *
 * Layout has two completely separate lives, and the plain one is the default:
 *   - no JS, or under 768px: a readable stack. Every card already carries its
 *     polished output AND the rough sentence, so nothing is missing.
 *   - JS at 768px and up: a sticky stage with an absolutely scattered field
 *     that scrolls on the scrollbar. Never a wheel handler, never a snap.
 * ========================================================================== */
.collage { position: relative; z-index: 2; }
.collage-scroll { position: relative; }
/* the cards ride above the clearing scene mounted behind them */
.collage-sticky {
  position: relative;
  z-index: 1;
  width: min(calc(100% - 64px), 1160px);
  margin-inline: auto;
  padding: clamp(76px, 9vw, 132px) 0 clamp(72px, 8vw, 116px);
}
.collage-field { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(22px, 3vw, 30px); }

.collage-heading { display: grid; justify-items: center; width: 480px; max-width: 100%; margin: 0 auto clamp(20px, 3vw, 34px); text-align: center; }
.collage-title {
  margin: 0;
  color: var(--dusk-ink);
  font-family: var(--font-display);
  font-size: clamp(38px, 4.6vw, 62px);
  font-weight: 460;
  line-height: 1.02;
  letter-spacing: -0.024em;
  font-variation-settings: "opsz" 72, "SOFT" 64, "WONK" 0;
  text-wrap: balance;
}
.collage-title > span { display: inline-block; }
/* the instruction is one sentence with the key set into it, not a caption strip
 * hanging off the side of the composition */
.collage-sub {
  max-width: 34ch;
  margin-top: 20px;
  color: rgba(244, 241, 230, 0.68);
  font-family: var(--font-body);
  font-size: 18px;
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: -0.008em;
  text-wrap: pretty;
}
/* ------------------------------------------------------------------ keycap */
/* One keycap object for the whole page, drawn in CSS, sized only by `--cap`.
 * Five layers, because a real cap is that many things: the cast shadow, the
 * skirt, the top face, the legends, and the dim that is the pressed state's
 * colour change. Apple's own legend layout on the fn key: globe bottom left,
 * word top right; the control cap carries the real ⌃ glyph, centred. */
.keycap {
  --cap: 34px;
  --keycap-ink: #efeade;
  position: relative;
  display: inline-block;
  width: var(--cap);
  height: var(--cap);
  flex: none;
}
.keycap-shadow {
  position: absolute;
  inset: 7% 3% -7% 3%;
  border-radius: calc(var(--cap) * 0.14);
  background: rgba(6, 10, 5, 0.34);
  filter: blur(calc(var(--cap) * 0.06));
  transform-origin: 50% 100%;
}
.keycap-body {
  position: absolute;
  inset: 0;
  border-radius: calc(var(--cap) * 0.115);
  /* the skirt: the wall of the cap, lit from above */
  background: linear-gradient(178deg, #2e332b 0%, #22271f 52%, #171a15 100%);
  box-shadow: inset 0 0 0 1px rgba(12, 14, 12, 0.45);
  padding: calc(var(--cap) * 0.026) calc(var(--cap) * 0.03) calc(var(--cap) * 0.068);
}
.keycap-face {
  position: relative;
  display: block;
  height: 100%;
  overflow: hidden;
  border-radius: calc(var(--cap) * 0.082);
  /* the top face, two shades lighter than the wall: if the face and the skirt
   * are close in value the cap stops being an object */
  background: linear-gradient(176deg, #5a6353 0%, #414937 46%, #333a2e 100%);
}
/* the edge catch: the one specular line where the face meets the air */
.keycap-face::before {
  content: "";
  position: absolute;
  z-index: 1;
  left: 7%;
  right: 7%;
  top: 0;
  height: max(1px, calc(var(--cap) * 0.008));
  background: linear-gradient(90deg, rgba(246, 243, 234, 0), rgba(246, 243, 234, 0.4) 18%, rgba(246, 243, 234, 0.52) 50%, rgba(246, 243, 234, 0.4) 82%, rgba(246, 243, 234, 0));
}
/* and the occlusion pooling at the bottom of the dish */
.keycap-face::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 88% at 50% 8%, rgba(246, 243, 234, 0.07), rgba(246, 243, 234, 0) 62%),
    linear-gradient(0deg, rgba(12, 14, 12, 0.36), rgba(12, 14, 12, 0) 34%);
}
.keycap-dim { position: absolute; z-index: 2; inset: 0; background: rgba(12, 14, 12, 0.3); opacity: 0; }
.keycap-word,
.keycap-globe,
.keycap-glyph { position: absolute; z-index: 3; color: var(--keycap-ink); font-family: var(--font-mono); line-height: 1; }
.keycap-word { top: calc(var(--cap) * 0.1); right: calc(var(--cap) * 0.105); font-size: calc(var(--cap) * 0.24); letter-spacing: 0.01em; opacity: 0.9; }
.keycap-globe { bottom: calc(var(--cap) * 0.095); left: calc(var(--cap) * 0.105); width: calc(var(--cap) * 0.26); height: calc(var(--cap) * 0.26); opacity: 0.88; }
.keycap-globe svg { display: block; width: 100%; height: 100%; }
.keycap-glyph { top: 50%; left: 50%; font-size: calc(var(--cap) * 0.42); opacity: 0.92; transform: translate(-50%, -46%); }
.keycap-ring {
  position: absolute;
  z-index: 1;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 2px rgba(168, 224, 99, 0.62), 0 0 0 7px rgba(168, 224, 99, 0.16);
  opacity: 0;
}
.keycap--held .keycap-body,
.is-pressed .keycap-body { transform: translateY(calc(var(--cap) * 0.026)); }
.keycap--held .keycap-ring,
.is-pressed .keycap-ring { opacity: 1; }
.keycap--held .keycap-dim,
.is-pressed .keycap-dim { opacity: 1; }
.keycap--caption { --cap: 34px; }
/* a keycap set inline in running text: it sits on the text baseline and takes
 * the line's own rhythm, so the sentence never jumps around it */
.keycap--inline { --cap: 25px; margin: 0 4px; vertical-align: -6px; }

/* ------------------------------------------------------------ card material */
.collage-slot { position: relative; }
.collage-tilt,
.collage-hover { display: block; height: 100%; }
.collage-card {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 168px;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  overflow: hidden;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 14px;
  background: var(--paper-1);
  color: var(--ink-0);
  font-family: var(--font-host);
  text-align: left;
  /* two pre-rendered layers: this one always on, the lift crossfaded over it.
   * box-shadow is never animated on a scrub. */
  box-shadow: 0 1px 2px rgba(6, 10, 5, 0.34), 0 16px 40px -12px rgba(6, 10, 5, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.72);
  cursor: pointer;
}
.collage-card:disabled { cursor: default; }
.collage-card__lift {
  position: absolute;
  z-index: -1;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 2px 6px rgba(6, 10, 5, 0.4), 0 34px 70px -18px rgba(6, 10, 5, 0.66);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-panel) var(--ease-enter);
}
.collage-slot[data-tier="front"] .collage-card__lift { opacity: 0.55; }
.collage-card[data-tone="dark"] {
  border-color: rgba(255, 255, 255, 0.2);
  background: #1e211d;
  color: #ecece8;
  box-shadow: 0 1px 2px rgba(6, 10, 5, 0.5), 0 18px 46px -12px rgba(0, 0, 0, 0.72), inset 0 1px 0 rgba(255, 255, 255, 0.09);
}

/* mini host chrome: the app, then where you are in it. Two signals, no more. */
.card-chrome {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--hairline-paper);
  background: rgba(246, 244, 236, 0.6);
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.2;
}
.card-chrome img { width: 20px; height: 20px; flex: none; object-fit: contain; }
.card-chrome b { color: var(--ink-0); font-weight: 500; }
.card-chrome i { color: rgba(27, 29, 24, 0.3); font-style: normal; }
.card-chrome span { min-width: 0; overflow: hidden; color: rgba(27, 29, 24, 0.62); font-weight: 400; text-overflow: ellipsis; white-space: nowrap; }
/* the one thing a card wants to say about itself, when it has one */
.card-flag {
  margin-left: auto;
  padding: 2px 8px;
  flex: none;
  border-radius: var(--radius-pill);
  background: rgba(37, 107, 47, 0.1);
  color: #2f6b34;
  font-size: 10.5px;
  font-style: normal;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.collage-card[data-tone="dark"] .card-chrome { border-bottom-color: rgba(255, 255, 255, 0.07); background: rgba(28, 30, 27, 0.7); }
.collage-card[data-tone="dark"] .card-chrome b { color: #ecece8; }
.collage-card[data-tone="dark"] .card-chrome i { color: rgba(236, 236, 232, 0.28); }
.collage-card[data-tone="dark"] .card-chrome span { color: rgba(236, 236, 232, 0.5); }

.card-out { min-height: 0; display: grid; align-content: center; overflow: hidden; padding: 14px 15px; }
/* the rough sentence, quietly, so the card is a whole before/after on its own */
.card-said {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: 9px;
  padding: 10px 15px 12px;
  border-top: 1px solid var(--hairline-paper);
  background: rgba(27, 29, 24, 0.028);
  font-family: var(--font-body);
}
.card-said__label {
  color: rgba(27, 29, 24, 0.55);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* In the stack the rough sentence gets two lines, because there is room and it
 * is half the proof. On the scattered stage it becomes one clipped line, so a
 * card stays a card. */
.card-said__text {
  display: -webkit-box;
  overflow: hidden;
  color: rgba(27, 29, 24, 0.62);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.35;
  quotes: "\201C" "\201D";
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.collage-card[data-tone="dark"] .card-said { border-top-color: rgba(255, 255, 255, 0.07); background: rgba(255, 255, 255, 0.03); }
.collage-card[data-tone="dark"] .card-said__label { color: rgba(236, 236, 232, 0.45); }
.collage-card[data-tone="dark"] .card-said__text { color: rgba(236, 236, 232, 0.56); }

.collage-card__pill {
  position: absolute;
  z-index: 4;
  right: 10px;
  bottom: 46px;
  padding: 5px 10px;
  border-radius: var(--radius-pill);
  background: rgba(11, 14, 10, 0.86);
  color: rgba(244, 241, 230, 0.92);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
  opacity: 0;
  pointer-events: none;
  transform: translate3d(0, 4px, 0);
  transition: opacity var(--dur-small) var(--ease-enter), transform var(--dur-small) var(--ease-enter);
}

/* -------------------------------------------------- the polished host output */
.out { min-width: 0; }
.out-body { color: inherit; font-size: 15px; font-weight: 470; line-height: 1.45; letter-spacing: -0.012em; }
.out-slack { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 9px; }
.out-slack__avatar { width: 26px; height: 26px; display: grid; place-items: center; border-radius: var(--radius-1); background: #4a154b; color: #fffaf5; font-family: var(--font-body); font-size: 13px; font-weight: 600; }
.out-slack__stack { min-width: 0; display: grid; gap: 3px; }
.out-slack__meta { display: flex; align-items: baseline; gap: 7px; font-family: var(--font-body); font-size: 11px; line-height: 1.2; }
.out-slack__meta b { color: var(--ink-0); font-weight: 500; }
.out-slack__meta span { color: rgba(27, 29, 24, 0.45); font-variant-numeric: tabular-nums slashed-zero; }
.out-mention { padding: 0 3px; border-radius: 3px; background: rgba(29, 108, 214, 0.12); color: #1264a3; font-weight: 550; }
.out-cursor { display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 12px; }
.out-cursor__gutter { display: grid; align-content: center; justify-items: end; gap: 13px; border-right: 1px solid rgba(255, 255, 255, 0.07); padding-right: 10px; color: rgba(236, 236, 232, 0.3); font: 11px/1 var(--font-mono); font-variant-numeric: tabular-nums slashed-zero; }
.out-cursor__gutter i { font-style: normal; }
.out-messages { display: flex; justify-content: flex-end; }
.out-bubble { width: fit-content; max-width: 92%; padding: 9px 13px; border-radius: 17px 17px 5px 17px; background: #2d7df6; color: #f8fbff; font-size: 15px; font-weight: 450; line-height: 1.4; }
.out-notes { display: grid; gap: 8px; }
.out-sprint { display: grid; gap: 7px; }
.out-body--lead { font-weight: 560; }
.out-body--math { font-variant-numeric: tabular-nums slashed-zero; letter-spacing: -0.004em; }
.out-list { margin: 0; padding-left: 17px; color: var(--ink-1); font-size: 14.5px; line-height: 1.5; }
.out-mail { display: grid; gap: 8px; }
/* a dictated letter keeps its shape: greeting, body, sign-off, name */
.out-letter { display: grid; gap: 7px; }
.out-letter p { font-size: 14.5px; font-weight: 470; line-height: 1.45; letter-spacing: -0.012em; }
.out-letter p:nth-child(3) { margin-top: 4px; }
/* the message you are replying to, already on screen before you speak */
.out-quoted {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: 8px;
  padding: 8px 10px;
  border-left: 2px solid rgba(27, 29, 24, 0.16);
  border-radius: 0 6px 6px 0;
  background: rgba(27, 29, 24, 0.035);
  color: rgba(27, 29, 24, 0.62);
  font-size: 13px;
  line-height: 1.4;
}
.out-quoted b { color: rgba(27, 29, 24, 0.78); font-weight: 550; }
.out-reminders { display: grid; gap: 9px; }
.out-reminders__list { color: rgba(27, 29, 24, 0.5); font-family: var(--font-body); font-size: 11px; font-weight: 500; }
.out-check { margin: 0; padding: 0; display: grid; gap: 7px; list-style: none; }
.out-check li { display: flex; align-items: center; gap: 9px; font-size: 14px; font-weight: 460; line-height: 1.3; }
.out-check li i { width: 13px; height: 13px; flex: none; border: 1.4px solid rgba(27, 29, 24, 0.28); border-radius: 50%; }
.out-terminal .out-body {
  color: #d8e9c9;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}
.out-terminal__prompt { margin-right: 8px; color: rgba(168, 224, 99, 0.72); }

/* ------------------------------------------ the sticky stage (JS, >= 1100px) */
/* Ten cards need room. Below 1100px the scatter is retired entirely for the
 * composed mini-collage further down — the clutter law says legibility wins. */
@media (min-width: 1100px) {
  html.v19-js .collage-scroll { height: 280vh; }
  html.v19-js .collage-sticky {
    position: sticky;
    top: 0;
    width: 100%;
    max-width: none;
    height: 100vh;
    margin: 0;
    padding: 0;
  }
  /* one design field, scaled to fit the viewport, so the composition the cards
   * were drawn into can never break apart on a shorter screen */
  html.v19-js .collage-field {
    position: absolute;
    left: 50%;
    top: calc(50% + 30px);
    width: 1328px;
    height: 768px;
    margin: -384px 0 0 -664px;
    display: grid;
    place-items: center;
    gap: 0;
    transform: scale(calc(var(--collage-scale, 1) * var(--collage-dim, 1)));
    transform-origin: 50% 50%;
    transition: transform 380ms var(--ease-enter), filter 380ms ease;
  }
  html.v19-js .collage-heading { margin: 0; }
  html.v19-js .collage-slot {
    position: absolute;
    left: var(--x);
    top: var(--y);
    width: var(--w);
    height: var(--h);
    opacity: 0;
  }
  html.v19-js .collage-field.is-collage-ready .collage-slot { opacity: 1; }
  html.v19-js .collage-field.is-collage-moving .collage-card { will-change: transform, opacity; }
  html.v19-js .card-said__text { display: block; text-overflow: ellipsis; white-space: nowrap; }
}

/* with no JS at all there is no scatter to build, so the composed mini-collage
 * is the layout at every width — three up on a wide screen, and every card
 * already carrying its whole before and after */
@media (min-width: 1100px) {
  html:not(.v19-js) .collage-field {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: start;
    gap: clamp(18px, 2vw, 26px);
  }
  html:not(.v19-js) .collage-heading { grid-column: 1 / -1; margin-bottom: clamp(28px, 4vw, 48px); }
  html:not(.v19-js) .collage-tilt { transform: rotate(var(--mini-rot, 0deg)); }
  html:not(.v19-js) .collage-slot:nth-child(3n + 3) { margin-top: 30px; }
  html:not(.v19-js) .collage-slot:nth-child(3n + 4) { margin-top: 16px; }
}
/* the dictation glyph is a cursor that only means something once it moves */
html:not(.v19-js) .hero-dictation-wave { display: none; }

/* ------------------------------------------------ the mini-collage (<1100px) */
/* V21 §4: the single-file vertical stack is dead. Below the scatter the ten
 * cards become a composed, staggered mini-collage in normal flow — two columns
 * under 768px, three above it. Each card carries a couple of degrees of tilt
 * and a column offset, so the pile still reads as a desk rather than a table.
 * Nothing is sticky, nothing is pinned, nothing is hidden. */
@media (max-width: 1099.98px) {
  .collage-sticky { padding: clamp(64px, 9vw, 96px) 0 clamp(56px, 7vw, 88px); }
  .collage-field {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    gap: clamp(16px, 3vw, 26px);
  }
  .collage-heading { grid-column: 1 / -1; width: 100%; margin-bottom: clamp(28px, 5vw, 44px); }
  .collage-slot { width: 100%; }
  .collage-tilt { transform: rotate(var(--mini-rot, 0deg)); transform-origin: 50% 50%; }
  .collage-card { height: auto; min-height: 0; }
  /* the stagger: the second column rides a little lower than the first, so the
   * two columns never line up into a grid */
  .collage-slot:nth-child(2n) { margin-top: clamp(18px, 4vw, 34px); }
  .card-out { align-content: start; }
}

@media (min-width: 768px) and (max-width: 1099.98px) {
  .collage-field { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .collage-slot:nth-child(2n) { margin-top: 0; }
  .collage-slot:nth-child(3n + 3) { margin-top: clamp(20px, 3vw, 38px); }
  .collage-slot:nth-child(3n + 4) { margin-top: clamp(10px, 1.6vw, 20px); }
}

/* the arrival: each card fades and rises once, as it comes into view */
@media (max-width: 1099.98px) {
  html.v19-js .collage-slot { opacity: 0; transform: translate3d(0, 22px, 0); transition: opacity 620ms var(--ease-enter), transform 620ms var(--ease-enter); }
  html.v19-js .collage-slot.is-stacked-in { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------ the expanded card */
html.is-collage-open { overflow: hidden; }
html.is-collage-open .collage-field { --collage-dim: 0.96; filter: saturate(0.82); pointer-events: none; }
.collage-modal { position: fixed; z-index: 200; inset: 0; display: grid; place-items: center; }
.collage-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  opacity: 0;
}
/* The frame fits the demo. A one line Terminal command and a four line letter
 * do not want the same box, and forcing both into 640px is what left half the
 * dialogs opening onto empty paper. The engine measures the demo before the
 * flight and writes `--dialog-h`; with no JS the default still holds. */
.collage-dialog {
  position: relative;
  z-index: 2;
  width: min(720px, 90vw);
  height: var(--dialog-h, min(80vh, 640px));
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 20px;
  background: var(--paper-1);
  color: var(--ink-0);
  font-family: var(--font-host);
  box-shadow: 0 2px 8px rgba(6, 10, 5, 0.4), 0 48px 100px -24px rgba(6, 10, 5, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.72);
}
.collage-dialog[data-tone="dark"] { background: #181a18; color: #ecece8; }
/* fixed inner size: the shell never reflows while the card grows into it, so
 * nothing inside smears or rewraps mid flight */
.collage-dialog__shell {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--demo-w, 100%);
  height: var(--demo-h, 100%);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
}
/* the card you clicked, riding the front of the flight and fading out as the
 * real panel is revealed underneath — App Store continuity, no text smearing
 * because neither layer is ever scaled */
.collage-dialog__ghost {
  position: absolute;
  z-index: 6;
  left: 0;
  top: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  pointer-events: none;
}
/* the polished sentence, lifting out of the kivi panel into the app */
.spot-fly { position: absolute; z-index: 7; margin: 0; pointer-events: none; }

.dialog-chrome {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 60px 14px 20px;
  border-bottom: 1px solid var(--hairline-paper);
  background: rgba(246, 244, 236, 0.66);
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.2;
}
.dialog-chrome img { width: 22px; height: 22px; flex: none; object-fit: contain; }
.dialog-chrome b { color: var(--ink-0); font-weight: 500; }
.dialog-chrome i { color: rgba(27, 29, 24, 0.3); font-style: normal; }
.dialog-chrome span { color: rgba(27, 29, 24, 0.62); font-weight: 400; }
.collage-dialog[data-tone="dark"] .dialog-chrome { border-bottom-color: rgba(255, 255, 255, 0.08); background: rgba(28, 30, 27, 0.76); }
.collage-dialog[data-tone="dark"] .dialog-chrome b { color: #ecece8; }
.collage-dialog[data-tone="dark"] .dialog-chrome span { color: rgba(236, 236, 232, 0.5); }
.dialog-close {
  position: absolute;
  right: 8px;
  top: 50%;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: rgba(27, 29, 24, 0.6);
  cursor: pointer;
  transform: translateY(-50%);
  transition: background-color var(--dur-micro) ease, color var(--dur-micro) ease;
}
.collage-dialog[data-tone="dark"] .dialog-close { color: rgba(236, 236, 232, 0.62); }

/* V21.1 composition law for all sixteen dialogs: the host is a FRAME, and the
 * app inside it is zoomed and aligned to that frame. Content sits on the
 * frame's own optical centre with one shared measure, so no demo ever opens
 * onto a field of empty paper with four small words in the corner. Threads
 * still sit at the bottom of their scrollback, because that is where a thread
 * is; everything else composes to centre. */
.dialog-host {
  position: relative;
  min-height: 0;
  display: grid;
  align-content: center;
  justify-items: start;
  padding: clamp(26px, 3.6vw, 40px) clamp(26px, 3.6vw, 42px);
}
.collage-dialog[data-collage-dialog="slack"] .dialog-host,
.collage-dialog[data-collage-dialog="messages"] .dialog-host,
.collage-dialog[data-collage-dialog="banter"] .dialog-host { align-content: end; }
.dialog-host__surface { position: relative; width: 100%; max-width: 60ch; }
/* the app text, zoomed to the frame: at this size the surface reads as the app
 * it is instead of a thumbnail of one */
.dialog-host .out { width: 100%; }
.dialog-host .out-body { font-size: 20px; line-height: 1.5; }
.dialog-host .out-bubble { font-size: 20px; padding: 11px 16px; border-radius: 20px 20px 6px 20px; }
.dialog-host .out-list { font-size: 19px; line-height: 1.55; padding-left: 22px; }
.dialog-host .out-letter { gap: 10px; }
.dialog-host .out-letter p { font-size: 19px; line-height: 1.5; }
.dialog-host .out-check li { font-size: 19px; gap: 12px; }
.dialog-host .out-check { gap: 12px; }
.dialog-host .out-reminders__list { font-size: 13px; }
.dialog-host .out-quoted { font-size: 15px; padding: 10px 13px; }
.dialog-host .out-slack { grid-template-columns: 32px minmax(0, 1fr); gap: 12px; }
.dialog-host .out-slack__avatar { width: 32px; height: 32px; font-size: 15px; }
.dialog-host .out-slack__meta { font-size: 13px; }
.dialog-host .out-cursor { grid-template-columns: 36px minmax(0, 1fr); gap: 16px; }
.dialog-host .out-cursor__gutter { gap: 17px; font-size: 13px; }
.dialog-host .out-terminal .out-body { font-size: 17px; }
.dialog-host .out-check li i { width: 16px; height: 16px; }
.dialog-host .out-sprint { gap: 10px; }
.dialog-host .out-notes { gap: 11px; }
.dialog-host__caret {
  position: absolute;
  left: 0;
  top: 2px;
  width: 2px;
  height: 20px;
  background: var(--green-deep);
  animation: caret-blink 1.1s steps(1, end) infinite;
}
.collage-dialog[data-tone="dark"] .dialog-host__caret { background: var(--green-glow); }

/* ------------------------------------------------------- the Spotlight Pass */
/* The sentence is prose, never mono: this is language being cleaned, not code.
 * Colour says WHY and a mark says WHAT, so the grammar survives colour
 * blindness — a removal always carries a strike, an assembly always carries a
 * pill and an underline. */
.spot {
  position: relative;
  display: grid;
  gap: 12px;
  padding: 18px clamp(20px, 3vw, 30px) 20px;
  border-top: 1px solid var(--hairline-dusk);
  background: var(--dusk-1);
  color: var(--dusk-ink);
}
.spot__head { position: relative; display: flex; align-items: center; gap: 11px; min-height: 34px; }
.spot__bird { position: relative; width: 34px; height: 34px; display: block; transform-origin: 50% 100%; animation: bird-breathe 4s ease-in-out infinite alternate; animation-play-state: paused; }
.spot__bird.is-idle-visible { animation-play-state: running; }
.spot__bird img { position: relative; z-index: 2; width: 34px; height: 34px; }
.spot__shadow { position: absolute; z-index: 1; left: 5px; right: 5px; bottom: -2px; height: 6px; border-radius: 50%; background: rgba(6, 10, 5, 0.6); filter: blur(2px); }
.spot__state {
  color: rgba(244, 241, 230, 0.5);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* a fixed slot, so a chip arriving can never push the sentence around */
.spot__chip-slot { position: relative; height: 24px; margin-top: -4px; }
.spot-chip {
  position: absolute;
  left: var(--chip-x, 0px);
  top: 0;
  padding: 3px 9px;
  border: 1px solid rgba(168, 224, 99, 0.26);
  border-radius: var(--radius-pill);
  background: rgba(168, 224, 99, 0.12);
  color: #d6efc1;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  opacity: 0;
  transform: translate3d(-50%, 0, 0);
}
/* a hairline leader, so the chip is visibly ABOUT the run under it */
.spot-chip::after { content: ""; position: absolute; left: 50%; top: calc(100% + 1px); width: 1px; height: 11px; background: rgba(168, 224, 99, 0.34); }
.spot-slot[data-op="filler"] .spot-strike { background: rgba(244, 241, 230, 0.6); }
.spot-slot[data-op="self-correction"] .spot-strike { background: var(--amber); opacity: 1; }
.spot-slot[data-op="self-correction"] .spot-underline { background: var(--green-glow); }
.spot__line {
  min-height: 3.6em;
  color: var(--dusk-ink);
  font-family: var(--font-body);
  font-size: clamp(19px, 2vw, 24px);
  font-weight: 440;
  line-height: 1.5;
  letter-spacing: -0.014em;
  text-wrap: pretty;
}
.spot-plain { display: inline; }
.spot-slot {
  position: relative;
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  vertical-align: bottom;
}
/* the block run carries its own leading, so closing the inline line above it
 * (see `listOnly` in the engine) never touches the list itself */
.spot-slot--block { display: block; margin-top: 4px; line-height: 1.5; white-space: normal; }
/* a sentence that becomes nothing but a list has no sentence left to reserve
 * room for: drop the reserved line so the list does not float in a void */
.spot__line:has(> .spot-slot--block:first-child),
.spot__line:has(.spot-slot--block) { min-height: 0; }
.spot-tok { position: relative; z-index: 2; display: inline-block; white-space: nowrap; }
.spot-slot--block .spot-tok { display: block; white-space: normal; }
.spot-tok--after { position: absolute; left: 0; top: 0; opacity: 0; }
.spot-strike { position: absolute; left: 0; right: 0; top: 52%; height: 1.5px; border-radius: 2px; background: currentColor; opacity: 0.75; transform: scaleX(0); transform-origin: left; }
.spot-underline { position: absolute; left: 0; right: 0; bottom: 0.1em; height: 2px; border-radius: 2px; background: var(--green-glow); opacity: 0; transform: scaleX(0); transform-origin: left; }
.spot-pill { position: absolute; z-index: 1; inset: 0.06em -0.22em 0.1em; border-radius: 6px; background: rgba(168, 224, 99, 0.18); opacity: 0; transform: scaleX(0); transform-origin: left; }
.spot-sweep { position: absolute; z-index: 3; inset: 0 -0.3em; background: linear-gradient(100deg, transparent 18%, rgba(214, 255, 167, 0.34), transparent 82%); opacity: 0; transform: translateX(-104%); pointer-events: none; }
.spot-list { margin: 6px 0 0; padding-left: 1.05em; font-size: 0.86em; line-height: 1.5; }
.spot-list li { margin-bottom: 2px; }
.spot__wave { display: flex; align-items: flex-end; gap: 3px; height: 14px; opacity: 0.5; }
.spot__wave i { width: 2px; height: 14px; border-radius: 2px; background: var(--green-glow); transform: scaleY(var(--bar)); transform-origin: 50% 100%; }

/* --------------------------------------------------------- replay affordance */
/* Invisible while the scene plays, back on settle. Counter-clockwise, because
 * clockwise reads as refresh and a triangle reads as "this never played". */
.replay-button {
  position: absolute;
  z-index: 9;
  right: 12px;
  bottom: 12px;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: rgba(255, 255, 255, 0.92);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease, background-color var(--dur-micro) ease, transform var(--dur-micro) var(--ease-enter);
}
/* a 44px target around a 32px button */
.replay-button::after { content: ""; position: absolute; inset: -6px; border-radius: 50%; }
.replay-button--paper { background: rgba(27, 29, 24, 0.28); color: var(--paper-1); }
.replay-button:disabled { display: none; }
.replay-button:active { transform: scale(0.94); }
figure.is-complete .replay-button,
.collage-dialog.is-replay-ready .replay-button { opacity: 0.7; pointer-events: auto; }
figure.is-playing .replay-button { opacity: 0; pointer-events: none; }
/* the dialog's button lives on the kivi panel, which is already near-black:
 * a dark scrim there would be a hole, so this one is lit instead */
.replay-button--dialog { right: 14px; bottom: 14px; background: rgba(255, 255, 255, 0.1); color: rgba(244, 241, 230, 0.92); }
.scene-media { position: relative; }

@media (hover: none), (pointer: coarse) {
  figure.is-complete .replay-button,
  .collage-dialog.is-replay-ready .replay-button { opacity: 0.85; }
}

@media (hover: hover) and (pointer: fine) {
  .collage-card:hover ~ * .collage-card__pill,
  .collage-hover:hover .collage-card__pill { opacity: 1; transform: translate3d(0, 0, 0); }
  .collage-hover:hover .collage-card__lift { opacity: 1; }
  .gallery-card:hover { transform: translateY(-3px); box-shadow: 0 2px 4px rgba(43, 36, 26, 0.08), 0 26px 52px -20px rgba(43, 36, 26, 0.36); }
  .gallery-card:hover .gallery-card__pill { opacity: 1; transform: translate3d(0, 0, 0); }
  .replay-button:hover { opacity: 1; background: rgba(0, 0, 0, 0.55); }
  .replay-button--paper:hover { background: rgba(27, 29, 24, 0.46); }
  .replay-button--dialog:hover { background: rgba(255, 255, 255, 0.18); }
  .dialog-close:hover { background: rgba(27, 29, 24, 0.07); color: var(--ink-0); }
  .collage-dialog[data-tone="dark"] .dialog-close:hover { background: rgba(255, 255, 255, 0.08); color: #ecece8; }
}


/* -------------------------------------------------------------- memory scene */
.memory-scene { min-width: 0; }
.messages-thread { min-height: 420px; display: grid; align-content: center; gap: 16px; padding: clamp(28px, 3.2vw, 38px); background: linear-gradient(180deg, #f8f7f2, #efede6); }
.message-row { display: flex; position: relative; }
.message-row > span:not(.receipt-chip),
.memory-sent-bubble { max-width: 76%; padding: 10px 13px; border-radius: 16px; font-size: 16px; line-height: 1.35; }
.message-row--received { justify-content: flex-start; }
.message-row--received > span { border-bottom-left-radius: 5px; background: #e5e5e4; }
.message-row--sent { justify-content: flex-end; margin-bottom: 48px; }
.memory-sent-bubble { border: 0; border-bottom-right-radius: 5px; background: #2d7df6; color: #f8fbff; text-align: left; cursor: pointer; }
.memory-sent-bubble:active { transform: scale(0.98); }
.message-row--sent strong { font-weight: 620; }
.memory-receipt { position: absolute; z-index: 2; right: 10px; top: calc(100% + 9px); }
.next-time-divider {
  display: flex;
  align-items: center;
  gap: 14px;
  color: rgba(27, 29, 24, 0.62);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.next-time-divider::before,
.next-time-divider::after { content: ""; height: 1px; flex: 1; background: rgba(27, 29, 24, 0.08); }
.message-compose { position: relative; min-height: 116px; display: grid; align-items: start; padding: 16px 18px; border: 1px solid var(--hairline-paper-strong); border-radius: var(--radius-3); background: var(--paper-1); box-shadow: inset 0 1px 2px rgba(43, 36, 26, 0.035); }
.message-compose p { max-width: 44ch; color: var(--ink-0); font-size: 16px; line-height: 1.5; }
.memory-compose-state { position: relative; grid-area: 1 / 1; min-width: 0; }
.memory-draft-line { position: relative; z-index: 2; width: fit-content; }
.memory-correction-word { position: relative; display: inline-block; overflow: hidden; font-weight: 620; vertical-align: bottom; white-space: nowrap; }
.memory-selection { position: absolute; z-index: -1; inset: 0 -1px; border-radius: 2px; background: var(--sel-blue); transform-origin: left; opacity: 0; }
[data-memory-correct] { position: absolute; inset: 0 auto auto 0; display: block; white-space: nowrap; }
[data-memory-letter] { display: inline-block; }
.memory-draft-receipt { position: absolute; z-index: 3; left: 0; top: 36px; }
.memory-proof-word { position: relative; display: inline-block; font-weight: 620; }
.memory-proof-word > i { position: absolute; z-index: 0; left: 0; right: 0; bottom: 0; height: 5px; border-radius: 50%; background: var(--green-tint); transform-origin: left; }
.memory-proof-word > span { position: relative; z-index: 1; }
.memory-compose-state--later { padding-bottom: 46px; }
.memory-from { position: absolute; z-index: 4; left: 0; bottom: 0; min-height: 34px; padding: 5px 8px; box-shadow: none; }
.memory-from::before { content: ""; position: absolute; left: 42px; bottom: 100%; width: 1px; height: 14px; background: rgba(37, 107, 47, 0.32); }

/* ------------------------------------------------------------- persona scene */
.persona-scene { min-width: 0; }
.persona-tabs { position: relative; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border-bottom: 1px solid var(--hairline-paper); }
.persona-tabs button {
  position: relative;
  min-width: 0;
  min-height: 70px;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-content: center;
  column-gap: 9px;
  padding: 10px 12px;
  border: 0;
  background: transparent;
  color: var(--ink-1);
  text-align: left;
  cursor: pointer;
}
.persona-tabs__underline { position: absolute; z-index: 2; left: 12px; bottom: -1px; width: calc(33.333% - 24px); height: 2px; background: var(--green-deep); transform-origin: left; }
.persona-tabs button img { grid-row: 1 / span 2; width: 28px; height: 28px; align-self: center; transition: transform var(--dur-small) var(--ease-enter); }
.persona-tabs button span { overflow: hidden; font-size: 13px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
.persona-tabs button small { color: rgba(27, 29, 24, 0.62); font-size: 12px; font-weight: 400; }
.persona-tabs button.is-active { color: var(--ink-0); }
.persona-tabs button:disabled { cursor: default; opacity: 1; }
.persona-source-stack { min-height: 96px; display: grid; align-items: center; }
/* the transcript ledger: sans caps rail label, mono only for what was spoken */
.persona-source { display: grid; grid-template-columns: 84px minmax(0, 1fr); align-items: baseline; gap: 16px; padding: 16px 0; }
.persona-source > span {
  color: rgba(27, 29, 24, 0.62);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.persona-source p {
  color: rgba(27, 29, 24, 0.7);
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.6;
  letter-spacing: -0.01em;
}
.persona-source p [data-persona-input-word],
[data-persona-output-word] { display: inline; }
/* the custom-cosmetics rule is the cause of everything the host window does, so
 * it is typeset as the hero of its own moment (V20 §2c): a Fraunces pull-quote,
 * real curly quotes hung outside the text box, and ONE caption row under it.
 * No card, no tint, no "custom cosmetics" label. */
.persona-quote { display: grid; justify-items: start; gap: 14px; margin: 0; }
.persona-quote blockquote {
  position: relative;
  width: fit-content;
  max-width: 32ch;
  margin: 0;
  color: var(--ink-0);
  font-family: var(--font-display);
  font-size: clamp(21px, 1.9vw, 24px);
  font-weight: 420;
  line-height: 1.3;
  letter-spacing: -0.01em;
  font-variation-settings: "opsz" 40, "SOFT" 48, "WONK" 0;
  text-wrap: pretty;
}
/* the opening quote hangs, so the first letter and the caption under it share
 * one optical rail (law 7) */
.persona-quote blockquote > span { display: block; text-indent: -0.42em; }
.persona-quote blockquote > i {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -7px;
  height: 2px;
  border-radius: 2px;
  background: rgba(37, 107, 47, 0.26);
  transform-origin: left;
}
/* name at 60% weight 500, role at 45% weight 400, same size, one line */
.persona-caption {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.3;
}
.persona-caption b { color: rgba(27, 29, 24, 0.62); font-weight: 500; }
.persona-caption i { color: rgba(27, 29, 24, 0.3); font-style: normal; }
.persona-caption span { color: rgba(27, 29, 24, 0.45); font-weight: 400; }

.persona-card-stack { min-height: 108px; display: grid; }
.persona-card-stack > [data-persona-card] { grid-area: 1 / 1; align-self: start; }
.persona-agent-link { position: relative; min-height: 42px; display: grid; place-items: center; }
.persona-agent-link::before { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--hairline-paper); }
.persona-agent-bird { position: relative; z-index: 1; width: 30px; height: 30px; display: grid; place-items: center; background: var(--paper-0); transform-origin: center; }
.persona-agent-bird img { width: 24px; height: 24px; }
.persona-host { margin-top: 0; transition: background-color var(--dur-panel) ease, color var(--dur-panel) ease, border-color var(--dur-panel) ease; }
.persona-host .mac-window__content { min-height: 220px; }
.persona-room { min-height: 220px; }
.persona-room--cursor { display: grid; grid-template-columns: 46px minmax(0, 1fr); background: #181a18; color: #ecece8; }
.cursor-gutter { display: grid; align-content: center; justify-items: end; gap: 15px; padding-right: 12px; border-right: 1px solid rgba(255, 255, 255, 0.06); color: rgba(236, 236, 232, 0.3); font: 11px/1 var(--font-mono); font-variant-numeric: tabular-nums slashed-zero; }
.cursor-agent-result { align-self: center; display: grid; gap: 14px; padding: 28px; }
.cursor-agent-result > span {
  color: rgba(168, 224, 99, 0.72);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.cursor-agent-result p { max-width: 40ch; font-family: var(--font-host); font-size: 16px; font-weight: 480; line-height: 1.55; letter-spacing: -0.012em; }
.persona-host--messages { background: #f4f5f7; color: var(--ink-0); }
.persona-host--slack { background: #f8f6f1; color: var(--ink-0); }
.persona-room--messages { align-content: center; padding: 34px; background: #f4f5f7; }
.messages-room-meta { display: grid; justify-items: center; margin-bottom: 28px; }
.messages-room-meta span { font-size: 13px; font-weight: 590; }
.messages-room-meta small { color: var(--ink-2); font-size: 11px; }
.persona-room--messages p { width: fit-content; max-width: 76%; margin-left: auto; padding: 11px 14px; border-radius: 18px 18px 5px 18px; background: #2d7df6; color: #f8fbff; font-size: 16px; line-height: 1.4; }
.persona-room--slack { align-content: center; padding: 28px; background: #f8f6f1; }
.slack-room-meta { display: flex; align-items: baseline; justify-content: space-between; padding-bottom: 13px; border-bottom: 1px solid var(--hairline-paper); }
.slack-room-meta strong { font-size: 13px; font-weight: 590; }
.slack-room-meta span { color: var(--ink-2); font-size: 11px; }
.slack-output { margin-top: 24px; padding-left: 38px; position: relative; }
.slack-output::before { content: "K"; position: absolute; left: 0; top: 0; width: 26px; height: 26px; display: grid; place-items: center; border-radius: var(--radius-1); background: #4a154b; color: #fffaf5; font-size: 12px; font-weight: 700; }
.slack-output > strong { font-size: 16px; }
.slack-output ul { margin: 8px 0 0; padding-left: 18px; color: var(--ink-1); font-size: 16px; line-height: 1.5; }
[data-persona-output-shell] { transform-origin: left center; }

/* =============================================================================
 * THE HEY-KIVI GALLERY — "point at anything. say the change"
 *
 * Six demos, composed 3x2 ON the paper document. Aligned, never scattered: the
 * scatter language belongs to the dark stage above, and repeating it here would
 * say the two sections are the same thing. They are not. Up there kivi is
 * writing; down here kivi is changing something that already exists.
 * ========================================================================== */
.gallery-grid { row-gap: 0; }
.gallery-grid > .chapter-header { grid-column: 1 / -1; }
.gallery {
  grid-column: 1 / -1;
  margin-top: clamp(46px, 5.4vw, 74px);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: clamp(18px, 2.2vw, 28px);
}
.gallery-slot { min-width: 0; display: grid; }
.gallery-card {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 250px;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  overflow: hidden;
  padding: 0;
  border: 1px solid rgba(27, 29, 24, 0.09);
  border-radius: 14px;
  background: var(--paper-1);
  color: var(--ink-0);
  font-family: var(--font-host);
  text-align: left;
  box-shadow: 0 1px 2px rgba(43, 36, 26, 0.06), 0 14px 34px -16px rgba(43, 36, 26, 0.28);
  cursor: pointer;
  transition: transform var(--dur-small) var(--ease-enter), box-shadow var(--dur-small) var(--ease-enter);
}
.gallery-card:disabled { cursor: default; }
.gallery-card .card-chrome { background: rgba(246, 244, 236, 0.72); }
.gallery-card__surface { min-height: 0; display: grid; align-content: start; overflow: hidden; padding: 15px 16px; }
/* the command that made it, quoted, with the kiwi that heard it */
.gallery-card__said {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 9px;
  padding: 11px 15px 12px;
  border-top: 1px solid var(--hairline-paper);
  background: rgba(27, 29, 24, 0.028);
  font-family: var(--font-body);
}
.gallery-card__bird { width: 18px; height: 18px; flex: none; }
.gallery-card__bird img { width: 18px; height: 18px; }
.gallery-card__said q {
  min-width: 0;
  overflow: hidden;
  color: rgba(27, 29, 24, 0.66);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.35;
  quotes: "\201C" "\201D";
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gallery-card__said em { color: rgba(27, 29, 24, 0.42); font-size: 11px; font-style: normal; white-space: nowrap; }
.gallery-card__pill {
  position: absolute;
  z-index: 4;
  right: 10px;
  bottom: 50px;
  padding: 5px 10px;
  border-radius: var(--radius-pill);
  background: rgba(11, 14, 10, 0.86);
  color: rgba(244, 241, 230, 0.92);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
  opacity: 0;
  pointer-events: none;
  transform: translate3d(0, 4px, 0);
  transition: opacity var(--dur-small) var(--ease-enter), transform var(--dur-small) var(--ease-enter);
}

/* ------------------------------------------------------ the hey-kivi surface */
/* One target block per surface, holding every face of itself at once. Face 0 is
 * what was on screen; the last face is what you asked for. With JS off the last
 * face is the one in flow, so the demo is already finished. */
.hk-surface { min-width: 0; font-size: 14px; line-height: 1.5; }
.hk-target { position: relative; overflow: hidden; }
[data-hk-target] > .hk-face { position: absolute; left: 0; top: 0; width: 100%; opacity: 0; }
[data-hk-target] > .hk-face:last-of-type { position: relative; opacity: 1; }
.hk-face { position: relative; z-index: 1; display: grid; gap: 7px; }
/* A selection wraps TEXT. The old single box spanned the whole target block,
 * so a two word line came out highlighted to the right edge of the dialog —
 * blue paint over nothing. The engine measures the rendered line boxes and
 * paints one band per line, ragged at the end exactly like a real one. */
.hk-sel {
  position: absolute;
  z-index: 0;
  inset: 0;
  opacity: 0;
  transform-origin: left center;
  pointer-events: none;
}
.hk-sel i {
  position: absolute;
  display: block;
  border-radius: 3px;
  background: var(--sel-blue);
}
/* the shimmer is bounded by the same measured region, so nothing sweeps across
 * empty paper either */
.hk-sweep {
  position: absolute;
  z-index: 3;
  left: var(--sweep-x, -10px);
  top: var(--sweep-y, -5px);
  width: var(--sweep-w, calc(100% + 20px));
  height: var(--sweep-h, calc(100% + 10px));
  background: linear-gradient(100deg, transparent 18%, rgba(255, 255, 255, 0.78), transparent 82%);
  opacity: 0;
  transform: translateX(-104%);
  pointer-events: none;
}
/* what changed, marked as well as coloured: a green wash that wipes in, over a
 * hairline that is there whether or not you can see the colour */
.hk-surface mark {
  padding: 0 1px;
  background: linear-gradient(to right, rgba(168, 224, 99, 0.34), rgba(168, 224, 99, 0.34)) no-repeat left center;
  background-size: var(--mark, 100%) 100%;
  box-shadow: inset 0 -2px 0 rgba(37, 107, 47, 0.32);
  color: inherit;
}
.hk-static { color: rgba(27, 29, 24, 0.55); }
.hk-caret { display: inline-block; width: 2px; height: 17px; background: var(--green-deep); vertical-align: -3px; animation: caret-blink 1.1s steps(1, end) infinite; }
.hk-empty,
.hk-compose { min-height: 22px; }

.hk-mail-fields {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr);
  align-items: baseline;
  gap: 5px 10px;
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--hairline-paper);
  color: var(--ink-2);
  font-size: 11.5px;
}
.hk-mail-fields b { color: var(--ink-1); font-weight: 500; }
.hk-mail-body { display: grid; gap: 11px; justify-items: start; }
.hk-mail-body .hk-target { width: 100%; }
/* the mail that arrived, sitting above the box you are about to write in */
.hk-mail-in {
  display: grid;
  gap: 4px;
  margin-bottom: 14px;
  padding: 11px 14px;
  border-left: 2px solid rgba(27, 29, 24, 0.16);
  border-radius: 0 8px 8px 0;
  background: rgba(27, 29, 24, 0.04);
}
.hk-mail-in__meta { display: flex; align-items: baseline; gap: 8px; font-family: var(--font-body); font-size: 11.5px; line-height: 1.2; }
.hk-mail-in__meta b { color: var(--ink-1); font-weight: 500; }
.hk-mail-in__meta span { color: var(--ink-2); font-variant-numeric: tabular-nums slashed-zero; }
.hk-mail-in__body { color: rgba(27, 29, 24, 0.7); }
/* the reply box: an empty field with a caret in it, drawn so it is obviously a
 * place to write rather than an accident of layout */
.hk-mail-body--reply {
  width: 100%;
  padding: 14px 16px;
  border: 1px solid var(--hairline-paper-strong);
  border-radius: var(--radius-3);
  background: var(--paper-1);
  box-shadow: inset 0 1px 2px rgba(43, 36, 26, 0.035);
}
.hk-face strong { font-weight: 640; }

.hk-slack { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 10px; }
.hk-slack__avatar { width: 26px; height: 26px; display: grid; place-items: center; border-radius: var(--radius-1); background: #4a154b; color: #fffaf5; font-family: var(--font-body); font-size: 13px; font-weight: 600; }
.hk-slack__stack { min-width: 0; display: grid; gap: 5px; }
.hk-slack__meta { display: flex; align-items: baseline; gap: 7px; font-family: var(--font-body); font-size: 11px; line-height: 1.2; }
.hk-slack__meta b { color: var(--ink-0); font-weight: 500; }
.hk-slack__meta span { color: rgba(27, 29, 24, 0.45); font-variant-numeric: tabular-nums slashed-zero; }

.hk-note { display: grid; gap: 10px; justify-items: start; }
.hk-note .hk-target { width: 100%; }
.hk-note--doc { gap: 0; }
.hk-doc-title { margin-bottom: 4px; font-size: 15px; font-weight: 600; }
.hk-doc-head { margin-top: 4px; font-size: 12px; font-weight: 600; letter-spacing: 0.01em; }
.hk-lines { display: grid; gap: 7px; padding: 3px 0 6px; }
.hk-lines i { width: var(--w, 90%); height: 5px; border-radius: 3px; background: rgba(27, 29, 24, 0.12); }

.hk-thread { display: grid; gap: 10px; justify-items: end; }
.hk-thread .hk-target { width: 100%; }
.hk-bubble { width: fit-content; max-width: 88%; padding: 8px 13px; font-size: 14px; line-height: 1.4; }
.hk-bubble--in { border-radius: 17px 17px 17px 5px; background: rgba(27, 29, 24, 0.07); color: var(--ink-0); }
.hk-bubble--out { border-radius: 17px 17px 5px 17px; background: #2d7df6; color: #f8fbff; font-variant-numeric: tabular-nums slashed-zero; }
.hk-target--reply { display: grid; justify-items: end; }
.hk-target--reply .hk-face { justify-items: end; }

.hk-sheet { display: grid; gap: 8px; }
.hk-sheet__head { display: flex; align-items: baseline; justify-content: space-between; padding-bottom: 6px; border-bottom: 1px solid var(--hairline-paper); color: var(--ink-2); font-family: var(--font-body); font-size: 11px; }
.hk-sheet__head b { color: var(--ink-1); font-weight: 500; }
.hk-cells { margin: 0; padding: 0; display: grid; gap: 0; list-style: none; }
.hk-cells li {
  padding: 6px 9px;
  border-bottom: 1px solid rgba(27, 29, 24, 0.08);
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
  white-space: pre;
}
.hk-cells li:last-child { border-bottom: 0; }

/* the panel where the command is spoken: the Spotlight panel's own material, so
 * the two galleries feel like one product */
.hk-panel { gap: 10px; }
.hk-chord { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; color: rgba(244, 241, 230, 0.45); font-size: 11px; }
.hk-chord i { font-style: normal; }
.hk-chord .keycap { --cap: 32px; }
.hk-says { display: grid; gap: 5px; }
.hk-say {
  color: var(--dusk-ink);
  font-family: var(--font-body);
  font-size: clamp(16px, 1.6vw, 19px);
  font-weight: 440;
  line-height: 1.45;
  letter-spacing: -0.012em;
  text-wrap: pretty;
}
.hk-say [data-hk-word] { display: inline-block; }
/* the gallery chip names the whole change, not one run of it, so it sits at
 * the head of the slot instead of pointing down at a token */
.hk-chip { left: 0; transform: none; }
.hk-chip::after { display: none; }
/* same composition law as the collage dialogs: the surface is zoomed and
 * centred in its frame, and only a thread anchors to the bottom */
.gallery-host { align-content: center; justify-items: stretch; }
.gallery-dialog[data-gallery-dialog="slack-report"] .gallery-host,
.gallery-dialog[data-gallery-dialog="messages-answer"] .gallery-host { align-content: end; }
/* the dialog is a container, not a control: programmatic focus should not ring
 * the whole panel in green */
.collage-dialog:focus,
.collage-dialog:focus-visible { outline: none; }
.gallery-dialog .hk-surface { width: 100%; max-width: 60ch; font-size: 18px; line-height: 1.5; }
.gallery-dialog .hk-cells li { padding: 9px 12px; font-size: 17px; }
.gallery-dialog .hk-bubble { padding: 10px 16px; font-size: 18px; }
.gallery-dialog .hk-mail-fields { gap: 7px 12px; margin-bottom: 18px; padding-bottom: 15px; font-size: 13.5px; }
.gallery-dialog .hk-mail-body { gap: 14px; }
.gallery-dialog .hk-slack { grid-template-columns: 32px minmax(0, 1fr); gap: 13px; }
.gallery-dialog .hk-slack__avatar { width: 32px; height: 32px; font-size: 15px; }
.gallery-dialog .hk-slack__meta { font-size: 13px; }
.gallery-dialog .hk-sheet__head { padding-bottom: 8px; font-size: 12.5px; }
.gallery-dialog .hk-doc-title { margin-bottom: 7px; font-size: 19px; }
.gallery-dialog .hk-doc-head { margin-top: 8px; font-size: 14px; }
.gallery-dialog .hk-lines { gap: 9px; padding: 5px 0 8px; }
.gallery-dialog .hk-lines i { height: 6px; }
.gallery-dialog .hk-caret { height: 21px; }

/* -------------------------------------------------------------------- footer */
.v19-footer { position: relative; z-index: 2; padding: 78px 0 30px; border-top: 1px solid var(--hairline-dusk); background: var(--stage); color: var(--dusk-ink-2); }
.v19-footer__grid { row-gap: 68px; }
.v19-footer__brand { grid-column: 1 / span 4; }
.v19-footer__brand img { width: 100px; height: 34px; }
.v19-footer__brand p { margin-top: 16px; color: var(--dusk-ink-3); font-family: var(--font-display); font-size: 18px; font-style: italic; }
.v19-footer__column { display: grid; align-content: start; gap: 8px; }
.v19-footer__column:nth-child(2) { grid-column: 7 / span 2; }
.v19-footer__column:nth-child(3) { grid-column: 10 / -1; }
.v19-footer__column h2 {
  margin-bottom: 8px;
  color: rgba(244, 241, 230, 0.5);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.v19-footer__column a { width: fit-content; color: var(--dusk-ink-2); font-size: 13px; }
.v19-footer__column .is-mono { font-family: var(--font-mono); font-size: 12px; font-weight: 400; }
.v19-footer__base { grid-column: 1 / -1; display: flex; justify-content: space-between; gap: 24px; padding-top: 26px; border-top: 1px solid var(--hairline-dusk); color: var(--dusk-ink-3); font-size: 12px; }
.v19-footer__base p:last-child { display: flex; gap: 22px; }

@media (hover: hover) and (pointer: fine) {
  .v19-nav__links > a:not(.v19-button):hover { opacity: 1; }
  .v19-button:hover { background: #b8ed78; box-shadow: 0 1px 0 rgba(244, 241, 230, 0.28) inset, var(--shadow-mid); }
  .v19-button--primary:hover .v19-listening-bars i { animation: cta-listen 540ms ease-in-out infinite; }
  .v19-button--primary:hover .v19-listening-bars i:nth-child(2) { animation-delay: 90ms; }
  .v19-button--primary:hover .v19-listening-bars i:nth-child(3) { animation-delay: 180ms; }
  .persona-tabs button:hover img { transform: translate3d(0, -1px, 0) rotate(3deg); }
  /* the button's one playful tell: it wobbles a couple of degrees, once */
  .v19-button--hero:hover { animation: hero-wobble 620ms var(--ease-enter) 1; }
  .v19-nav__mark:hover .v19-nav__tittle--first { animation: tittle-hop 320ms var(--ease-enter) 1; }
  .v19-nav__mark:hover .v19-nav__tittle--second { animation: tittle-hop 320ms var(--ease-enter) 60ms 1; }
  .v19-footer__column a:hover { color: var(--dusk-ink); }
  .v19-footer__base a:hover { color: var(--dusk-ink-2); }
}
.v19-button--primary:focus-visible .v19-listening-bars i { animation: cta-listen 540ms ease-in-out infinite; }
.v19-button--primary:focus-visible .v19-listening-bars i:nth-child(2) { animation-delay: 90ms; }
.v19-button--primary:focus-visible .v19-listening-bars i:nth-child(3) { animation-delay: 180ms; }
@keyframes tittle-hop { 48% { transform: translate3d(0, -2px, 0); } }
/* V22 §4: the wordmark's own dots answer when the lens finds the bird. Same
 * hop as the hover tell, a little higher and twice, so it reads as the mark
 * noticing rather than as a stray hover. Outside the hover media query, because
 * the egg fires from a pointer that is nowhere near the nav. */
.v19-nav__mark.is-egg-found .v19-nav__tittle--first { animation: tittle-egg-hop 420ms var(--ease-enter) 2; }
.v19-nav__mark.is-egg-found .v19-nav__tittle--second { animation: tittle-egg-hop 420ms var(--ease-enter) 90ms 2; }
@keyframes tittle-egg-hop { 44% { transform: translate3d(0, -4px, 0); } }

/* -------------------------------------------------------------- responsive */
@media (max-width: 1059.98px) {
  .v19-band--hero { min-height: 0; }
  .hero-copy { grid-column: 1 / -1; max-width: 760px; }
  .hero-copy h1 { font-size: clamp(64px, 11vw, 92px); }
  .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .chapter-grid > .chapter-header,
  .chapter-grid--reverse > .chapter-header { grid-column: 1 / span 8; grid-row: 1; }
  .chapter-grid > figure,
  .chapter-grid--reverse > figure { grid-column: 1 / -1; grid-row: 2; width: min(100%, 820px); justify-self: center; }
  .chapter-header { max-width: 620px; }
  .persona-scene { width: min(100%, 760px) !important; }
}

/* V21 §4: the expanded card on a phone is a SHEET, not a takeover. The backdrop
 * stays visible around it, so it is obvious you are still on the page and that
 * tapping outside puts it away. It scrolls inside itself if it has to. */
@media (max-width: 767.98px) {
  .collage-modal { padding: max(24px, env(safe-area-inset-top)) 0 max(24px, env(safe-area-inset-bottom)); }
  .collage-dialog {
    width: min(92vw, 520px);
    height: auto;
    max-height: 84vh;
    border-radius: 20px;
  }
  .collage-dialog__shell { position: relative; width: 100%; height: auto; max-height: 84vh; }
  .dialog-host { overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .collage-card { min-height: 0; }
  .card-said__text { -webkit-line-clamp: 3; }
  .spot__line { font-size: 17px; }
  .spot { padding: 15px 18px 17px; }
  .dialog-host .out-body { font-size: 15.5px; }
  .dialog-host .out-bubble { font-size: 15.5px; }
  .dialog-host .out-list { font-size: 14.5px; }
  /* the same step down for everything else the composition pass scaled up */
  .dialog-host .out-letter p { font-size: 15px; }
  .dialog-host .out-check li { font-size: 15px; }
  .dialog-host .out-terminal .out-body { font-size: 14px; }
  .dialog-host .out-quoted { font-size: 13.5px; }
  .gallery-dialog .hk-surface { font-size: 15px; }
  .gallery-dialog .hk-cells li { font-size: 14.5px; }
  .gallery-dialog .hk-bubble { font-size: 15px; }
  .gallery-dialog .hk-doc-title { font-size: 16px; }
}

@media (max-width: 720px) {
  .v19-grid { width: calc(100% - 40px); column-gap: 20px; }
  .v19-nav { height: 64px; }
  .v19-nav__mark { grid-column: 1 / span 4; }
  .v19-nav__mark { min-height: 44px; display: flex; align-items: center; }
  .v19-nav__links { grid-column: 5 / -1; gap: 12px; }
  .v19-nav__links > a:not(.v19-button) { display: none; }
  .v19-band--hero { padding: 112px 0 96px; }
  .hero-grid { row-gap: 52px; }
  .hero-copy h1 { max-width: 8ch; font-size: clamp(56px, 17vw, 76px); }
  .hero-actions { margin-top: 36px; }
  .hero-actions .v19-button { width: 100%; }
  .v19-button--hero { min-height: 60px; padding: 18px 26px; font-size: 18px; }
  .hero-proximity-wave {
    right: 20px;
    bottom: 20px;
    left: 20px;
    grid-template-columns: repeat(40, minmax(1px, 1fr));
    gap: 3px;
    opacity: 0.46;
  }
  .hero-proximity-wave > span:nth-child(n + 41) { display: none; }
  .chapter-grid { row-gap: 44px; }
  .chapter-grid > .chapter-header,
  .chapter-grid--reverse > .chapter-header { grid-column: 1 / -1; }
  .chapter-header h2 { font-size: clamp(40px, 12vw, 52px); }
  .chapter-subline { max-width: 36ch; margin-top: 20px; }

  .mac-window__toolbar { grid-template-columns: 72px minmax(0, 1fr) 72px; padding-inline: 12px; }
  .collage-title { font-size: clamp(34px, 11vw, 46px); }
  .collage-sub { max-width: 36ch; line-height: 1.7; }
  /* a narrow card is a small card: the host text steps down with it, so a
   * three line message does not become a seven line one */
  .card-out { padding: 13px 14px; }
  .card-chrome { padding: 9px 12px; font-size: 11px; }
  /* only a card that carries a flag needs a second chrome line for it */
  .card-chrome:has(.card-flag) { flex-wrap: wrap; }
  .card-flag { margin-left: 0; }
  .card-said { padding: 9px 12px 10px; }
  .card-said__label { font-size: 10px; }
  .card-said__text { font-size: 11.5px; }
  .out-body { font-size: 13.5px; }
  .out-bubble { font-size: 13.5px; }
  .out-list { font-size: 13px; }
  .out-letter p { font-size: 13px; }
  .out-check li { font-size: 13px; }
  .out-quoted { font-size: 12px; }
  .out-terminal .out-body { font-size: 12px; }
  .v19-doc { padding: clamp(56px, 12vw, 84px) 20px 44px; }
  .doc-divider { margin-block: clamp(64px, 14vw, 92px); }
  /* one column: equal row heights would only manufacture dead space */
  .gallery { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; gap: 16px; margin-top: 34px; }
  .gallery-card { min-height: 0; }

  .messages-thread { min-height: 412px; padding: 26px 18px; }
  .message-row > span:not(.receipt-chip),
  .memory-sent-bubble { max-width: 88%; font-size: 16px; }
  .receipt-chip__copy strong,
  .receipt-chip__copy small,
  .next-time-divider { font-size: 12px; }
  .memory-receipt { right: 0; }
  .message-compose { min-height: 126px; padding: 15px; }

  .persona-tabs button { min-height: 76px; grid-template-columns: 1fr; grid-template-rows: 30px auto; justify-items: center; gap: 4px; padding: 9px 4px; text-align: center; }
  .persona-tabs button img { grid-row: auto; width: 28px; height: 28px; }
  .persona-tabs button small { display: none; }
  .persona-source-stack { min-height: 132px; }
  .persona-source { grid-template-columns: minmax(0, 1fr); gap: 8px; padding: 12px 0; }
  .persona-source > span:first-child { grid-column: 1; }
  .persona-source p { grid-column: 1; font-size: 13px; }
  .persona-card-stack { min-height: 124px; }
  .persona-quote blockquote { font-size: 19px; }
  .persona-agent-link { min-height: 38px; }
  .persona-host .mac-window__content,
  .persona-room { min-height: 232px; }
  .cursor-agent-result { padding: 22px 18px; }
  .cursor-agent-result p { font-size: 16px; }
  .persona-room--messages,
  .persona-room--slack { padding: 22px 18px; }
  .persona-room--messages p { max-width: 88%; }


  .v19-band--closing { min-height: 640px; padding-block: 104px; }
  .closing-perch { transform: scale(0.86); margin-bottom: 4px; }
  .closing-grid h2 { font-size: clamp(48px, 14vw, 64px); }
  .closing-note a { min-height: 44px; display: inline-flex; align-items: center; margin-block: -14px; }

  .v19-footer { padding-top: 64px; }
  .v19-footer__grid { row-gap: 48px; }
  .v19-footer__brand { grid-column: 1 / -1; }
  .v19-footer__column:nth-child(2) { grid-column: 1 / span 5; }
  .v19-footer__column:nth-child(3) { grid-column: 7 / -1; }
  .v19-footer__base { flex-direction: column; }
  .v19-footer__base a { min-height: 44px; display: inline-flex; align-items: center; }
  .v19-footer__column h2 { font-size: 12px; }
  .v19-footer__column a { min-width: 44px; min-height: 44px; display: flex; align-items: center; }
}

@media (max-width: 359.98px) {
  .v19-grid { width: calc(100% - 32px); column-gap: 16px; }
  .v19-nav__mark { grid-column: 1 / span 5; }
  .v19-nav__links { grid-column: 6 / -1; }
  .hero-copy h1 { font-size: 52px; }
  .card-said__text { font-size: 12px; }
  .mac-window__toolbar { grid-template-columns: 68px minmax(0, 1fr) 68px; }
  .message-row > span:not(.receipt-chip),
  .memory-sent-bubble { max-width: 92%; }
  .receipt-chip { gap: 6px; padding-inline: 7px; }
  .persona-room--cursor { grid-template-columns: 36px minmax(0, 1fr); }
  .cursor-agent-result { padding-inline: 14px; }
}

@media (max-width: 479.98px) {
  .v19-nav__progress { display: none; }
}

/* touch: the art at rest STAYS (V22.2 — it is part of the page, and a finger is
 * not a reason to lose it); only the aperture goes, because there is no pointer
 * to move it and a lens parked off-screen is 300px of mask for nothing. The JS
 * gates it too; this is the belt to that pair of braces. */
@media (hover: none), (pointer: coarse) {
  .dither-art__vivid,
  .dither-art__egg { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  /* reduced motion keeps the art at rest and loses the lens: the artwork is
   * information, the aperture chasing the pointer is motion, and V22.2 is
   * exactly the split that makes that separation possible again. */
  .dither-art__vivid,
  .dither-art__egg { display: none !important; }
  .v19-stage__aurora { animation: none !important; }
  .v19-nav__progress.is-idle > i { animation: none !important; opacity: 1; }
  html.v19-js:not(.v19-hero-dictated) [data-hero-word] { opacity: 1 !important; filter: none !important; transform: none !important; }
  .hero-dictation-wave__caret { opacity: 0 !important; }
  .hero-proximity-wave > span { transform: scaleY(var(--wave-height)) !important; }
  html.v19-js:not(.v19-hero-loaded) [data-hero-load],
  html.v19-js .chapter-grid > .chapter-header,
  html.v19-js .chapter-grid > figure { opacity: 1; filter: none; transform: none; }
  /* the collage settles the moment it is built: no entry, no recession, no
   * drift. The composition is still the composition, it just holds still. */
  html.v19-js .collage-slot { opacity: 1 !important; transform: none !important; }
  html.v19-js .collage-field { transition: none; }
  .v19-doc { transform: none !important; }
  .spot__bird { animation: none !important; }
  .dialog-host__caret,
  .hk-caret { display: none; }
  .hk-sel { opacity: 0 !important; }
  .gallery-card:hover { transform: none; }
  .replay-button { transition: none; }
}

/* =============================================================================
 * MAC-ONLY DOWNLOAD (phones and tablets)
 * A .dmg cannot be opened on a touch device, so the download buttons give way
 * to a line saying where to get it. Hiding the button is also what keeps the
 * download_click event off phones: display:none takes it out of the hit
 * testing, so the delegated click listener never sees it.
 *
 * pointer:coarse AND a width bound, not width alone — a narrow desktop window
 * is still a desktop and keeps its button. Desktop is untouched either way.
 * ========================================================================== */
.mac-download-note { display: none; }

@media (pointer: coarse) and (max-width: 1024px) {
  .mac-download { display: none; }
  .mac-download-note { display: block; }

  /* The hero message stands where the button stood. 60px is that button's own
     min-height at this width (.v19-button--hero), so taking its place shifts
     nothing above or below it. Each line is its own block and both balance,
     so neither ever drops a single word onto a line of its own. */
  .mac-download-note--hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 60px;
    text-align: center;
  }
  .mac-download-note__lead {
    font-family: var(--font-display);
    font-weight: 460;
    font-size: 20px;
    color: var(--dusk-ink);
    text-wrap: balance;
  }
  .mac-download-note__sub {
    font-size: 13px;
    color: var(--dusk-ink-2);
    text-wrap: balance;
  }

  /* 36px is the margin the closing button carried (.closing-grid > .v19-button),
     so the message keeps that same distance from "kivi is ready when you are"
     rather than sitting against it. It also carries .closing-note, which is
     where its muted colour and 13px come from. */
  .mac-download-note--closing { margin-top: 36px; }

  /* the footer note sits among 13px links; match them */
  .v19-footer__column .mac-download-note { font-size: 13px; }
}

/* =============================================================================
 * /windows — the Kivi for Windows waitlist
 * One screen, no scroll, at 1440x900, 1280x720 and 375x667. The page is a flex
 * column under the fixed 72px nav: the hero takes whatever height is left and
 * centres its block, and the one-line footer sits at the bottom of it. The
 * home page's own hero band is not reused — its `min-height: max(900px,100svh)`
 * would force a scrollbar at 720px tall on its own.
 * ========================================================================== */
.windows-page {
  min-height: 100svh;
  padding-top: 72px;              /* the fixed .v19-nav */
  display: flex;
  flex-direction: column;
}

.windows-hero {
  flex: 1;
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;               /* the dither scene is sized for a taller band */
  padding: clamp(12px, 3vh, 32px) 0;
}
.windows-hero__block {
  position: relative;
  z-index: 3;
  width: min(100% - 40px, 760px);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(14px, 2.4vh, 26px);
  text-align: center;
}

/* two lines, always: each line is its own block, so the break never depends on
   where the measure happens to fall */
.windows-title {
  font-family: var(--font-display);
  font-weight: 460;
  font-size: clamp(30px, 6.2vw, 88px);
  line-height: 1.02;
  letter-spacing: -0.028em;
  margin: 0;
}
.windows-title__line { display: block; }
.windows-title__lime { color: var(--green-glow); }
.windows-title__pale { color: var(--dusk-ink); }

/* .chapter-subline is the site's measure for a sentence under a heading, but
   it is coloured --ink-1 for the paper sections and capped at 34ch. On the dark
   stage it takes the muted dusk ink, and the cap is lifted so the sentence sits
   on one line on a desktop. */
.windows-sub {
  margin: 0;
  max-width: none;
  color: var(--dusk-ink-2);
  text-wrap: balance;
}

.windows-foot {
  flex: none;
  position: relative;
  z-index: 3;
  padding: 0 0 clamp(14px, 2.2vh, 22px);
  color: var(--dusk-ink-3);
  font-size: 12px;
}
.windows-foot__inner {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.windows-foot p { margin: 0; }

@media (max-width: 720px) {
  .windows-page { padding-top: 64px; }        /* .v19-nav is 64px here */
  .windows-foot__inner { justify-content: center; gap: 4px 14px; }
}
