/* Lumen — lumenapp.news. One sheet for all four pages.

   Colour has three roles: glyph (--*-t), boundary (--*-e, >= 3:1), decoration (raw stops).
   THE GLYPH RAMP IS BELOW WCAG AA BY DECISION — lens text measures 2.02:1 to 4.40:1.
   tool/check_contrast.py measures and prints every one of them on each run, and gates
   everything else. */

/* ---------------------------------------------------------------- fonts */

@font-face { font-family:"Manrope"; src:url("assets/manrope-400.woff2") format("woff2"); font-weight:400; font-style:normal; font-display:swap }
@font-face { font-family:"Manrope"; src:url("assets/manrope-700.woff2") format("woff2"); font-weight:700; font-style:normal; font-display:swap }
@font-face { font-family:"Manrope"; src:url("assets/manrope-800.woff2") format("woff2"); font-weight:800; font-style:normal; font-display:swap }
@font-face { font-family:"Space Mono"; src:url("assets/spacemono-400.woff2") format("woff2"); font-weight:400; font-style:normal; font-display:swap }
@font-face { font-family:"Space Mono"; src:url("assets/spacemono-700.woff2") format("woff2"); font-weight:700; font-style:normal; font-display:swap }

/* ---------------------------------------------------------------- tokens */

:root {
  --mint: #eaf1f0;
  --card: #ffffff;
  --ink:  #10211f;
  --shell:     #33413f;     /* phone bezel */
  --shell-rim: #0d1211;     /* the rim between bezel and screen — must stay DARKER than
                               --shell, or the frame loses the step a real device has */
  --shell-btn: #3d4b49;     /* its side buttons — must stay LIGHTER than --shell: they sit
                               outside the frame, so on the ink band a darker mark vanishes */
  --ink2: #5c6b69;         /* secondary text — 4.87:1 on mint, 5.58:1 on white */
  --ink3: #9ba9a7;          /* RULES AND DIVIDERS ONLY, never a glyph — 2.13:1 on mint */
  --line: rgba(16, 33, 31, .09);
  --line2: rgba(16, 33, 31, .2);
  --mono: "Space Mono", ui-monospace, monospace;

  --teal:   #00bfae;
  --azure:  #2d9cff;
  --violet: #7a5bff;
  --pink:   #ff2d8e;
  --cyan:    #00a6cc;       /* --cyan and --magenta live only inside --grad-rule */
  --magenta: #ff6fd8;

  /* Every lens colour reaches the ramp through these four names: revertible in four lines. */
  --teal-t:   #00bfae;
  --azure-t:  #2d9cff;
  --violet-t: #7a5bff;
  --pink-t:   #ff2d8e;

  /* Boundary ramp — PINNED near 3.1, not "one step darker": raw violet already clears 3.0,
   so --violet-e is lighter than --violet-t. Do not collapse them. */
  --teal-e:   #00988b;
  --azure-e:  #278be5;
  --violet-e: #8a72ff;
  --pink-e:   #fc2c8d;

  /* The only red on the site. It exists for the opening hint's border and nothing else -
     it is NOT an error colour, and nothing else may borrow it. */
  --nudge-ring: #e5484d;

  --accent:   var(--teal-t);
  --edge:     var(--teal-e);
  --focus:    #10211f;

  --grad: linear-gradient(96deg, var(--teal), var(--azure) 34%, var(--violet) 62%, var(--pink));
  /* --grad's stops exactly, on the opposite axis (96 + 180). Change one, change both. */
  --grad-rev: linear-gradient(276deg, var(--teal), var(--azure) 34%, var(--violet) 62%, var(--pink));
  --grad-anim: linear-gradient(96deg, var(--teal), var(--azure) 28%, var(--violet) 54%, var(--pink) 78%, var(--teal));
  --grad-rule: linear-gradient(90deg, var(--teal), var(--cyan), var(--magenta), var(--pink));
  --grad-cta: linear-gradient(140deg, #00bfae 0%, #2bd4ff 46%, #ff6fd8 100%);

  --wrap: 1200px;
  --pad: 20px;
  --header-h: 54px;
}

/* Lens identities. --cd is the same identity on the ink band, where the raw stops MOSTLY
   pass — violet is the one that does not, hence .l-framing's lightened hex. */
.l-records  { --c: var(--pink-t);   --ce: var(--pink-e);   --craw: var(--pink);   --cd: var(--pink) }
.l-timeline { --c: var(--azure-t);  --ce: var(--azure-e);  --craw: var(--azure);  --cd: var(--azure) }
.l-volume   { --c: var(--teal-t);   --ce: var(--teal-e);   --craw: var(--teal);   --cd: var(--teal) }
/* The one literal: raw violet is too dark for ink, so this is a lightened stop rather than
   the token. check_contrast.py reads this hex out of the sheet — keep it a literal. */
.l-framing  { --c: var(--violet-t); --ce: var(--violet-e); --craw: var(--violet); --cd: #8870ff }
/* No lens picked. The four above own all four hues, so the only honest identity left for
   "none" is neutral - the hero opens achromatic and takes its colour when you choose.
   --craw and --c must both resolve: .hgrid > .mock's color-mix() and .claim b read them
   with no fallback. Never a literal hex here; check_contrast.py's tokens() takes the LAST
   --name: #hex in the sheet. */
.l-feed     { --c: var(--ink);       --ce: var(--ink2);       --craw: var(--mint);    --cd: var(--ink) }

/* ---------------------------------------------------------------- base */

* { box-sizing: border-box }
html { scroll-padding-top: calc(var(--header-h) + 16px) }
html, body { margin: 0 }

body {
  background: var(--mint);
  color: var(--ink);
  font-family: "Manrope", system-ui, sans-serif;
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

a { color: inherit }
img { max-width: 100% }

/* !important: an author `display` beats the UA rule. The two exceptions hide by visibility, keeping their grid cell. */
[hidden]:not(.claim):not(.lenscard) { display: none !important }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 3px;
}
:focus:not(:focus-visible) { outline: none }
/* Without this, WebKit paints a rule across every section a link jumps to. CI gates it. */
[tabindex="-1"]:focus { outline: none }
.band-dark :focus-visible { outline-color: #fff }
.band-dark .card :focus-visible { outline-color: var(--focus) }

.vh {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

.skip {
  position: absolute; left: 12px; top: -60px; z-index: 100;
  background: var(--ink); color: #fff;
  padding: 12px 18px; border-radius: 10px;
  font-family: var(--mono); font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  transition: top .16s ease;
}
.skip:focus { top: 12px }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--pad) }

.lab {
  margin: 0;
  font-family: var(--mono); font-size: 11px; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--c, var(--accent));
}

/* INVARIANT: every gradient phrase wears .gt or .gtx — print and forced-colors carve out
   exactly these names. background-image, never the shorthand, which resets background-clip. */
.gt, .gtx {
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
.gt { background-image: var(--grad) }
.gtx { background-image: var(--grad-anim); background-size: 220% 100%; animation: drift 9s ease-in-out infinite alternate }
@keyframes drift { to { background-position: 100% 0 } }
.grad-rule { height: 2px; background: var(--grad-rule); margin: 0 }
/* .grad-rule is a <p>, so on the legal pages `.doc p` (0,1,1) beats it (0,1,0). */
.doc .grad-rule, .notfound .grad-rule { margin: 0 }

/* ---------------------------------------------------------------- mark */

.mark { display: block; flex: none }
.mark svg { display: block; width: 100%; height: auto }

/* ---------------------------------------------------------------- topbar */

.topbar {
  position: sticky; top: 0; z-index: 50;
  background: var(--mint);
  border-bottom: 1px solid transparent;
  transition: border-color .25s, box-shadow .25s;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar {
    background: color-mix(in oklab, var(--mint) 88%, transparent);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  }
}
.is-scrolled .topbar { border-bottom-color: var(--line); box-shadow: 0 6px 20px -12px rgba(16,33,31,.25) }
/* The legal pages and the 404 load no JavaScript, so .is-scrolled is never set there. */
.doc .topbar, .notfound .topbar { box-shadow: 0 6px 20px -14px rgba(16,33,31,.18) }

.top { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 24px 0 }
.top .brand { display: flex; align-items: center; gap: 11px; font-weight: 800; font-size: 24px; letter-spacing: -.031em; text-decoration: none }
.top .brand .mark { width: 34px }
.top nav { display: flex; gap: 26px; align-items: center }
.top nav a { font-family: var(--mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink2); text-decoration: none }
.top nav a:not(.cta):not(.swap) { padding-bottom: 3px; border-bottom: 2px solid transparent }
.top nav a.swap { padding: 6px 0 }
@media (hover: hover) { .top nav a:hover { color: var(--accent) } }
.top nav a:not(.cta):not(.swap)[aria-current] { color: var(--ink); border-bottom-color: var(--edge) }

.swap { display: inline-grid; place-items: center end; overflow: hidden }
.swap span { grid-area: 1 / 1; place-self: center end; white-space: nowrap; transition: opacity .22s ease, transform .28s cubic-bezier(.2,.7,.2,1), visibility .22s }
.swap .a { transition-delay: .12s }
.swap .b { opacity: 0; visibility: hidden; transform: translateY(9px); transition-delay: 0s }
.is-scrolled .swap .a { opacity: 0; visibility: hidden; transform: translateY(-9px); transition-delay: 0s }
.is-scrolled .swap .b { opacity: 1; visibility: visible; transform: none; transition-delay: .12s }
.top nav a.cta, .top nav a.cta:hover { color: #fff }

/* On the home page the lockup lives in the hero until you scroll. */
.home .top .brand { opacity: 0; visibility: hidden; transform: translateY(-4px); transition: opacity .24s ease, transform .28s cubic-bezier(.2,.7,.2,1), visibility .24s }
.home.is-scrolled .top .brand { opacity: 1; visibility: visible; transform: none }

/* ---------------------------------------------------------------- cta */

.cta {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  background: var(--grad-cta); background-size: 220% 220%;
  color: #fff; border: none; border-radius: 999px;
  /* letter-spacing adds its space after the last glyph too; put the same amount back. */
  text-indent: .14em;
  padding: 13px 24px; min-height: 44px;
  font-family: var(--mono); font-size: 11px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer; text-decoration: none;
  animation: ctaflow 8s ease-in-out infinite alternate;
}
@media (hover: hover) { .cta:hover { filter: saturate(1.1) brightness(1.08); color: #fff } }
.cta:active { transform: translateY(1px) }
@keyframes ctaflow { 0% { background-position: 0 0 } 100% { background-position: 100% 100% } }

/* ---------------------------------------------------------------- footer */

.foot { margin-top: 0 }
.doc .foot { margin-top: 40px }
.doc .foot, .notfound .foot { border-top: 1px solid var(--line) }
.foot .row {
  max-width: var(--wrap); margin: 0 auto;
  padding: 20px var(--pad) 24px;
  display: flex; justify-content: space-between; align-items: center; gap: 26px; flex-wrap: wrap;
}
.foot .sig { display: flex; align-items: center; gap: 10px; margin: 0 }
.foot .sig .mark { width: 22px }
.foot .sig span { font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase }
.foot .sig span:not(.gt) { color: var(--ink2) }
.foot .links { display: flex; gap: 8px 18px; align-items: center; flex-wrap: wrap; justify-content: center }
.foot .links a { font-family: var(--mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink2); text-decoration: none }
.foot .links .c { color: var(--ink) }
@media (hover: hover) { .foot .links a:hover { color: var(--accent) } }


/* ================================================================ home */

.hero { position: relative; padding: 22px 0 10px; overflow: hidden }
/* Phone order. The desktop grid restores DOM order, so this only applies in one column. */
.hgrid > div:first-child { display: flex; flex-direction: column }
.hgrid .chips { order: 1 }
.hgrid .lensout { order: 2; margin-top: 18px }
.glow { position: absolute; border-radius: 50%; pointer-events: none }
/* Positioned, so .wrap paints over the glows in DOM order. No z-index. */
.hero > .wrap { position: relative }
.hero .g1 { width: 620px; height: 620px; background: radial-gradient(circle, var(--teal) 0%, transparent 70%); left: -170px; top: -130px; opacity: .14 }
.hero .g2 { width: 560px; height: 560px; background: radial-gradient(circle, var(--pink) 0%, transparent 70%); right: -130px; top: 150px; opacity: .11 }

.lockup { display: flex; align-items: center; justify-content: center; gap: 15px; margin: 0 0 34px; transition: opacity .32s ease, transform .32s cubic-bezier(.2,.7,.2,1) }
.lockup .mark { width: 52px; margin: 0 -6.6px }
.lockup .lt { display: flex; flex-direction: column; gap: 4px; align-items: center }
/* The wordmark is flat ink and NEVER gradient: it is the one canonical form the store
   listings depend on. No .gt / .gtx here or on .top .brand .wm. */
.lockup .wm { font-size: 40px; font-weight: 800; letter-spacing: -.031em; line-height: 1 }
/* Both wordmarks are split per character for the entrance animation. */
.lockup .wm, .top .brand .wm { white-space: nowrap }
.lockup .wm i, .top .brand .wm i { display: inline-block; font-style: normal }
.lockup .tag { font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .17em; text-indent: .17em; text-transform: uppercase; color: var(--accent) }
.home.is-scrolled .lockup { opacity: 0; transform: scale(.55) translateY(-20px) }

.divlab { display: flex; align-items: center; gap: 14px; margin: 28px 0 12px; scroll-margin-top: 12px }
.divlab::before, .divlab::after { content: ""; height: 1px; background: var(--line); flex: 1 }
.divlab span { display: inline-flex; align-items: center; gap: 9px; font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--ink2) }
.divlab span::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--ce, var(--edge)); transition: background .4s ease }

/* One span per layout, so a screen reader is handed exactly one of them. */
.intro .wide { display: none }
/* Chrome ignores height, margin and line-height on a <br>, so the break is an empty block. */
.intro .narrow .nl { display: block; height: 10px }

.intro .teal { color: var(--teal-t) }
.intro .pink { color: var(--pink-t) }
/* This is the PHONE value; the desktop 21px lives in the 940 block. */
.intro { font-size: 15.5px; line-height: 1.48; color: var(--ink); margin: 18px 0 0; padding: 0; text-align: center }

/* Every lens occupies one grid cell, so the reserved height is the tallest real string.
   start, not end: the default feed claim is a line shorter than the lens claims between 940
   and 1200, and bottom-aligning parked that slack directly under "Pick a lens" as a hole.
   Top-aligned it falls before the source line instead, where it reads as leading. */
.claims { display: grid; align-items: start; margin: 0 }
/* wrap, not the inherited `pretty` from body. WebKit's `pretty` evaluates the whole paragraph
   and deliberately wraps "sooner than the maximum limit" to even out the rag - right for body
   copy, wrong here: in Safari it left the default claim's first line at 72% of the measure where
   first-fit gives 87%. (Chromium's `pretty` only adjusts the last few lines, so this reads as a
   Safari-only bug and does not reproduce in Chrome.) The claims carry &nbsp; on their closing
   words instead, which is what first-fit needs to not strand one. */
.claim {
  grid-area: 1 / 1;
  margin: 0; font-size: 25px; line-height: 1.18; letter-spacing: -.03em; font-weight: 800;
  text-wrap: wrap;
}
.claim[hidden] { display: block; visibility: hidden; pointer-events: none }
.claim b { color: var(--c); font-weight: 800 }

.srcline { display: flex; align-items: flex-start; gap: 8px; margin-top: 12px }
.srcline .d { width: 8px; height: 8px; border-radius: 50%; background: var(--ce, var(--edge)); flex: none; margin-top: .3em }
.srcline span { font-family: var(--mono); font-size: 9.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ink2) }
/* The default state's source line names the four lenses by colouring one word each, in chip
   order. (0,2,0) beats `.srcline span` (0,1,1), so these have to stay class-on-class. */
.srcline .lw { color: var(--c) }
/* Full ink for the lead phrase, against the --ink2 of the rest of the line: the four lens
   words below carry all the colour this line needs. The bullet keeps the ramp — it is the
   feed state's only colour cue, standing for all four at once. `background`, not the
   shorthand, or .d's own `background: var(--ce)` would wipe the image back out. */
.srcline.l-feed .d { background-image: var(--grad-rev) }
.srcline .lead { color: var(--ink) }

/* Three signals, not one: the design's single 2.02:1 border was invisible. */
.chips { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin: 0; padding: 0; list-style: none }
.chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; min-height: 44px; padding: 0 5px;
  border-radius: 14px; background: var(--card); border: 1px solid var(--line);
  font-family: inherit; font-size: 12.5px; font-weight: 700; color: var(--ink);
  cursor: pointer; white-space: nowrap;
  transition: border-color .18s, box-shadow .18s, color .18s;
}
.chip .d { width: 7px; height: 7px; border-radius: 50%; background: var(--ce); flex: none }
@media (hover: hover) { .chip:hover { border-color: var(--ce) } }
.chip:active { transform: translateY(1px) }
.chip[aria-pressed="true"] {
  border-color: var(--ce); font-weight: 800;
  box-shadow: 0 0 0 2px var(--ce);
}

/* Opening hint: with nothing picked, no chip wears a ring, so the row can read as a legend
   rather than a control. home.js adds .nudge on load and drops it on the first tap of ANY
   chip. In quickly (1.5s), then five shakes a long way apart; 3px of travel, not more,
   because the phone grid's gutter is only 6px. NOTE: 1.5s + 5 x 3.5s is ~19s over which the
   chip intermittently moves, past the 5s at which WCAG 2.2.2 wants a pause control - the
   first tap of any chip is the stop, and prefers-reduced-motion removes it outright. */
@keyframes nudge {
  /* One 3.5s cycle: ~875ms of hint, then 2.6s of rest. The ring is the SAME 2px the chip
     wears when selected, so the hint previews the state it is inviting - just in red. Ring
     and transform are deliberately offset: the red arrives before the movement and leaves
     after it, so neither edge snaps. The 0% shadow must be spelled out rather than left at
     `none`, or there is nothing for the colour to interpolate from. */
  0%, 100% { transform: none;              border-color: var(--line);       box-shadow: 0 0 0 2px transparent }
  4%       {                               border-color: var(--nudge-ring); box-shadow: 0 0 0 2px var(--nudge-ring) }
  7%       { transform: translateX(-3px) }
  10%      { transform: translateX(3px) }
  13%      { transform: translateX(-2px) }
  15%      { transform: translateX(2px) }
  18%      { transform: none;              border-color: var(--nudge-ring); box-shadow: 0 0 0 2px var(--nudge-ring) }
  25%      {                               border-color: var(--line);       box-shadow: 0 0 0 2px transparent }
}
.chip.nudge { animation: nudge 3.5s ease-in-out 1.5s 5 }

.hbtns { display: none }
.topcta { display: none }
.hnote { font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink) }

/* ---------------------------------------------------------------- cards */

.mock { display: flex; flex-direction: column; gap: 12px }
.mockcol { display: contents }

/* --cd is the lens identity that clears 4.5:1 on the ink band, the ground here at every width. */
.reglink {
  display: flex; align-items: center; justify-content: center; text-align: center;
  min-height: 44px; margin-top: 12px; padding: 8px 14px;
  border: 1px solid var(--cd); border-radius: 99px;
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--cd);
  text-decoration: none;
}
@media (hover: hover) { .reglink:hover { background: var(--cd); color: var(--ink) } }
/* Hiding the card's own copy keeps .two at exactly one tab stop — see the invariant below. */
.two .card .src-link { display: none }
.mock > .pin { display: contents }
/* One grid cell, so the phone is as tall as the tallest card and does not resize on tap. */
.stack { display: grid }
.stack > .lenscard { grid-area: 1 / 1 }
.stack > .lenscard[hidden] { visibility: hidden; pointer-events: none }

.card {
  background: var(--card); color: var(--ink);
  border-radius: 20px; padding: 16px;
  display: flex; flex-direction: column; gap: 10px;
  box-shadow: 0 3px 14px rgba(16,33,31,.05);
}
.kick { display: flex; align-items: center; gap: 8px; font-family: var(--mono); font-size: 9.5px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; color: var(--c) }
.kick i { width: 8px; height: 8px; border-radius: 50%; background: var(--c); flex: none }
.card h3, .card h4 { margin: 0; font-size: 19px; line-height: 1.22; letter-spacing: -.025em; font-weight: 800 }
.card .meta { font-family: var(--mono); font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink2) }
.card .para { margin: 0; font-size: 14.5px; line-height: 1.5; color: var(--ink2) }
.card .para b { color: var(--ink) }

.pin .shot { display: block; width: 100%; height: auto; border-radius: 20px }
/* One cell: all four shots share a crop, so the frame never resizes as the picker swaps.
   Hidden ones need no rule here — [hidden]:not(.claim):not(.lenscard) already covers them. */
.shotstack { display: grid; overflow: hidden }
.shotstack > .shot { grid-area: 1 / 1 }
/* The feed frame's framing is in the pixels now: the capture is taken with the list scrolled
   12 logical points, so the greeting sits where the lens frames' pill sits. It used to be
   nudged with a transform here; that is gone, and nothing should reintroduce it - a frame
   that needs a CSS pan is a frame that was cropped wrong. */

/* A stack, not a translating track: the transition lives only on the two live states, so a
   slide leaving .out snaps back to the parked right with nothing drawn in between. */
.screens { display: grid; overflow: hidden; border-radius: 20px; cursor: pointer }
/* Radius on the images too: Chrome clips a composited layer with a RECTANGLE. */
.screens .shot { grid-area: 1 / 1; transform: translateX(100%) }
.screens .shot.on { transform: none }
.screens .shot.out { transform: translateX(-100%) }
.screens .shot.on, .screens .shot.out { transition: transform .4s cubic-bezier(.2,.7,.2,1) }

/* 8px dot + 16px gap = 24px centre to centre, exactly tangent for WCAG 2.5.8. Tighten the
   gap and the control fails. ::before carries the target to 44 tall without taking layout. */
.dots { display: none; justify-content: center; align-items: center; gap: 16px; margin: 10px 0 22px }
body.deck-js .dots { display: flex }
.dots button {
  position: relative; width: 8px; height: 8px; padding: 0; border: 0;
  border-radius: 99px; background: var(--c, var(--ink3)); cursor: pointer;
  transition: width .22s cubic-bezier(.2,.7,.2,1);
}
.dots button[aria-pressed="true"] { width: 22px }
.dots button::before { content: ""; position: absolute; inset: -18px -8px }
@media (hover: hover) {
  .dots button:not([aria-pressed="true"]):hover { box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 30%, transparent) }
}

/* home.js advances the deck on this element's animationend, so it is a dependency, not
   decoration. clip-path, NEVER scaleX: a transform scales the radii and the veil spills. */
.dots button::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: var(--ink); opacity: .34;
  clip-path: inset(0 100% 0 0);
}
body.deck-run .dots button[aria-pressed="true"]::after { animation: deck-dwell 3s linear forwards }
body.deck-hold .dots button[aria-pressed="true"]::after,
body.sheet-open .dots button[aria-pressed="true"]::after { animation-play-state: paused }
@keyframes deck-dwell { to { clip-path: inset(0 0 0 0) } }

/* ---------------------------------------------------------------- the edition

   INVARIANT: the two columns carry .l-volume and .l-records, and every colour in the
   section arrives from them as --c / --ce / --craw. A column missing its lens class falls
   back to --accent, which is also teal, so the mistake is invisible on screen. */

/* Shown only under .ed-js, so with home.js absent no dead button is rendered. */
.ed-lead, .ed-seg { display: none }
.ed-lead { margin: 15px 0 0; font-size: 16px; line-height: 1.5; color: var(--ink2) }
.ed-lead .teal { color: var(--teal-t) }
.ed-lead .pink { color: var(--pink-t) }
.ed-lead b { color: var(--ink) }

.ed-seg { grid-template-columns: 1fr 1fr; gap: 5px; margin-top: 22px;
  background: rgba(16, 33, 31, .06); border-radius: 14px; padding: 5px }
/* A gradient fill beats any `color`, so the state carries fill, shadow and weight instead. */
.ed-tab { min-height: 44px; padding: 0 4px; border: none; border-radius: 10px; background: none;
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink);
  cursor: pointer; white-space: nowrap }
.ed-tab[aria-pressed="true"] { background: var(--card); font-weight: 800;
  box-shadow: 0 2px 6px rgba(16, 33, 31, .09) }
/* Re-declares the ramp so darkening the glyph tokens restores this control with them. */
.ed-tab .gtx { background-image: linear-gradient(96deg,
  var(--teal-t), var(--azure-t) 28%, var(--violet-t) 54%, var(--pink-t) 78%, var(--teal-t)) }

#edition .sechead h2 .gt { display: block }

.ed-grid { display: flex; flex-direction: column; gap: 44px }
.ed-col { display: flex; flex-direction: column; gap: 14px }

/* Stacked by default; the row form is opted into at 1200. */
.ed-colhd { margin: 0; padding-bottom: 12px; border-bottom: 2px solid var(--ce); font-weight: 400 }
.ed-colhd b { display: block; font-size: 19px; line-height: 1.22; font-weight: 800; letter-spacing: -.026em }
.ed-colhd span { display: block; margin-top: 6px;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--c) }

.ed-list { display: flex; flex-direction: column; gap: 7px; margin: 0; padding: 0; list-style: none }
.ed-item { background: var(--card); border-radius: 12px; padding: 11px 13px;
  display: flex; flex-direction: column; gap: 4px; box-shadow: 0 1px 5px rgba(16,33,31,.04) }
.ed-item p { margin: 0; font-size: 13.5px; line-height: 1.35; font-weight: 700; letter-spacing: -.015em }
.ed-item .ed-src { align-self: flex-end }

/* Violet, not either column's colour, so the two identities stay clean. */
.ed-item em { font-style: normal; color: var(--violet-t);
  background: rgba(122, 91, 255, .09); border-radius: 3px; padding: 0 2px; margin: 0 -2px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone }

/* INVARIANT: tap targets. Padding holds the box at 25.2px and the wrappers at an 8px gap,
   both over WCAG 2.5.8's floor. --c, not --ce: the label clears 4.5:1 on its own tint. */
.ed-src { display: inline-flex; align-items: center; border-radius: 99px;
  padding: 5px 10px; border: 1px solid var(--ce);
  background: rgba(0, 191, 174, .09);
  font-family: var(--mono); font-size: 8.5px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--c);
  text-decoration: none; white-space: nowrap }
@media (hover: hover) { .ed-src:hover { background: var(--c); color: var(--card) } }
/* On phone the feed's outlet is plain type, so filling it would invent a chip. */
@media (hover: hover) and (max-width: 939.98px) {
  .ed-js .ed-item .ed-src:hover { background: none; color: var(--ink); text-decoration: underline }
}

.ed-regs { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 0 }
.ed-reg { display: inline-flex; align-items: center; border-radius: 99px;
  padding: 5px 10px; border: 1px solid var(--pink-e);
  background: var(--card);
  font-family: var(--mono); font-size: 8.5px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--pink-t);
  text-decoration: none; white-space: nowrap; flex: none }
@media (hover: hover) { .ed-reg:hover { background: var(--pink-t); color: var(--card) } }

.ed-all { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 0 }
.ed-all .ed-src { color: var(--teal-t); border-color: var(--teal-e) }
@media (hover: hover) { .ed-all .ed-src:hover { background: var(--teal-e); color: var(--card) } }


/* INVARIANT: outer radius = inner radius + padding, or the band thins at the corners.
   background-image, not the shorthand — a later `background:` floods the card. */
.ed-frame { border-radius: 21px; padding: 3px;
  background-image: var(--grad-anim); background-size: 220% 100%;
  animation: drift 9s ease-in-out infinite alternate }
.ed-frame > .card { border-radius: 18px }

/* Stacked, the columns need a visible join; side by side they do not. */
.ed-col-lumen { position: relative }
.ed-col-lumen::before { content: ""; position: absolute; left: 50%; top: -34px;
  width: 2px; height: 22px; margin-left: -1px; background: var(--edge) }
.ed-col-lumen::after { content: ""; position: absolute; left: 50%; top: -16px;
  width: 9px; height: 9px; margin-left: -4.5px; border-radius: 50%;
  background: var(--card); border: 2px solid var(--edge) }

#edition .card .para b { background: rgba(255, 45, 142, .1); border-radius: 3px; padding: 0 2px; margin: 0 -2px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone }

.ed-foot { display: flex; flex-direction: column; gap: 16px; margin-top: 30px }
.ed-foot div { padding-top: 18px; border-top: 1px solid var(--line) }
.ed-foot b { display: block; margin-bottom: 8px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--c) }
.ed-foot span { font-size: 15px; line-height: 1.45; color: var(--ink2) }
.stamp {
  margin: 0;
  align-self: flex-start;
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink2);
  border: 1px dashed var(--line2); border-radius: 99px; padding: 3px 9px;
}

.ctx { border-radius: 14px; padding: 13px; display: flex; flex-direction: column; gap: 9px; background: #fff; border: 1px solid var(--line) }
@supports (background: color-mix(in oklab, red 8%, white)) {
  .ctx { background: color-mix(in oklab, var(--craw) 8%, #fff); border-color: var(--ce) }
}
.ctx .ch { display: flex; justify-content: space-between; gap: 10px; font-family: var(--mono); font-size: 9.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--c) }
.ctx .r { display: flex; justify-content: space-between; gap: 12px; font-size: 13.5px; line-height: 1.35 }
.ctx .r i { font-style: normal; display: block; font-family: var(--mono); font-size: 9px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink2) }
.ctx .r b { font-family: var(--mono); font-size: 13.5px; flex: none }
.ctx .r.tot { border-top: 1px solid var(--line2); padding-top: 9px; font-weight: 800 }

.band { padding: 46px 0; border-top: 1px solid var(--line) }
.band-dark { background: var(--ink); color: #fff; border-top: none; position: relative; overflow: hidden }
.band + .band-dark, .band-dark + .band { border-top: none }
.band-dark .g3 { width: 700px; height: 700px; background: radial-gradient(circle, var(--azure) 0%, transparent 70%); opacity: .2; left: 38%; top: -280px }
.band > .wrap { position: relative }

.sechead { margin-bottom: 30px }
.band h2 { font-size: 26px; line-height: 1.14; letter-spacing: -.032em; font-weight: 800; margin: 14px 0 0; max-width: 24em }
.band-dark h2 { color: #fff }
.body { font-size: 16.5px; line-height: 1.55; color: var(--ink2); margin: 16px 0 0; max-width: 34em }
.band-dark .body { color: rgba(255,255,255,.78) }
.band-dark .lab { color: var(--cd, #fff) }

/* Lens cards. The cap is the boundary ramp; the label is the glyph ramp. */
.lcards { display: flex; flex-direction: column; gap: 12px; margin-top: 26px; padding: 0; list-style: none }
.lc { background: var(--card); color: var(--ink); border-radius: 20px; padding: 20px; border-top: 4px solid var(--ce); display: flex; flex-direction: column; gap: 10px }
.lc .ln { display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--c) }
.lc .li { width: 13px; height: 13px; flex: none; display: block }
.lc h3 { margin: 0; font-size: 19px; line-height: 1.2; letter-spacing: -.025em; font-weight: 800 }
.lc p { margin: 0; font-size: 15.5px; line-height: 1.45; color: var(--ink2) }
.lc .eg { font-size: 14.5px; line-height: 1.42; color: var(--ink); border-top: 1px solid var(--line); padding-top: 12px; margin-top: auto }
.lc .eg em { font-style: normal; color: var(--c); font-weight: 700 }

.checks { display: flex; flex-direction: column; margin-top: 22px; padding: 0; list-style: none }
.checks li { padding: 15px 0; border-bottom: 1px solid var(--line) }
.checks li:last-child { border-bottom: none }
.band-dark .checks li { border-bottom-color: rgba(255,255,255,.14) }
.checks b { display: block; font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--accent); margin-bottom: 5px }
.band-dark .checks b { color: var(--teal) }
.checks p { margin: 0; font-size: 15.5px; line-height: 1.45; color: var(--ink2) }
.band-dark .checks p { color: rgba(255,255,255,.78) }

/* DOM order is the mobile reading order; desktop moves the notes with grid, not `order`. */
.vals { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none }
/* Baseline, not start: the number is 12px mono and the heading 19px. */
.note { display: grid; grid-template-columns: 24px 1fr; gap: 14px; padding: 16px 0; border-bottom: 1px solid var(--line); align-items: baseline }
.note:last-child { border-bottom: none }
.band-dark .note { border-bottom-color: rgba(255,255,255,.14) }
.note .k { font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--c) }
.band-dark .note .k { color: var(--cd) }
.note h3 { margin: 0 0 4px; font-size: 17px; font-weight: 800; letter-spacing: -.022em }
.band-dark .note h3 { color: #fff }
.note p { margin: 0; font-size: 15.5px; line-height: 1.45; color: var(--ink2) }
.band-dark .note p { color: rgba(255,255,255,.78) }

/* feat: one DOM, two shapes. display:contents flattens the desktop wrappers so `order` can
   interleave the grandchildren on phone; at 940 the wrappers become real columns.

   INVARIANT, binding .feat-weave and .two: a container that reorders must never move a
   focusable descendant across another one. .feat-weave's three screen pills move as one
   contiguous block and cross only prose; .two holds exactly one tab stop at every width. */
.feat { display: flex; flex-direction: column; gap: 12px }
.feat > .ftext { display: contents }
.feat .lab { order: 1 }
.feat > .ftext > h2 { order: 2 }
.feat > .ftext > .body { order: 3 }
.feat .vals { order: 5 }

/* NOTE: a second @media (max-width: 939.98px) block lives near the foot of this file. This
   one stays here, beside .feat, because moving it would change source order. */
@media (max-width: 939.98px) {
  /* Catch-all first: `order` defaults to 0, which sorts BEFORE .lab's 1. Matched through
     .mock, not under it — a > chain that stops matching leaves .mock at order 0. */
  .feat-weave .mock .pin > *,
  .feat-weave .vals > * { order: 20 }
  .feat-weave .mock, .feat-weave .vals { display: contents }
  .feat-weave .mock .pin > .frame { order: 4 }   /* must out-specify the (0,3,0) catch-all */
  .feat-weave > .mockcol > * { order: 20 }
  .feat-weave > .mockcol > .dots { order: 5 }
  .feat-weave .note:nth-of-type(1) { order: 6 }
  .feat-weave .note:nth-of-type(2) { order: 7 }
  .feat-weave .note:nth-of-type(3) { order: 8 }
}

/* Phone-only: the 940 block hides .brk outright. A div, not a <br>: Chrome ignores height,
   margin and line-height on a <br>, so it cannot both end the line and set the gap. */
.brk { display: flex; align-items: center; gap: 0; margin: 26px var(--pad) 0 }
.brk::before, .brk::after { content: ""; flex: 1; height: 1px; background: var(--line) }

/* ---------------------------------------------------------------- dock */

/* Fixed, not sticky: a sticky element still reserves its height at the end of the document.
   The bar is the anchor and .cta a span, so the whole 280x60 takes the tap. */
.dock {
  position: fixed; left: var(--pad); right: var(--pad); z-index: 45;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: 14px;
  padding: 8px 8px 8px 26px;
  /* Opaque first: the translucency arrives only with the blur that makes it readable. */
  background: var(--card);
  border: 1px solid rgba(0, 191, 174, .26);
  border-radius: 99px;
  box-shadow: 0 0 0 3px rgba(0, 191, 174, .07), 0 10px 28px -12px rgba(16, 33, 31, .4);
  color: var(--ink); text-decoration: none; cursor: pointer;
  transition: transform .28s cubic-bezier(.45,0,.55,1), opacity .28s cubic-bezier(.45,0,.55,1);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .dock {
    background: rgba(255, 255, 255, .88);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    border-color: color-mix(in oklab, var(--teal) 22%, var(--line));
  }
}
/* flex: 1 is load-bearing: with no grower the pill sits in the middle of the bar. */
.dock .dtext { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0 }
.dock .dtext b { font-size: 15.5px; font-weight: 800; letter-spacing: -.024em; line-height: 1.1 }
.dock .hnote { font-size: 9px; letter-spacing: .13em; line-height: 1 }

.dock .cta {
  flex: none; min-height: 42px; padding: 0 21px;
  font-size: 10.5px; letter-spacing: .15em; text-indent: .15em;
  position: relative;
  /* Both, not either: dropping the animation alone parks the window at teal-to-cyan. */
  background-size: 100% 100%; animation: none;
  box-shadow: 0 6px 16px -6px rgba(122, 91, 255, .6);
}
/* A pseudo-element, so the pulse is an opacity change, not a repaint inside a backdrop-filter. */
.dock .cta::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  box-shadow: 0 0 0 4px rgba(0, 191, 174, .34);
  opacity: .25; pointer-events: none;
  animation: ringglow 2s ease-in-out infinite alternate;
}
@keyframes ringglow { to { opacity: 1 } }
@media (hover: hover) { .dock:hover .cta { filter: saturate(1.1) brightness(1.08) } }
.dock:active .cta { transform: translateY(1px) }


/* ---------------------------------------------------------------- dialog */

dialog.soon {
  position: fixed; border: none; padding: 0; margin: auto;
  width: min(468px, calc(100vw - 40px));
  background: var(--card); border-radius: 26px;
  box-shadow: 0 30px 70px -20px rgba(16,33,31,.5);
  color: var(--ink);
}
dialog.soon::backdrop { background: rgba(16,33,31,.46) }
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  dialog.soon::backdrop { -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px) }
}
dialog.soon .sheet-body { padding: 36px 32px 30px; text-align: center; display: flex; flex-direction: column; align-items: stretch; gap: 12px }
dialog.soon .mark { width: 60px; margin: 0 auto }
dialog.soon h2 { margin: 6px 0 0; font-size: 27px; line-height: 1.14; letter-spacing: -.035em; font-weight: 800 }
dialog.soon p { margin: 0; font-size: 16px; line-height: 1.55; color: var(--ink2) }
dialog.soon .plats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; width: 100%; margin: 12px 0 4px; padding: 0; list-style: none }
dialog.soon .plat { position: relative; overflow: hidden; background: var(--mint); border-radius: 16px; padding: 15px 14px 17px; display: flex; flex-direction: column; gap: 5px; align-items: center }
dialog.soon .plat.live::after { content: ""; position: absolute; left: 0; bottom: 0; height: 3px; width: 40%; background: linear-gradient(90deg, transparent, var(--teal), var(--azure), var(--violet), var(--pink), transparent); animation: sweep 2.6s ease-in-out infinite }
dialog.soon .plat.queued::after { content: ""; position: absolute; left: 14px; right: 14px; bottom: 0; height: 3px; opacity: .5; background: repeating-linear-gradient(90deg, var(--ink3) 0 4px, transparent 4px 9px) }
@keyframes sweep { 0% { transform: translateX(-100%) } 100% { transform: translateX(250%) } }
/* Full-width track: the highlight travels by background-position, not by moving the box. */
@keyframes sweepwide { 0% { background-position: -60% 0 } 100% { background-position: 160% 0 } }
dialog.soon .pn { font-size: 16px; font-weight: 800; letter-spacing: -.02em }
dialog.soon .ps { font-family: var(--mono); font-size: 9.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--c) }
dialog.soon .note { display: block; padding: 0; border: 0; grid-template-columns: none; font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; color: var(--ink2) }
.close-x {
  position: absolute; top: 14px; right: 16px;
  width: 36px; height: 36px; border: none; border-radius: 50%;
  background: var(--mint); color: var(--ink2); font-size: 17px; line-height: 1; cursor: pointer; font-family: inherit;
}
@media (hover: hover) { .close-x:hover { background: var(--ink); color: #fff } }
.close-wide { display: none }

/* ================================================================ legal pages */

/* Overriding the two tokens narrows every page part with no second set of rules. */
.doc, .notfound { --wrap: 820px; --pad: 22px }
.doc { line-height: 1.6 }
.doc-main { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--pad) }

.eyebrow { display: inline-flex; align-items: center; gap: 12px; font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--accent); margin: 0 }
.eyebrow::before { content: ""; width: 30px; height: 2px; background: var(--edge) }

.doc-head { padding: 52px 0 0 }
.doc-head h1 { font-size: 29px; line-height: 1.1; letter-spacing: -.035em; font-weight: 800; margin: 18px 0 10px; max-width: 18em }
.updated { font-family: var(--mono); font-size: 11px; letter-spacing: .13em; text-transform: uppercase; color: var(--ink2); margin: 0 }
.lede { font-size: 17px; line-height: 1.5; color: var(--ink2); margin: 18px 0 0; max-width: 44em }
/* .doc p (0,1,1) outranks .lede (0,1,0), so the margin above never landed and the summary sat flush against the date stamp. (0,2,0) wins wherever .doc p applies. */
.doc-head > .lede { margin-top: 22px }

.stage { margin: 34px 0 0; background: var(--card); border-radius: 22px; padding: 8px; box-shadow: 0 4px 18px rgba(16,33,31,.06) }
.stage dl { display: grid; grid-template-columns: 1fr; gap: 2px; margin: 0 }
.stage dl > div { padding: 16px; display: flex; flex-direction: column; gap: 8px; border-radius: 16px }
.stage dt { font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--accent) }
.stage dl > div:last-child dt { color: var(--pink-t) }
.stage dd { margin: 0; font-size: 15.5px; line-height: 1.5; color: var(--ink2) }
.stage dd strong { color: var(--ink); font-weight: 800 }

/* Column-major by CSS, not by scrambling the DOM, so it reads 1..13 aloud. */
.contents { margin: 38px 0 0; padding: 24px 0 0; border-top: 1px solid var(--line) }
.contents ol { display: grid; grid-auto-flow: row; grid-template-rows: none; gap: 2px 40px; margin: 0; padding: 0; list-style: none; counter-reset: toc }
.contents li { counter-increment: toc }
.contents a { display: flex; gap: 12px; align-items: baseline; text-decoration: none; font-size: 15px; padding: 5px 0 }
.contents a::before { content: counter(toc) "."; font-family: var(--mono); font-size: 12px; color: var(--ink2); flex: none; min-width: 22px }
@media (hover: hover) {
  .contents a:hover { color: var(--accent) }
  .contents a:hover::before { color: var(--accent) }
}

.doc section.s { padding: 32px 0 0; margin-top: 32px; border-top: 1px solid var(--line) }
.doc h2 { display: flex; gap: 11px; align-items: baseline; font-size: 22px; line-height: 1.15; letter-spacing: -.03em; font-weight: 800; margin: 0 0 16px }
.doc h2 .n { font-family: var(--mono); font-size: 13px; color: var(--accent); flex: none; font-weight: 700 }
.doc h3 { font-size: 18px; letter-spacing: -.02em; font-weight: 800; margin: 26px 0 10px }
.doc p { margin: 0 0 15px }
/* Scoped by ancestor: unscoped, `.doc a` ties with `.contents a` and underlines all 13 rows. */
.doc main :is(p, li, dd, td) a,
.doc .stage dd a,
.doc .contact .addr a { text-decoration: underline; text-decoration-color: var(--edge); text-decoration-thickness: 1.5px; text-underline-offset: 3px }
@media (hover: hover) { .doc a:hover { color: var(--accent) } }
.doc code { font-family: var(--mono); font-size: .88em; background: rgba(16,33,31,.05); border-radius: 5px; padding: 1px 5px }
.doc strong { font-weight: 800 }

/* Scoped to ul: a flex list item suppresses ::marker entirely, so ol keeps its numbers. */
.doc ul { margin: 0 0 15px; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 10px }
/* Block, not flex: a flex list item splits an <strong> opener into its own column. */
.doc ul > li { display: block; position: relative; padding-left: 19px }
.doc ul > li::before { content: ""; position: absolute; left: 0; top: .62em; width: 6px; height: 6px; border-radius: 50%; background: var(--edge) }
.doc main ol { margin: 0 0 15px; padding-left: 1.4em }
.doc main ol > li { display: list-item; margin-bottom: 10px }
.doc main ol > li::marker { font-family: var(--mono); font-size: .9em; color: var(--ink2) }

.contact { margin: 28px 0 0; background: var(--card); border-radius: 20px; padding: 20px; box-shadow: 0 4px 18px rgba(16,33,31,.06); border-left: 5px solid var(--edge); display: flex; flex-direction: column; gap: 11px }
.contact .addr { font-size: 19px; font-weight: 800; letter-spacing: -.03em; margin: 0; overflow-wrap: anywhere }
.contact p:last-child { margin: 0; font-size: 16.5px; color: var(--ink2) }

/* ---------------------------------------------------------------- disclosure */

/* One disclosure maintained twice. tool/check_disclosure_sync.py asserts they agree. */
.recipients { margin: 0 0 22px }
.recipients table { display: none }
.rec-card { background: var(--card); border-radius: 18px; padding: 18px 20px; margin: 0 0 10px; box-shadow: 0 3px 14px rgba(16,33,31,.05); border-left: 4px solid var(--edge) }
.rec-card .who { font-size: 16.5px; font-weight: 800; letter-spacing: -.02em; margin: 0 0 12px; line-height: 1.35 }
.rec-card dl { margin: 0; display: grid; grid-template-columns: 68px 1fr; gap: 8px 14px }
.rec-card dt { font-family: var(--mono); font-size: 9.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--ink2); padding-top: 3px }
.rec-card dd { margin: 0; font-size: 15px; line-height: 1.45 }

@media (min-width: 640px) {
  .rec-card { display: none }
  .recipients table { display: table; width: 100%; border-collapse: separate; border-spacing: 0; background: var(--card); border-radius: 18px; overflow: hidden; box-shadow: 0 3px 14px rgba(16,33,31,.05) }
  .recipients th { text-align: left; font-family: var(--mono); font-size: 9.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--ink2); padding: 14px 16px; border-bottom: 2px solid var(--edge) }
  .recipients td { padding: 14px 16px; font-size: 14.5px; line-height: 1.45; border-bottom: 1px solid var(--line); vertical-align: top }
  .recipients tr:last-child td { border-bottom: none }
  .recipients .c-who { width: 21% } .recipients .c-what { width: 30% }
  .recipients .c-when { width: 22% } .recipients .c-why { width: 27% }
}

/* ================================================================ 404 */

.notfound { min-height: 100vh; min-height: 100svh; display: flex; flex-direction: column }
.notfound main { flex: 1; display: grid; place-items: center; padding: 56px var(--pad) 64px; position: relative; overflow: hidden }
.nf { position: relative; max-width: 560px; text-align: center; display: flex; flex-direction: column; align-items: center }
.nf .code { font-family: var(--mono); font-size: 12px; font-weight: 700; letter-spacing: .24em; text-transform: uppercase; color: var(--ink2); margin: 0 0 22px }
.nf .mark { width: 64px; margin: 0 0 22px }
.nf h1 { font-size: 31px; line-height: 1.08; letter-spacing: -.04em; font-weight: 800; margin: 0 0 14px }
.nf p { margin: 0; font-size: 17px; line-height: 1.5; color: var(--ink2); max-width: 30em }
.nf .cta { margin-top: 28px }
.notfound .g1 { width: 560px; height: 560px; background: radial-gradient(circle, var(--teal) 0%, transparent 68%); opacity: .13; left: -150px; top: -130px }
.notfound .g2 { width: 500px; height: 500px; background: radial-gradient(circle, var(--pink) 0%, transparent 68%); opacity: .09; right: -120px; bottom: -100px }

/* ================================================================ wide */

@media (min-width: 700px) {
  .doc, .notfound { --pad: 40px }
  .doc-head { padding: 56px 0 0 }
  .stage dl > div { padding: 22px 22px 24px; gap: 9px }
  .contents a { font-size: 15.5px; padding: 5px 0 }
  .contact { border-radius: 22px; padding: 26px 28px 28px; gap: 12px }
  .contact .addr { font-size: 24px }
  .doc .foot { margin-top: 64px }
  .nf .code { font-size: 13px; margin: 0 0 26px }
  .nf .mark { width: 88px; margin: 0 0 26px }
  .nf h1 { font-size: 52px; line-height: 1.04; margin: 0 0 18px }
  .nf p { font-size: 19px }
  .nf .cta { margin-top: 34px }
  .notfound main { padding: 64px var(--pad) 84px }
  .doc-head h1 { font-size: 46px; line-height: 1.06 }
  .lede { font-size: 19px; line-height: 1.55; margin: 24px 0 0 }
  .doc-head > .lede { margin-top: 28px }
  .stage dl { grid-template-columns: 1fr 1fr }
  /* Spanning the orphan let its text cross the column divider, so it stays an ordinary cell. */
  .stage dl > div:last-child { grid-column: 1 }
  /* Per page, not a custom property: a style= attribute never arrives past the CSP. */
  .contents ol { grid-auto-flow: column; gap: 6px 40px }
  .doc-privacy .contents ol { grid-template-rows: repeat(7, auto) }
  .doc-support .contents ol { grid-template-rows: repeat(3, auto) }
  .doc h2 { font-size: 26px; gap: 14px }
  .doc section.s { padding: 42px 0 0; margin-top: 42px }
  .doc h2 .n { font-size: 15px }
}

/* The phone treatment still applies here; it holds up as long as it stops stretching. */
@media (min-width: 600px) and (max-width: 939.98px) {
  .home { --wrap: 560px; --pad: 24px }
  /* The one band where the measure is a constant - 560 wrap less 48 of padding is always 512 -
     so the claim size can be picked for it exactly rather than guessed. 25px leaves the default
     claim's first line at 84% of that 512; 28px takes it to 94% and all five sit at 90-99%. */
  .claim { font-size: 28px }
  #edition .ed-frame { max-width: 460px }
  .home .dock { max-width: max-content; margin-inline: auto }
  .brk { margin-left: auto; margin-right: auto; max-width: 512px }
}

@media (min-width: 940px) {
  :root { --pad: 48px; --header-h: 94px }
  /* --gsize/--gtop are read by the bar's copy of this circle below: same circle, one source.
     .g0 has no base rule, so its background must stay identical to .hero .g1's. */
  .hero .g1, .home .topbar .g0 {
    --gsize: 1280px; --gtop: -460px;
    width: var(--gsize); height: var(--gsize); left: -500px; opacity: .11;
    background: radial-gradient(circle, var(--teal) 0%, transparent 70%);
  }
  .hero .g1 { top: var(--gtop) }
  .hero .g2 { width: 1180px; height: 1180px; right: -440px; top: -160px; opacity: .08 }
  .hero { padding: 34px 0 84px }
  .lockup { justify-content: center; gap: 20px; margin: 0 0 40px; transform-origin: 50% 50%;
            --markdur: 900ms; --chardur: 320ms; --step: 145ms }
  .lockup .lt { gap: 12px }
  .lockup .wm { font-size: 66px; line-height: .92 }
  .lockup .mark { width: 132px; margin: 0 -16.7px }
  .lockup .tag { font-size: 14px }

  /* Animates FROM the hidden state with fill-mode both, so if it never runs the mark is
     simply there. home.js adds .anim to replay it. */
  .lockup.anim .mark .dot, .top .brand.anim .mark .dot {
    transform-box: view-box; transform-origin: 16px 16px;
    animation: dotgrow var(--markdur) cubic-bezier(.4,0,.2,1) both;
  }
  .lockup.anim .mark .ring, .top .brand.anim .mark .ring {
    /* An SVG circle starts at three o'clock; 67.23 is 2 pi r for r = 10.7. */
    transform-box: view-box; transform-origin: 16px 16px; transform: rotate(-90deg);
    stroke-dasharray: 67.23; stroke-dashoffset: 0;
    animation: ringdraw var(--markdur) cubic-bezier(.4,0,.2,1) both;
  }
  .lockup.anim .wm i, .top .brand.anim .wm i { animation: charin var(--chardur) ease-out both }
  /* Staggered so the fifth letter finishes with the ring and the dot. .vh is child 1. */
  .lockup.anim .wm i:nth-child(2), .top .brand.anim .wm i:nth-child(2) { animation-delay: calc(var(--step) * 0) }
  .lockup.anim .wm i:nth-child(3), .top .brand.anim .wm i:nth-child(3) { animation-delay: calc(var(--step) * 1) }
  .lockup.anim .wm i:nth-child(4), .top .brand.anim .wm i:nth-child(4) { animation-delay: calc(var(--step) * 2) }
  .lockup.anim .wm i:nth-child(5), .top .brand.anim .wm i:nth-child(5) { animation-delay: calc(var(--step) * 3) }
  .lockup.anim .wm i:nth-child(6), .top .brand.anim .wm i:nth-child(6) { animation-delay: calc(var(--step) * 4) }

  .top .brand { --markdur: 600ms; --chardur: 200ms; --step: 100ms }

  /* The tagline fades as a whole across the same window, so all three parts land together. */
  .lockup.anim .tag { animation: fadein var(--markdur) ease-in both }

  @keyframes fadein { from { opacity: 0 } to { opacity: 1 } }
  @keyframes dotgrow { from { transform: scale(0) } to { transform: scale(1) } }
  @keyframes ringdraw { from { stroke-dashoffset: 67.23 } to { stroke-dashoffset: 0 } }
  @keyframes charin { from { opacity: 0; transform: translateY(.18em) } to { opacity: 1; transform: none } }

  /* The bar carries its own half of the hero's circle: the bar must cover the hero, the
     hero's content the glow, and the glow the bar — no one stacking order does all three.
     The clip goes on .topbar, and its border must be 0 with it: `overflow` clips to the
     padding box, so a reserved 1px border leaves a strip the glow cannot reach. */
  .home .topbar { overflow: hidden; border-bottom: 0 }
  .home.is-scrolled .topbar {
    box-shadow: inset 0 -1px 0 var(--line), 0 6px 20px -12px rgba(16, 33, 31, .25);
  }
  /* Anchored from the BOTTOM off the same two properties: one circle. --header-h says 94
     and the bar renders 93, so driving it off that leaves a 1px seam. */
  .home .topbar .g0 { top: auto; bottom: calc(-1 * (var(--gsize) + var(--gtop))) }
  .home .topbar > .wrap { position: relative }

  .hgrid { display: grid; grid-template-columns: 1.25fr .85fr; gap: 56px; align-items: center }
  .intro { text-align: center; max-width: 920px; margin: 0 auto 48px; padding: 0; font-size: 21px }
  .intro .wide { display: inline }
  .intro .narrow { display: none }
  .divlab { display: flex; gap: 20px; margin: 0 0 34px }
  .claim { font-size: 46px; line-height: 1.06; letter-spacing: -.032em }
  .hgrid > div:first-child { display: block }
  .foot .row { padding: 26px var(--pad) 30px }
  .foot .links { gap: 24px }
  .grad-rule { height: 3px }
  .srcline { margin-top: 18px; gap: 10px }
  .srcline .d { width: 10px; height: 10px; margin-top: .32em }
  .srcline span { font-size: 12px; letter-spacing: .1em }
  .chips { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 30px }
  .chip { width: auto; min-height: 44px; gap: 9px; padding: 11px 18px 11px 14px; border-radius: 999px; font-size: 15px }
  .chip .d { width: 9px; height: 9px }
  .dock { display: none }               /* above the fold there is room for the real buttons */
  .hbtns { display: flex; gap: 26px; margin-top: 34px; align-items: center; flex-wrap: wrap }
  .topcta { display: inline-flex }
  .home .top .brand { opacity: 1; visibility: visible; transform: none }
  /* Opacity only: the box keeps its size in both states. */
  .home .top .brand .mark,
  .home .top .brand .wm { opacity: 0; transition: opacity .24s ease }
  .home.is-scrolled .top .brand .mark,
  .home.is-scrolled .top .brand .wm { opacity: 1 }

  .band { padding: 86px 0 }
  .band h2 { font-size: 42px; line-height: 1.08 }
  .feat h2 { font-size: 38px; margin: 16px 0 20px }
  .sechead { display: grid; grid-template-columns: 200px 1fr; gap: 44px; align-items: baseline; margin-bottom: 52px }
  .sechead h2 { margin: 0 }
  /* A .body inside .sechead is a third grid item and lands in column 1 unless told. */
  .sechead .body { grid-column: 2 }
  #edition .sechead { display: block }
  #edition .sechead h2 { margin: 16px 0 0; max-width: 20em }
  #lenses .sechead { display: block; text-align: right }
  #lenses .sechead h2 { margin: 16px 0 0 auto; max-width: 26em }

  /* --- the edition, two up ------------------------------------------------------------- */
  .ed-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: start }
  /* Side by side, nothing to join. */
  .ed-col-lumen::before, .ed-col-lumen::after { content: none }
  .ed-colhd { padding-bottom: 14px }
  .ed-list { gap: 8px }
  .ed-item { border-radius: 13px; padding: 14px 16px }
  .ed-item p { font-size: 15px; letter-spacing: -.018em }
  .ed-item .ed-src { font-size: 9.5px; letter-spacing: .12em; padding: 5px 10px }

  /* #edition (1,1,0) beats .mock .card (0,2,0). Scope by class, never by element. */
  .ed-frame { border-radius: 25px }
  .ed-frame > .card { border-radius: 22px }
  #edition .card { border-radius: 22px; padding: 22px; gap: 12px; box-shadow: 0 4px 18px rgba(16,33,31,.06) }
  #edition .card h4 { font-size: 24px; line-height: 1.2; letter-spacing: -.028em }
  #edition .card .para { font-size: 16px }
  #edition .card .meta { font-size: 10.5px }
  #edition .ctx { border-radius: 16px; padding: 15px 16px; gap: 10px }
  #edition .ctx .ch { font-size: 10px; gap: 12px }
  #edition .ctx .r { font-size: 15px; gap: 14px }
  #edition .ctx .r b { font-size: 15px }

  .ed-foot { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; margin-top: 32px }
  .ed-foot div { display: grid; grid-template-columns: 84px 1fr; gap: 18px; align-items: baseline; padding-top: 22px }
  .ed-foot b { display: block; margin-bottom: 0; font-size: 11px }
  .ed-foot span { font-size: 16.5px; line-height: 1.48 }
  .body { font-size: 19px }
  /* Subgrid aligns all four cards; the row does it, so .eg's auto top margin has to go. */
  .lcards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; grid-template-rows: auto auto 1fr auto }
  .lc { display: grid; grid-template-rows: subgrid; grid-row: span 4;
        border-radius: 24px; padding: 26px 24px 28px; gap: 13px }
  .lc .eg { margin-top: 0; font-size: 15.5px; line-height: 1.4; padding-top: 14px }
  .lc .ln { font-size: 11px }
  .lc p { font-size: 14.5px }
  .lc h3 { font-size: 21px }

  /* Two real columns: `order` has no grandchildren left to move, so DOM order governs. */
  .feat { display: grid; grid-template-columns: minmax(330px, .92fr) 1.08fr; gap: 0 76px; align-items: center }
  .feat > .ftext { display: block; grid-column: 2; grid-row: 1 }
  .feat > .mockcol { grid-column: 1; grid-row: 1; justify-self: center;
                     display: flex; flex-direction: column; width: 330px }
  .feat > .mockcol > .mock { width: 100% }
  /* Ink to ink: without this the pills sit flush on the shell and read as device chrome. */
  .feat > .mockcol > .dots { margin: 18px 0 0 }
  .feat .vals { display: flex; margin-top: 24px }

  /* The inset is the APP's warm paper, not the site's cool mint: at 12px a cool ring around
     a warm screenshot reads as a misregistered print. */
  .mock {
    background: #fff; border-radius: 42px; padding: 10px; gap: 0;
    box-shadow: 0 2px 6px rgba(16,33,31,.06), 0 40px 80px -28px rgba(16,33,31,.4);
  }
  .mock > .pin {
    display: flex; flex-direction: column; gap: 11px;
    background: #faf8f4; border-radius: 33px; overflow: hidden; padding: 12px;
  }

  /* A catch-all, not a list, so anything added to these pins later is hidden by default. */
  .hgrid > .mock > .pin > :not(.shot):not(.shotstack):not(.frame),
  .two     .mock > .pin > :not(.shot):not(.shotstack):not(.frame) { display: none }

  .reglink { min-height: 34px; font-size: 8.5px }
  /* min(), not 330: the hero's column is under 330px from 940 to 967. The wash is declared
     after the flat #fff, so without color-mix the shell stays white. */
  .hgrid > .mock { width: min(330px, 100%); margin: 0 auto;
                   background: color-mix(in oklab, var(--craw) 4%, #fff);
                   transition: background .4s ease }
  .two .mock { width: 100% }

  /* DORMANT while the frames hold screenshots — nothing below matches today. Delete only
     when no frame can ever show a card again. */
  .mock .card { border-radius: 20px; padding: 14px; gap: 9px }
  .mock .card h3 { font-size: 15.5px; line-height: 1.24 }
  .mock .card .meta { font-size: 9px }
  .mock .kick { font-size: 9px }
  .mock .kick i { width: 7px; height: 7px }
  .mock .card .para { font-size: 11px; line-height: 1.5 }
  .mock .ctx { padding: 11px 12px; gap: 8px; border-radius: 14px }
  .mock .ctx .ch { font-size: 9px }
  .mock .ctx .r { font-size: 11px; line-height: 1.3 }
  .mock .ctx .r i { font-size: 8.5px }
  .mock .ctx .r b { font-size: 11.5px }
  .mock .stamp { font-size: 8px; padding: 2px 8px }

  /* Row gap stays 0 — .checks carries its own margin. */
  .two { display: grid; grid-template-columns: 1.05fr .95fr; gap: 0 70px; grid-template-rows: 1fr auto auto 1fr; align-items: start }
  .two > .tx { grid-column: 1; grid-row: 2 }
  .two > .checks { grid-column: 1; grid-row: 3 }
  /* The wrapper is the grid item, so adding the link chip costs the grid nothing. */
  .two > .mockcol {
    grid-column: 2; grid-row: 1 / -1; align-self: center;
    display: flex; flex-direction: column; gap: 12px; width: 330px; margin: 0 auto;
  }
  .brk { display: none }
  .checks li { display: grid; grid-template-columns: 120px 1fr; gap: 20px; align-items: baseline; padding: 17px 0 }
  .checks b { font-size: 11px; margin-bottom: 0 }
  .checks p { font-size: 16px }
  .note { grid-template-columns: 26px 1fr; gap: 18px; padding: 20px 0 }
  .note .k { font-size: 12px }
  .note h3 { font-size: 19px }
  .note p { font-size: 16px }
}

/* 1200 is derived, not chosen: --wrap caps the measure at 1104px there, so it is the first
   width that is safe and stays safe. If the chip or the headlines change, re-derive it. */
@media (min-width: 1200px) {
  .ed-colhd { display: flex; justify-content: space-between; align-items: baseline; gap: 16px }
  .ed-colhd b, .ed-colhd span { display: inline }
  .ed-colhd span { margin-top: 0; font-size: 10.5px; letter-spacing: .13em }
  .ed-item { flex-direction: row; justify-content: space-between; align-items: baseline; gap: 18px }
  .ed-item .ed-src { order: 0; flex: none; align-self: baseline }
}

/* At the breakpoint four chips want 474px in a 469px column, so tighten rather than restack. */
@media (min-width: 940px) and (max-width: 1023.98px) {
  .chips { gap: 8px }
  .chip { padding: 11px 15px 11px 12px; gap: 8px }
}

@media (min-width: 1024px) {
  .claim { font-size: 54px }
}

/* The context-block header pair needs 284.5px and a 390 phone has 286. */
@media (max-width: 389.98px) {
  #edition .ctx .ch { font-size: 8.5px; letter-spacing: .08em }
}

@media (max-width: 359px) {
  /* nowrap so a regression surfaces as overflow, which is checked, not a silent wrap. */
  #edition .ctx { padding: 10px }
  #edition .ctx .ch { font-size: 8px; letter-spacing: .05em; gap: 6px }
  #edition .ctx .ch span { white-space: nowrap }
  .chips { grid-template-columns: repeat(2, 1fr) }
  .dock { padding-left: 18px; gap: 10px }
}

/* ================================================================ phone

   Everything below must stay ABOVE @media print: paper lays out near 794px, which is
   inside max-width 939.98px, and only the print block's `display: none` carries
   !important, so a mobile rule added after print would win on paper. */

@media (max-width: 939.98px) {

  /* Scoped inside this query — unscoped, the desktop frames would be empty rectangles. */
  .two .mock > .pin > .card { display: none }
  .hgrid .stack, .hgrid .stamp { display: none }

  /* A device, because a band the width of the text column is a rectangle whatever edge you
     put on it. The frame narrows to hold a whole screen, buying the silhouette at the cost
     of legibility inside the picture — which is why desktop, at 330px in a 1440 page, needs
     no bezel. Graphite, not --ink: --ink is .band-dark's ground. Both radii below beat their
     base rules on SOURCE ORDER, not specificity, so nothing here may move above them. */
  .shotstack, .frame {
    position: relative;
    width: 260px; margin-inline: auto;
    border: 5px solid var(--shell); border-radius: 34px;
    /* The rim. A child paints over its parent's inset shadow, so the 1px of padding is what
       keeps the screenshot clear of the padding-box edge where the ring is drawn - remove
       the padding and the ring vanishes under the image. */
    padding: 1px;
    /* .screens draws no ground, so mid-slide the rounded corners leave a notch. */
    background: #faf8f4;
    box-shadow: inset 0 0 0 1px var(--shell-rim),
                0 2px 5px rgba(16,33,31,.10), 0 18px 34px -18px rgba(16,33,31,.42);
  }
  .pin .shot, .screens { border-radius: 28px }   /* 34 − 5 − 1, so the nesting is concentric */
  /* NOT display: .screens is a one-cell grid, and blocking it stacks the three slides. */
  .frame > .screens { width: 100% }

  /* One bar cloned twice by box-shadow. Fixed lengths, not percentages — a box-shadow
     offset rejects a percentage and drops the whole declaration silently.
     Do not try `overflow: clip` + a clip-margin on .screens to avoid the .frame wrapper: it
     lets these buttons out, but lets the SLIDES out too, over the bezel mid-transition. */
  .shotstack::before, .frame::before {
    content: ""; position: absolute; z-index: 1;
    /* -8, not -3: an absolute offset resolves against the PADDING box. */
    left: -8px; top: 76px; width: 3px; height: 26px;
    border-radius: 3px; background: var(--shell-btn);
    box-shadow: 0 34px 0 var(--shell-btn), 0 70px 0 var(--shell-btn);
  }
  .shotstack::after, .frame::after {
    content: ""; position: absolute; z-index: 1;
    right: -8px; top: 118px; width: 3px; height: 48px;
    border-radius: 3px; background: var(--shell-btn);
  }
  /* .hero is overflow:hidden, so without this padding the shadow is guillotined. */
  .home .hero { padding-bottom: 28px }

  .mock .stamp { order: -1; margin-bottom: 0 }
  /* The mark break IS the divider down here, so the band must not draw a second one. */
  .home main > .band:first-of-type { border-top: none }

  .two .mock { margin-top: 22px }

  .top { padding: 13px 0 }
  .top .brand { font-size: 17px; gap: 9px }
  .top .brand .mark { width: 24px }
  .top nav { gap: 18px }

  /* Fixed, not sticky, so returning to the top costs no layout shift. */
  .home.nav-auto .topbar {
    position: fixed; top: 0; left: 0; right: 0;
    transform: translateY(-100%); opacity: 0;
    transition: transform .28s cubic-bezier(.45,0,.55,1), opacity .28s cubic-bezier(.45,0,.55,1),
                border-color .25s, box-shadow .25s;
  }
  .home.nav-auto.is-scrolled .topbar { transform: none; opacity: 1 }

  /* Ordered after the .is-scrolled rule it overrides — same specificity, later wins. */
  .home.nav-auto.focus-mode .topbar { transform: translateY(-100%); opacity: 0 }

  /* Gated on .nav-auto so with home.js absent the dock is simply always there.
     pointer-events, because an opacity-0 bar still takes taps. Focus is left alone: the
     focusin handler depends on a hidden dock being reachable by Tab. */
  .home.nav-auto .dock { transform: translateY(calc(100% + 24px + env(safe-area-inset-bottom, 0px))); opacity: 0; pointer-events: none }
  .home.nav-auto.show-dock .dock { transform: none; opacity: 1; pointer-events: auto }
  /* Ordered last, same specificity as .show-dock, so picking a lens still clears the dock. */
  .home.nav-auto.focus-mode .dock { transform: translateY(calc(100% + 24px + env(safe-area-inset-bottom, 0px))); opacity: 0; pointer-events: none }
  /* Last of the four, so it wins over .show-dock and focus-mode alike. */
  .home.nav-auto.sheet-open .dock { opacity: 0; pointer-events: none }
  /* viewport-fit=cover, so env() is a floor, not the spacing: the 30 is the air above. */
  .home.nav-auto .hero { padding-top: calc(30px + max(22px, env(safe-area-inset-top, 0px))) }

  /* em, not px, so a raised system text size grows the clearance with the bar. */
  .home .foot .row { padding-bottom: calc(6.375em + env(safe-area-inset-bottom, 0px)) }

  /* The card mocks keep their sizes: they read as app UI at app scale. After the 700px block. */
  body { font-size: 16px }
  .body { font-size: 15.5px }
  .lc p, .checks p, .note p { font-size: 14.5px }
  .note h3 { font-size: 16px }
  .lc .eg, .card .para { font-size: 14px }
  /* Scoped so the hero cards keep the 14px they were tuned for. */
  #edition .card .para { font-size: 14.5px }
  #edition .ed-frame > .card { row-gap: 14px; padding: 18px 16px }

  /* Child combinators throughout: the story card is itself .card.l-volume, so a descendant
     selector would blank its own pane. */
  .ed-js .ed-lead { display: block }
  .ed-js .ed-seg { display: grid }
  /* .sechead's 30px collapses over .ed-lead's 15, so it goes to zero, not overridden. */
  .ed-js #edition .sechead { margin-bottom: 0 }
  .ed-js #edition .ed-grid { margin-top: 14px }
  .ed-js #edition .ed-foot { margin-top: 14px }
  .ed-js #edition .ed-foot > div { padding-top: 14px }

  /* Safe only because .ed-foot holds no focusable descendant — the same invariant
     .feat-weave carries. If a link ever lands in the verdict, this rule has to go. */
  .ed-js #edition > .wrap { display: flex; flex-direction: column }
  .ed-js #edition > .wrap > .sechead { order: 1 }
  .ed-js #edition > .wrap > .ed-lead { order: 2 }
  .ed-js #edition > .wrap > .ed-seg  { order: 3 }
  .ed-js #edition > .wrap > .ed-foot { order: 4 }
  .ed-js #edition > .wrap > .ed-grid { order: 5 }

  /* INVARIANT: still a link, so still a tap target. "LBC" sets 21px, so the horizontal
     padding is load-bearing, not symmetry; the equal negative margin gives the space back. */
  .ed-js #edition .ed-item .ed-src {
    order: -1; align-self: flex-start;
    border: none; border-radius: 0; background: none;
    color: var(--c);
    padding: 7px 8px; margin: -7px -8px;
  }
  #edition .lab { font-size: 10.5px; letter-spacing: .2em }
  .ed-js #edition[data-pane="0"] > .wrap > .ed-grid > .ed-col-lumen,
  .ed-js #edition[data-pane="0"] > .wrap > .ed-foot > .l-records,
  .ed-js #edition[data-pane="1"] > .wrap > .ed-grid > .ed-col.l-volume,
  .ed-js #edition[data-pane="1"] > .wrap > .ed-foot > .l-volume { display: none }
  /* With one pane showing, the spine and dot join nothing. */
  .ed-js .ed-col-lumen::before, .ed-js .ed-col-lumen::after { content: none }
  /* Same clip as .vh, written out because .ed-colhd returns to normal at 940 and on paper. */
  .ed-js .ed-colhd { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    border: 0; clip-path: inset(50%); overflow: hidden; white-space: nowrap }
  .lede { font-size: 16px }
  .stage dd { font-size: 14.5px }
  .contact p:last-child { font-size: 15.5px }
  .rec-card dd { font-size: 14.5px }
  .contents a { font-size: 14.5px }

  /* .ftext is display:contents, so these margins stop collapsing. Let the gap space them. */
  .feat > .ftext > h2 { margin-top: 0 }

  /* Woven into the card run, the notes are annotations rather than list rows. */
  .feat-weave .note { border-bottom: none; padding: 2px 0 10px }

  /* The contents list belongs directly under the stamp, not behind 908px of summary cards. */
  .doc-head { display: flex; flex-direction: column }
  /* .doc p (0,1,1) beats .lede (0,1,0), and in a flex column that margin sums. */
  .doc-head > .lede, .doc-head > .updated { margin-bottom: 0 }
  .doc-head > .contents { order: 1 }
  .doc-head > .stage { order: 2 }
  /* .eyebrow is inline-flex; as a stretched flex item it would run the full column. */
  .doc-head > .eyebrow { align-self: flex-start }
}

/* The footer row is a correct space-between line from 540px up. */
@media (max-width: 539.98px) {
  .foot .row { flex-direction: column; align-items: center; gap: 18px; text-align: center }
}

/* home.js owns the exit: transitioning a dialog out of the top layer needs `overlay`, which
   iOS does not have, so a pure-CSS version would animate in and then vanish. */
@media (max-width: 599.98px) {
  dialog.soon {
    /* The UA caps dialog:modal at calc(100% - 2em - 6px); ours happened to equal that. */
    width: 100%; max-width: none; max-height: 88svh;
    margin: auto auto 0;
    border-radius: 26px 26px 0 0;
    overscroll-behavior: contain;
  }
  /* .46s == home.js's SHEET_MS. Change one, change the other. */
  .soon-js dialog.soon { transform: translateY(100%); transition: transform .46s cubic-bezier(.2,.7,.2,1) }
  .soon-js dialog.soon.up { transform: none }
  .soon-js dialog.soon::backdrop { opacity: 0; transition: opacity .46s ease }
  .soon-js dialog.soon.up::backdrop { opacity: 1 }
  dialog.soon .sheet-body { padding: 34px 22px calc(22px + env(safe-area-inset-bottom)) }
  dialog.soon .plats { grid-template-columns: 1fr }
  dialog.soon .plat { flex-direction: row; justify-content: space-between; padding: 14px 16px }
  /* The sweep is a 40% band timed for a ~170px tile; across a full-width row it crawls. */
  dialog.soon .plat.live::after {
    left: 0; right: 0; width: auto; transform: none;
    background-size: 45% 100%; background-repeat: no-repeat;
    animation: sweepwide 3.4s ease-in-out infinite;
  }
  dialog.soon .plat.queued::after { left: 0; right: 0; width: auto }
  /* .close-x is absolute inside a scrollable dialog, so it scrolls away when tall. */
  .close-x { display: none }
  .close-wide {
    display: block; width: 100%; margin: 10px 0 0;
    position: sticky; bottom: 0;
    background: var(--card); color: var(--ink2); border: none;
    min-height: 44px; cursor: pointer;
    font-family: var(--mono); font-size: 10.5px; font-weight: 700;
    letter-spacing: .14em; text-indent: .14em; text-transform: uppercase;
  }
  @media (hover: hover) { .close-wide:hover { color: var(--ink) } }
}

/* Against the content column, not the viewport, so they do not drift off a 2560px screen. */
.glowwrap { position: absolute; inset: 0; max-width: 1400px; margin: 0 auto; pointer-events: none }

/* ================================================================ print */

@media print {
  /* A background-image does not print, so border the card, which has the right radius. */
  .ed-frame { background-image: none; padding: 0 }
  .ed-frame > .card { border: 1px solid #999 }
  .ed-col-lumen::before, .ed-col-lumen::after { content: none }
  /* Paper is inside the phone query and these classes are on <body>. */
  .ed-lead, .ed-seg { display: none !important }
  .dots { display: none !important }
  #edition .ed-col { display: flex !important }
  #edition .ed-foot > div { display: block !important }
  #edition .ed-colhd { position: static; width: auto; height: auto; margin: 0;
    clip-path: none; overflow: visible }
  :root { --mint: #fff; --ink2: #333; --line: #ccc }
  body { background: #fff; color: #000; font-size: 11pt }
  .topbar, .dock, .foot, .glow, .glowwrap, .skip, dialog.soon, .cta, .contents { display: none !important }
  .doc-main { max-width: none; padding: 0 }
  .doc section.s { page-break-inside: auto; border-top: 1px solid #ccc }
  .doc h2, .doc h3 { page-break-after: avoid }
  .recipients table { display: table !important; width: 100% }
  .rec-card { display: none !important }
  .doc a { text-decoration: underline; color: #000 }
  .gt, .gtx { background-image: none; -webkit-text-fill-color: currentColor; color: #000 }
  .doc a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; word-break: break-all }
  .stage, .contact, .rec-card, .recipients table { box-shadow: none; border: 1px solid #ccc }
  /* Chrome prints borders but not shadows, so the bezel would land as a solid slab. */
  .shotstack, .frame { border-width: 1px; border-color: #999; box-shadow: none }
  .shotstack::before, .shotstack::after, .frame::before, .frame::after { display: none }
}

/* ================================================================ forced colors */

/* Forced-colors drops the background image and keeps the transparent fill, so a gradient
   phrase disappears — taking half an h2 with it. */
@media (forced-colors: active) {
  .ed-frame { background-image: none; padding: 0 }
  .ed-frame > .card { border: 1px solid CanvasText }
  .gt, .gtx { background-image: none; -webkit-text-fill-color: currentColor; color: CanvasText }
  .grad-rule, .cta { forced-color-adjust: none }
  .chip[aria-pressed="true"] { border-color: Highlight; box-shadow: 0 0 0 2px Highlight }
  .ed-tab[aria-pressed="true"] { outline: 2px solid Highlight; outline-offset: -2px }
  .kick i, .chip .d, .srcline .d, .divlab span::before { forced-color-adjust: none; background: CanvasText }
  .dots button { forced-color-adjust: none; background: CanvasText }
  /* Stops being drawn, but must NOT be removed: home.js advances on its animationend. */
  .dots button::after { opacity: 0 }
  dialog.soon .plat.live::after, dialog.soon .plat.queued::after { forced-color-adjust: none }
}

/* ================================================================ reduced motion */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .home .top .brand, .lockup { opacity: 1; transform: none }
  .home.is-scrolled .lockup { opacity: 1; transform: none }
  /* An animation with no fill reverts on end, so a single instant run is worse than none. */
  dialog.soon .plat.live::after { animation: none; left: 0; right: 0; width: auto; transform: none; background-size: 100% 100% }
  .ed-frame { animation: none; background-position: 0 0 }
  .dock .cta::after { animation: none; opacity: .44 }
  .chip.nudge { animation: none }
}
