/* ============================================================================
   Pragma marketing site

   Light by default, and deliberately neutral: Pragma is its own product, not a
   Systo skin, so the warm cream grounds and the orange-on-everything are gone.
   What is left is white, near-black ink, and the brand orange used twice, on
   a filled control and on your own message. One accent across a lot of white
   reads as a point of view; the same orange everywhere reads as a theme.

   The palette mirrors the app's `pragma` skin value for value, so the site and
   the product are the same design rather than two drifting versions of one.

   Motion follows Apple's Designing Fluid Interfaces: springs live in JS (see
   motion.js), and CSS handles only press feedback and non-gestural transitions.
   ========================================================================== */

/* ── tokens ────────────────────────────────────────────────────────────── */
:root {
  --flare:       #FF532E;  /* the brand orange, unmodified: mark and edges */
  --ember:       #FF7A3D;  /* its lighter partner, gradients only */
  --accent:      #CE3F19;  /* the tone dark enough to carry white (4.83:1) */
  --accent-text: #C63C16;  /* links and emphasis on a white ground (5.17:1) */
  --grape:       #5B3DF5;  /* one cool counterpoint, on a single mascot */

  --paper:    #FFFFFF;   /* cards, panels, the app window */
  --ground:   #F4F4F6;   /* the page itself */
  --ground-2: #ECECF0;   /* alternating bands, hover fills */
  --inset:    #F0F0F3;   /* sunken surfaces: code blocks, search fields */
  --line:     #E3E3E9;   /* hairline between surfaces */
  --line-2:   #C9C9D3;   /* a border that has to be seen on its own */

  --ink:    #16161A;     /* body text, 18:1 on white */
  --text-2: #55555E;     /* secondary, 7.4:1 */
  --text-3: #6E6E78;     /* metadata, 5.3:1, still real text */

  --grad: linear-gradient(135deg, var(--ember), var(--accent));

  --r-sm: 10px;
  --r:    14px;
  --r-lg: 20px;
  --r-xl: 28px;

  /* On white, shadow does the separating that borders did on the dark build:
     a tight contact shadow plus a wide soft one. */
  --shadow-1: 0 1px 2px rgba(20, 20, 34, .05), 0 4px 12px -6px rgba(20, 20, 34, .10);
  --shadow-2: 0 1px 3px rgba(20, 20, 34, .07), 0 24px 56px -28px rgba(20, 20, 34, .28);

  --press: 110ms cubic-bezier(.2, .7, .3, 1);

  --maxw: 1180px;
  --gut: clamp(20px, 5vw, 56px);
  /* read by main.js too, so the drag bounds match the rendered size */
  --blob: 82px;
}

/* ── reset ─────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  font-size: clamp(15px, .55vw + 13.4px, 17px);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* clip, not hidden: `hidden` promotes body to the scroll container and
     breaks anything that measures documentElement.scrollTop */
  overflow-x: clip;
}

img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
code { font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace; font-size: .9em; }

.skip {
  position: absolute; left: -999px; top: 0; z-index: 100;
  background: var(--accent); color: #fff; padding: .7rem 1.1rem; border-radius: 0 0 var(--r-sm) 0;
}
.skip:focus { left: 0; }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 3px;
  border-radius: 6px;
}

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gut); }

/* ── type (tracking is size-specific, never one value for everything) ─── */
.display {
  font-family: "Inter Tight", "Inter", system-ui, sans-serif;
  font-size: clamp(3.2rem, 8.2vw, 7.5rem);
  font-weight: 900;
  line-height: 0.93;
  /* Tracking is size-specific: at this size 0 reads as gaps between letters,
     and the value that looks right at 3rem looks slack at 7.5rem. */
  letter-spacing: -0.052em;
  margin: .3em 0 .42em;
  text-wrap: balance;
}
.h2 {
  font-family: "Inter Tight", "Inter", system-ui, sans-serif;
  font-size: clamp(2.3rem, 5.6vw, 4.6rem);
  font-weight: 900;
  line-height: 0.98;
  letter-spacing: -0.042em;
  margin: .18em 0 .5em;
  text-wrap: balance;
}
.h2--big { font-size: clamp(3rem, 8vw, 6.6rem); letter-spacing: -0.05em; }

/* The emphasised word. The dark build set this in an italic serif with a
   gradient fill, which was Systo's signature, not Pragma's. Weight and the
   accent do the same job without borrowing another brand's voice. */
em { font-style: normal; font-weight: 800; color: var(--accent-text); }

/* Announcement pill. Sits where the eyebrow was, but it is a link, so it is
   built as one and gets the same press feedback as every other control. */
.pill-news {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .42rem .5rem .42rem .8rem;
  border-radius: 999px;
  background: var(--paper); border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  font-size: .82rem; color: var(--text-2);
  transition: transform var(--press), border-color var(--press), box-shadow var(--press);
  will-change: transform;
}
.pill-news:hover { border-color: var(--line-2); box-shadow: var(--shadow-2); }
.pill-news b { color: var(--ink); font-weight: 600; }
.pill-news span:last-of-type { color: var(--text-3); }
.pill-news__dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
  animation: beacon 2.6s ease-out infinite;
}
.pill-news svg { color: var(--text-3); }

/* The mark, set into the headline at cap height. It is the only thing on the
   page allowed to interrupt a sentence. */
.display__mark {
  display: inline-block; vertical-align: -0.16em;
  width: 0.92em; height: 0.92em; margin: 0 .06em;
}

.eyebrow {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .18em;      /* small text wants positive tracking */
  text-transform: uppercase;
  color: var(--text-3);
  margin: 0;
  display: flex; align-items: center; gap: .6em;
}
.eyebrow .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  animation: beacon 2.6s ease-out infinite;
}
@keyframes beacon {
  0%   { box-shadow: 0 0 0 0 rgba(206, 63, 25, .45); }
  70%  { box-shadow: 0 0 0 9px rgba(206, 63, 25, 0); }
  100% { box-shadow: 0 0 0 0 rgba(206, 63, 25, 0); }
}
.lede, .sec__lede {
  font-size: clamp(1.1rem, 1.4vw + .82rem, 1.45rem);
  line-height: 1.55;
  letter-spacing: -0.004em;
  color: var(--text-2);
  max-width: 56ch;
  margin: 0 0 2rem;
}

/* ── buttons ───────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55em;
  padding: .78em 1.35em;
  border: 1px solid transparent;
  border-radius: 999px;
  font-weight: 600;
  font-size: .95rem;
  letter-spacing: -0.008em;
  cursor: pointer;
  user-select: none; -webkit-user-select: none;
  transition: transform var(--press), background-color var(--press), border-color var(--press), box-shadow var(--press);
  will-change: transform;
}
.btn--sm { padding: .5em 1em; font-size: .86rem; }
.btn--lg { padding: .92em 1.7em; font-size: 1.02rem; }

/* ── the keycap treatment ────────────────────────────────────────────────
   Adapted from the authored rectangle-button family in threeui
   (neuform-isolated/gradient-pill-button + launch-button), kept as a pill
   rather than a rectangle. Three ideas carried over:

   1. Depth by stacking, not by one big blur. Six shadow layers at doubling
      offsets, each pulled back by half its own blur, so the edge stays crisp
      while the ambient shadow spreads. One 24px shadow cannot do both.
   2. A gradient hairline, painted as a masked ::before ring rather than a
      border, so the top edge catches light and the bottom sits in shade.
   3. A physical press. The button rests on a hard bottom edge; on pointer-down
      the edge collapses and the cap travels down by exactly the height it
      lost, so the top face lands where the shadow was instead of shrinking.  */
.btn--primary {
  position: relative;
  color: #fff;
  background-image: var(--grad);
  --cap: 3px;
  --cap-ink: #A32F0F;
  box-shadow:
    0 0 0 1px rgba(163, 47, 15, .40),
    0 var(--cap) 0 var(--cap-ink),
    0 1px 1px -0.5px rgba(206, 63, 25, .16),
    0 3px 3px -1.5px rgba(206, 63, 25, .16),
    0 6px 6px -3px rgba(206, 63, 25, .18),
    0 12px 12px -6px rgba(206, 63, 25, .20),
    0 24px 24px -12px rgba(206, 63, 25, .24);
  transition: transform 90ms cubic-bezier(.2,.7,.3,1), box-shadow 90ms cubic-bezier(.2,.7,.3,1);
}
/* the light-catching ring: a 1px gradient border a plain border cannot draw */
.btn--primary::before {
  content: ""; position: absolute; inset: 0; padding: 1px; border-radius: inherit;
  background: linear-gradient(180deg, rgba(255,255,255,.55), rgba(255,255,255,0) 45%, rgba(0,0,0,.22));
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  pointer-events: none;
}
.btn--primary:hover {
  box-shadow:
    0 0 0 1px rgba(163, 47, 15, .44),
    0 var(--cap) 0 var(--cap-ink),
    0 1px 1px -0.5px rgba(206, 63, 25, .18),
    0 3px 3px -1.5px rgba(206, 63, 25, .18),
    0 6px 6px -3px rgba(206, 63, 25, .22),
    0 14px 14px -7px rgba(206, 63, 25, .26),
    0 30px 30px -15px rgba(206, 63, 25, .30);
}
/* Pressed: the cap edge goes, and the button drops by exactly that much, so it
   reads as travel rather than as the whole control getting smaller. */
.btn--primary[data-press].is-pressed,
.btn--primary:active {
  transform: translateY(var(--cap));
  box-shadow:
    0 0 0 1px rgba(163, 47, 15, .45),
    0 0 0 var(--cap-ink),
    0 1px 1px -0.5px rgba(206, 63, 25, .16),
    0 2px 2px -1px rgba(206, 63, 25, .14);
}
.btn--ghost {
  color: var(--ink);
  border-color: var(--line-2);
  background: var(--paper);
  box-shadow: var(--shadow-1);
}
.btn--ghost:hover { background: var(--ground-2); }
.btn--allow { color: #fff; background: var(--accent); font-weight: 700; }
.btn--deny  { color: var(--text-2); border-color: var(--line-2); background: var(--paper); }
.btn--deny:hover { color: var(--ink); background: var(--ground-2); }

/* Apple §1: feedback on pointer-down, never on release */
[data-press].is-pressed { transform: scale(.965); }
.btn--lg[data-press].is-pressed { transform: scale(.975); }
/* the keycap presses by travelling, not by shrinking, so it opts out of the
   generic scale above (which is declared first, so this wins) */
.btn--primary[data-press].is-pressed { transform: translateY(var(--cap)); }

/* ── nav: translucent chrome, content scrolls under (Apple §12) ────────── */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  transition: background-color 260ms ease, box-shadow 260ms ease;
}
/* The blur alone separates the bar from what scrolls under it. The old fade
   started at 0.8 alpha, which read as a step at the join rather than a fade. */
.nav::after {
  content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 20px;
  background: linear-gradient(to bottom, rgba(255, 255, 255, .55), rgba(255, 255, 255, 0));
  opacity: 0; transition: opacity 260ms ease; pointer-events: none;
}
.nav.is-stuck {
  background: rgba(255, 255, 255, .72);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
}
.nav.is-stuck::after { opacity: 1; }

.nav__inner {
  max-width: var(--maxw); margin-inline: auto;
  padding: .85rem var(--gut);
  display: flex; align-items: center; gap: 1.4rem;
}
.brand { display: flex; align-items: center; gap: .55rem; margin-right: auto; }
.brand__mark { width: 30px; height: 30px; flex: none; display: block; }
.brand__mark svg { width: 100%; height: 100%; }
.brand__word { font-weight: 800; letter-spacing: -0.03em; font-size: 1.12rem; }
.brand__by {
  font-size: .64rem; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-3);
  padding: .3em .6em; border: 1px solid var(--line); border-radius: 999px;
}
.nav__links { display: flex; gap: 1.35rem; font-size: .9rem; color: var(--text-2); font-weight: 500; }
.nav__links a { position: relative; padding: .2rem 0; transition: color var(--press); }
.nav__links a::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: -2px; height: 2px;
  background: var(--accent); border-radius: 2px;
  transition: right 260ms cubic-bezier(.2,.8,.2,1);
}
.nav__links a:hover { color: var(--ink); }
.nav__links a:hover::after { right: 0; }
@media (max-width: 900px) {
  .brand__by { display: none; }
  /* the only nav action on a phone, so it needs a real target */
  .nav .btn--sm { min-height: 44px; padding-inline: 1.15em; }
}
/* the five links still fit to about 660px, so they stay until they do not */
@media (max-width: 700px) { .nav__links { display: none; } }

/* ── hero ──────────────────────────────────────────────────────────────── */
.hero { position: relative; padding: clamp(7rem, 15vh, 11rem) 0 clamp(4rem, 9vw, 7rem); overflow: clip; }
/* The wash is faded out before `overflow: clip` on .hero reaches it, or the
   clip slices a live gradient and the cut itself reads as a section line. */
.hero__field {
  position: absolute; inset: -20% -10% 0; pointer-events: none; z-index: 0; contain: strict;
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 97%);
  mask-image: linear-gradient(to bottom, #000 62%, transparent 97%);
}
/* Light, not glow: on white these are a faint warm wash, an order of magnitude
   softer than the dark build's, or the page turns peach. */
.glow { position: absolute; border-radius: 50%; }
.glow--flare { width: 62vw; height: 62vw; left: 40%; top: -10%; background: radial-gradient(closest-side, rgba(255,122,61,.15), transparent 74%); }
.glow--honey { width: 40vw; height: 40vw; left: 62%; top: 28%; background: radial-gradient(closest-side, rgba(255,180,80,.12), transparent 74%); }
.glow--grape { width: 54vw; height: 54vw; left: -18%; top: 10%; background: radial-gradient(closest-side, rgba(91,61,245,.07), transparent 76%); }

.hero__grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  gap: clamp(2rem, 4vw, 4rem); align-items: center;
}
@media (max-width: 1000px) { .hero__grid { grid-template-columns: 1fr; } }

.hero__cta { display: flex; flex-wrap: wrap; gap: .7rem; margin-bottom: 2.6rem; }

.hero__facts {
  display: flex; flex-wrap: wrap; gap: 0; margin: 0; padding: 0; list-style: none;
  border-top: 1px solid var(--line);
}
.hero__facts li { padding: 1rem 1.4rem 0 0; margin-right: 1.4rem; border-right: 1px solid var(--line); }
.hero__facts li:last-child { border-right: 0; margin-right: 0; }
.hero__facts strong { display: block; font-size: 1.02rem; letter-spacing: -0.02em; }
.hero__facts span { font-size: .74rem; letter-spacing: .04em; color: var(--text-3); }

@media (max-width: 620px) {
  .hero__cta, .download__row { flex-direction: column; align-items: stretch; }
  .hero__cta .btn, .download__row .btn { width: 100%; }
  .hero__facts { display: grid; grid-template-columns: 1fr 1fr; column-gap: 1.4rem; }
  .hero__facts li { flex: none; border-right: 0; margin-right: 0; padding-right: 0; }
}

/* ── the app window: a live recreation of Pragma in its own skin ───────── */
.hero__app { perspective: 1400px; }
.win {
  border-radius: var(--r-lg);
  background: var(--paper);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-2);
  overflow: hidden;
  transform-style: preserve-3d;
  will-change: transform;
}
.win__bar {
  display: flex; align-items: center; gap: .8rem;
  padding: .62rem .9rem;
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}
.traffic { display: flex; gap: .42rem; }
.traffic i { width: 10px; height: 10px; border-radius: 50%; background: #d8d8de; }
.traffic i:first-child { background: #ff5f57; }
.traffic i:nth-child(2) { background: #febc2e; }
.traffic i:nth-child(3) { background: #28c840; }
.win__title { font-size: .78rem; color: var(--text-3); font-weight: 500; letter-spacing: .02em; }

/* height, not min-height. With an indefinite row the single auto track sizes
   to the chat's max-content, so overflow:hidden below never clips and the whole
   page pumps by up to 390px every time the demo loop adds a message. */
.win__body { display: grid; grid-template-columns: 168px minmax(0,1fr); height: 430px; }
@media (max-width: 560px) { .win__body { grid-template-columns: 62px minmax(0,1fr); height: 400px; } }

.win__side { border-right: 1px solid var(--line); padding: .7rem .55rem; display: flex; flex-direction: column; gap: .5rem; background: var(--paper); }
.side__search { height: 28px; border-radius: 8px; background: var(--inset); display: flex; align-items: center; padding-inline: .5rem; }
.side__search span { width: 52%; height: 6px; border-radius: 3px; background: #d5d5dd; }
.side__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.side__item {
  display: flex; align-items: center; gap: .55rem; padding: .48rem .5rem;
  border-radius: 10px; cursor: default;
  transition: background-color var(--press);
}
.side__item.is-on { background: var(--ground-2); }
.side__item .av { width: 28px; height: 28px; flex: none; }
.side__meta { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.side__name { font-size: .8rem; font-weight: 600; letter-spacing: -0.01em; }
.side__sub { font-size: .68rem; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.side__unread {
  margin-left: auto; min-width: 17px; height: 17px; padding-inline: 5px; border-radius: 9px;
  background: var(--accent); color: #fff; font-size: .62rem; font-weight: 700;
  display: grid; place-items: center;
}
.side__foot { margin-top: auto; display: flex; flex-direction: column; gap: .1rem; padding-top: .5rem; border-top: 1px solid var(--line); }
.side__row { display: flex; align-items: center; gap: .5rem; padding: .4rem .5rem; font-size: .76rem; color: var(--text-3); }
.ic { width: 13px; height: 13px; border-radius: 3px; border: 1.4px solid currentColor; opacity: .8; flex: none; }
.ic--plug { border-radius: 50%; }

/* Collapsed rail. Must come after every rule it overrides. Same specificity,
   so source order decides. */
@media (max-width: 560px) {
  .side__meta, .side__search span, .side__foot, .side__unread { display: none; }
  .side__item { justify-content: center; padding-inline: 0; }
  /* 82% of a 62px-rail column is about three words per line */
  .msg { max-width: 92%; }
  .chat__scroll { padding: .75rem .6rem; }
}

/* min-height:0 as well as min-width: a grid item's automatic minimum is its
   content, which defeats the clip just as surely as the row height did. */
.win__chat { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--ground); }
.chat__head { display: flex; align-items: center; gap: .6rem; padding: .6rem .9rem; border-bottom: 1px solid var(--line); background: var(--paper); }
.chat__who { display: flex; align-items: center; gap: .5rem; font-size: .9rem; letter-spacing: -0.01em; }
.chat__avatar { width: 24px; height: 24px; display: block; }
.chat__model {
  margin-left: auto; font-size: .7rem; font-weight: 500;
  padding: .3em .7em; border-radius: 999px; border: 1px solid var(--line-2); color: var(--text-2);
  background: var(--paper);
  transition: color var(--press), border-color var(--press);
}
.chat__model.is-flash { color: var(--accent-text); border-color: var(--accent); }

.chat__scroll { flex: 1; overflow: hidden; position: relative; padding: .9rem; }
.chat__stream { display: flex; flex-direction: column; gap: .6rem; will-change: transform; }

.msg { max-width: 82%; padding: .62rem .85rem; border-radius: 16px; font-size: .86rem; line-height: 1.5; will-change: transform, opacity; }
/* your own messages carry the accent, the way a messaging app does */
.msg--me { align-self: flex-end; background: var(--accent); color: #fff; border-bottom-right-radius: 6px; }
.msg--bot { align-self: flex-start; background: var(--paper); border: 1px solid var(--line); border-bottom-left-radius: 6px; box-shadow: var(--shadow-1); }
.msg--bot code { background: var(--inset); padding: .1em .35em; border-radius: 4px; color: var(--accent-text); }
.msg--me code { background: rgba(255,255,255,.22); padding: .1em .35em; border-radius: 4px; }

.typing { display: inline-flex; gap: 4px; align-items: center; height: 10px; }
.typing i { width: 5px; height: 5px; border-radius: 50%; background: var(--text-3); animation: blip 1.2s ease-in-out infinite; }
.typing i:nth-child(2) { animation-delay: .16s; }
.typing i:nth-child(3) { animation-delay: .32s; }
@keyframes blip { 0%,60%,100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

/* the in-window approval card materialises: blur and scale together (Apple §12) */
.askcard {
  align-self: stretch; max-width: 100%;
  border-radius: 14px; padding: .7rem .8rem;
  background: rgba(255, 255, 255, .86);
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-2);
  will-change: transform, opacity, filter;
}
.askcard__t { font-size: .78rem; color: var(--text-2); margin: 0 0 .45rem; }
.askcard__t b { color: var(--ink); }
/* wrap rather than scroll: a hidden horizontal scroller cut the command in
   half on a phone, and the whole point of the card is reading the command */
.askcard pre { margin: 0 0 .6rem; padding: .45rem .6rem; border-radius: 8px; background: var(--inset); border: 1px solid var(--line); white-space: pre-wrap; overflow-wrap: anywhere; }
.askcard pre code { font-size: .76rem; color: var(--accent-text); }
.askcard__row { display: flex; gap: .45rem; }
/* the page says "Tap Allow", so Allow has to be tappable */
.askcard__row .btn { flex: 1; min-height: 44px; padding: .5em 1em; font-size: .8rem; }

.chat__composer {
  display: flex; align-items: center; gap: 2px; flex-wrap: nowrap;
  margin: .3rem .9rem .9rem; padding: .65rem .9rem;
  border-radius: 999px; background: var(--paper);
  border: 1px solid var(--line-2); font-size: .84rem; color: var(--text-3);
}
.chat__composer > span:first-child { min-width: 0; white-space: nowrap; overflow: hidden; }
.caret { flex: none; width: 1.5px; height: 14px; background: var(--accent); animation: caret 1.05s steps(1) infinite; }
@keyframes caret { 50% { opacity: 0; } }

.win__hint { margin: .9rem 0 0; text-align: center; font-size: .78rem; color: var(--text-3); }
.win__hint b { color: var(--accent-text); }

/* ── strip / pillars ───────────────────────────────────────────────────── */
.strip { padding: clamp(2rem, 5vw, 4rem) 0; }
/* Three cards, so three columns: auto-fit resolved to two between 585 and 885,
   and because the grid paints --line as its own background to draw the
   hairlines, the empty third cell was a solid grey card-shaped hole.
   padding-inline:0 because this element is also the .wrap, and .wrap's gutter
   is padding, which the grey background was painting. */
.strip__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px;
  background: var(--line); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden;
  box-shadow: var(--shadow-1);
  padding-inline: 0;
  width: calc(100% - var(--gut) * 2);
  max-width: calc(var(--maxw) - var(--gut) * 2);
}
@media (max-width: 900px) { .strip__grid { grid-template-columns: 1fr; } }
.pill { background: var(--paper); padding: clamp(1.3rem, 2.4vw, 2rem); transition: background-color 240ms ease; }
.pill:hover { background: var(--ground-2); }
.pill h3 { margin: 0 0 .55rem; font-size: 1.06rem; font-weight: 700; letter-spacing: -0.02em; }
.pill p { margin: 0; color: var(--text-2); font-size: .93rem; line-height: 1.62; }
.pill code { color: var(--accent-text); background: var(--inset); padding: .08em .3em; border-radius: 4px; }

/* ── sections ──────────────────────────────────────────────────────────── */
.sec { padding: clamp(4.5rem, 10vw, 8.5rem) 0; position: relative; }
/* Bands separate by ground tone alone. A border here paints a hairline at both
   edges of every alt section, which is four full-width rules down the page. */
.sec--alt { background: var(--paper); }
.sec--tight { padding-block: clamp(3.5rem, 7vw, 6rem); }
.sec__head { max-width: 62ch; }
.sec__head--center { margin-inline: auto; text-align: center; }
.sec__head--center .eyebrow { justify-content: center; }

/* A centred column has to centre its children's BOXES, not just the lines of
   text inside them. .sec__lede caps itself at 56ch with margin-left 0, so on a
   wide screen the text centred perfectly inside a box sitting hard against the
   left edge, which is how the download heading and its subheading ended up
   184px out of line with each other. margin-inline:auto is a no-op on anything
   already full width, so this is safe to apply to the whole column. */
.sec__head--center > *,
.download__inner > * { margin-inline: auto; }

.split { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: clamp(2rem, 5vw, 5rem); align-items: center; }
.split--rev .split__copy { order: 2; }
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }
  .split--rev .split__copy { order: 0; }
}

.ticks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .55rem; }
.ticks li { position: relative; padding-left: 1.6rem; color: var(--text-2); font-size: .95rem; }
.ticks li::before {
  content: ""; position: absolute; left: 0; top: .5em;
  width: 9px; height: 9px; border-radius: 50%; background: var(--accent);
}

/* ── playpen (draggable mascots) ───────────────────────────────────────── */
.playpen { position: relative; margin: 2.5rem 0 3rem; }
.playpen__label {
  position: absolute; top: 1rem; left: 1.2rem; z-index: 2; margin: 0;
  font-size: .7rem; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-3); pointer-events: none;
}
.playpen__stage {
  position: relative; height: clamp(190px, 26vw, 260px);
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background:
    radial-gradient(120% 140% at 80% 0%, rgba(255,122,61,.10), transparent 62%),
    radial-gradient(100% 120% at 10% 100%, rgba(91,61,245,.07), transparent 62%),
    var(--paper);
  box-shadow: var(--shadow-1);
  overflow: hidden;
  touch-action: none;
}
.blob {
  position: absolute; top: 0; left: 0;
  width: var(--blob); height: var(--blob);
  cursor: grab;
  will-change: transform;
  touch-action: none;
  -webkit-user-select: none; user-select: none;
  filter: drop-shadow(0 10px 16px rgba(20, 20, 34, .18));
}
.blob.is-held { cursor: grabbing; }
.blob svg { pointer-events: none; }

/* On touch, a full-width `touch-action: none` toy is a scroll trap. Give the
   page vertical swipes back and keep the horizontal ones for the bots. */
/* any-pointer, not pointer: a touch laptop's primary pointer is the trackpad,
   so `pointer: coarse` left it trapped in the toy. */
@media (any-pointer: coarse) {
  .playpen__stage, .blob { touch-action: pan-y; }
}
@media (max-width: 430px) { :root { --blob: 56px; } }

/* ── cards ─────────────────────────────────────────────────────────────── */
.cards {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .7rem;
}
@media (max-width: 900px) { .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 560px) { .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.card {
  display: flex; flex-direction: column; align-items: flex-start; gap: .1rem;
  padding: 1.1rem 1rem 1.2rem;
  border-radius: var(--r-lg);
  background: var(--paper);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  text-align: left;
  transition: transform var(--press), box-shadow 240ms ease, border-color 240ms ease;
  will-change: transform;
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); border-color: var(--line-2); }
.card[data-press].is-pressed { transform: translateY(-1px) scale(.98); }
/* the chosen one, said twice: a ring for colour vision and a lift for everyone */
.card.is-on {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), var(--shadow-2);
  transform: translateY(-3px);
}
.card__face { display: block; width: 54px; height: 54px; margin-bottom: .8rem; pointer-events: none; }
.card__name { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.022em; }
.card__role {
  font-size: .66rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-3);
}
.card.is-on .card__role { color: var(--accent-text); }

/* One answer for whichever card is chosen, rather than five paragraphs at once */
.jobcard {
  margin-top: 1rem; padding: 1.3rem 1.4rem;
  border-radius: var(--r-lg);
  background: var(--paper); border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  min-height: 108px;
}
.jobcard__job { margin: 0; color: var(--text-2); font-size: 1rem; line-height: 1.55; max-width: 62ch; }
.jobcard__say {
  margin: .7rem 0 0; font-size: .95rem; line-height: 1.5;
  color: var(--accent-text); font-style: italic; max-width: 62ch;
}

/* ── model picker ──────────────────────────────────────────────────────── */
.picker {
  position: relative;
  display: flex; flex-wrap: wrap; gap: 4px;
  padding: 5px;
  border-radius: var(--r-xl); background: var(--ground-2); border: 1px solid var(--line);
}
.picker__thumb {
  position: absolute; top: 0; left: 0;
  border-radius: 999px; background: var(--paper);
  box-shadow: 0 1px 2px rgba(20,20,34,.10), 0 4px 10px -4px rgba(20,20,34,.20);
  will-change: transform, width, height;
  z-index: 0;
}
.picker__opt {
  position: relative; z-index: 1;
  /* A third each, so six models read as two deliberate rows of three rather
     than five-and-a-stray. Halves below 560, which gives three even rows. */
  flex: 1 1 calc(33.333% - 4px);
  background: none; border: 0; cursor: pointer;
  padding: .62em 1em; border-radius: 999px;
  font-size: .85rem; font-weight: 600; letter-spacing: -0.012em;
  color: var(--text-2);
  transition: color 200ms ease, transform var(--press);
  white-space: nowrap;
}
.picker__opt.is-on { color: var(--ink); }
.picker__opt:active { transform: scale(.95); }
/* Coarse pointers get a 44px target whatever the label length. */
@media (pointer: coarse) { .picker__opt { min-height: 44px; } }
@media (max-width: 560px) { .picker__opt { flex-basis: calc(50% - 4px); font-size: .8rem; } }

.picker__meta { display: flex; align-items: baseline; gap: .7rem; margin-top: 1.1rem; min-height: 1.6em; }
.picker__vendor { font-weight: 700; letter-spacing: -0.02em; }
.picker__note { font-size: .78rem; color: var(--text-3); }
.picker__hint { margin: .5rem 0 0; font-size: .8rem; color: var(--text-3); }

/* ── hands: pick where the computer is ─────────────────────────────────── */
.placement { max-width: 780px; margin-inline: auto; margin-top: 2.4rem; display: grid; gap: 1rem; }
.picker--wide { width: 100%; }
.placement__note {
  margin: 0; text-align: center; color: var(--text-2);
  font-size: .95rem; line-height: 1.55; min-height: 3em;
  max-width: 58ch; margin-inline: auto;
}

/* The bot's machine stays dark whichever it is: it is a computer, not a Pragma
   surface, and a white rectangle inside a white card would vanish. The tint
   moves with the placement so the switch has something visible to change. */
.screenframe {
  border-radius: var(--r); overflow: hidden; border: 1px solid var(--line-2);
  background: #17171C; display: flex; flex-direction: column;
  box-shadow: var(--shadow-2);
  transition: box-shadow 300ms ease;
}
.screenframe__bar { display: flex; align-items: center; gap: .35rem; padding: .5rem .7rem; background: #1F1F26; border-bottom: 1px solid #2C2C35; }
.screenframe__bar i { width: 7px; height: 7px; border-radius: 50%; background: #45454F; }
.screenframe__bar span { margin-left: .5rem; font-size: .68rem; letter-spacing: .04em; color: #8A8A96; }
.screenframe__view {
  position: relative; flex: 1; min-height: 230px; padding: 1rem; overflow: hidden;
  background: radial-gradient(90% 90% at 30% 10%, var(--sf-tint, rgba(91,61,245,.20)), transparent 70%), #17171C;
  transition: background 420ms ease;
}
.screenframe[data-place="cloud"] { --sf-tint: rgba(91,61,245,.22); }
.screenframe[data-place="vm"]    { --sf-tint: rgba(31,157,87,.20); }
.screenframe[data-place="host"]  { --sf-tint: rgba(255,122,61,.22); }
.sf-win {
  position: absolute; border-radius: 9px; overflow: hidden;
  border: 1px solid rgba(255,255,255,.14); background: rgba(255,255,255,.05);
  box-shadow: 0 16px 34px -20px rgba(0,0,0,.9);
  padding: 22px 12px 0;
  transition: left 520ms cubic-bezier(.2,.8,.2,1), top 520ms cubic-bezier(.2,.8,.2,1),
              width 520ms cubic-bezier(.2,.8,.2,1), height 520ms cubic-bezier(.2,.8,.2,1);
}
.sf-win i { position: absolute; inset: 0 0 auto 0; height: 15px; background: rgba(255,255,255,.08); border-bottom: 1px solid rgba(255,255,255,.10); }
.sf-win b { display: block; height: 6px; border-radius: 3px; background: rgba(255,255,255,.16); margin-bottom: 7px; }
.sf-win b:nth-child(2) { width: 68%; }
.sf-win b:nth-child(3) { width: 44%; }
.sf-win b:nth-child(4) { width: 56%; }
.sf-win--a { left: 7%; top: 13%; width: 45%; height: 54%; }
.sf-win--b { left: 41%; top: 33%; width: 51%; height: 52%; background: rgba(255,83,46,.10); border-color: rgba(255,83,46,.3); }
.sf-win--b i { background: rgba(255,83,46,.18); border-bottom-color: rgba(255,83,46,.24); }
.sf-win--b b { background: rgba(255,160,80,.42); }
/* each placement arranges its windows differently, so the switch reads as a
   different machine rather than a recoloured one */
.screenframe[data-place="vm"] .sf-win--a   { left: 10%; top: 18%; width: 38%; height: 46%; }
.screenframe[data-place="vm"] .sf-win--b   { left: 46%; top: 26%; width: 44%; height: 58%; }
.screenframe[data-place="host"] .sf-win--a { left: 5%;  top: 9%;  width: 56%; height: 66%; }
.screenframe[data-place="host"] .sf-win--b { left: 52%; top: 40%; width: 43%; height: 46%; }
.cursor {
  position: absolute; left: 0; top: 0; width: 13px; height: 13px; z-index: 3;
  border-radius: 50% 50% 50% 2px; transform: rotate(-8deg);
  background: #fff; box-shadow: 0 2px 6px rgba(0,0,0,.6);
  will-change: transform;
}

/* ── approval card ─────────────────────────────────────────────────────── */
.approval {
  border-radius: var(--r-lg); padding: 1.3rem; max-width: 520px;
  background: rgba(255, 255, 255, .82);
  backdrop-filter: blur(20px) saturate(170%);
  -webkit-backdrop-filter: blur(20px) saturate(170%);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-2);
  will-change: transform;
}
.approval__head { display: flex; gap: .8rem; align-items: flex-start; margin-bottom: 1rem; }
.approval__face { width: 38px; height: 38px; flex: none; }
.approval__head b { font-weight: 700; }
.approval__head div { font-size: .95rem; line-height: 1.45; }
.approval__head span { display: block; font-size: .78rem; color: var(--text-3); }
.approval__cmd { margin: 0 0 1rem; padding: .8rem .95rem; border-radius: var(--r-sm); background: var(--inset); border: 1px solid var(--line); overflow-x: auto; }
.approval__cmd code { color: var(--accent-text); font-size: .88rem; }
.approval__row { display: flex; gap: .6rem; }
.approval__row .btn { flex: 1; }
.approval__result { margin: 1rem 0 0; min-height: 1.4em; font-size: .86rem; color: var(--text-2); }
.approval__result b { color: var(--accent-text); }
.approval.is-answered { border-color: var(--accent); }

/* ── connect an app ─────────────────────────────────────────────────────── */
.connect { display: grid; gap: 1rem; justify-items: center; margin-top: 2.4rem; }
.connect__grid { display: flex; flex-wrap: wrap; gap: .55rem; justify-content: center; max-width: 720px; }
.chip--btn {
  cursor: pointer; font: inherit; font-size: .88rem; font-weight: 500;
  transition: transform var(--press), background-color var(--press),
              border-color var(--press), color var(--press);
}
.chip--btn i { transition: background var(--press), transform var(--press); }
.chip--btn:hover { border-color: var(--line-2); }
.chip--btn.is-on {
  border-color: var(--accent); color: var(--ink);
  background: color-mix(in oklab, var(--accent) 8%, var(--paper));
}
/* the dot becomes a tick when connected: one glyph, no layout shift */
.chip--btn.is-on i {
  background: var(--accent);
  clip-path: polygon(14% 52%, 38% 76%, 88% 20%, 100% 34%, 38% 100%, 2% 64%);
  transform: scale(1.25);
  border-radius: 0;
}
.chip--btn.is-pressed { transform: scale(.94); }
.connect__status {
  margin: 0; text-align: center; color: var(--text-2);
  font-size: .95rem; min-height: 1.5em; max-width: 54ch;
}

/* ── pull the cable ─────────────────────────────────────────────────────── */
.cable {
  display: inline-flex; align-items: center; gap: .7rem;
  margin-top: 1rem; padding: .55rem .95rem .55rem .6rem;
  border-radius: 999px; border: 1px solid var(--line-2);
  background: var(--paper); box-shadow: var(--shadow-1);
  font-size: .9rem; font-weight: 600; color: var(--ink); cursor: pointer;
  transition: transform var(--press), border-color var(--press);
}
.cable[data-press].is-pressed { transform: scale(.97); }
.cable__switch {
  width: 40px; height: 24px; border-radius: 999px; flex: none;
  background: var(--accent); position: relative;
  transition: background 240ms ease;
}
.cable[aria-pressed="true"] .cable__switch { background: var(--line-2); }
.cable__knob {
  position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(20,20,34,.35);
  transition: transform 240ms cubic-bezier(.2,.8,.2,1);
}
.cable[aria-pressed="true"] .cable__knob { transform: translateX(16px); }

.offline { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .45rem; }
.offline li {
  display: flex; align-items: baseline; gap: .6rem;
  font-size: .9rem; color: var(--text-2);
}
.offline li::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: #1F9D57; transition: background 240ms ease;
}
.offline li[data-ok="false"] { color: var(--text-3); }
.offline li[data-ok="false"]::before { background: var(--line-2); }
.offline li span { margin-left: auto; font-size: .82rem; color: var(--text-3); }
.live--off { background: var(--line-2); box-shadow: none; }
#tree[data-online="false"] .tree__path { color: var(--text-3); }

/* ── marquee ───────────────────────────────────────────────────────────── */
.marquee { position: relative; margin-top: 2.6rem; overflow: hidden; padding-block: .6rem; }
.marquee__track { display: flex; gap: .7rem; width: max-content; will-change: transform; }
.chip {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .62rem 1.05rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--paper);
  box-shadow: var(--shadow-1);
  font-size: .88rem; font-weight: 500; white-space: nowrap; color: var(--text-2);
}
.chip i { width: 9px; height: 9px; border-radius: 3px; background: var(--grad); flex: none; }
.marquee__fade { position: absolute; top: 0; bottom: 0; width: 14%; pointer-events: none; }
.marquee__fade--l { left: 0;  background: linear-gradient(to right, var(--ground), transparent); }
.marquee__fade--r { right: 0; background: linear-gradient(to left,  var(--ground), transparent); }
.marquee__count {
  text-align: center; margin: 1.8rem auto 0;
  max-width: var(--maxw); padding-inline: var(--gut);
  font-size: .88rem; color: var(--text-3);
}
.marquee__count b { color: var(--ink); font-size: 1rem; }

/* ── local ─────────────────────────────────────────────────────────────── */
.local { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,.85fr); gap: clamp(2rem,5vw,4.5rem); align-items: center; }
@media (max-width: 900px) { .local { grid-template-columns: 1fr; } .local__tree { max-width: 520px; } }
.tree {
  border-radius: var(--r-lg); padding: 1.4rem;
  background: var(--paper); border: 1px solid var(--line);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  box-shadow: var(--shadow-1);
}
.tree__path { margin: 0 0 .9rem; font-size: .84rem; color: var(--accent-text); font-weight: 600; }
.tree ul { list-style: none; margin: 0 0 1.1rem; padding: 0 0 0 .3rem; display: flex; flex-direction: column; gap: .55rem; }
.tree li { display: flex; gap: .8rem; align-items: baseline; font-size: .82rem; border-left: 2px solid var(--line); padding-left: .9rem; }
.tree__k { color: var(--ink); }
.tree__v { color: var(--text-3); font-size: .74rem; margin-left: auto; text-align: right; }
@media (max-width: 360px) {
  .tree li { flex-direction: column; align-items: flex-start; gap: .15rem; }
  .tree__v { margin-left: 0; text-align: left; }
}
.tree__foot { margin: 0; padding-top: .9rem; border-top: 1px solid var(--line); font-size: .74rem; color: var(--text-3); display: flex; align-items: center; gap: .5rem; }
.live { width: 7px; height: 7px; border-radius: 50%; background: #1F9D57; box-shadow: 0 0 0 3px rgba(31,157,87,.18); }

/* ── the clone box ──────────────────────────────────────────────────────── */
@media (max-width: 560px) {
}

/* ── download ──────────────────────────────────────────────────────────── */
.download {
  text-align: center; position: relative; overflow: clip;
  background-image: radial-gradient(56% 52% at 50% 46%, rgba(255,122,61,.16), rgba(255,122,61,.04) 52%, transparent 78%);
}
.download__inner { position: relative; }
.download__mark { display: block; width: clamp(92px, 13vw, 132px); margin: 0 auto 1.6rem; }
.download__row { display: flex; flex-wrap: wrap; gap: .7rem; justify-content: center; margin-top: 2rem; }
.download__fine { margin: 1.4rem 0 0; font-size: .76rem; letter-spacing: .02em; color: var(--text-3); }
.download__note {
  margin: 1.1rem auto 0; max-width: 46ch;
  font-size: .84rem; line-height: 1.55; color: var(--text-2);
}
.download__link { color: var(--accent-text); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }

/* A platform with no published build. Reads as "not yet", not as a button that
   failed: no pointer, no press feedback, and no href to follow. */
.btn--muted {
  color: var(--text-3);
  border-color: var(--line);
  background: var(--ground-2);
  box-shadow: none;
  cursor: default;
}
.btn--muted:hover { background: var(--ground-2); border-color: var(--line); }
.btn--muted.is-pressed { transform: none; }

/* ── footer ────────────────────────────────────────────────────────────── */
.foot { padding: 2.4rem 0; background: var(--paper); }
.foot__inner { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.6rem; }
.foot__by { margin: 0 auto 0 0; font-size: .9rem; color: var(--text-2); }
.foot__by b { color: var(--ink); }
.foot__legal { margin: 0; font-size: .76rem; color: var(--text-3); }
.foot__gh {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .45rem .85rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--ground);
  font-size: .84rem; font-weight: 600; color: var(--text-2);
  transition: border-color var(--press), color var(--press), background-color var(--press);
}
.foot__gh:hover { border-color: var(--line-2); color: var(--ink); background: var(--ground-2); }
.download__link { color: var(--accent-text); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.foot__link { color: var(--text-2); text-decoration: underline; text-underline-offset: 2px; }
.foot__link:hover { color: var(--accent-text); }

/* ── reveal ─────────────────────────────────────────────────────────────
   Entrances are not gestural (nobody grabs a heading mid-fade), so they run
   on CSS transitions, which keeps content visible even if the rAF clock is
   starved. The `.js` gate means no-JS gets the page, not a blank screen.     */
.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity 620ms cubic-bezier(.16, 1, .3, 1), transform 620ms cubic-bezier(.16, 1, .3, 1);
  will-change: opacity, transform;
}
.js [data-reveal].is-in { opacity: 1; transform: none; will-change: auto; }

/* in-window chat entrances, same reasoning */
.msg, .askcard { transition: opacity 420ms cubic-bezier(.16,1,.3,1), transform 420ms cubic-bezier(.16,1,.3,1), filter 420ms cubic-bezier(.16,1,.3,1); }
.msg.is-pre   { opacity: 0; transform: translateY(12px) scale(.96); }
.askcard.is-pre { opacity: 0; transform: translateY(20px) scale(.9); filter: blur(9px); }
.askcard.is-out {
  opacity: 0; transform: scale(.94); filter: blur(6px);
  height: 0 !important; padding-block: 0 !important; margin-top: -.6rem;
  overflow: hidden;
  transition: opacity 260ms ease, transform 260ms ease, filter 260ms ease,
              height 320ms cubic-bezier(.3,0,.2,1), padding 320ms cubic-bezier(.3,0,.2,1), margin 320ms cubic-bezier(.3,0,.2,1);
}

/* ── accessibility preferences (Apple §14) ─────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; }
  .btn, .pill, .card { transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease; }
  [data-press].is-pressed { transform: none; opacity: .82; }
  .btn--primary[data-press].is-pressed { transform: none; }
  .js [data-reveal] { opacity: 1 !important; transform: none !important; transition: none; }
  .msg.is-pre, .askcard.is-pre { opacity: 1; transform: none; filter: none; }
  .glow { display: none; }
  .marquee { padding-inline: var(--gut); }
  .marquee__track { transform: none !important; flex-wrap: wrap; width: 100%; justify-content: center; }
  /* the track carries the list twice so it can scroll seamlessly; with no
     scroll the second copy is just noise, and the edge fades fade nothing */
  .marquee__track > .chip:nth-child(n+17) { display: none; }
  .marquee__fade { display: none; }
  .caret, .eyebrow .dot { animation: none; }
  .win { transform: none !important; }
}
@media (prefers-reduced-transparency: reduce) {
  .nav.is-stuck, .askcard, .approval { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--paper); }
}
@media (prefers-contrast: more) {
  :root { --text-2: #33333B; --text-3: #44444C; --line: #B8B8C4; --line-2: #8E8E9C; }
  .glow { display: none; }
}

/* ============================================================================
   Scroll narrative (see scroll.js)

   Everything in here is inert without JavaScript: a split heading that never
   gets split is an ordinary heading, and a mask with nothing translated inside
   it shows the text. The page reads complete with the scripts blocked.
   ========================================================================== */

/* GSAP writes opacity and transform inline, so the CSS transition that drives
   the fallback reveal would fight it for the same properties.

   will-change is cleared here too. The fallback drops it in .is-in once the
   transition is done; the GSAP path never adds that class, so every revealed
   element on the page would keep a promoted compositor layer for the lifetime
   of the visit. scroll.js sets it per tween instead, and takes it off again. */
[data-scroll="gsap"] [data-reveal] { transition: none; will-change: auto; }

/* A line of a split heading. The mask is what makes the words rise out of
   nothing rather than slide over the line above; overflow has to be clipped on
   a block, and the inner span is what actually moves. */
.line { display: block; overflow: hidden; padding-bottom: 0.06em; margin-bottom: -0.06em; }
/* No will-change here on purpose: a heading animates once and then never
   again, and promoting all of them for the life of the page is a compositor
   layer each for nothing. scroll.js promotes a line for its tween and drops it
   afterwards. */
.line__in { display: block; }
.w { display: inline-block; }

/* The section numeral. Big, pale, and behind everything: a page marker, not a
   heading, so it must never win the eye or be read aloud. */
.numeral {
  position: absolute;
  top: clamp(-1rem, 1vw, 1rem);
  right: var(--gut);
  font-family: "Inter Tight", "Inter", system-ui, sans-serif;
  font-size: clamp(7rem, 20vw, 20rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.06em;
  color: var(--ground-2);
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
.sec { isolation: isolate; }
.sec > .wrap { position: relative; z-index: 1; }

/* Marquee. The band is clipped, the track is twice its content so the seam is
   always off-screen, and scroll.js moves it. */
.band {
  overflow: clip;
  padding-block: clamp(1.4rem, 3vw, 2.4rem);
  border-block: 1px solid var(--line);
  background: var(--paper);
}
.band__track { display: flex; width: max-content; will-change: transform; }
.band__row { display: flex; align-items: center; gap: clamp(1.6rem, 3vw, 3rem); padding-right: clamp(1.6rem, 3vw, 3rem); }
.band__row span {
  font-family: "Inter Tight", "Inter", system-ui, sans-serif;
  font-size: clamp(1.1rem, 2vw, 1.9rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text-3);
  white-space: nowrap;
}
.band__row span:nth-child(even) { color: var(--accent-text); }
.band__row i { width: 7px; height: 7px; border-radius: 50%; background: var(--line-2); flex: none; }

@media (prefers-reduced-motion: reduce) {
  /* Nothing here animates, so the only job left is making sure the static page
     is the complete one. */
  .line__in { transform: none !important; }
  .band__track { transform: none !important; }
}

/* ============================================================================
   The claim: a pinned headline that leaves to the left, and a sentence that
   arrives on the same scroll.

   The strip is ONE row: [panel exactly one viewport wide][the sentence]. That
   is the whole idea. Built as a column with a band underneath it would share
   every part and none of the effect, because the headline has to leave for the
   sentence to be arriving.

   Everything here is inert without JavaScript: [data-stage="static"] is set by
   scroll.js when it will not run the pin, and it is also what you get if the
   script never loads, because the attribute's absence leaves the same styles
   the media query below applies.
   ========================================================================== */
/* Default is the BLOCK layout, and the pin is opt-in via [data-stage="pinned"],
   which scroll.js sets only when it is actually going to run. The other way
   round, a page with no JavaScript, or a reader with reduced motion, gets a
   100vh section holding a strip several viewports wide and no way to move it:
   a sentence running off the side of the screen with nothing to scroll. */
.stage { position: relative; overflow: clip; padding: 0; }
.stage__strip {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 40px; padding: clamp(4rem, 9vw, 7rem) 0; width: auto;
}
.stage[data-stage="pinned"] { height: 100vh; }
.stage[data-stage="pinned"] .stage__strip {
  flex-direction: row; flex-wrap: nowrap; align-items: center;
  height: 100%; width: max-content; gap: 0; padding: 0;
}

/* --stage-vw comes from documentElement.clientWidth, not 100vw: vw counts the
   scrollbar gutter, which would make panel one wider than the screen and leave
   a sliver of the sentence showing before the drag starts. */
.stage__panel { position: relative; flex: 0 0 auto; width: 100%; display: flex; align-items: center; }
.stage[data-stage="pinned"] .stage__panel { flex: 0 0 var(--stage-vw, 100vw); height: 100%; width: auto; }
.stage__head { position: relative; z-index: 2; width: 100%; }
.stage__lines { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; margin: 18px 0 0; }

/* overflow:clip is what lets each line rise out from under its own block. The
   block is a SIBLING of the text, not a background on it: a background cannot
   be scaled independently of the glyphs sitting on it. */
.stage__line { position: relative; display: inline-block; overflow: clip; padding: 8px 20px 14px; }
.stage__mark {
  position: absolute; inset: 0; border-radius: 8px;
  background: var(--ink); transform: scaleX(0); transform-origin: left center;
}
.stage__line--alt .stage__mark { background: var(--flare); transform-origin: right center; }
.stage__text {
  position: relative; display: block;
  font-family: "Inter Tight", "Inter", system-ui, sans-serif;
  font-weight: 900; letter-spacing: -0.045em; line-height: 1;
  font-size: clamp(2rem, 5.6vw, 4.8rem);
  color: var(--paper);
}
/* Ink on flare, never paper: white on #FF532E measures 3.1:1 and fails AA.
   The near-black clears it comfortably. */
.stage__line--alt .stage__text { color: var(--ink); }
.stage__sub {
  margin-top: 26px; max-width: 54ch;
  font-size: clamp(1.02rem, 1.2vw + .8rem, 1.28rem); line-height: 1.6; color: var(--text-2);
}

/* The sentence. nowrap is load-bearing: the pin distance IS scrollWidth minus
   the viewport, so the sentence's own length decides how far it travels. */
.stage__track {
  display: flex; align-items: center; flex-wrap: wrap; row-gap: 0.14em;
  gap: 0.24em; flex: 0 0 auto; padding: 0 var(--gut);
  font-family: "Inter Tight", "Inter", system-ui, sans-serif;
  font-weight: 900; letter-spacing: -0.04em; line-height: 1.08;
  font-size: clamp(1.7rem, 7vw, 3rem);
}
/* nowrap is load-bearing once pinned: the drag distance IS scrollWidth minus
   the viewport, so the sentence's own length decides how far it travels. */
.stage[data-stage="pinned"] .stage__track {
  flex-wrap: nowrap; white-space: nowrap; padding: 0 14vw 0 0;
  font-size: clamp(2.4rem, 7vw, 6rem);
}
.stage__word { color: var(--ink); }
.stage__chip {
  padding: 0.06em 0.2em 0.12em; border-radius: 0.1em;
  /* A degree off level. Square blocks in a row read as a table; a slight
     rotation reads as marker on paper. */
  transform: rotate(-1.1deg);
}
.stage__chip--ink { background: var(--ink); color: var(--paper); }
.stage__chip--flare { background: var(--flare); color: var(--ink); transform: rotate(1deg); }
.stage__chip--ember { background: var(--ember); color: var(--ink); }

.stage__dot { display: inline-block; flex-shrink: 0; }
.stage__dot--disc {
  width: 0.38em; height: 0.38em; border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #FFB27A, var(--ember) 62%, var(--accent));
}
.stage__dot--ring {
  width: 0.38em; height: 0.38em; border-radius: 50%; box-sizing: border-box;
  border: 0.1em solid var(--flare);
}

/* Marks in the run of the sentence. Offset with `top`, NOT a transform: GSAP
   animates rotation and scale on these, and a CSS transform would be
   overwritten by the first tween, dropping every mark onto the baseline. */
/* A mark's lift is bounded to half the line box. Past that it stops reading as
   part of the sentence and starts reading as a stray graphic sitting under it,
   which is exactly what a chevron at 1.4em did: it cleared the text entirely,
   and mid-spin it pointed downward, so it read as a scroll-down cue. */
.stage__inline {
  position: relative; top: clamp(-0.6em, var(--lift, 0), 0.6em); z-index: 2;
  display: inline-block; flex-shrink: 0; width: 1.05em; margin: 0 0.08em;
}
.stage__inline svg { width: 100%; height: auto; display: block; overflow: visible; }
/* The turning ones are the only marks still moving after they land, so they
   can carry the extra size. */
.stage__inline--spin { width: 1.35em; }
/* Overlapping marks. Negative margins pull the neighbouring words in under the
   mark so it sits ON the sentence rather than punching a gap in it, while the
   element keeps a real box: a zero-width wrapper puts transform-origin on its
   left edge and makes the spin orbit the text instead of turning on centre. */
.stage__inline--over {
  width: 1.9em; z-index: 4;
  margin-left: calc(1.9em * -0.52); margin-right: calc(1.9em * -0.52);
}
.stage__inline--over.stage__inline--spin { width: 2em; margin-left: -1.04em; margin-right: -1.04em; }

.stage__decos { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.stage__deco { position: absolute; display: block; }
.stage__deco svg { width: 100%; height: 100%; display: block; overflow: visible; }
/* Parked in the right-hand third, clear of the measure: a decorative mark
   behind a headline is worse than no mark. */
.stage__deco--ring  { right: 9%;  top: 16%; width: clamp(38px, 4vw, 60px); aspect-ratio: 1; }
.stage__deco--curve { right: 5%;  top: 42%; width: clamp(88px, 9.6vw, 164px); aspect-ratio: 9/7; }
.stage__deco--plus  { right: 20%; top: 72%; width: clamp(32px, 3.4vw, 52px); aspect-ratio: 1; }

/* Without the pin there is nothing to reveal the headline blocks, so they are
   already drawn, and the floating marks have no drift to ride. */
.stage:not([data-stage="pinned"]) .stage__mark { transform: scaleX(1); }
.stage:not([data-stage="pinned"]) .stage__decos { display: none; }
.stage:not([data-stage="pinned"]) .stage__inline--over { margin-left: 0; margin-right: 0; }

@media (max-width: 899px) {
  .stage__decos { display: none; }
  .stage__line { padding: 6px 14px 10px; }
}

/* ── the film ─────────────────────────────────────────────────────────────
   A poster that becomes a video. The frame holds a 16:9 box whatever the
   width, so nothing on the page moves when the video replaces the still:
   an element that resizes on load pushes everything below it, and the
   thing below this is the roster somebody is about to read. */
.film__frame {
  position: relative;
  margin: clamp(2rem, 4vw, 3rem) auto 0;
  max-width: 1040px;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-xl);
  overflow: clip;
  border: 1px solid var(--line);
  background: var(--paper);
  box-shadow: var(--shadow-2);
}
.film__video { display: block; width: 100%; height: 100%; object-fit: cover; background: var(--paper); }

/* The overlay covers the whole frame, so the target is the poster rather
   than a small button on top of it: on a phone the difference between those
   two is whether it can be hit at all. */
.film__play {
  position: absolute; inset: 0;
  /* Both children in one row at the bottom of the frame, where the scrim is. */
  display: flex; align-items: flex-end; justify-content: flex-start;
  gap: .75rem; padding: clamp(1rem, 3vw, 1.75rem);
  border: 0; cursor: pointer; color: #fff; text-align: left;
  /* Dark only where the label sits: a scrim over the whole poster would
     hide the frame it was chosen for. */
  background: linear-gradient(to top, rgba(12, 12, 16, .55) 0%, rgba(12, 12, 16, .18) 34%, rgba(12, 12, 16, 0) 62%);
  transition: opacity .18s ease, visibility .18s ease;
}
.film__play:hover .film__play-icon { transform: scale(1.06); }
.film__play:focus-visible { outline: 3px solid var(--accent-text); outline-offset: -3px; }
.film__play-icon {
  flex: none; width: clamp(46px, 5vw, 58px); aspect-ratio: 1; border-radius: 50%;
  background: var(--accent); display: grid; place-items: center;
  box-shadow: 0 10px 30px -10px rgba(12, 12, 16, .7);
  transition: transform .18s ease;
}
/* A triangle from a border, so the button carries no image and no icon font. */
.film__play-icon::after {
  content: ""; width: 0; height: 0; margin-left: .22em;
  border-left: .72em solid #fff;
  border-block: .44em solid transparent;
}
/* No `align-self` here. The flex line is the whole frame, so centring the
   label centred it against the poster rather than against the icon: it
   landed in the middle of the artwork, white on a light background, across
   the words already printed there. Both children sit at flex-end, which is
   where the scrim is dark. */
.film__play-text { font-weight: 640; font-size: clamp(1rem, 1.6vw, 1.15rem); letter-spacing: -.01em; }
.film__play-sub { display: block; font-weight: 400; font-size: .8em; opacity: .82; }
/* `visibility` as well as opacity, because a button at zero opacity is still
   a button: it keeps its place in the tab order and its line in the
   accessibility tree, so a keyboard reaches a control nobody can see, sitting
   over a video that is already playing. Visibility transitions with the fade
   rather than cutting, so the look is unchanged. */
.film__frame.is-playing .film__play { opacity: 0; visibility: hidden; pointer-events: none; }

@media (max-width: 640px) {
  .film__frame { border-radius: var(--r-lg); }
}
