/* desk.css — the front desk: the lobby, the doors, the pass, and the room.
   Straight off the eight approved artboards (AATAM/PRD Docs/listen-front-desk/design).
   Flat panels, one hairline mark per pattern, Manrope, midnight and dawn.

   ⛔ NO photographs, NO libraries, NO emoji, NO icon fonts, NO outside
   requests. Everything drawn here is a colour, a hairline, a gradient or an
   inline drawing this file or the sprite in the page carries.

   THE SURFACE LAW (his read, 2026-09-05: "I like that embossed look which is
   very premium — use that card feel everywhere"). Every card on the desk is
   the SAME surface, declared once as `--emboss`: a panel, an outer hairline, a
   light along the top edge, a shade along the bottom, and the card lying a
   millimetre off the page. Three layers, never more — a stack of six costs a
   repaint every time the board redraws. The atmospheres under it are gradients
   between named colours, never an image, and the only thing that moves is the
   room that is open right now. The whole system: DESIGN.md.

   app.css is a different stylesheet for /host and is not touched here.

   THE TWO THEMES
   Dawn is the default for the desk pages and follows the phone; the switch in
   the footer overrides it in both directions. The room page is always midnight
   (it ships with data-theme="dark" and never shows the switch), and the
   checkout sheet is always dawn over the dimmed room (.dawn re-points the
   tokens inside itself).
*/

/* ── the tokens ──────────────────────────────────────────────────────────── */
/*
   ⚠ TWO HEXES ARE NOT THE ARTBOARD'S, ON PURPOSE — measured, not guessed.
   The artboard's faint grey is #B5B5B5 in dawn and #5E655F in midnight, and
   both are used for whole sentences: "Put the phone down.", the running clock,
   "N with you", the refund line. Measured against the panels they sit on they
   come out at 2.3:1 and 3.2:1, where readable body text needs 4.5:1. So the
   faint token is darkened in dawn and lightened in midnight until it passes,
   and the hairline the unfilled rings are drawn with is taken to 3:1, which is
   the bar for something you are meant to be able to see the shape of.

     --ink-3  dawn  #5A615A on #E3DCCF = 4.7:1   (was #B5B5B5 = 2.2:1)
     --ink-3  night #AAAAAA on #111111 = 5.3:1   (was #5E655F = 3.2:1)
                    …and 4.8:1 on the night field --night-atmo #202020, which is
                    where the pass writes it. At the artboard's #AAAAAA that sum
                    was 4.49:1 — under the bar by a hair, on the one surface he
                    holds in his hand. Measured 2026-09-06, node design-review/contrast.mjs.
     --ink-2  dawn  #575E58 on #E3DCCF = 4.9:1   (was #5E655F = 4.2:1)
     --gold   dawn  #7E5A15 on #E3DCCF = 4.6:1   (was #B8862B = 2.4:1)
     --hair-2 dawn  #8B857B on #F6F0E8 = 3.2:1   (was #C9C1B4 = 1.6:1)
     --hair-2 night #737373 on #000000 = 3.4:1   (was #3A4744 = 2.0:1)

   The gold is the one that hurts: the breathing's mark is meant to glow, and a
   readable gold on paper is closer to bronze. It stays the artboard's #D9A441
   in midnight, which is where the breathing tile actually lives, and only the
   daylight value moves. Everything else is the artboard's own hex, unchanged.
   Flagged for his read.
*/
/* Every token is declared in all four places on purpose: bare :root (dawn),
   the phone's own dark preference, and both explicit switch positions. A token
   that only exists inside a media query is a token that vanishes the moment
   somebody taps the switch. */

/*
   THE SURFACE AND COLOUR TOKENS (2026-09-05) — the second half of the table.
   `--tint-*` are the three door families as SOLID colours, never an alpha, so
   test/pages-a11y.test.js can sum the contrast on the real ground a word sits
   on rather than guess what a transparency composites to.

   COLOUR MEANS FOUR THINGS HERE AND NOTHING ELSE:
     --live  open now, alive, filling      (the dot, the count, "Open now")
     --gold  the pass                      (the seal, the stamp, the breath)
     --warn  this one needs you            (missed, refused, the link is down)
     --tint  which family a door belongs to (atmosphere only, never a word)
   Gold used to carry the pass AND every warning, which is why a missed week
   and a stamp were one colour. --warn takes the warnings.
*/

:root {
  color-scheme: light;
  --ground:   #F6F0E8;
  --panel:    #ECE5DA;
  --panel-2:  #E3DCCF;
  --ink:      #000000;
  --ink-2:    #575E58;
  --ink-3:    #5A615A;
  --hair:     #E6DED3;
  --hair-2:   #8B857B;
  --live:     #2D6A4F;
  --gold:     #7E5A15;
  --warn:     #9B2C1F;
  --btn-bg:   #000000;
  --btn-ink:  #FBF6EF;
  --btn-h:    54px;
  --veil:     rgba(6,11,13,0.06);
  --card-edge:  #D8CFC0;
  --card-hi:    rgba(255,255,255,0.66);
  --card-lo:    rgba(6,11,13,0.05);
  --card-drop:  rgba(6,11,13,0.07);
  --tint:          #E3DCCF;
  --tint-people:   #EAD9C6;
  --tint-unknown:  #D8E0DF;
  --tint-breath:   #F2E2BF;
  --grain:      0.05;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ground:   #000000;
    --panel:    #111111;
    --panel-2:  #191919;
    --ink:      #FBF6EF;
    --ink-2:    #B5B5B5;
    --ink-3:    #AAAAAA;
    --hair:     #292929;
    --hair-2:   #737373;
    --live:     #D4D4D4;
    --gold:     #D9A441;
    --warn:     #F08C80;
    --btn-bg:   #FBF6EF;
    --btn-ink:  #000000;
    --btn-h:    58px;
    --veil:     rgba(251,246,239,0.08);
    --card-edge:  #303030;
    --card-hi:    rgba(251,246,239,0.07);
    --card-lo:    rgba(0,0,0,0.40);
    --card-drop:  rgba(0,0,0,0.45);
    --tint:          #191919;
    --tint-people:   #1B1B1B;
    --tint-unknown:  #1C1C1C;
    --tint-breath:   #202020;
    --grain:      0.07;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --ground:   #000000;
  --panel:    #111111;
  --panel-2:  #191919;
  --ink:      #FBF6EF;
  --ink-2:    #B5B5B5;
  --ink-3:    #AAAAAA;
  --hair:     #292929;
  --hair-2:   #737373;
  --live:     #D4D4D4;
  --gold:     #D9A441;
  --warn:     #F08C80;
  --btn-bg:   #FBF6EF;
  --btn-ink:  #000000;
  --btn-h:    58px;
  --veil:     rgba(251,246,239,0.08);
  --card-edge:  #303030;
  --card-hi:    rgba(251,246,239,0.07);
  --card-lo:    rgba(0,0,0,0.40);
  --card-drop:  rgba(0,0,0,0.45);
  --tint:          #191919;
  --tint-people:   #1B1B1B;
  --tint-unknown:  #1C1C1C;
  --tint-breath:   #202020;
  --grain:      0.07;
}

:root[data-theme="light"] {
  color-scheme: light;
  --ground:   #F6F0E8;
  --panel:    #ECE5DA;
  --panel-2:  #E3DCCF;
  --ink:      #000000;
  --ink-2:    #575E58;
  --ink-3:    #5A615A;
  --hair:     #E6DED3;
  --hair-2:   #8B857B;
  --live:     #2D6A4F;
  --gold:     #7E5A15;
  --warn:     #9B2C1F;
  --btn-bg:   #000000;
  --btn-ink:  #FBF6EF;
  --btn-h:    54px;
  --veil:     rgba(6,11,13,0.06);
  --card-edge:  #D8CFC0;
  --card-hi:    rgba(255,255,255,0.66);
  --card-lo:    rgba(6,11,13,0.05);
  --card-drop:  rgba(6,11,13,0.07);
  --tint:          #E3DCCF;
  --tint-people:   #EAD9C6;
  --tint-unknown:  #D8E0DF;
  --tint-breath:   #F2E2BF;
  --grain:      0.05;
}

/* The hero tile, the pass card's head and the checkout's mark tile are dark in
   BOTH themes — that is what the artboards draw. These four never move. */
:root, :root[data-theme] {
  --night:       #000000;
  --night-panel: #111111;
  --night-ink:   #FBF6EF;
  --night-ink-2: #B5B5B5;
  --night-live:  #D4D4D4;
  /* The field inside a night surface: the quiet glow the mark stands in on the
     hero, the pass and the room's gate. One colour, one step off the panel. */
  --night-atmo:  #202020;
  /* AATAM's own green, off public/img/logo-mark.svg. It is the emblem's colour
     and nothing else's: it is an identity, not a state, and the desk already
     has four colours that mean things (DESIGN.md). 7.4:1 on the pass panel. */
  --brand:       #52B788;
  /* Which of the two the triangle is drawn in. The card is midnight in both
     themes, so this is one value, not a pair. */
  --emblem:      var(--night-ink);
}

/* ── the one scale, the one easing, the one card (2026-09-05) ────────────── */
/* Radii off one scale, so two rounded things on a screen never disagree by two
   pixels. One easing, two durations. One shadow table: `--emboss` a card at
   rest · `--lift` a thing raised off the page · `--emboss-lift` a card picked
   up · `--emboss-night` the same card cut from the dark stock (the hero, the
   pass, the checkout's mark tile), which the artboards draw dark in BOTH
   themes. */
:root, :root[data-theme] {
  --r-xl:   24px;
  --r-lg:   20px;
  --r-md:   14px;
  --r-sm:   10px;
  --r-xs:   4px;
  --r-pill: 999px;
  --ease:  cubic-bezier(0.2, 0.7, 0.2, 1);
  /* THE ROOM'S TOP BAR, said once (2026-09-05). The ? and the Leave button are
     the only two fixed things inside a sitting, and they were on two different
     rows: the ? padded for the notch and Leave did not, so on any phone with an
     inset they sat forty-seven pixels apart and the cross landed over the
     piece's own title. One offset, one height, and the stage keeps that band
     clear on every screen that shows them. */
  --roombar-top: calc(16px + env(safe-area-inset-top));
  --roombar-h: 44px;
  --dur-1: 0.18s;
  --dur-2: 0.3s;
  --emboss:
    inset 0 1px 0 var(--card-hi),
    inset 0 -1px 0 var(--card-lo),
    0 1px 2px var(--card-drop);
  --lift: 0 6px 16px var(--card-drop);
  --emboss-lift:
    inset 0 1px 0 var(--card-hi),
    inset 0 -1px 0 var(--card-lo),
    var(--lift);
  --emboss-night:
    inset 0 1px 0 rgba(251,246,239,0.09),
    inset 0 -1px 0 rgba(0,0,0,0.5),
    0 2px 6px rgba(6,11,13,0.28);
}

/* A patch of dawn inside a midnight page: the checkout sheet. */
.dawn {
  color-scheme: light;
  --ground:   #F6F0E8;
  --panel:    #ECE5DA;
  --panel-2:  #E3DCCF;
  --ink:      #000000;
  --ink-2:    #575E58;
  --ink-3:    #5A615A;
  --hair:     #E6DED3;
  --hair-2:   #8B857B;
  --live:     #2D6A4F;
  --gold:     #7E5A15;
  --warn:     #9B2C1F;
  --btn-bg:   #000000;
  --btn-ink:  #FBF6EF;
  --btn-h:    54px;
  --veil:     rgba(6,11,13,0.06);
  --card-edge:  #D8CFC0;
  --card-hi:    rgba(255,255,255,0.66);
  --card-lo:    rgba(6,11,13,0.05);
  --card-drop:  rgba(6,11,13,0.07);
  --tint:          #E3DCCF;
  --tint-people:   #EAD9C6;
  --tint-unknown:  #D8E0DF;
  --tint-breath:   #F2E2BF;
  --grain:      0.05;
  color: var(--ink);
}

/* A patch of midnight inside a dawn page: the pass, which is the same deep
   object in both themes (his read, 2026-09-05 — the reference he sent is the
   lobby's dark hero tile: "this is the main card, the pass"). Everything
   inside it asks for --ink, --hair, --veil as usual and gets the midnight
   answer, which is how the input, the tick and the seal stay right without a
   rule each. `.freepass` is spelt into the selector rather than into the
   markup: the card's class attribute is a template three tests pin. */
.night, .freepass {
  color-scheme: dark;
  --ground:   #000000;
  --panel:    #111111;
  --panel-2:  #191919;
  --ink:      #FBF6EF;
  --ink-2:    #B5B5B5;
  --ink-3:    #AAAAAA;
  --hair:     #292929;
  --hair-2:   #737373;
  --live:     #D4D4D4;
  --gold:     #D9A441;
  --warn:     #F08C80;
  --btn-bg:   #FBF6EF;
  --btn-ink:  #000000;
  --btn-h:    58px;
  --veil:     rgba(251,246,239,0.08);
  --card-edge:  #303030;
  --card-hi:    rgba(251,246,239,0.07);
  --card-lo:    rgba(0,0,0,0.40);
  --card-drop:  rgba(0,0,0,0.45);
  --tint:          #191919;
  --tint-people:   #1B1B1B;
  --tint-unknown:  #1C1C1C;
  --tint-breath:   #202020;
  --grain:      0.07;
  color: var(--ink);
}

/* ── the type, served from here ──────────────────────────────────────────── */
/*
   Manrope, the variable face, self-hosted since 2026-09-04. The room page used
   to pull it from Google Fonts — a render-blocking stylesheet from a second
   origin, then the file from a third — before it could draw a word. Now the
   two subsets a Dutch and an English name can need sit under /fonts/, the
   licence beside them (public/fonts/LICENSE, SIL OFL 1.1), and `font-display:
   swap` means the words are on screen in the system face at once and change
   over when the woff2 lands. Files from @fontsource-variable/manrope 5.3.0
   (Manrope v20). Only the room page has dropped the Google link so far; the
   desk pages still carry theirs, and the two declarations do not fight — same
   family, same weight axis, and whichever arrives serves the same glyphs.
*/
@font-face {
  font-family: Manrope;
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(/fonts/manrope-latin-wght-normal.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Manrope;
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(/fonts/manrope-latin-ext-wght-normal.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── the ground ──────────────────────────────────────────────────────────── */

* { box-sizing: border-box; }

html { background: var(--ground); }
html, body { margin: 0; padding: 0; min-height: 100%; }

body {
  background: var(--ground);
  color: var(--ink);
  font-family: Manrope, -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', system-ui, sans-serif;
  /* 16px, not 14. Material 3 sets body-large at 16 sp and Apple's HIG sets
     body at 17 pt; 16 is the floor they agree on, and it is the size every
     unclassed word on the desk inherits. His read, 2026-09-06: *"very hard to
     read … cognitively readable and industry standard."* */
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
}

/* Several things here are display:flex, which beats the UA stylesheet and
   leaves a "hidden" element on screen. The JS uses el.hidden throughout. */
[hidden] { display: none !important; }

button, input, textarea, select { font: inherit; color: inherit; letter-spacing: inherit; }
button { cursor: pointer; background: none; border: 0; padding: 0; color: inherit; }
a { color: inherit; text-decoration: none; }
:focus-visible { outline: 2px solid var(--live); outline-offset: 3px; border-radius: var(--r-xs); }
svg { display: block; }

/* ── the column ──────────────────────────────────────────────────────────── */
/* 390 by design, fluid to 430, and on a desktop the same column centred on the
   page ground. Nothing ever gets wider than a phone. */

.page {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
  padding: 28px 24px calc(28px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-height: 100vh;
  min-height: 100dvh;
}

/* ── type ────────────────────────────────────────────────────────────────── */

.eyebrow {
  margin: 0;
  font-size: 12px; font-weight: 600; line-height: 1.35;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-2);
}
.h1     { margin: 0; font-size: 30px; font-weight: 600; line-height: 1.1;  letter-spacing: -0.02em; }
.h2     { margin: 0; font-size: 24px; font-weight: 600; line-height: 1.15; letter-spacing: -0.015em; }
.h3     { margin: 0; font-size: 18px; font-weight: 600; line-height: 1.3;  letter-spacing: -0.01em; }
.body   { margin: 0; font-size: 16px; line-height: 1.5;  color: var(--ink-2); }
/* ⛔ FAINT IS A WEIGHT AND A SIZE, NEVER LESS CONTRAST (2026-09-06). It carries
   whole sentences — "Put the phone down.", the running clock, the schedule's
   own answer — so it reads at 14px and at the same 4.5:1 as everything else.
   The quiet comes from being one step smaller and one ink further back. */
.faint  { margin: 0; font-size: 14px; line-height: 1.5;  color: var(--ink-3); }
.wordmark { font-size: 13px; font-weight: 700; letter-spacing: 0.22em; }

/* ── the pieces every page is built from ─────────────────────────────────── */

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px; border-radius: var(--r-pill);
  font-size: 13px; font-weight: 600; letter-spacing: 0.02em;
  background: var(--veil); color: var(--ink);
}
.pill.solid { background: var(--ink); color: var(--ground); }
/* Alive. The one place a green is allowed to be a shape rather than a word,
   and it is the thing the eye should find first on the lobby. */
.dot-live { width: 6px; height: 6px; border-radius: 50%; background: var(--live); flex-shrink: 0; }

/* ── THE CARD: one surface, every card on the desk ───────────────────────── */
/* There is ONE card here and it is named once. Anything that is a card joins
   this list; nothing declares its own radius, edge or shadow again. Real-card
   cues, restrained: a hairline round the outside, a light along the top edge,
   a shade along the bottom, and the card lying a millimetre off the page. */
.card,
.tile,
.freepass,
.passcard,
.endcard,
.passblock,
.deleteask,
.pkcard,
.formcard,
.offercard,
.flagchip,
.sit,
.shopcard,
.shopdoor,
.dep.full,
.dep.mine,
.appcard,
.ladder,
.mecard,
.passempty {
  position: relative;
  /* Its own stacking context, so the atmosphere below can sit at -1: above the
     card's ground, under every word on it, without a z-index on the words. */
  isolation: isolate;
  border-radius: var(--r-lg);
  background: var(--panel);
  border: 1px solid var(--card-edge);
  box-shadow: var(--emboss);
}
/* Picked up — held under a thumb, or the one that is chosen. */
.card.held, .tile.held, .dep.mine {
  box-shadow: var(--emboss-lift);
}
/* Cut from the dark stock, in both themes: the hero, the pass card, the head
   of the six-week card, the checkout's mark tile. */
.hero, .freepass, .passcard .head, .marktile {
  box-shadow: var(--emboss-night);
}

/* THE ATMOSPHERE. A soft field behind the mark, made of two named colours and
   nothing else — no image, no request, no library. `--tint` says which family
   this surface belongs to and is set by the mark the pattern already carries
   (marks.js is the one source; the door writes `data-mark` and the stylesheet
   reads it). Under 200 bytes each, and none of them moves. */
/* Five surfaces carry one, and no more: the lobby's tiles, a door's head, the
   till's offer — plus the pass and the room's gate, which have their own below.
   A form does not; a page a person landed on because something went wrong
   wants to be legible, not atmospheric. */
.tile::before,
.doorhead::before,
.offercard::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: radial-gradient(120% 86% at 50% 8%, var(--tint), transparent 76%);
  pointer-events: none;
}
[data-mark="group"], [data-mark="gap"], [data-mark="alone"] { --tint: var(--tint-people); }
[data-mark="rings"]     { --tint: var(--tint-unknown); }
[data-mark="breathing"] { --tint: var(--tint-breath); }

.tile { overflow: hidden; }
.tile-sm { border-radius: var(--r-md); }
.markbox { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.markbox svg { color: inherit; }

/* ── the icons: one drawing per glyph, per page ──────────────────────────── */
/* A sprite of <symbol>s sits at the top of every page's <body> and every icon
   on that page is one <use> of it, so a glyph is drawn once and every page
   draws the same one. It is INLINE and not a file: <use href="a-file.svg#id">
   has never worked in Safari, and `script-src 'self'` rules out an import map
   or a runtime that would paper over it. Stroke 1.4, like the pass glyph and
   the chevrons it replaces. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.i {
  width: 22px; height: 22px; flex-shrink: 0;
  fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.i-sm { width: 16px; height: 16px; }
.i-lg { width: 24px; height: 24px; }
.i-solid { fill: currentColor; stroke: none; }

.rule { height: 1px; background: var(--hair); border: 0; margin: 0; }
.band {
  padding: 16px 0;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}

/* The one primary action. Full pill, 54/58px, never two on a screen.
   `.big` is the room page's own name for the same button; both pages have
   always called it that and existing tests read the markup. */
.btn, .big {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; min-height: var(--btn-h);
  border-radius: var(--r-pill);
  background: var(--btn-bg); color: var(--btn-ink);
  font-size: 16px; font-weight: 700; letter-spacing: -0.01em;
  text-align: center; padding: 0 20px;
  box-shadow: var(--lift);
  transition: transform var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.btn:disabled, .big:disabled { opacity: 0.35; cursor: default; }
/* A button that is reachable but not yet usable. It looks the same as a
   disabled one and behaves like a real one for a keyboard — see Y-08. */
.btn[aria-disabled="true"], .big[aria-disabled="true"], .big.waiting { opacity: 0.35; }
.btn:active:not(:disabled), .big:active:not(:disabled) { transform: scale(0.99); box-shadow: none; }
.btn-outline, .big.ghost {
  background: transparent; color: var(--ink);
  border: 1px solid var(--ink);
  box-shadow: none;
}
/* A secondary action is a text link, never a second button. */
.textlink {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 8px;
  font-size: 15px; font-weight: 600; color: var(--ink-2);
}
.textlink:hover { color: var(--ink); }
.live-ink { color: var(--live); }
.gold-ink { color: var(--gold); }

/* A hairline-underline field, which is every field on every screen. */
.field {
  width: 100%; min-height: 52px;
  background: transparent; border: 0; border-bottom: 1px solid var(--hair);
  border-radius: 0; padding: 0; color: var(--ink);
  font-size: 16px;
  appearance: none;
}
/* ⛔ THE LABEL IS NOT THE PLACEHOLDER (2026-09-06, WCAG 3.3.2). A placeholder
   is gone at the first keystroke, which is exactly when somebody looks away —
   and an autofill that drops a name into the email box then looks correct.
   Every field a person types into carries one of these, above it, always on. */
.fieldlabel {
  display: block; margin-bottom: 2px;
  font-size: 13px; font-weight: 600; color: var(--ink-2);
}
.field::placeholder { color: var(--ink-3); }
.field:focus { outline: none; border-bottom-color: var(--ink); }
/* The label takes a line of its own inside the row, so hiding the row hides
   the label with it and nothing is left labelling a field that is not there. */
.field-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; border-bottom: 1px solid var(--hair); }
.field-row .fieldlabel { flex-basis: 100%; margin: 0 0 -2px; }
.field-row .field { border-bottom: 0; }
.field-note { font-size: 13px; color: var(--ink-3); white-space: nowrap; }

/* Six rings: the arc, on the door, the ending and the pass. */
.rings { display: flex; justify-content: space-between; gap: 8px; }
.rings.tight { justify-content: flex-start; }
.ring {
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--ink); box-sizing: border-box; flex-shrink: 0;
}
.ring.sat    { background: var(--ink); }
.ring.future { border-color: var(--hair-2); }
.ring.now    { box-shadow: 0 0 0 3px var(--ground), 0 0 0 4px var(--ink); }
.ring-step { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.ring-step .n { font-size: 13px; color: var(--ink-3); }
.ring-step .n.on { color: var(--ink); }
.ring-sm { width: 28px; height: 28px; }
.ring-xs { width: 22px; height: 22px; }

.stack   { display: flex; flex-direction: column; }
.stack-4 { display: flex; flex-direction: column; gap: 4px; }
.stack-8 { display: flex; flex-direction: column; gap: 8px; }
.stack-12{ display: flex; flex-direction: column; gap: 12px; }
.stack-16{ display: flex; flex-direction: column; gap: 16px; }
.stack-24{ display: flex; flex-direction: column; gap: 24px; }
.spread  { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
/* A link on its own line is a target, whatever the type size says. */
.spread.faint a { display: inline-flex; align-items: center; min-height: 44px; }
.centre  { text-align: center; }
.grow    { flex: 1 1 auto; min-width: 0; }
.mark    { display: block; }
.push    { margin-top: auto; }

/* The message a page shows when something is not there. One plain sentence. */
.notice { margin: 0; font-size: 16px; color: var(--ink-2); }
.notice.bad { color: var(--ink); }

/* ── the head and the foot every desk page carries ───────────────────────── */
/* ONE frame on every page (2026-09-05, NAVIGATION.md). The head is three
   cells that never move: the way back on the left, only where the page has a
   parent · the wordmark in the middle, a link home everywhere but on the lobby
   · the pass on the right, a link everywhere but on the pass itself, where it
   is drawn quiet. Fixed columns, so an empty cell leaves the other two exactly
   where they were on the last page. Every cell is a 44px target. */

.deskhead {
  display: grid; grid-template-columns: 44px 1fr 44px; align-items: center;
  min-height: 44px; margin: -8px -12px 0;
  /* The ground the live dot is pinned to, and the only reason it is here: the
     dot is taken OUT of this grid (see the corner rule below), so it cannot
     move a cell whatever word it is showing. */
  position: relative;
}
.deskhead .backlink { grid-column: 1; margin: 0; }
.deskhead .wordmark {
  grid-column: 2; justify-self: center;
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 8px;
}
.deskhead .iconbtn { grid-column: 3; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; margin: 0; }
/* The page you are on: the same glyph, quiet, and not a link. */
.deskhead .iconbtn.on { color: var(--ink-3); }
.deskfooter {
  margin-top: auto; padding-top: 16px;
  display: flex; align-items: center; justify-content: center; gap: 18px; flex-wrap: wrap;
  font-size: 13px; color: var(--ink-3);
}
.deskfooter a, .deskfooter button {
  color: var(--ink-3); font-size: 13px;
  min-height: 44px; min-width: 44px; padding: 0 8px;
  display: inline-flex; align-items: center; justify-content: center;
}
.deskfooter a:hover, .deskfooter button:hover { color: var(--ink); }
/* The page you are on, in the foot: said in ink, still a link that goes nowhere new. */
.deskfooter a[aria-current="page"] { color: var(--ink); }

.backlink { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; margin: -10px 0 0 -12px; }

/* The room's own head: the same two cells, above the door and only there.
   Once the pass is in — loading, ready, playing — the head goes, and the way
   out is the one deliberate button with its "Leave?" sheet. A tap on the
   wordmark can never stop the sound, because it is not on the screen while
   there is sound. The room ships with the notch in mind.

   ⛔ AND IT COMES BACK AT THE END (2026-09-05). The way out used to be the one
   screen in the studio with no way anywhere: no studio, no pass, no schedule —
   only whichever card the sitting had earned. There is no sound left to stop
   by then, so the two cells return the moment the piece is over, and the frame
   is unbroken from the first page to the last. The rating is not lost by it:
   it is written on the phone (rating-owed.js) and asked again on the way back.

   ⛔ AND ON THE REFUSAL TOO (2026-09-06, `NEXT.md` F7). #screen-error is the
   one screen that says the sound could not be reached — so there is no sound
   to stop, and there was no way anywhere either: no studio, no pass, no help,
   only a Try again that had just failed. A dead end, in the one place §0
   rule 6 may not be broken. The head comes back here for the same reason it
   comes back at the end. */
.roomhead { margin: 0 -12px 8px; padding-top: env(safe-area-inset-top); }
.stage:has(.screen.on:not(#screen-locked):not(#screen-join):not(#screen-ended):not(#screen-error)) .roomhead { display: none; }

/* ── /me with nothing on the phone (2026-09-05) ──────────────────────────── */
/* The blank card, one line, and the two ways forward — the first a button, the
   second a quiet link, never two buttons on one screen. */
.passempty { align-items: center; }
.passempty .textlink { color: var(--ink); }
/* The blank card here has no input to stand in for the name (the gate's
   does), so the name line draws its hairline where the name would go, and
   the foot — no stamps, no issue date — stays off rather than an empty row. */
.freepass-me[data-state="blank"] .freepass-name { display: block; }
.freepass-me[data-state="blank"] .freepass-foot { display: none; }

/* ── S1 · the lobby ──────────────────────────────────────────────────────── */

/* THE HERO IS THE REFERENCE OBJECT (the tile he sent, 2026-09-05): a deep
   surface, the pill at the top, the mark centred in a quiet field, the eyebrow
   and the title at the foot, the play circle in the corner. Everything else on
   the desk is drawn to match it, so this rule stays short — the surface, the
   emboss and the atmosphere all come from the card and tile rules above. */
.hero {
  height: 400px;
  background: var(--night-panel);
  color: var(--night-ink);
  border-color: #303030;
  --tint: var(--night-atmo);
  display: block; width: 100%; text-align: left;
}
/* THE ONE THING THAT MOVES. A room that is open right now breathes — the
   field behind the mark, nine seconds in and out, nothing else on the desk.
   The dot is already the truth of "open now", so the stylesheet asks it
   rather than being told a second time. */
.hero:has(#hero-dot:not([hidden]))::before {
  animation: roombreathe 9s var(--ease) infinite;
}
@keyframes roombreathe {
  0%, 100% { transform: scale(1);    opacity: 0.80; }
  50%      { transform: scale(1.07); opacity: 1; }
}
.hero .markbox { align-items: flex-start; padding-top: 70px; color: var(--night-ink); }
.hero-pill { position: absolute; left: 20px; top: 20px; }
.hero-pill .pill { background: rgba(251,246,239,0.09); color: var(--night-ink); }
.hero-pill .dot-live { background: var(--night-live); }
.hero-foot {
  position: absolute; left: 20px; right: 20px; bottom: 22px;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 16px;
}
.hero-foot .eyebrow, .hero-foot .body { color: var(--night-ink-2); }
.hero-foot .h2 { color: var(--night-ink); max-width: 18ch; font-size: clamp(24px, 3vw, 36px); }
.playcircle {
  width: 64px; height: 64px; border-radius: 50%; flex-shrink: 0;
  background: var(--night-ink); color: var(--night);
  display: flex; align-items: center; justify-content: center;
}

.nextline { display: flex; flex-direction: column; gap: 8px; }
.nextline .spread { font-size: 13px; color: var(--ink-2); }
.progress { height: 1px; background: var(--hair); }
.progress > i { display: block; height: 1px; background: var(--ink); }

/* The free breath: always the gold family, so its tile is warm wherever it
   sits. It is the one room anybody can walk into, every hour, for nothing. */
.breathe { height: 104px; display: flex; align-items: center; width: 100%; text-align: left; --tint: var(--tint-breath); }
.breathe .markleft { position: absolute; left: 22px; top: 0; bottom: 0; display: flex; align-items: center; color: var(--gold); }
.breathe .inner {
  position: relative; display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: 0 20px 0 106px;
}

.doors { display: flex; gap: 16px; scroll-snap-type: x proximity; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.doors::-webkit-scrollbar { display: none; }
.door {
  display: flex; flex-direction: column; gap: 12px; width: 152px; flex-shrink: 0; text-align: left; scroll-snap-align: start;
}
.door .tile { height: 176px; color: var(--ink); }
.door .name { font-size: 14px; line-height: 1.35; }

/* ── S1 · the departures board ───────────────────────────────────────────── */
/* Every piece that is screening, next hour first, in ONE list — the airport
   board. His ask, twice: *"if a timezone is overlapping any session from any
   timezone it is joining it should be shown on the list… that's the only way to
   crowd people together."*

   ⛔ A ROW WITH PEOPLE IN IT MUST NOT LOOK LIKE AN EMPTY ONE. That difference is
   the entire argument for the section — a person who cannot see a room filling
   has no reason to wait for it — so a full row is a filled panel with the live
   colour on its count, and an empty one is a hairline and nothing else.

   No new look: a ground, a panel one step off it, a hairline and type. The
   `mine` row is marked by the panel and the word, never by a shadow or a
   colour that means something else. */

.board { display: flex; flex-direction: column; }

.dep {
  display: flex; align-items: center; gap: 12px;
  min-height: 56px; padding: 9px 12px;
  border-bottom: 1px solid var(--hair);
  width: 100%; text-align: left;
}
.dep:last-child { border-bottom: 0; }

/* Tabular figures, because a column of times that shuffles sideways as the
   digits change is the one thing a timetable may not do. */
.dep-time { display: flex; flex-direction: column; gap: 1px; width: 56px; flex-shrink: 0; }
.dep-time .t {
  font-size: 19px; font-weight: 600; letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.dep-time .d { font-size: 13px; line-height: 1.25; color: var(--ink-3); }

.dep-what { display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; min-width: 0; }
/* ⛔ THE ROW'S TITLE IS THE MOST-SCANNED OBJECT IN THE STUDIO, and it was the
   smallest thing on the board: 14px, cut mid-phrase at about 24 characters —
   "The meeting where yo…" on every row of the lobby AND of the schedule, which
   is the row's whole job unread (the critic's read of JOURNEYS-V2, #8). It is
   17px now, and it WRAPS TO TWO LINES rather than truncating: the row is 56px
   with room for it, and a moment named in four words is the thing a person is
   choosing between. The line under it still truncates — it is a detail, and a
   detail may be cut.
   `overflow-wrap: anywhere` is the third line of it: a moment title is a phrase
   and phrases break at spaces, but one long unbroken word — a URL somebody
   pasted into a title, a German compound — would otherwise push the row off a
   360px screen instead of wrapping inside it. */
.dep-what .n {
  font-size: 17px; font-weight: 600; line-height: 1.25;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; white-space: normal; overflow-wrap: anywhere;
}
.dep-what .s { font-size: 13px; line-height: 1.25; color: var(--ink-3); }
.dep-what .s { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* THE ONE LINE EVERY ITEM CARRIES (2026-09-06, entitle.js): what this row says
   to THIS pass — *Included in your pass* · *Add to your pass · six weeks* ·
   *On your pass · until 15 Oct*. Under the title at the meta layer's 13 px
   floor, in the second ink so it reads as a fact about the pass and not as a
   badge; never a colour, never a lock, never a grey row. `free` stays the meta
   line's own small mark and this line is not drawn for it. */
.dep-what .e { font-size: 13px; line-height: 1.25; color: var(--ink-2); }
/* The same line said ONCE on a schedule block, above the hours it names. */
.dayp-ent { margin: 0; font-size: 13px; line-height: 1.35; color: var(--ink-2); }
/* And on its own — under the hero's meta, under a door's pass line, on the
   till's summary: 16 / 1.5, the body's own scale, in the second ink. */
.entline { margin: 0; font-size: 16px; line-height: 1.5; color: var(--ink-2); }
.dep-what .tag {
  align-self: flex-start;
  font-size: 12px; font-weight: 700; line-height: 1.4;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink);
}

.dep-who {
  display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  flex-shrink: 0; text-align: right;
}
.dep-who .c { font-size: 13px; font-weight: 600; line-height: 1.25; color: var(--ink-3); }
.dep-who .w { font-size: 13px; line-height: 1.25; color: var(--ink-3); }
/* HOW MANY OF THEM ARE FROM HERE (2026-09-06, his ask: *"top rooms by country
   — whichever country they connect from"*). A whisper under the wait, at the
   scale the rest of the row's right-hand column reads at, so a flag glyph that
   fills its em cannot make the row taller than the words beside it — the same
   measurement the pass's meta line makes. It is drawn only where the number is
   real: never "0 from 🇳🇱". */
.dep-who .f { font-size: 13px; line-height: 1.25; color: var(--ink-2); }
/* ⛔ AND IT DECLARES NO SURFACE OF ITS OWN. A row can only carry this line when
   the server was willing to say how many people are in that room — the split is
   left off every row below the threshold — so a row with this line is always
   already `.full`, and `.full` is already the card. `here` is a state in the
   class list for the stylesheet and the tests to read, not a second card. */
.dep.here .dep-who .f { color: var(--ink); font-weight: 600; }

/* A row that carries something — a crowd, or their own name on it — is a panel
   rather than a hairline. That is the whole visual argument: a filled row and an
   unfilled one cannot be mistaken for each other at a glance. */
/* A row that carries something is a CARD — the same card as everything else
   on the desk — and an empty one stays a hairline. That is the whole visual
   argument, and now it is the product's own surface making it. */
.dep.full, .dep.mine { border-radius: var(--r-md); margin: 2px 0; }
/* People are already in there. */
.dep.full { background: var(--panel); }
.dep.full .dep-who .c { color: var(--live); }
/* It is going right now — walk in. */
.dep.now .dep-who .w { color: var(--live); font-weight: 600; }
/* Theirs. Last, so it wins the ground on a row that is also full. */
.dep.mine { background: var(--panel-2); }

/* ── S2 · a pattern's door ───────────────────────────────────────────────── */

.doorpage { padding: 0; gap: 0; }
/* The door's head is full-bleed, so it is not a card — but it is a surface,
   and it carries its family's atmosphere. `isolation` is what lets that sit
   under the words: without it the -1 layer goes behind the panel itself. */
.doorhead {
  position: relative; isolation: isolate; min-height: 470px; background: var(--panel);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 20px 24px 24px;
}
.doorhead .bigmark { position: absolute; left: 0; right: 0; top: 118px; display: flex; justify-content: center; color: var(--ink); min-height: 120px; }
/* S-02: the panel holds its shape while the answer is on its way. Empty nodes
   collapse to nothing and everything below them jumps when the words land —
   which on a door is the moment somebody is deciding whether to walk in. The
   heights are the drawn ones, so nothing moves when the text arrives. */
#door-eyebrow { min-height: 14px; }
#door-title { min-height: 99px; }
.doorhead .pillrow { min-height: 30px; }
#door-cta-text { min-height: 19px; }
#door-price, #door-arc-line { min-height: 19px; }
.doorhead:empty, #door-title:empty::after { content: ''; }
.doorbody { display: flex; flex-direction: column; gap: 24px; padding: 24px 24px calc(28px + env(safe-area-inset-bottom)); }
.pillrow { display: flex; gap: 8px; flex-wrap: wrap; }

/* ── the room page: the stage and its screens ────────────────────────────── */
/* The room is always midnight. These rules are what app.css used to give this
   page; nothing here reaches /host. */

.stage {
  position: relative; z-index: 1;
  width: 100%; max-width: 430px; margin: 0 auto;
  min-height: 100vh; min-height: 100dvh;
  padding: 24px 24px calc(28px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column;
}
.screen { display: none; flex: 1 1 auto; flex-direction: column; }
.screen.on { display: flex; }

.brandline { display: flex; align-items: center; gap: 7px; margin: 0 0 auto; color: var(--ink); font-weight: 700; font-size: 13px; letter-spacing: 0.22em; }

/* 0 · the locked door — and, before the door answers, the stage */
#screen-locked { justify-content: center; align-items: center; text-align: center; gap: 16px; }
/* The ring the first byte draws: the gate ring's vocabulary at a quarter of
   the size, turning slowly so a phone can tell "working" from "stuck". The
   inline <style> in room.html carries the same rule for the frames before
   this file arrives; keep the two in step. */
.stagering { position: relative; width: 56px; height: 56px; color: var(--ink); }
.stagering svg { position: absolute; inset: 0; animation: stageturn 6s linear infinite; }
.stageline { margin: 0; font-size: 15px; color: var(--ink-2); }
@keyframes stageturn { to { transform: rotate(360deg); } }
#screen-locked .lead { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.3; letter-spacing: -0.015em; max-width: 300px; }
#locked-next { font-size: 15px; font-weight: 600; color: var(--ink); text-decoration: underline; text-underline-offset: 4px; min-height: 44px; display: inline-flex; align-items: center; }

/* 1 · the gate — the card is the form (2026-09-05) */
/* One object on the screen: the pass. The ring is the sitting's clock above
   it, smaller than it was; the button and one quiet line sit under it; the
   reminder tick is the last thing on the screen. Nothing else stands here. */
/* THE WAY IN IS ABOVE THE FOLD, and these two numbers are why (2026-09-06).
   On a 360x780 phone nine minutes early, "I'm in" used to sit ENTIRELY below
   the fold: a countdown, six flag chips, a wallet row and half a card, and no
   way in. The order changed first (ring -> the card -> the button, everything
   else under it); these are the last two rows of the arithmetic — a tighter
   rhythm between the gate's four blocks, and the ring's own box down to the
   drawing it holds. Measured with `node design-review/shoot-readable.mjs`,
   which prints where the button starts on every run. */
/* ⛔ THE WAY IN ENDS ABOVE 640 px ON EVERY PHONE DOWN TO 320 × 568 (the
   critic's verdict, 2026-09-06; JOURNEYS-V2 §3.8). Measured, not guessed:
   `node design-review/measure-gate.mjs` opens the gate on a fresh phone — no
   card, no name, the card at its tallest — and exits 1 if #join's bottom edge
   is below the line before anybody scrolls. The numbers below are what made
   it true: the ring 184 → 136, the count 40 → 34, and the card's own rhythm
   tightened by two to eight pixels a block (the name line stays a 52 px field,
   nothing under the 13 px label floor). `test/gate-fold.test.js` holds them. */
#screen-join { gap: 12px; }
.gatering { position: relative; height: 136px; display: flex; align-items: center; justify-content: center; }
/* A quiet field behind the countdown, so the ring stands in something rather
   than floating on black. One gradient, no motion — the clock is the only
   thing on this screen that is allowed to change. */
.gatering::before {
  content: ''; position: absolute; width: 320px; height: 320px; border-radius: 50%;
  background: radial-gradient(circle, var(--night-atmo), transparent 66%);
  pointer-events: none;
}
.gatering .ringsvg { position: absolute; color: var(--ink); }
.gatering .middle { position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.gatecount { font-size: 34px; font-weight: 400; letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums; }
/* The narrowest phone anybody still carries (320 wide): the stage's side
   padding comes in so the card is 16 px wider and the note's sentence wraps
   one line less, and the ring comes down a step. Measured at 320 × 568 by the
   same script; nothing here touches a phone wider than 340. */
@media (max-width: 340px) {
  .stage:has(#screen-join.on) { padding-left: 16px; padding-right: 16px; }
  .gatering { height: 120px; }
  .gatering .ringsvg { width: 120px; height: 120px; }
  .gatecount { font-size: 30px; }
}
.gateunder { margin: 0; font-size: 14px; line-height: 1.3; color: var(--ink-2); text-align: center; }
/* "until it starts" and "· 41 already in" on two lines inside the ring, now
   that the ring is 136 (120 on the narrowest phone): on one line the pair is
   wider than the ring and the stroke ran through the words. The count's text
   is the register's (room.gate.crowd), untouched; only the line breaks. */
.gateunder #join-who { display: block; }
/* When it starts and what happens if they miss it, one faint line under the clock. */
/* Left, like every other quiet line under the button: a scanning eye needs an
   edge to run down, and this one sits directly under the "what is missing"
   line, which is left too — centred, the two read as one wandering paragraph. */
.gateline { margin: 0; font-size: 14px; color: var(--ink-3); }
.gateline:has(> #gate-late:empty) { display: none; }
#join-lead { margin-top: 0; }
.consent { display: flex; align-items: flex-start; gap: 0; }
/* The drawn box is 20px because that is what the artboard draws. What a finger
   lands on is 44, which is what the rule says. The two are not the same thing. */
.consent-check {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex-shrink: 0; margin: -12px 0 -12px -12px;
}
.consent-check input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; }
.consent-check .box { display: block; width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--ink); }
.consent-check input:checked + .box { background: var(--ink); }
.consent-check input:focus-visible + .box { outline: 2px solid var(--live); outline-offset: 3px; }
.consent-words { font-size: 15px; color: var(--ink-2); line-height: 1.5; padding-top: 3px; }
/* "the note" is one link inside the sentence, and so are the two ways back
   under the button. A word in a sentence cannot carry a 44px box without
   pushing the lines apart, so the thumb's target is drawn OVER it instead:
   the same words, a hit area a half-inch tall, not one pixel moved. */
.consent-open, .gateway .quiet-link { position: relative; display: inline; }
.consent-open::after, .gateway .quiet-link::after { content: ''; position: absolute; left: 0; right: 0; top: -12px; bottom: -12px; }
.consent-open { font-size: 15px; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.gatefoot { display: flex; flex-direction: column; gap: 8px; padding-top: 0; }
/* THE TWO WAYS PAST A RECOMMENDATION (2026-09-06). One offer above, big; these
   two under it, side by side and equally quiet — neither is the one we want
   them to take, so neither is louder than the other. They wrap to two lines on
   a narrow phone rather than shrinking, and each is a 44 px target. */
.skiprow {
  display: flex; flex-wrap: wrap; gap: 4px 20px;
  align-items: center; justify-content: center;
}
.skiprow .quiet-link { min-height: 44px; display: inline-flex; align-items: center; }
/* The one quiet line under the button, whichever it is tonight. It breaks
   only at "· or", never inside a phrase. */
.gateway { line-height: 1.6; }
.gateway .quiet-link { color: var(--ink); font-weight: 600; }
.gateway .together { white-space: nowrap; }
/* The reminder, last, out of the way. */
.gateextra { margin-top: auto; padding-top: 14px; display: flex; flex-direction: column; gap: 2px; }
.remindline { padding: 0; }
.remindline .consent-words { font-size: 13px; color: var(--ink-3); padding-top: 4px; }

/* 2, 3, 4 · ONE ROOM, THREE MOMENTS (2026-09-05) */
/* His read: *"everything is misaligned there — the delay, the screen, the
   question mark, things are overlapping. Fix that and make it atmospheric,
   because that's the main screen where people will be spending a lot of
   time."* And his second: *"the whole experience should feel like syncing the
   clock, getting everyone ready, getting everyone in."*

   So the three screens inside a sitting are ONE object in three states, not
   three layouts: a head line, the ring, the crowd and the clock at the foot.
   The ring's top edge is a fixed distance from the top of the stage on all
   three, so its centre never jumps as the room goes from filling up, to
   waiting, to running — and the foot grows downwards from the bottom, so a
   calendar button or a data note can never push the ring off its line. */
.roomscreen { justify-content: flex-start; padding-bottom: 44px; }
/* The two bands the fixed things live in: the ? and Leave at the top, the rest
   button at the bottom. Nothing on these screens is drawn into either, so no
   word is ever under a button and no button is ever under the notch. */
.roomscreen .playhead { padding-top: calc(var(--roombar-top) + var(--roombar-h)); min-height: 0; }
/* Where the ring hangs. A share of the screen rather than a fixed number, so a
   tall phone does not leave a hand's width of black under it — and the SAME
   share on all three screens, which is what keeps the ring's centre on one
   line as the room fills, waits and runs.

   ⛔ AND THE FOOT IS NO LONGER GLUED TO THE BOTTOM (2026-09-06). It was
   `margin-top: auto`, which on a 390×844 phone left **~330 px of empty black**
   between the instruction under the ring and the crowd line — on the ready
   screen AND on the playing one, which is the screen a person looks at for
   twenty minutes. The gap is a capped one now, so the three bands read as one
   object instead of two things at opposite ends of a void, and whatever is
   left over falls at the BOTTOM of the screen, where it is not a hole: the
   connection line and the rest button live down there.

   ⛔ The ring did NOT move to fix it. `min(10vh, 88px)` is untouched, so the
   law above still holds exactly — one number for all three screens, the ring's
   centre on one line as the room fills, waits and runs. Only the space under
   it changed. Checked at 320×568, 360×780 and 390×844. */
.roomscreen .playmid  { margin-top: min(10vh, 88px); margin-bottom: min(14vh, 116px); }
.roomscreen .playfoot { margin-top: 0; }

.playhead { display: flex; align-items: baseline; gap: 8px; font-size: 13px; color: var(--ink-3); }
.playhead .slot { margin-left: auto; color: var(--ink-2); }
.playmid {
  position: relative; isolation: isolate;
  display: flex; flex-direction: column; align-items: center; gap: 20px;
}
/* THE ATMOSPHERE. A soft field behind the ring, tinted by the piece's own
   family — the same `--tint` the door tiles use, off the same one source
   (marks.js writes `data-mark`, the stylesheet reads it) — so the room a
   person walked in from and the room they are sitting in are the same colour.
   One gradient, no image, no request. */
.playmid::before {
  content: ''; position: absolute; z-index: -1; top: -34px;
  width: 330px; height: 330px; border-radius: 50%;
  background: radial-gradient(circle, var(--tint), transparent 68%);
  pointer-events: none;
}
/* An unnamed piece stands in the studio's own night field rather than in the
   near-black the dark theme gives `--tint` — scoped to the room, so a door tile
   for an unnamed pattern is untouched. */
.stage[data-mark="plain"] { --tint: var(--night-atmo); }
/* ⛔ THE ONE THING IN THE ROOM THAT MOVES, and only while the piece is running.
   Nine seconds in and out, the same breath the lobby's open room takes — the
   same keyframe, never a second one. It stops for somebody who asked for
   nothing to move, and while the screen is rested (which hides the stage
   outright; this is the belt). */
#screen-play .playmid::before { animation: roombreathe 9s var(--ease) infinite; }
body.resting .playmid::before { animation: none; }

.dot { position: relative; width: 220px; height: 220px; display: flex; align-items: center; justify-content: center; }
.dot::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: 1px solid var(--hair);
}
.dot .ringprog { position: absolute; inset: 0; color: var(--ink); }
.dot .pip { position: absolute; width: 5px; height: 5px; border-radius: 50%; background: var(--ink); }
.count { position: relative; font-size: 60px; font-weight: 400; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
/* What stands in the middle of the ring on the other two screens: the
   percentage while the bytes come down, the one word once they are here. */
.dot .pct, .dot .lead { position: relative; max-width: 150px; }
.pct { margin: 0; font-size: 44px; font-weight: 400; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }

.playfoot { display: flex; flex-direction: column; gap: 10px; font-size: 13px; color: var(--ink-3); }
.playfoot .with { display: flex; align-items: center; gap: 8px; }
.playfoot .spread { align-items: flex-end; }
#screen-loading .playmid, #screen-ready .playmid, #screen-loading .playfoot, #screen-ready .playfoot { text-align: center; }
#screen-loading .playfoot, #screen-ready .playfoot { align-items: center; }
.nothing-playing { display: block; text-align: center; min-height: 44px; padding-top: 4px; font-size: 13px; }

/* ── the crowd: the room line, and where the room is sitting ─────────────── */
/* His ask, 2026-09-05: *"a small scrollable list of country flags with the
   counts, and that's it — premium, minimalistic, but still good."* One row of
   chips, biggest first, no names and no map: the map is the lobby's, and in
   here it would be something to look at instead of something to close your
   eyes for. Nothing on this row moves. */
.crowdcount { color: var(--ink-2); }
.flagrow {
  display: flex; gap: 6px; align-self: stretch;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* The fade at the right edge says "there is more" without a scrollbar or an
     arrow — the same eight pixels in both directions, so a row that fits shows
     no edge at all. */
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
  scrollbar-width: none;
}
.flagrow::-webkit-scrollbar { display: none; }
.flagrow[hidden] { display: none !important; }
/* The chip IS the card, at chip size — the surface, the hairline and the
   emboss all come from the one card rule above, and nothing is said twice. */
.flagchip {
  flex: 0 0 auto; scroll-snap-align: start;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; font-size: 13px; color: var(--ink-2);
}
.flagchip b { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink); }
/* The flag is sized to the type's own x-height, not to a line of its own: an
   emoji at 14px on a 12px line is a row of chips that all sit level. */
.flagmark { font-size: 15px; line-height: 1; }
/* Where the platform draws no flag — Windows has never shipped one — the two
   letters stand in its place, which is still the true answer and still reads
   (flags.js asks the platform rather than assuming). */
.flagcode { font-size: 10px; font-weight: 700; letter-spacing: 0.06em; color: var(--ink); }
.lead  { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.3; letter-spacing: -0.015em; }
.sub   { margin: 0; font-size: 15px; color: var(--ink-2); }
.hint  { margin: 0; font-size: 14px; color: var(--ink-3); }
.title { margin: 0; font-size: 16px; font-weight: 600; }
.clock { margin: 0; font-variant-numeric: tabular-nums; }
#play-word { color: var(--ink-3); font-size: 15px; }
.closebtn { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; margin: -11px -11px -11px 0; color: var(--ink-3); }

/* 5 · the way out */
#screen-ended { gap: 32px; padding-top: 18px; }
/* `.endtop` and the 26 px question that stood in it went with the reorder of
   2026-09-06 — the rating is no longer the biggest thing on this screen, and
   there is no second emblem above it. The way out's own rules are in one
   section at the foot of this file. */
.stars { display: flex; }
/* The glyph is the artboard's 30px; the button around it is 56, because a
   thumb reaching for a star with the eyes just open does not aim. */
.star { width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; color: var(--hair-2); }
.star svg { width: 30px; height: 30px; fill: none; stroke: currentColor; stroke-width: 1; stroke-linejoin: round; }
.star.lit { color: var(--ink); }
.star.lit svg { fill: currentColor; }
.star:active { transform: scale(0.94); }
.reveal { display: flex; flex-direction: column; gap: 16px; width: 100%; }
#rate-ask .note-box { margin-top: 24px; }
.note-box {
  width: 100%; min-height: 48px; resize: none;
  background: transparent; border: 0; border-bottom: 1px solid var(--hair);
  color: var(--ink); font-size: 16px; padding: 12px 0 8px;
}
.note-box::placeholder { color: var(--ink-3); }
.note-box:focus { outline: none; border-bottom-color: var(--ink); }
#rate-ask { display: flex; flex-direction: column; align-items: center; }
#rate-skip { margin-top: 24px; padding: 12px 24px; min-height: 44px; }
.quiet-link { font-size: 14px; color: var(--ink-3); }
.quiet-link:hover { color: var(--ink); }
/* THE ONE LINE A NEWCOMER IS MEANT TO FOLLOW (2026-09-06). It is quiet on
   purpose — the hero is what is ON, and this sits under it — but it is a LINK,
   so it is underlined the way the room's own way-on is, and it is a target a
   thumb can find rather than one line of 13 px type. */
#hero-milestone {
  min-height: 44px; display: flex; align-items: center; justify-content: center;
  text-decoration: underline; text-decoration-color: var(--hair-2); text-underline-offset: 5px;
}
.err { margin: 0; font-size: 14px; color: var(--warn); min-height: 18px; }

/* the one card */
.endcard { display: flex; flex-direction: column; gap: 16px; padding: 22px 20px; }
.endcard .btn { min-height: 54px; font-size: 16px; }
.endlinks { display: flex; flex-direction: column; gap: 8px; align-items: center; text-align: center; }
.row-2 { display: flex; gap: 12px; width: 100%; }
/* The ladder on the way out (2026-09-09): four quiet rows off the type
   scale, one button, and a hairline over the top. Not a card — nothing here
   names a radius, a ground or a shadow of its own. */
.ladder { display: flex; flex-direction: column; gap: 0; padding: 20px 20px 16px; counter-reset: rung; }
.ladder > .h3 { margin-bottom: 6px; }
.ladder .rung { display: flex; flex-direction: column; gap: 6px; padding: 16px 0 16px 34px; position: relative; border-top: 1px solid var(--hair); counter-increment: rung; }
.ladder .rung::before { content: '0' counter(rung); position: absolute; left: 0; top: 18px; font-size: 12px; color: var(--ink-3); }
.ladder .rung-name { font-weight: 600; color: var(--ink); }
.ladder .rung .hint { padding: 4px 9px; border: 1px solid var(--hair-2); border-radius: var(--r-pill); white-space: nowrap; }
.ladder .rung .btn { margin-top: 10px; }
.ladder .quiet-link { align-self: flex-start; margin-top: 8px; }
/* The app card: the picture on top, one line, two quiet store buttons. */
.appcard { display: flex; flex-direction: column; gap: 10px; padding: 20px; overflow: hidden; }
.appcard::before { content: ''; display: block; height: 120px; margin: -20px -20px 8px; background: linear-gradient(180deg, rgba(0,0,0,.1), rgba(0,0,0,.45)), var(--session-art) center 45% / cover; }
.appcard .row-2 { margin-top: 6px; }
.btn.store { flex: 1 1 0; min-height: 48px; font-size: 15px; font-weight: 600; background: transparent; color: var(--ink); border: 1px solid var(--hair-2); }

/* 6 · something went wrong */
#screen-error { justify-content: center; align-items: center; gap: 16px; text-align: center; }

/* the two sheets that were always here */
.sheet {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(6,11,13,0.72);
  display: flex; align-items: flex-end; justify-content: center;
}
.sheet-card {
  width: 100%; max-width: 430px;
  background: var(--ground); color: var(--ink);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 24px 24px calc(28px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 16px;
  max-height: 88vh; overflow-y: auto;
}
.grab { width: 36px; height: 3px; border-radius: var(--r-pill); background: var(--hair); align-self: center; }
.body-text { margin: 0; font-size: 16px; line-height: 1.5; color: var(--ink-2); }

/* resting the screen: pixels only, never the sound */
.rest-btn {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(20px + env(safe-area-inset-bottom)); z-index: 20;
  min-height: 44px; padding: 0 16px;
  font-size: 13px; color: var(--ink-3);
}
.rest-btn:hover { color: var(--ink); }
/* ⛔ THE SAME OFFSET AS THE ?, and that is the whole fix (2026-09-05). This
   was a bare `top: 16px` while `.roomhelp` padded for the notch, so on every
   phone with an inset the two were on different rows and the cross drifted down
   over the piece's title. One token, both buttons, one row. */
.leave { position: fixed; top: var(--roombar-top); right: 16px; z-index: 20; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; color: var(--ink-3); }
.leave:hover { color: var(--ink); }
.rested {
  position: fixed; inset: 0; z-index: 60;
  background: #000;
  display: flex; align-items: flex-end; justify-content: center;
  padding-bottom: calc(96px + env(safe-area-inset-bottom));
}
body.resting .stage, body.resting .foot, body.resting .rest-btn { display: none; }
/* ⛔ BUT NEVER THE WAY OUT (2026-09-06). This line used to hide `.leave` too,
   so the one screen a person is most likely to want off — face down, twenty
   minutes in, a colleague at the door — was the one screen with no way out of
   it: bring the screen back, find the cross, then confirm. Three taps to stop.
   The cross stays where it always is, over the black rather than under it, at
   the same near-nothing weight as the way back — so the screen is still dark
   and still calm, and stopping is one tap and the question. */
body.resting .leave { z-index: 70; color: rgba(251,246,239,0.42); }
body.resting .leave:hover { color: rgba(251,246,239,0.7); }
.wake-btn {
  background: #000;
  border: 1px solid rgba(251,246,239,0.28);
  color: rgba(251,246,239,0.55);
  border-radius: var(--r-pill); min-height: 48px; padding: 0 24px;
  font-size: 15px;
}

/* the connection line: only ever says something true */
.foot { display: flex; align-items: center; justify-content: center; gap: 8px; padding-top: 14px; font-size: 13px; color: var(--ink-3); }
.foot[hidden] { display: none !important; }
/* ⛔ AND IT CLEARS THE REST BUTTON (2026-09-06). `.rest-btn` is FIXED to the
   bottom of the screen and the connection line is the last thing in the room's
   flow, so the two landed on the same row — "Rest the screen" written straight
   through the word. Nobody had seen it, because the line itself was blank for
   the whole sitting until the tap was made to say so on the same day. The line
   sits above the button now, by the button's own height. */
.stage .foot { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }
.led { width: 5px; height: 5px; border-radius: 50%; background: var(--hair-2); }
.led.live { background: var(--live); }
.led.off  { background: var(--warn); }

/* ── S6 · the checkout sheet ─────────────────────────────────────────────── */

.dimmed { position: fixed; inset: 0; z-index: 30; background: rgba(6,11,13,0.72); display: flex; align-items: flex-end; justify-content: center; }
.desksheet {
  width: 100%; max-width: 430px;
  background: var(--ground); color: var(--ink);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 12px 24px calc(28px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 20px;
  max-height: 92vh; overflow-y: auto;
}
.marktile {
  width: 56px; height: 56px; border-radius: var(--r-md); flex-shrink: 0;
  background: var(--night-panel); color: var(--night-ink);
  display: flex; align-items: center; justify-content: center;
}
/* THE TILL. The offer is one card, warm, so the moment somebody is asked for
   money looks like the thing they are buying and not like a checkout form. */
.offercard { --tint: var(--tint-breath); padding: 22px 20px 24px; }
.dashline { display: flex; align-items: center; gap: 12px; font-size: 16px; }
.dashline i { display: block; width: 14px; height: 1px; background: var(--ink); flex-shrink: 0; }
.price { display: flex; align-items: baseline; gap: 10px; padding: 16px 0; border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair); }
.price .amount { font-size: 32px; font-weight: 600; letter-spacing: -0.025em; }
.paybuttons { display: flex; flex-direction: column; gap: 10px; }

/* ── THE SHOP (his call, 2026-09-06) ─────────────────────────────────────── */
/* One pass, and everything it can carry, as cards on the one card surface.
   They stack, because the studio's column is a phone wherever it is opened
   (the `.page` law above) — one card under the next, each the full width of
   the column. The legibility contract, applied where type gets small
   (JOURNEYS-V2 §3.9): a card's title 17 px wrapping, the lines 16 / 1.5, the
   term and the price at the 13 px floor, one button per card at the pill's
   own height. No badges, no tier colours, no crossed-out prices, no
   countdown, no scarcity — and a card the person already holds says so where
   its button was, never greyed and never ticked. */
.shopcards { display: grid; grid-template-columns: 1fr; gap: 16px; }
.shopcard { padding: 20px 20px 22px; display: flex; flex-direction: column; gap: 16px; }
.shopcard-name { margin: 0; font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; overflow-wrap: anywhere; }
.shopcard-term { margin: 0; font-size: 13px; line-height: 1.35; color: var(--ink-2); }
.shopcard-opens .dashline span { font-size: 16px; line-height: 1.5; color: var(--ink-2); }
.shopcard-price { margin: 0; font-size: 13px; line-height: 1.35; color: var(--ink-2); }
/* The shape, not on sale: the fact where a price would be, and nothing to tap. */
.shopcard-soon { margin: 0; font-size: 13px; line-height: 1.35; color: var(--ink-3); }
/* Where the button was, once it is theirs: one line, the pill's own height,
   so the card does not shrink the moment it is held. */
.shopcard-held, .shopdoor-held {
  margin: 0; min-height: var(--btn-h); display: flex; align-items: center;
  font-size: 16px; line-height: 1.5; font-weight: 600; color: var(--ink);
}
/* The doors, one card each: the door name, who you become, the price, one button. */
.shopdoors { display: grid; grid-template-columns: 1fr; gap: 12px; }
.shopdoor { padding: 18px 20px 20px; display: flex; flex-direction: column; gap: 10px; }
.shopdoor-name { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; overflow-wrap: anywhere; }
.shopdoor-who { margin: 0; font-size: 16px; line-height: 1.5; color: var(--ink-2); }
.shopdoor-price { margin: 0; font-size: 13px; line-height: 1.35; color: var(--ink-2); }

/* ── S8 · the pass ───────────────────────────────────────────────────────── */

.passcard { overflow: hidden; display: flex; flex-direction: column; }
.passcard .head {
  position: relative; isolation: isolate;
  min-height: 160px; background: var(--night-panel); color: var(--night-ink);
  display: flex; align-items: flex-end; padding: 18px;
}
.passcard .head::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(100% 80% at 50% 10%, var(--night-atmo), transparent 74%);
  pointer-events: none;
}
.passcard .head .eyebrow { color: var(--night-ink-2); }
.passcard .head .mark { position: absolute; right: 18px; top: 22px; color: var(--night-ink); }
.passcard .foot-in { display: flex; flex-direction: column; gap: 16px; padding: 18px; }
.wk { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 0; border-bottom: 1px solid var(--hair); }
.wk:last-child { border-bottom: 0; }
.wk .who { display: flex; align-items: center; gap: 12px; }
.wk .t { font-size: 16px; }
.wk .t.now { font-weight: 600; }
.wk .t.future { color: var(--ink-3); }
.wk .side { font-size: 13px; font-weight: 600; }
.wk .side.open { color: var(--live); }
.wk .side.gone { color: var(--ink-3); }
.wk .side.future { color: var(--ink-3); font-weight: 400; }

/* ── the small pages: done, find, help ───────────────────────────────────── */

/* Help, Find my pass and No-more-emails: one form, on one card, so the two
   pages a person reaches when something has gone wrong look like the rest of
   the studio rather than a browser dialog. */
.formcard { display: flex; flex-direction: column; gap: 22px; padding: 28px 24px; }
/* ONE PRIMARY ACTION PER SCREEN, which Find my pass was breaking: a phone that
   can do face-or-fingerprint drew two solid pills, and a person choosing
   between two equal buttons is a person deciding rather than doing. The fast
   way stays the button; the email falls back to the outline. On a phone with
   no passkey the passkey button is hidden and the email is solid again — the
   stylesheet asks the page which it is, so no JS has to say. */
.formcard:has(#pk-signin:not([hidden])) #find-send {
  background: transparent; color: var(--ink); border: 1px solid var(--ink); box-shadow: none;
}
.choices { display: flex; flex-direction: column; }
.choice {
  display: flex; align-items: center; gap: 12px;
  min-height: 56px; padding: 8px 0; border-bottom: 1px solid var(--hair);
  font-size: 16px; text-align: left; width: 100%;
}
.choice .box { width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--hair-2); flex-shrink: 0; }
.choice[aria-pressed="true"] .box { border-color: var(--ink); background: var(--ink); }
.slotlist { display: flex; flex-direction: column; }
.slotrow { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; border-bottom: 1px solid var(--hair); font-size: 16px; width: 100%; text-align: left; }
.slotrow:last-child { border-bottom: 0; }
.slotrow .when { font-weight: 600; }
.slotrow .state { font-size: 14px; color: var(--ink-2); }

/* Said out loud, never drawn. The visible countdown ticks ten times a second,
   which is unusable read aloud, so the ring is aria-hidden and this carries the
   same fact once a minute instead. */
.said {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ── the devices somebody has saved (2026-09-04) ─────────────────────────── */

/* The key sits beside the wordmark, not instead of it. */
.headtools { display: flex; align-items: center; gap: 4px; }

.devicelist { list-style: none; margin: 0; padding: 0; }
.device {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 0; border-bottom: 1px solid var(--hair);
}
.device:last-child { border-bottom: 0; }
.device .t { font-size: 16px; }
/* A thumb reaching for Remove is still a thumb. */
.device .quiet-link { display: inline-flex; align-items: center; min-height: 44px; }

/* ── the card at the door, and the wallet it goes into ───────────────────── */
/*
 * A free pass, drawn like the front desk's own surfaces so the listener page
 * and the desk read as ONE product rather than two apps that share a domain.
 * The name lands on it as it is typed; on the tap it flies into the little row
 * of cards this phone is carrying — over a download that is already running,
 * never in front of one.
 */
/* ⛔ `.freepass`, NOT `.passcard`. `.passcard` is the six-week card on /me and
   has had its own rules since the front desk was built; a second block under
   the same name here would quietly restyle that page from this one. */
/* THE SURFACE IS THE DARK ONE, IN BOTH THEMES (his read, 2026-09-05, with the
   reference: the lobby's hero tile). The pass is the one object in this
   product a person collects, and it is cut from midnight stock the way the
   hero and the six-week card's head already are — a deep face, the mark as
   its emblem, the name as its title, the stamps as its meta, gold as the one
   warm accent. The `.night, .freepass` token patch above is what makes the
   input, the tick and the seal come out right without a rule each.
   The radius, the edge and the emboss come from the one card rule. */
.freepass {
  padding: 16px 20px 12px; display: flex; flex-direction: column;
  transition: transform 0.42s var(--ease), opacity 0.42s var(--ease);
}
/* The atmosphere on the pass: ONE quiet field, and the mark stands in it.
   ⛔ THE GOLD ARCS ARE GONE (his read, 2026-09-05: "looks horrible and non
   premium"). Three hairline arcs swept out of the bottom-right corner, which
   is exactly where the seal, the chips and the meta line live: the card read
   as a widget with something ruled across it. Nothing decorative crosses a
   word or the seal, so the decoration went and the field stayed — sat behind
   the emblem, where the reference tile puts its own. One gradient. */
.freepass::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: radial-gradient(84% 46% at 50% 34%, var(--night-atmo), transparent 72%);
  pointer-events: none;
}
/* The grain: the only texture in the product, and only on the pass face, so a
   card feels like stock and a panel feels like paint. 260 bytes of SVG noise,
   inline, at five per cent. */
.freepass::after {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  opacity: var(--grain); pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 120px 120px;
}
/* The header line: the sitting and its hour. Hidden while there is nothing
   true to say — a link opened cold, before the door has answered. */
.freepass-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding-bottom: 12px; border-bottom: 1px solid var(--hair);
  font-size: 12px; line-height: 1.3; color: var(--ink-2);
}
.freepass-top:has(> .freepass-sitting:empty) { display: none; }
.freepass-sitting { flex: 1 1 auto; min-width: 0; }
.freepass-slot { flex-shrink: 0; color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
/* The kind and the studio mark: the home screen's eyebrow and its hairline drawing. */
.freepass-head { display: flex; align-items: center; gap: 8px; padding-top: 12px; min-height: 36px; }
/* The kind is the pill in the top corner of the reference tile: what this card
   is, said once, on a patch of the card's own light. */
.freepass-kindrow {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 12px; border-radius: var(--r-pill); background: var(--veil);
}
.freepass-kind { font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink); }
/* The small mark a known card carries: this one has a way back. */
.freepass-saved { font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--live); }
/* ⛔ THE DOOR'S MARK IS NOT ON THE PASS (his call, 2026-09-05: "I don't like
   the three concentric circles coming in the right side of the card — that
   doesn't make sense"). The slot stays because freepass.js writes into it and
   three tests pin it as the template; it is simply not drawn. The door marks
   go on living where they mean something: the tiles, the board and the stamps.
   A follow-up can take the element and the brandCard() calls out together. */
.freepass-brand { display: none; color: var(--ink); }
.freepass-brand svg { display: block; height: 26px; width: auto; }

/* THE ISSUER AND THE EMBLEM. A card says who issued it, in the corner, the way
   the header says it: the AATAM triangle and the wordmark, letter-spaced —
   the deck's own lockup (studio/…/aatam-deck/assets/logo-wordmark.svg), drawn
   here from the one sprite symbol.

   AT THE GATE the card is a form, so the lockup stays whole in the corner and
   nothing stands behind the name or the tick. ON /me the triangle steps out of
   the corner into the middle of the card — the object in the middle of the
   hero tile he sent — and the wordmark keeps the corner on its own. One order
   in the markup, two arrangements, nothing written twice. */
.freepass-issuer {
  font-size: 11px; font-weight: 700; letter-spacing: 0.22em;
  color: var(--ink-2);
}
.freepass-emblem {
  margin-left: auto; display: flex; align-items: center;
  pointer-events: none; color: var(--emblem);
}
/* An <svg> holding a <use> has no intrinsic size: `width: auto` resolves to
   the whole containing block and pushes the wordmark off the card. Both axes,
   always, for every glyph drawn from the sprite. */
.freepass-emblem .i { width: 17px; height: 17px; stroke-width: 1.6; }
/* THE NAME LINE. While the card is blank or unsigned the input IS the line —
   the card's own type, a hairline under it, "Your name" where the name goes.
   Once the pass is issuing, issued or restored the input gives way to the
   name as drawn. One place to write, and it is unmistakably on the card. */
.freepass-nameline { margin-top: 6px; }
/* THE LABEL OVER THE NAME LINE, ALWAYS VISIBLE (the critic's verdict,
   2026-09-06; WCAG 3.3.2). 13 px is the floor for a line a person is asked to
   read; it goes with the input once the pass is issuing, issued or restored,
   because there is nothing to type any more. */
.freepass-label { display: block; font-size: 13px; font-weight: 600; line-height: 1.4; color: var(--ink-2); }
.freepass[data-state="issuing"] .freepass-label, .freepass[data-state="issued"] .freepass-label, .freepass[data-state="restored"] .freepass-label { display: none; }
.freepass-input {
  display: block; width: 100%; min-height: 52px;
  background: transparent; border: 0; border-bottom: 1px solid var(--hair-2); border-radius: 0; padding: 0;
  color: var(--ink); font-size: 26px; font-weight: 600; letter-spacing: -0.015em; line-height: 1.2;
  appearance: none;
}
.freepass-input::placeholder { color: var(--ink-3); font-weight: 500; }
.freepass-input:focus { outline: none; border-bottom-color: var(--ink); }
.freepass-name { display: none; min-height: 52px; padding-top: 12px; font-size: 26px; font-weight: 600; letter-spacing: -0.015em; line-height: 1.2; color: var(--ink); }
/* A blank card is blank: a hairline where the name goes, never a stand-in
   name. "Guest" is no longer a name (PASS-WORKFLOW §8, 2026-09-05). */
.freepass-name:empty::before { content: ''; display: block; width: 44%; height: 1px; margin-top: 18px; background: var(--hair-2); }
.freepass[data-state="issuing"] .freepass-input, .freepass[data-state="issued"] .freepass-input, .freepass[data-state="restored"] .freepass-input { display: none; }
.freepass[data-state="issuing"] .freepass-name, .freepass[data-state="issued"] .freepass-name, .freepass[data-state="restored"] .freepass-name { display: block; }
.freepass-where { display: block; margin-top: 6px; font-size: 12px; color: var(--ink-2); min-height: 17px; }
/* Up and away, towards the row of cards above it. */
.freepass.flying { transform: translateY(-28px) scale(0.82); opacity: 0; }

/* ── the five states of the one card (2026-09-05, PASS-WORKFLOW §6) ─────────
   blank · unsigned · issuing · issued · restored — one attribute, no second
   element. The signature line is the note's tick, ON the card; the footer
   carries the stamp row, the word Signed/Unsigned, and the stamp itself. */
.freepass-sign {
  display: flex; align-items: flex-start; gap: 0;
  margin-top: 12px; padding: 12px 0 0; border-top: 1px solid var(--hair);
}
.freepass-sign .consent-words { flex: 1 1 auto; }
.freepass-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--hair); min-height: 24px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3);
}
.freepass-stamps { flex: 1 1 auto; color: var(--ink-2); }
.freepass-signed { flex-shrink: 0; }
.freepass[data-signed="true"] .freepass-signed { color: var(--ink); }
/* The stamp: the studio mark and the date, landing. It is drawn from the
   server's answer and never before it; 300 ms is the whole ceremony. */
.freepass-stamp {
  display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  padding: 4px 8px; border-radius: var(--r-pill); border: 1px solid var(--gold);
  color: var(--ink); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase;
  transform: scale(1.35) rotate(-6deg); opacity: 0;
  transition: transform var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease);
}
.freepass-stamp.landed { transform: none; opacity: 1; }
.freepass-mark svg { width: 14px; height: 14px; }
.freepass[data-state="blank"] .freepass-kind, .freepass[data-state="blank"] .freepass-where { color: var(--ink-3); }
/* The round trip in flight: the card holds its breath. */
.freepass[data-state="issuing"] { opacity: 0.7; transform: scale(0.99); transition: transform var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease); }
/* Issued or brought back: the signature line has done its job and goes; the
   card is a card now, not a form. */
.freepass[data-state="issued"] .freepass-sign, .freepass[data-state="restored"] .freepass-sign { display: none; }
.freepass[data-state="issued"], .freepass[data-state="restored"] { border-color: var(--hair-2); }

/* Under the card: a refusal's sentence and the step that follows it, then
   the button, then the one quiet line. Each is a target a thumb can find. */
#pass-said { min-height: 0; }
#pass-said:empty { display: none; }
#pass-next, #not-you { align-self: center; min-height: 44px; display: inline-flex; align-items: center; font-weight: 600; color: var(--ink); }
#pass-find { color: var(--ink); }

/* The card at the end of a sitting, with tonight's stamp landing on it. */
.endpass { display: flex; flex-direction: column; gap: 10px; }

/* The cards this phone is carrying, above the card, labelled by their own
   aria-label so the words are written once. The chips are the lobby's pills. */
.wallet { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.wallet::before {
  content: attr(aria-label); flex-basis: 100%; margin-bottom: 2px;
  font-size: 13px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2);
}
/* ⛔ 44px LIKE EVERY OTHER TARGET (2026-09-06). `.stamp` is 44, `#hero-milestone`
   is 44, `.skiprow .quiet-link` is 44 — the house rule is 44 and this one
   object was 36. It is the chip that says WHICH PERSON this phone is; missing
   it puts somebody else's pass on the screen. */
.chip {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
  border-radius: var(--r-pill); background: var(--veil); color: var(--ink);
  font-size: 13px; font-weight: 600; letter-spacing: 0.02em;
  max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chip.on { background: var(--ink); color: var(--ground); }

/* ── /me · the pass in your hand (2026-09-05, PASS-WORKFLOW §6a) ──────────
   The gate's own card, in state issued, and under it everything on it. No
   second card component, no icon legend: the studio mark for a sitting, a
   hairline for everything else, the desk's own tokens throughout. */
.mepage { gap: 26px; }
/* LARGE: THE CARD IS THE PAGE, and it is the tile he pointed at, in this
   order and no other: a small pill top-left · the AATAM wordmark top-right ·
   THE MARK ALONE, centred in a large quiet field · at the foot, on the left,
   the eyebrow, the name as the title and the meta line · on the right, one
   circle. Nothing else on the face.

   ⛔ IT IS A GRID, NOT A STACK OF MARGINS (2026-09-05, after his read of the
   live card: "looks horrible and non premium"). The old card ordered a flex
   column and hung the seal off `position: absolute`, so with thirteen
   sittings on it the chips wrapped onto a second row and ran under the seal,
   and the emblem sat on top of the eyebrow. Here the field is a ROW with a
   real minimum — two fifths of the card on a 390 px phone — and the seal has
   a COLUMN of its own that no word can enter: the title truncates, the seal
   never moves.

   The markup is the gate's, to the letter. `display: contents` on the head
   and the foot hands their children straight to this grid, which is how one
   card is two arrangements without a word being written twice. */
.freepass-me {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) 84px;
  grid-template-rows: auto minmax(172px, 1fr) auto auto auto;
  grid-template-areas:
    "kind    saved   issuer"
    "field   field   field"
    "eyebrow eyebrow seal"
    "title   title   seal"
    "meta    meta    seal";
  column-gap: 0;
  min-height: 424px;
  padding: 18px 20px 20px;
}
.freepass-me .freepass-head,
.freepass-me .freepass-foot { display: contents; }
/* THE PILL: what this card is, said once, quietly — sentence case, no
   tracking, one line. It was oversized letter-spaced capitals in a fat pill
   and it shouted louder than the name. A pattern name longer than the pill
   is cut, so it can never push the wordmark off the corner. */
.freepass-me .freepass-kindrow {
  grid-area: kind; justify-self: start; align-self: center;
  max-width: 100%; padding: 6px 14px;
}
.freepass-me .freepass-kind {
  display: block; max-width: 178px;
  font-size: 13px; font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* "Saved" is not the pill's business, and it is not a state anything on the
   desk goes green for — the devices section below says it in full. Ten
   pixels, quiet, beside the pill. */
.freepass-me .freepass-saved {
  grid-area: saved; justify-self: start; align-self: center; margin-left: 10px;
  font-size: 10px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ink-3);
}
.freepass-me .freepass-issuer { grid-area: issuer; justify-self: end; align-self: center; }
/* THE FIELD: the mark alone, centred, with real air above and below it, and
   no word inside it. A hairline at the reference's own weight — the old one
   was drawn four times as thick as the tile it was copied from. */
.freepass-me .freepass-emblem {
  grid-area: field; margin: 0; justify-content: center; align-items: center;
}
.freepass-me .freepass-emblem .i { width: 144px; height: 144px; stroke-width: 0.28; }
/* The eyebrow: what this is a pass to. One line, cut before it can wrap. */
.freepass-me .freepass-where {
  grid-area: eyebrow; align-self: end; margin: 0; min-height: 0;
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.freepass-me .freepass-nameline { grid-area: title; min-width: 0; margin: 0; }
.freepass-me .freepass-name {
  font-size: 32px; min-height: 40px; padding-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The meta: one plain line, the way the hero says "3 min · headphones · free". */
.freepass-me .freepass-issued {
  grid-area: meta; align-self: end; margin: 0;
  font-size: 12px; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* THE SEAL, where the reference puts the play circle: the count of sittings
   inside a gold ring, in the column the words cannot reach. Gold means the
   pass and nothing else on this card. */
.freepass-me .freepass-stamps {
  grid-area: seal; align-self: end; justify-self: end; flex: none;
  width: 76px; height: 76px; border-radius: 50%; padding: 0 8px;
  display: flex; align-items: center; justify-content: center; text-align: center;
  border: 1px solid var(--gold); color: var(--gold);
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; text-transform: none; line-height: 1.25;
}
.freepass-me .freepass-stamps:empty { display: none; }
/* Nothing sat yet: no seal, so the column closes and the words take the
   width back. */
.freepass-me:has(.freepass-stamps:empty) {
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) 0;
}
.freepass-issued { flex-shrink: 0; color: var(--ink-3); }
/* THE COUNTRIES, on the meta line (his ask, 2026-09-05: "add the country of the
   users — whichever country they join from — on the pass, as a small
   indicator. So it even sounds more personalised."). A WHISPER: the meta's own
   size, its own line box so a row of emoji cannot make the line taller than the
   words, and sat on the same baseline as them. It lives INSIDE the meta
   element, so the grid's law holds unchanged — the line truncates and the
   seal, which has a column no word can enter, never moves.
   ⛔ The gate's card draws none: the meta line there is the pattern, not a
   history. */
.freepass-flags {
  margin-left: 8px;
  font-size: 12px;
  line-height: 1;
  vertical-align: baseline;
  letter-spacing: 0.02em;
}
/* A platform that draws no flag glyph — Windows, most notably — gets the two
   letters in the eyebrow's own type rather than a row of grey boxed capitals
   in the platform's emoji metrics, which is the loudest thing that could
   happen to this face. public/js/desk/flags.js measures which it is. */
.freepass-flags.codes {
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em; color: var(--ink-3);
}

/* ── the stamp row, UNDER the card (2026-09-05) ───────────────────────────
   ⛔ IT IS OFF THE CARD'S FACE. On the face, five chips and a "+8" wrapped
   onto a second row and sat on top of the seal — the single worst thing in
   the shot he sent. Under the card it is a shelf of evenings: ONE line that
   scrolls sideways and never wraps, the count first, then the last five with
   the door's mark and the date. Each is still the button that scrolls the
   ledger to that evening, and it is still a thumb's width tall.
   The gate carries no stamp row at all; the card there is a form. */
.stamprow {
  display: flex; flex-wrap: nowrap; align-items: center; gap: 8px;
  margin-top: -8px; padding-right: 10px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 30px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 30px), transparent);
}
.stamprow::-webkit-scrollbar { display: none; }
.stamp {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  min-height: 44px; padding: 0 14px;
  border-radius: var(--r-pill); border: 0; background: var(--veil);
  color: var(--ink-2); font-size: 13px; font-weight: 600;
  letter-spacing: 0; text-transform: none; white-space: nowrap;
  scroll-snap-align: start;
}
.stamp svg { width: 14px; height: 14px; flex: none; color: var(--gold); }
/* ⛔ THE COUNT IS A SNAP TARGET TOO. It is not a chip, and with only the chips
   marked the browser snapped the row to the first of them on the very first
   layout — the "+8" was laid out, then scrolled out of sight before anybody
   saw it. Everything in a snapping row is a stop, or nothing is. */
.stamp-more {
  flex: none; padding-right: 2px; scroll-snap-align: start;
  font-size: 13px; font-weight: 600; letter-spacing: 0.04em; color: var(--ink-3);
}

/* ── THE FRONT PAGE'S PASS BLOCK (2026-09-06, his ask) ────────────────────
   *"The site home page should open with the user's pass directly with the
   correct details like flag, etcetera, and the last sessions."*

   ⛔ ONE CARD ELEMENT, TWO ARRANGEMENTS, AND THIS IS THE HALF THAT SWITCHES.
   The front page carries the gate's whole markup — an input, the note to tick,
   the signature line, the landing stamp — because a phone that holds nothing
   is handed a pass right there. The moment the card exists, `.freepass-me` is
   added and the same nodes become the grid /me draws; these four are the ones
   with no place in that composition, and a grid item with no area gets
   auto-placed, which is a signature line landing across somebody's name. */
.homepass { display: flex; flex-direction: column; gap: 14px; }
.freepass-me .freepass-sign,
.freepass-me .freepass-signed,
.freepass-me .freepass-stamp,
.freepass-me .freepass-input { display: none; }
.issue { align-items: stretch; }

/* THE LAST SITTINGS: one row of cards that scrolls sideways, newest first.
   *"The last sessions very clearly in a horizontal scrollable card list so
   that they know that this isn't captured or no sessions in the start when
   they haven't done any."*

   It is the stamp row's mechanics — snap, no scrollbar chrome, a fade at the
   right edge — at card size instead of chip size, because on the front page
   this IS the shelf of evenings rather than an index into a ledger below. The
   card itself declares no radius, ground, edge or shadow: `.sit` is in the one
   card rule at the top of this file. */
.sittings {
  display: flex; flex-wrap: nowrap; align-items: stretch; gap: 10px;
  padding: 2px 10px 4px 2px; margin: -2px -16px -4px -2px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
}
.sittings::-webkit-scrollbar { display: none; }
.sit {
  flex: none; width: 172px; min-height: 128px;
  display: flex; flex-direction: column; gap: 6px;
  padding: 14px; scroll-snap-align: start;
  color: inherit; text-decoration: none;
}
.sit-mark { display: block; height: 22px; color: var(--gold); }
.sit-mark svg { display: block; height: 22px; width: auto; }
.sit-title {
  font-size: 16px; font-weight: 600; line-height: 1.25; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.sit-meta { font-size: 13px; line-height: 1.35; color: var(--ink-2); margin-top: auto; }
.sit-stars { display: inline-flex; gap: 1px; color: var(--hair-2); }
.sit-stars svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.2; stroke-linejoin: round; }
.sit-stars svg.lit { color: var(--ink); fill: currentColor; }
/* NOTHING ON IT YET is a card, not a blank: everybody's pass looks like this on
   the day they get one, and a row that simply is not there tells that person
   nothing. Its mark is quiet — there is no evening to put a gold stamp on —
   and where the studio still owes them the first sitting the whole card is the
   link to it, at a thumb's height. */
.sit-none .sit-mark { color: var(--ink-3); }
.sit-cta {
  display: flex; align-items: center; min-height: 24px;
  font-size: 13px; font-weight: 600; color: var(--ink);
}
.sit-cta::after { content: ' ›'; }

/* The ledger: one column, by month, newest first; one line and one quiet
   detail per row. */
.ledger { display: flex; flex-direction: column; gap: 20px; }
.lmonth { display: flex; flex-direction: column; }
.lmonth .eyebrow { padding-bottom: 8px; }
.lrow { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-top: 1px solid var(--hair); }
.lrow.lit { background: var(--veil); margin: 0 -8px; padding-left: 8px; padding-right: 8px; border-radius: var(--r-sm); }
/* The mark column is one width for every row, so the lines stay flush: the
   studio mark for a sitting, and for everything else a hairline where the
   mark would be. */
.lmark { width: 20px; height: 20px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: var(--ink); }
.lmark:empty::before { content: ''; display: block; width: 12px; height: 1px; background: var(--hair-2); }
.lmark svg { width: 16px; height: 16px; }
.lbody { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.lmain { font-size: 16px; line-height: 1.5; color: var(--ink); }
.ldetail { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 13px; line-height: 1.45; color: var(--ink-2); }
.lstars { display: inline-flex; gap: 1px; color: var(--hair-2); }
.lstars svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.2; stroke-linejoin: round; }
.lstars svg.lit { color: var(--ink); fill: currentColor; }
.lreceipt { display: inline-flex; align-items: center; min-height: 44px; margin: -12px 0; font-weight: 600; color: var(--ink); }

/* Your pass: the pattern, six small week marks, the receipt, the refund line. */
.passblock { padding: 18px 20px; }
/* The block's one link lines up with its words, not with the middle. */
.passblock .textlink { align-self: flex-start; padding-left: 0; }
.weekmarks { display: flex; gap: 8px; }
.wmark { width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--ink); box-sizing: border-box; }
.wmark.sat { background: var(--ink); }
.wmark.open { box-shadow: 0 0 0 3px var(--panel), 0 0 0 4px var(--ink); }
/* A week that went by is a fact, not an alarm: a dashed hairline, said quietly.
   ⛔ AND NOTHING ANYWHERE CALLS IT A FAILURE (2026-09-06). On day one of a paid
   pass, week one is a week that ended before the pass existed — the six weeks
   are back-dated seven days so that week two opens the minute somebody pays —
   and it was drawn and read out as a week they had let go by. Nobody has failed
   anything on day one, or on any day: the mark is `gone`, and it is no louder
   than a week still to come. */
.wmark.gone { border-color: var(--ink-3); border-style: dashed; }
.wmark.future { border-color: var(--hair-2); }

/* The reminder switch: one control, drawn as one, with a thumb-sized box. */
.switch {
  position: relative; display: inline-flex; align-items: center; flex-shrink: 0;
  min-width: 60px; min-height: 44px; padding: 0 4px;
}
.switch::before {
  content: ''; display: block; width: 44px; height: 26px; border-radius: var(--r-pill);
  border: 1px solid var(--ink); background: transparent; box-sizing: border-box;
}
.switch .knob {
  position: absolute; left: 7px; top: 50%; width: 20px; height: 20px; margin-top: -10px;
  border-radius: 50%; background: var(--ink); transition: transform var(--dur-1) var(--ease);
}
.switch[aria-checked="true"]::before { background: var(--ink); }
.switch[aria-checked="true"] .knob { transform: translateX(18px); background: var(--ground); }
.switch:disabled { opacity: 0.5; }

/* The way out: two quiet lines, and one honest confirmation. */
.wayout { align-items: flex-start; padding-top: 8px; border-top: 1px solid var(--hair); }
.wayout .textlink { padding-left: 0; }
.deleteask { width: 100%; padding: 16px 18px; border-radius: var(--r-md); }

/* The quiet offer to keep this device, at the end of an evening. */
.pkcard {
  display: flex; flex-direction: column;
  gap: 12px; padding: 20px;
}

/* ── motion ──────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  /* One thing on the front desk moves for its own sake — the field behind the
     open room's mark — and it stops here, at the size it rests at. Everything
     else is the download bar's fill, the two things that squash under a
     finger, and any animation a future rule adds. The ring does not spin and
     the sheet does not slide — they are drawn where they belong and stay put. */
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .hero::before { animation: none !important; transform: none; opacity: 1; }
  .playmid::before { animation: none !important; transform: none; opacity: 1; }
  /* The breath on /first rests at the size it starts at, so the drawing is
     still a drawing and nothing on the page moves. */
  .breathring circle { animation: none !important; transform: none; opacity: 1; }
  .firstbreath { transform: none !important; }
  /* The live dot rests, and still says what it says: the colour is the state,
     the breath was only ever the flourish. */
  .livedot-dot { animation: none !important; transform: none; opacity: 1; }
  .btn:active:not(:disabled), .big:active:not(:disabled), .star:active { transform: none; }
}

/* ── the help sheet ──────────────────────────────────────────────────────── */
/* A question mark on every screen, and one sheet of words per screen (his ask,
   2026-09-05). It is not a navigation: the card lifts from the bottom over the
   page you are already on, the page behind goes inert, and the cross, the dark
   or Escape puts it away. Same surface language as the pass card and the
   checkout sheet — a ground-coloured panel, one hairline, no shadow, no
   gradient — and one easing for the one thing that moves. */

/* The head's right cell holds two glyphs now: the question mark, then the pass.
   The cell spans from the wordmark's column to the edge and aligns to the end,
   so the two 44px targets sit at the right margin and the wordmark stays
   exactly where it was on every page. */
.deskhead .headright {
  grid-row: 1; grid-column: 2 / 4; justify-self: end;
  display: flex; align-items: center;
}
/* The two cells that were placed by column alone are pinned to the first row
   as well, or the browser hands the wordmark a row of its own rather than let
   the right-hand cell sit across the track it does not use. */
.deskhead .backlink, .deskhead .wordmark { grid-row: 1; }
.deskhead .helpbtn { color: var(--ink-3); }
.deskhead .helpbtn:hover { color: var(--ink); }

/* ── THE LIVE DOT, on the head's second row ──────────────────────────────── */
/* His ask, 2026-09-06: *"on every page of the screen … there will be a green
   dot and 'connected live' should be displayed in green just like a live
   [broadcast]. So it gives a feeling of people that something is live and
   connected right now."*

   WHY IT IS A ROW AND NOT A CELL. It was drawn in the head's right cell first.
   "Reconnecting…" at 11px is 95px wide, measured in Manrope in a real browser
   — with the two 44px glyphs beside it that is 195px of a 366px head, and the
   wordmark, which is centred, runs straight into it at every phone width. The
   three cells are the frame's law (NAVIGATION.md) and none of them may move,
   so the dot takes a row of its own under the wordmark, centred: it is on
   every page, it fits at 320px, and it reads the way a broadcast reads.

   Colour is one of the four meanings and no new one: ALIVE (--live) when this
   phone is connected — the same green as an open room — and NEEDS YOU (--warn)
   while it is trying to get back, which is the very colour the room's own
   connection line has used for exactly this since it was written (`.led.off`).
   Not connected yet and not connected at all are quiet ink, because neither is
   news. Both colours clear 4.5:1 on every ground (test/pages-a11y.test.js).

   The dot breathes ONLY while it is green, on `roombreathe` — the keyframe the
   open room and the playing room already use, never a new one — and it stops
   for prefers-reduced-motion at the size it rests at. */
.deskhead .livedot {
  /* OUT OF THE GRID, in the corner. `position: absolute` is the whole
     guarantee: an element that is not in the flow cannot push the wordmark,
     whatever it says, at any width — measured at 320, 360 and 390 px, the
     wordmark's centre does not move by a single pixel between the three
     states. `top: -4px` puts the line in the page's own top margin, clear of
     the back arrow's glyph (which is centred in its 44px box, so its ink
     starts 10px down), which is how it costs no height at all. */
  position: absolute; top: -4px; left: 0;
  /* And it can never REACH the wordmark either: half the head, less the
     wordmark's own half-width (37.4px) and 6.6px of air. A word longer than
     that is cut with an ellipsis rather than allowed to touch the mark. */
  max-width: calc(50% - 44px);
  /* It is a status, not a control, and it lies over the back arrow's 44px
     target: nothing here may take a tap that was meant for the way home. */
  pointer-events: none;
}
.livedot {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 13px;
  font-size: 13px; font-weight: 600; line-height: 1;
  color: var(--ink-3);
  white-space: nowrap; overflow: hidden;
}
#livedot-word { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.livedot-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--hair-2); flex-shrink: 0;
  transform-origin: 50% 50%;
}
.livedot[data-state="live"] { color: var(--live); }
.livedot[data-state="live"] .livedot-dot {
  background: var(--live);
  animation: roombreathe 3s var(--ease) infinite;
}
.livedot[data-state="reconnecting"] { color: var(--warn); }
.livedot[data-state="reconnecting"] .livedot-dot { background: var(--warn); }

/* The room keeps its own, because the room takes the head away the moment the
   pass is in. One quiet target opposite the Leave button, clear of the ring and
   of the clock, and gone again at the end of the sitting. */
.roomhelp {
  position: fixed; z-index: 20;
  /* ⛔ THE SAME TOKEN AS THE LEAVE BUTTON, and not `env()` written out again.
     Two literals is how the two ended up on different rows in the first place:
     one of them was updated and the other was not. */
  top: var(--roombar-top); left: 16px;
  width: 44px; height: 44px; display: none; align-items: center; justify-content: center;
  color: var(--ink-3);
}
.roomhelp:hover { color: var(--ink); }
.stage:has(.screen.on:not(#screen-locked):not(#screen-join):not(#screen-ended):not(#screen-error)) .roomhelp { display: flex; }

.helpsheet {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(6,11,13,0.6);
  display: flex; align-items: flex-end; justify-content: center;
}
.helpsheet-card {
  width: 100%; max-width: 430px;
  background: var(--ground); color: var(--ink);
  border-top: 1px solid var(--hair);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 12px 24px calc(24px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 16px;
  max-height: 84vh; overflow-y: auto;
}
.helpsheet-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.helpsheet-head .h3 { padding-top: 8px; }

/* One hairline per line — the dash the checkout's own list is drawn with. */
.helpsheet-lines { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 12px; }
.helpsheet-lines li { position: relative; margin: 0; padding-left: 22px; font-size: 16px; line-height: 1.5; color: var(--ink-2); }
.helpsheet-lines li::before {
  content: ""; position: absolute; left: 0; top: 10px; width: 12px; height: 1px; background: var(--hair-2);
}
.helpsheet-foot { display: flex; align-items: center; gap: 18px; border-top: 1px solid var(--hair); }
.helpsheet-foot .textlink { padding-left: 0; }

/* ── THE FIRST SITTING, ASKED FULL SCREEN (2026-09-06, his redesign) ──────
   *"A blocking screen pop-up, full screen … and then a clear menu."*

   It is the sheet above, at full height, and it declares no ground, no radius
   and no shadow of its own — `.helpsheet-card` is the card and this only says
   how tall it is and what is in it. A menu of three, in the order and the
   weight he asked for: the one that goes somewhere is the button, and the two
   that stay are quiet links under it. */
.primersheet .helpsheet-card {
  min-height: 100dvh; max-height: none;
  border-radius: 0; border-top: 0;
  justify-content: center; gap: 24px;
  padding: calc(28px + env(safe-area-inset-top)) 24px calc(28px + env(safe-area-inset-bottom));
}
/* The emblem alone in a quiet field, the way the pass carries it: this is the
   studio introducing itself, and the mark is the whole of the decoration. */
.primer-mark { display: flex; justify-content: center; color: var(--ink-3); }
.primer-mark .i { width: 64px; height: 64px; stroke-width: 0.5; }
.primer-menu { padding-top: 4px; }
.primer-menu .textlink { align-self: center; min-height: 44px; }
.primer-menu .quiet-link { color: var(--ink-3); }
/* The one line under the three answers — "you can find it any time under your
   pass" — so nobody feels they are closing a door. 13 px is the floor for a
   line a person is asked to read (JOURNEYS-V2 §2.5); centred like the menu
   above it, and hidden with the two quiet answers when the sheet is a wall. */
.primer-foot { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-3); text-align: center; }
/* The page behind a full-screen sheet does not scroll under it. */
body.sheet-open { overflow: hidden; }

/* The whole building, read straight through, above the form on /desk/help. */
.helpindex-list { display: flex; flex-direction: column; }
.helpindex-item { border-top: 1px solid var(--hair); }
.helpindex-item:last-child { border-bottom: 1px solid var(--hair); }
.helpindex-name {
  list-style: none; cursor: pointer;
  min-height: 52px; display: flex; align-items: center;
  font-size: 16px; font-weight: 600; color: var(--ink);
}
.helpindex-name::-webkit-details-marker { display: none; }
.helpindex-name::after {
  content: ""; margin-left: auto; width: 8px; height: 8px;
  border-right: 1px solid var(--hair-2); border-bottom: 1px solid var(--hair-2);
  transform: rotate(45deg); transition: transform var(--dur-1) var(--ease);
}
.helpindex-item[open] .helpindex-name::after { transform: rotate(-135deg); }
.helpindex-item .helpsheet-lines { padding-bottom: 18px; }
/* ── the schedule ────────────────────────────────────────────────────────── */
/* His second anchor (2026-09-05): "simple navigation which takes them to home
   page immediately or the schedule." The lobby's board is the next few hours;
   /schedule is the whole day, and nothing else on it. The rows are the SAME
   rows (.board / .dep above) — the only thing this page adds is the day they
   fall on, said once, as a heading a scanning thumb can stop at. */

.schedulepage .days { display: flex; flex-direction: column; gap: 28px; }
.daygroup { display: flex; flex-direction: column; gap: 8px; }
/* The day sticks to the top of the screen while its own rows scroll past, so
   somebody halfway down a long board still knows which day they are reading —
   the one thing a paper timetable gets for free. */
.daygroup > .eyebrow {
  position: sticky; top: 0; z-index: 1;
  padding: 6px 0; margin: 0;
  background: var(--ground);
}
/* The row's own day chip is what the lobby's board needs, where there is no
   heading above it. Here the heading has already said the day, so the chip is
   the same fact twice and it goes. */
.schedulepage .dep-time .d { display: none; }

/* ONE THING THAT IS ON, AND ITS HOURS UNDER IT (2026-09-06). The day used to be
   one chronological column, so a shelf of two pieces printed the same title
   eleven times down one screen. The title and what it asks for are said once,
   here, and every hour keeps its own row, its own count and its own link
   underneath — a listings page, not a till roll. The rows inside carry no name
   of their own (`departureRow(..., named: false)`); they carry it as a label,
   so a link is never read out as "07:00, 23 already in" and nothing else. */
.dayp { display: flex; flex-direction: column; }
.dayp + .dayp { margin-top: 20px; }
/* The row's title, moved up onto the block that holds its hours — so it is the
   same 17px object it is on the lobby's board, at the same weight, and reading
   the schedule is reading the same thing in a different arrangement. */
.dayp-name { font-size: 17px; font-weight: 600; line-height: 1.25; margin: 0; }
.dayp-meta { font-size: 13px; line-height: 1.25; color: var(--ink-3); margin: 2px 0 6px; }
/* With the name gone the row is a time, a gap and a crowd; the gap is what
   keeps the count on the right edge where it is on every other board. */
.dep.unnamed .dep-what { min-height: 1px; }

/* ── the world map ───────────────────────────────────────────────────────── */
/* Where people are joining from (his ask, 2026-09-05). A panel like every
   other, one hairline over the line of countries, and the drawing itself is a
   canvas — dots on the land, lit where somebody sat. Nothing here is a colour
   of its own: the ink and the live green are read off the tokens by
   worldmap.js, so both themes are right and the switch moves it too.
   The 8/3 is the mask's own shape (public/js/desk/landmask.js, 288 × 108), held
   in CSS so the card does not jump while the first paint works out its width.
   TWO CANVASES, stacked: the land underneath, painted once, and the people over
   it, cleared and redrawn while a country breathes. Same box, same shape. */
.worldmap { padding: 20px; display: flex; flex-direction: column; gap: 16px; }
.wm-stack { position: relative; display: block; }
.wm-canvas { display: block; width: 100%; aspect-ratio: 8 / 3; }
.wm-over { position: absolute; inset: 0; }

/* The five biggest: the flag, the country, the count. A live dot goes in front
   of a country with somebody in a room right now. */
.wm-places {
  margin: 0; padding-top: 16px; border-top: 1px solid var(--hair);
  display: flex; flex-wrap: wrap; gap: 10px 20px;
  font-size: 13px; line-height: 1.45;
}
.wm-place { display: inline-flex; align-items: baseline; gap: 6px; color: var(--ink-2); }
.wm-place b { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink); }
.wm-place .dot-live { align-self: center; }

/* ⛔ THE FLAG IS SIZED AGAINST THE NAME, NOT AGAINST THE CARD (his note,
   2026-09-05: *"they have to look properly sized so that it looks good for the
   eyes"*). A flag glyph fills its em box top to bottom, where a capital fills
   about seven tenths of it — so a flag set at the caption's own 12 px reads a
   third taller than the word beside it, which is the sticker he did not want.
   At 9 px on a 12 px line it comes out the height of the capitals, and the
   baseline is the type's own. `line-height: 1` keeps it off the line box. */
.wm-flag {
  font-size: 10px; line-height: 1; letter-spacing: 0;
  margin-right: 1px; font-variant-emoji: emoji;
}

/* And where a platform draws no flag at all (Windows, most of Linux), the two
   letters in the eyebrow's own type — a mark, not two grey capitals pretending
   to be one. `worldmap.js flagsSupported` decides which of the two is written. */
.wm-iso {
  font-size: 10px; line-height: 1; font-weight: 600; letter-spacing: 0.06em;
  padding: 2px 4px; border-radius: var(--r-xs); margin-right: 1px;
  background: var(--panel-2); color: var(--ink-3);
}

/* ── the first sitting (/first, 2026-09-06) ──────────────────────────────── */
/* His call, 2026-09-05: *"everything which is a prerequisite or a primer is
   always available — it's not on a schedule … available in the app as a
   provision … streamed on mobile in a beautiful, very user-friendly, aesthetic
   way."* So it is a PAGE, and it is night by design like the room: a sitting
   is not a browse, and the page a person closes their eyes on should not be
   the colour of a page they read.

   Three acts, one at a time, exactly the way the room's screens work — one
   `hidden` per act and nothing else. No new card surface, no new radius, no
   new shadow: the field below is a `.tile`'s atmosphere on the page's own
   ground, and the pass is the pass. */
.firstpage { gap: 18px; }
.firstpage .act { display: none; flex: 1 1 auto; flex-direction: column; gap: 18px; }
.firstpage .act.on { display: flex; }

/* THE THRESHOLD's quiet field. The seventh atmosphere, and it is the studio's
   own rather than a door family's: the first sitting belongs to no pattern, so
   it is tinted by the emblem's night field (`--night-atmo`) and nothing else.
   One gradient, no image, no request — the rule DESIGN.md states for all of
   them. */
.firstfield {
  position: relative; isolation: isolate;
  border-radius: var(--r-lg);
  background: var(--night-panel);
  color: var(--night-ink);
  border: 1px solid #303030;
  box-shadow: var(--emboss-night);
  padding: 36px 24px 28px;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  overflow: hidden;
}
.firstfield::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: radial-gradient(120% 86% at 50% 8%, var(--night-atmo), transparent 76%);
  pointer-events: none;
}
.firstfield .eyebrow { color: var(--night-ink-2); }
.firstfield .h1 { color: var(--night-ink); }
.firstfield .body { color: var(--night-ink-2); max-width: 272px; }
.firstmark { display: flex; align-items: center; justify-content: center; color: var(--night-ink); }
.firstmark .i { width: 40px; height: 40px; }
.firstmeta { font-size: 13px; letter-spacing: 0.02em; color: var(--night-ink-2); }

/* WHAT IT ASKS FOR — drawn, not described, and SIDE BY SIDE so the threshold
   is one screen rather than a list to scroll past. Three marks in a row: the
   sprite's own glyphs for the first two and one breathing ring for the third.
   Under 8 KB all in, because none of it is an asset. */
.readyrow {
  margin: 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
}
.readystep {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center;
}
/* The column is the width; the label wraps inside it rather than running
   into its neighbour on a 360 px phone. */
.readystep .faint { width: 100%; }
.readymark {
  width: 40px; height: 40px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-2);
}
.readymark .i { width: 22px; height: 22px; }
/* The breath is a drawing rather than a glyph, so it is sized to LOOK the same
   weight as the two beside it rather than to the same box. */
.readymark .breathring { width: 30px; height: 30px; }

/* THE BREATH. The one keyframe added since 2026-09-05, and the third on the
   whole desk: two rings that expand and settle on a slow loop, so a person
   reads the pace before they hear it. Six seconds — in, and out — which is the
   pace the piece itself keeps. It stops for prefers-reduced-motion, below,
   where it rests at the size it starts at. */
.breathring { width: 40px; height: 40px; overflow: visible; }
.breathring circle { transform-origin: 50% 50%; }
.breathring-out { animation: breathe 6s var(--ease) infinite; opacity: 0.5; }
.breathring-in  { animation: breathe 6s var(--ease) infinite; }
@keyframes breathe {
  0%, 100% { transform: scale(1);    opacity: 0.55; }
  50%      { transform: scale(1.28); opacity: 1; }
}

/* THE SITTING. The media, full-bleed inside the page's own column: a video
   that covers it, or the breathing ring in a quiet field when the piece is
   sound. Nothing on this screen but the piece, one arc and one clock. */
.firststage {
  position: relative; isolation: isolate;
  border-radius: var(--r-lg); overflow: hidden;
  background: var(--night-panel); color: var(--night-ink);
  border: 1px solid #303030;
  box-shadow: var(--emboss-night);
  aspect-ratio: 4 / 5;
  display: flex; align-items: center; justify-content: center;
}
.firststage::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(circle at 50% 46%, var(--night-atmo), transparent 70%);
  pointer-events: none;
}
.firstvideo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.firstbreath { display: flex; align-items: center; justify-content: center; color: var(--night-ink); }
.firstbreath[hidden] { display: none; }
.firstbreath .breathring circle { animation: none; }
.firstbreath .breath-guide-ring { stroke-width: .16; opacity: .25; }
.firstbreath .breath-halo { stroke-width: 1.4; opacity: .07; }
.firstbreath .breath-rim { stroke-width: .3; }
.firstbreath .breath-polygon { display: none; }
.firstbreath[data-shape="box"] .breath-circle,
.firstbreath[data-shape="triangle"] .breath-circle,
.firstbreath[data-shape="box"] .breath-guide-ring,
.firstbreath[data-shape="triangle"] .breath-guide-ring { display: none; }
.firstbreath[data-shape="box"] .breath-polygon,
.firstbreath[data-shape="triangle"] .breath-polygon { display: inline; }
.firstbreath .breath-outline { stroke-width: .22; opacity: .22; }
.firstbreath .breath-trace { stroke-width: .4; stroke-linejoin: round; stroke-linecap: round; }
.firstbreath .breath-bead { fill: currentColor; stroke: none; }
.firstbreath[data-reduced="true"] .breath-trace,
.firstbreath[data-reduced="true"] .breath-bead { display: none; }
.firstpreview {
  width: 100%; min-height: 92px; padding: 14px 18px; border: 1px solid var(--hair-2);
  border-radius: var(--r-md); display: flex; align-items: center; gap: 18px;
  color: var(--ink); text-align: left;
}
.firstpreview[hidden] { display: none; }
.firstpreview-shape { flex: none; width: 58px; height: 58px; color: var(--ink); }
.firstpreview-shape .breathring { width: 58px; height: 58px; }
.firstguideword { position: absolute; bottom: 32px; left: 24px; right: 24px; text-align: center; color: var(--night-ink); font-size: 18px; }
.accountsave { margin-top: 28px; padding-top: 24px; border-top: 1px solid var(--line); }
.accountsave button:disabled { opacity: .45; }
.breathring-lg { width: 240px; height: 240px; }
/* A video is the picture; the ring has no business over it. */
.firststage[data-kind="video"] .firstbreath { display: none; }

/* ONE CONTROL, and the progress is the ring around it — the same object the
   room draws at 220, at 88 here because this screen has a picture on it. */
.firstcontrols { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.ringbtn {
  position: relative; width: 88px; height: 88px; border-radius: 50%;
  background: none; border: 0; padding: 0; color: var(--ink);
  display: flex; align-items: center; justify-content: center;
}
.ringbtn .ringprog { position: absolute; inset: 0; width: 88px; height: 88px; color: var(--ink); }
/* The whole ring, faint, so the arc reads as a share of something rather than
   as a crescent floating on black. One hairline, no second colour. */
.ringbtn .ringtrack { color: var(--hair-2); opacity: 0.9; }
.ringbtn .i { width: 26px; height: 26px; }

/* The card here is the sitting's own, so its second line — the pattern, on the
   room's gate — has nothing to say and takes no room. */
.firstpage .freepass-where:empty { display: none; }
/* If you have more time (2026-09-06): the longer version, under the one next
   step, behind a hairline — two quiet lines, neither louder than the other. */
.longoffer { margin-top: 8px; padding-top: 20px; border-top: 1px solid var(--hair); }
.longoffer .textlink { align-self: center; min-height: 44px; }
.longoffer .quiet-link { color: var(--ink-3); }

/* THE WAY OUT. The card is moved in here at the end — one card on the page,
   and it is the one the name went onto — and the act stands in the middle of
   the screen rather than at the top of it: there is nothing under it, and a
   closing screen with a hand's width of black below reads as unfinished. */
.firstpage #first-out { justify-content: center; gap: 22px; }
.firstpage .endpass { display: flex; flex-direction: column; gap: 12px; }
.firstpage #first-out-line { max-width: 300px; margin: 0 auto; }

/* ⭐ THE SECOND TOUR, on the way out (his law, 2026-09-06 late: progressive
   disclosure — how the pass works is told AFTER the first sitting has moved
   them, never before). It borrows the arrival's tour outright — `.tour`,
   `.tour-step`, `.tour-dots`, `.quiet-btn`, all above — so a person who met
   one tour at the door recognises the second on sight, and this screen adds no
   new object to the desk.

   Two things are its own. The steps that EXPLAIN are read, so they are left
   aligned with an edge to run down (JOURNEYS-V2 §2.5); the step that CLOSES is
   the way out that has always shipped, and stays centred. And the lines of
   what a pass opens are a list rather than a paragraph — four facts, one
   thought each, a hairline dot in front. */
.firsttour { text-align: left; }
.firsttour .h2 { max-width: 320px; }
.firsttour-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 12px; }
.firsttour-line { display: flex; align-items: flex-start; gap: 10px; color: var(--ink-2); }
.firsttour-line::before {
  content: ''; flex: 0 0 auto; width: 5px; height: 5px; margin-top: 8px;
  border-radius: 50%; background: var(--hair-2);
}
/* HOW LONG IT LASTS, then what it is: the bold half is the term — six weeks,
   forever — because that is the question this step answers. The shop's own
   words underneath, and none of the shop's cards. */
.firsttour-term { display: block; color: var(--ink); font-weight: 600; }

/* ══ THE WAY OUT — the screen that returns people (2026-09-06) ═════════════
   Added at the foot of the file on purpose: this is one screen's own section,
   written while the type scale was being set elsewhere, so nothing above is
   touched and a merge cannot lose either half.

   The screen's order is the fix (JOURNEYS-V2 §3.6 · CRITIC #5, #21): a line of
   what happened → the card with the stamp landing → THE NEXT HOUR → the rating,
   quieter, under it. It used to open on five stars at 26 px, which is the
   screen one person left from and never came back to.

   ⛔ EVERYTHING LEFT-ALIGNED. The only centred line in the room is the one the
   room speaks while the sound is playing. A scanning eye needs an edge to run
   down (JOURNEYS-V2 §2.5), and this screen is read with the eyes just open. */
#screen-ended { align-items: stretch; text-align: left; }
#screen-ended .endpass,
#screen-ended .endlinks,
#screen-ended #rate-ask { align-items: stretch; text-align: left; }

/* The next hour: one line, one quiet fact under it, one button. The button is
   the only primary action above the fold, and it is a link into a real room.
   The way to the whole day belongs to that block and not to the question under
   it, so the two sit in one group with their own small gap. */
.nextout { display: flex; flex-direction: column; gap: 4px; }
.nexthour { display: flex; flex-direction: column; gap: 10px; }
.nexthour .big { margin-top: 6px; text-decoration: none; }
#end-next-day {
  min-height: 44px; display: flex; align-items: center;
  text-decoration: underline; text-decoration-color: var(--hair-2); text-underline-offset: 5px;
}

/* The rating, after the hour and quieter than it: the question is a heading,
   not a headline, and the stars run from the same left edge as the words. */
.rateask { display: flex; flex-direction: column; gap: 14px; }
/* The stars run from the same left edge as the words; the 56 px target around
   each 30 px glyph is what makes the row start a thumb's width to the left. */
#screen-ended .stars { justify-content: flex-start; margin-left: -14px; }
#rate-skip { align-self: flex-start; padding-left: 0; }

/* ── THE ARRIVAL — the studio's door opening (JOURNEYS-V2 §2, 2026-09-06) ──
   His words: *"a full-screen experience that splashes up and says welcome to
   the studio in a very fashionable way, with elements appearing staggered,
   fast. And then the door opens — a curtain opening left and right."*

   A LAYER, NEVER A ROUTE. `#arrive` is `display: none` until a script puts
   `arriving` on the root, so a page whose scripts never run is the lobby with
   its doors open, and the ceremony costs nothing — its seconds are the
   seconds the board was fetching anyway.

   THE BEATS ARE TRANSITION DELAYS, and the numbers here are `BEATS` in
   arrive.js (test/css-desk.test.js holds the two together): the triangle at
   120 ms, A A T A M at 320, the welcome at 480, the leading line at 640, the
   fact at 860 — every word rises the same 8 px on the one easing, and the
   whole thing is started by ONE class, `on`, in one animation frame. The
   doors are the fourth keyframe on the desk, `part`: two panels off the
   centre line, 520 ms, and they stop at rest like the other three. Each half
   carries the whole composition, clipped to its own side, so the emblem and
   the words ride out with their half and fade at sixty per cent of the way.

   Night stock in both themes, one gradient (`--night-atmo`, the studio's own
   — it belongs to no door family), no image, no request, no sound. The
   message is larger than the furniture: the welcome at 21 px lands first, the
   leading line at 29 px lands last and largest and wraps to two lines by
   design (the critic's verdict, 2026-09-06). Centred — the one screen that
   is, beside the room's single instruction line. */
.arrive {
  display: none; position: fixed; inset: 0; z-index: 60; overflow: hidden;
  background: var(--night); color: var(--night-ink); text-align: center;
  /* How far open the finger has pulled them, 0 → 1. `arrive.js` writes it,
     and only when the number moves. The doors read it; nothing else does. */
  --drag: 0;
  /* The gesture belongs to the doors, so the page underneath never scrolls
     out from under a thumb that is opening them. */
  touch-action: none;
}
html.arriving .arrive { display: block; }

/* ⛔ A CONTROL THAT IS DRAWN MUST WORK (2026-09-06, verified live with
   `document.elementFromPoint`). Through the whole arrival and the whole
   welcome card, the head's `?` and pass glyph rendered at FULL opacity and
   every tap on them landed somewhere else — `div.arrive-face` during the
   ceremony, the bare body once the card was up, because `welcome.js open()`
   puts `inert` on `#lobby` and the head is inside it. A live-looking control
   that does nothing is the one interaction this audience reads as "broken",
   rather than as "not yet".

   And neither layer CAN let a tap through, by design: the arrival is a curtain
   and the welcome card is `aria-modal` over an inert page, which is the whole
   point of a modal. So the honest half of "make them work or do not draw them"
   is the second half. They come back, live, the instant the layer goes down —
   which is what the tour step means by *"It is always up here, top right"*,
   and that step draws the glyph inline in its own sentence.
   Guard: `test/css-desk.test.js`. */
html.arriving .deskhead .headright,
html.welcoming .deskhead .headright { visibility: hidden; }
.arrive-half {
  position: absolute; top: 0; bottom: 0; width: 50%; overflow: hidden; background: var(--night);
  /* THE FINGER'S OWN TRAVEL: the pull up IS the doors' travel apart, so they
     move with the thumb rather than after it. On release short of the
     threshold `--drag` goes back to 0 and this transition closes them. */
  transform: translateX(calc(var(--part-to) * var(--drag)));
  transition: transform 260ms var(--ease);
}
/* …and while the finger is down nothing is eased at all: the doors are
   exactly where the thumb is, not 260 ms behind it. */
.arrive.dragging .arrive-half { transition-duration: 0s; }
.arrive-left  { left: 0;  --part-to: -100%; }
.arrive-right { right: 0; --part-to: 100%; }
.arrive-face {
  position: absolute; top: 0; bottom: 0; width: 200%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 0 24px;
  background: radial-gradient(90% 56% at 50% 42%, var(--night-atmo), var(--night) 100%);
  /* The words step back as the doors are pulled open — the room behind is
     what is being reached for, not the poster on the door. */
  opacity: calc(1 - var(--drag) * 0.72);
}
.arrive-left  .arrive-face { left: 0; }
.arrive-right .arrive-face { right: 0; }
.arrive-mark {
  display: flex; color: var(--night-ink);
  opacity: 0; transform: scale(0.94);
  transition: opacity 260ms var(--ease) 120ms, transform 260ms var(--ease) 120ms;
}
.arrive-mark .i { width: 72px; height: 72px; stroke-width: 0.5; }
.arrive-wordmark {
  margin-top: 18px; padding-left: 0.42em;
  font-size: 13px; font-weight: 700; letter-spacing: 0.42em; color: var(--night-ink);
}
.arrive-welcome { margin-top: 44px; font-size: 21px; font-weight: 500; line-height: 1.3; color: var(--night-ink-2); }
.arrive-line {
  margin-top: 12px; max-width: 330px;
  font-size: 29px; font-weight: 600; line-height: 1.15; letter-spacing: -0.02em; color: var(--night-ink);
}
.arrive-wordmark, .arrive-welcome, .arrive-line {
  opacity: 0; transform: translateY(8px);
  transition: opacity 240ms var(--ease), transform 240ms var(--ease);
}
.arrive-wordmark { transition-delay: 320ms; }
.arrive-welcome  { transition-delay: 480ms; }
.arrive-line     { transition-delay: 640ms; }
.arrive-fact {
  margin-top: 36px; min-height: 20px;
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 600; color: var(--night-ink-2);
  opacity: 0; transition: opacity 200ms var(--ease) 860ms;
}
.arrive-fact .dot-live { background: var(--night-live); }
/* ⛔ THE INVITATION — the seventh beat, and the doors then WAIT (his call,
   2026-09-06: *"the arrival door should wait and let them swipe to enter"*).
   A handle and one line, at the bottom where a thumb already is, on the same
   1 180 ms the doors used to part on. It is the only thing on this layer that
   asks anything of anybody, and it asks quietly: the handle is the studio's
   own ink at a whisper, the line is 14 px, the same size and colour as the
   fact above it and a screen's height away from it. Nothing pulses, nothing
   bounces, no new keyframe — a still invitation is the confident one. */
.arrive-invite {
  position: absolute; left: 0; right: 0; bottom: calc(46px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  opacity: 0; transition: opacity 260ms var(--ease) 1180ms;
}
.arrive-invite .grip { width: 46px; height: 3px; border-radius: var(--r-pill); background: rgba(251, 246, 239, 0.30); }
.arrive-invite .say { font-size: 14px; font-weight: 600; color: var(--night-ink-2); }
/* It lands on its beat, and from then on it is answered by the pull itself:
   half a thumb's travel and it is gone, before the doors are. It lives INSIDE
   the face, so it also rides out with its own half of the door and fades with
   it when somebody simply taps. */
.arrive.on .arrive-invite { opacity: calc(1 - var(--drag) * 2); }
/* ⛔ AND THE DELAY COMES OFF WITH THE DURATION. The invitation's transition
   carries a 1 180 ms delay (its own beat); leaving that on under the finger
   meant the words stayed lit for a second while the doors were already
   halfway open — caught in `design-review/after-swipe/*-02-under-the-finger`
   before this line existed. */
.arrive.dragging .arrive-invite { transition-duration: 0s; transition-delay: 0ms; }
.arrive.on .arrive-mark { opacity: 1; transform: none; }
.arrive.on .arrive-wordmark, .arrive.on .arrive-welcome, .arrive.on .arrive-line { opacity: 1; transform: none; }
.arrive.on .arrive-fact.has { opacity: 1; }
/* A fact that lands after its beat fades in where it is. */
.arrive-fact.late { transition-delay: 0ms; }
/* The doors part. */
.arrive.open .arrive-half { animation: part 520ms var(--ease) forwards; }
@keyframes part { to { transform: translateX(var(--part-to)); } }
.arrive.open .arrive-face { opacity: 0; transition: opacity 312ms var(--ease); }
/* A HOLDER'S NOD (§2.2, split on the pass): the wordmark and one true fact,
   no stagger, no panels, 600 ms and gone — *"a returning user, we take
   immediately into the app."* */
.arrive.nod .arrive-mark, .arrive.nod .arrive-welcome, .arrive.nod .arrive-line,
.arrive.nod .arrive-invite { display: none; }
.arrive.nod .arrive-wordmark { margin-top: 0; font-size: 15px; transition-delay: 0ms; }
.arrive.nod .arrive-fact { margin-top: 20px; transition-delay: 0ms; }
.arrive.nod.open .arrive-half { animation: none; }
.arrive.nod.open { opacity: 0; transition: opacity 240ms var(--ease); }

/* ── THE DOOR ITSELF: a pass, in their name (his call, 2026-09-06 late) ────
   *"having a named and signed entry on the pass is mandatory to enter the
   listen studio … That is what the arrival should block. Nothing else should
   be shown before that."* A second layer, from the head down — the head
   stays, because the pass button in its corner is what the tour points at —
   on the page's own ground, so the doors part onto the same stock the lobby
   is printed on. It holds the one card, in the gate's own arrangement, and
   after the card is issued the two-step tour under it. `#lobby` is inert
   while it is up, like the primer sheet. */
.welcome {
  display: none; position: fixed; left: 0; right: 0; bottom: 0;
  top: calc(66px + env(safe-area-inset-top));
  z-index: 40; overflow-y: auto;
  background: var(--ground); color: var(--ink);
}
html.welcoming .welcome { display: block; }
.welcome-col {
  width: 100%; max-width: 430px; margin: 0 auto;
  padding: 6px 24px calc(28px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 22px;
}
/* The label IS the question (his words: "What should we call you?"), 13 px,
   above the line, always on — never a placeholder that vanishes. */
.freepass-label { margin: 10px 0 0; color: var(--ink-2); }
.freepass[data-state="issuing"] .freepass-label,
.freepass[data-state="issued"] .freepass-label,
.freepass[data-state="restored"] .freepass-label,
.freepass-me .freepass-label { display: none; }
/* THE FOUR SCREENS, one class each (welcome.js). The two studio cards first;
   then the card and its one button; then the one step under the issued card.
   Only one of the three is ever on the screen, and the lobby is painted
   underneath the whole time. */
.welcome.studio-on .welcome-head,
.welcome.studio-on .freepass,
.welcome.studio-on .issue { display: none; }
.welcome.tour-on .welcome-head, .welcome.tour-on .issue, .welcome.tour-on .studio { display: none; }
/* A COUPLE OF THINGS ABOUT THE STUDIO (his call, 2026-09-06). Two cards, one
   at a time, on the page's own ground — no panel, no border: the words ARE
   the screen. The rule is 27 px (the legibility contract's 26–28 for an
   onboarding card, JOURNEYS-V2 §2.5), the sentence 16/1.5 like every other
   sentence in the studio, and the primary sits a full 32 px under them so
   the eye finishes reading before it finds the button. */
.studio { display: flex; flex-direction: column; gap: 30px; padding-top: 10px; }
.studio-card { display: flex; flex-direction: column; gap: 12px; }
.studio-card .h2 { font-size: 27px; line-height: 1.15; max-width: 15ch; }
.studio-card .body { max-width: 34ch; }
/* Which of the two, then the one thing to do, then the quiet way past it —
   the dots belong with the words they count, not between two buttons. */
.studio-foot { display: flex; flex-direction: column; gap: 12px; }
.studio-foot .tour-dots { margin-bottom: 4px; }

/* ⛔ THE SMALLEST PHONE STILL SEES THE BUTTON. On a 320 × 568 screen the head,
   the card and the way in came to 617 px — the one screen a stranger MUST act
   on, with its button an inch below the glass (measured, not guessed:
   `node design-review/measure-door.mjs`, and the shot that caught it is
   `design-review/after-swipe/320-dark-05-pass-blank`). Nothing is cut and no
   type shrinks: the rhythm tightens, and only inside this layer — the same
   card is drawn at the room's gate and on /first, which have their own
   measured arithmetic (`design-review/measure-gate.mjs`) and must not move. */
@media (max-height: 640px) {
  .welcome-col { gap: 12px; padding-top: 0; }
  .welcome .welcome-head { gap: 4px; }
  .welcome .freepass { padding: 12px 18px 10px; }
  .welcome .freepass-head { padding-top: 8px; min-height: 30px; }
  .welcome .freepass-nameline { margin-top: 2px; }
  .welcome .freepass-input { min-height: 40px; }
  .welcome .freepass-name { min-height: 36px; padding-top: 4px; }
  .welcome .freepass-sign { margin-top: 8px; padding-top: 8px; }
  /* The line that says where a card was issued is empty at the door, and an
     empty line still reserved 23 px of a 568 px screen. */
  .welcome .freepass-where:empty { display: none; }
  .welcome .freepass-foot { margin-top: 8px; padding-top: 8px; }
  .studio { gap: 22px; padding-top: 0; }
  .welcome .keep { gap: 12px; }
  .welcome .keep .stack-8 { gap: 6px; }
  .welcome .keep .field-row { gap: 4px; }
  .welcome .keep .field { min-height: 44px; }
  .tour { gap: 12px; }
  .tour-step { gap: 12px; }
  .tour-step .stack-8 { gap: 4px; }
  .welcome .btn { min-height: 52px; }
}
/* ⭐ KEEP THIS PASS — the offer inside the beat the card is already held for
   (his call, 2026-09-06: *"you have to give them an option to add Face ID
   and/or email immediately … or you can do it later also"*). No panel and no
   border: it is the same stock the step under the card is printed on, because
   it IS that moment and not a screen of its own. One loud way, then two quiet
   ones, then the sentence that makes *later* a real answer. The same block is
   drawn on the way out of the first sitting (public/first.html), so every rule
   here is written to the class and never to the layer. */
.keep { display: flex; flex-direction: column; gap: 18px; }
/* The field only exists once somebody has asked for it, and when it does it
   carries the whole of the second way: the question, the line, the button and
   the one sentence that says what it is not. */
.keep .field-row { padding-bottom: 4px; }
.keep-form { padding-top: 2px; }
/* The way past, set apart from the two ways in. Three buttons in one column
   read as one list of three ways to keep the card, and this one is not that:
   it writes nothing, and it is offered again on the way out of the first
   sitting and from the pass. */
.keep-past { margin-top: 10px; }

/* THE TOUR: the card above, one step at a time below, two dots, one button
   each. Step one says the one thing; step two is the nudge. */
.tour { display: flex; flex-direction: column; gap: 16px; }
.tour-step { display: flex; flex-direction: column; gap: 22px; }
.tour-point { display: flex; align-items: center; gap: 10px; color: var(--ink); font-weight: 600; }
.tour-point .i { width: 22px; height: 22px; }
.tour-dots { display: flex; justify-content: center; }
.tour-dot { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; }
.tour-dot::before { content: ''; display: block; width: 6px; height: 6px; border-radius: 50%; background: var(--hair-2); }
.tour-dot.on::before { background: var(--ink); }
/* The real second choice: a 44 px quiet button in the same place, never a
   grey line under a white button (the critic, 2026-09-06). */
.quiet-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 100%;
  min-height: 44px; font-size: 15px; font-weight: 600; color: var(--ink-2);
}
.quiet-btn:hover { color: var(--ink); }

/* ── THE PASS ON THE LOBBY: a quick view, one button (his call, 2026-09-06 late)
   *"The home page doesn't have to be the pass, but a quick view of the pass
   with a button, View my pass, so the full screen honours everything."* One
   strip: what kind, the name, the seal's count and the countries as a
   whisper; the full face and the ledger live on /me. */
.passstrip { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 16px 18px; }
.passstrip-words { min-width: 0; flex: 1 1 auto; }
.passstrip .h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.passstrip-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 14px; line-height: 1.5; color: var(--ink-2); }
.passstrip-meta .freepass-flags { margin-left: 0; }
.passstrip-btn {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 16px; border-radius: var(--r-pill);
  border: 1px solid var(--hair-2); color: var(--ink);
  font-size: 15px; font-weight: 600; white-space: nowrap;
}

/* ── THE TILE A REEL EARNED (§2.3c): the hero's own stock, one tile, never a
   shelf of them — the door's name as the title, the identity line under it,
   the free mark and the hour on the button, not in prose. */
.reeltile {
  height: 300px; display: block; width: 100%; text-align: left;
  background: var(--night-panel); color: var(--night-ink); border-color: #303030;
  --tint: var(--night-atmo);
}
.reeltile .markbox { align-items: flex-start; padding-top: 52px; color: var(--night-ink); }
.reeltile-foot { position: absolute; left: 20px; right: 20px; bottom: 22px; }
.reeltile-foot .eyebrow, .reeltile-foot .body { color: var(--night-ink-2); }
.reeltile-foot .h2 { color: var(--night-ink); }

/* ── A HOLDER'S WEEK, and this week's slots (§0 rule 8, 2026-09-06 late) ──
   The card asks; the sheet shows the hours, morning · afternoon · evening in
   the reader's own clock, each slot a row a thumb can hit. */
.weekcard { display: flex; flex-direction: column; gap: 16px; padding: 20px; }
.weeksheet .helpsheet-card { gap: 14px; }
.weekgroups { display: flex; flex-direction: column; gap: 18px; }
.slotgroup { display: flex; flex-direction: column; }
.slotgroup .eyebrow { padding-bottom: 6px; }
.slot {
  display: flex; align-items: center; gap: 14px;
  min-height: 56px; border-top: 1px solid var(--hair); color: var(--ink);
}
.slot-time { flex: none; width: 56px; font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.slot-day { flex: none; font-size: 13px; color: var(--ink-3); width: 76px; }
.slot-what { flex: 1 1 auto; min-width: 0; font-size: 16px; line-height: 1.5; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The skip, last and quietest, under the hours (NEXT.md G3, 2026-09-06): the
   point of the ask is a booking, so the way past it is never the first thing
   on the screen. Same foot as the help sheet's, with no top rule of its own
   because the last slot row already draws one. */
.weekfoot { padding-top: 4px; }
.weekfoot .textlink { padding-left: 0; color: var(--ink-3); }

/* ── "YOUR SLOT": which days are most likely (his call, 2026-09-06) ────────
   Two quiet rows of chips and one button. The chip is the studio's own chip
   (44 px, the house target); the row wraps, because seven days and a cadence
   do not fit across a phone and must never scroll sideways. */
.slotsheet .helpsheet-card { gap: 14px; }
.slotsheet .eyebrow { padding-top: 4px; }
.chiprow { display: flex; flex-wrap: wrap; gap: 8px; }
.chiprow .chip { max-width: none; cursor: pointer; }
.chiprow .chip[aria-pressed="true"] { background: var(--ink); color: var(--ground); }
/* A sheet that cannot be answered yet says so by being QUIET, never by being
   a grey slab and never by being red (design-review/after-slot: at 0.4 opacity
   the biggest thing on the screen was a dead button). It waits, in the studio's
   own veil, until there is something to keep. */
.slotsheet .btn[disabled] {
  background: var(--veil); color: var(--ink-3); pointer-events: none;
}
/* The seat, said back on the thank-you and on the pass. */
#done-kept { padding-top: 4px; }
/* The way out is left-aligned, all of it (the wayout rebuild, 2026-09-06):
   the pick sat centred over a left-aligned "The whole day" under it. */
#end-next-pick { justify-content: flex-start; }
#done-slot-open { align-self: center; }
#slot-mine { color: var(--ink); }

/* Reduced motion REPLACES the arrival, it does not shorten it (§2.1): the
   five elements appear together, hold, and the whole layer cross-fades out
   over 240 ms — same words, same length, nothing that slides or parts. */
@media (prefers-reduced-motion: reduce) {
  .arrive-mark, .arrive-wordmark, .arrive-welcome, .arrive-line, .arrive-fact { transition-delay: 0ms !important; transform: none !important; }
  /* The invitation arrives WITH the five, and there is nothing to pull: a
     press or a swipe cross-fades the whole layer away (arrive.js wires no
     drag here at all, so `--drag` never moves off 0). */
  .arrive-invite { transition-delay: 0ms !important; }
  .arrive-half { transition-duration: 0s !important; }
  .arrive.open .arrive-half { animation: none !important; }
  .arrive.open .arrive-face { opacity: 1 !important; }
  .arrive.open { opacity: 0; transition: opacity 240ms var(--ease) !important; transition-duration: 240ms !important; }
}

/* September 2026 · the Studio family.
   Layout only: the same page IDs, routes, pass states and transport stay intact.
   Photographs belong to discovery, never behind form fields or room controls. */
.page > *, .doorbody > *, .spread > * { min-width: 0; }
.page .h1 { text-wrap: balance; }
.page > .deskhead { margin-bottom: 8px; }
.deskfooter { gap: 4px 8px; padding: 18px 0 0; border-top: 1px solid var(--hair); }
.deskfooter > a, .deskfooter > button, .footmore > summary { padding: 0 12px; }
.footmore { position: relative; }
.footmore > summary {
  display: flex; align-items: center; gap: 8px; min-height: 44px;
  cursor: pointer; list-style: none; font-size: 13px; color: var(--ink-2);
}
.footmore > summary::-webkit-details-marker { display: none; }
.footmore > summary::after { content: '+'; font-size: 18px; }
.footmore[open] > summary::after { content: '−'; }
.footmore-links {
  position: absolute; right: 0; bottom: calc(100% + 8px); z-index: 20;
  width: 236px; padding: 8px; display: grid; background: var(--panel);
  border: 1px solid var(--card-edge); border-radius: var(--r-md); box-shadow: var(--lift);
}
.deskfooter .footmore-links a { justify-content: flex-start; color: var(--ink); padding: 10px 12px; }
.deskfooter a:hover, .footmore summary:hover { background: var(--veil); border-radius: var(--r-sm); }
.hero::before {
  background: linear-gradient(180deg, rgba(0,0,0,.06) 10%, rgba(0,0,0,.12) 35%, rgba(0,0,0,.86) 100%), var(--session-art) center / cover;
}
.hero .markbox { opacity: .85; }
.hero-foot .eyebrow, .hero-foot .body, .hero-foot .entline { color: #FFFFFF; }
.hero-pill .pill { background: rgba(0,0,0,.64); }
.hero-foot { left: 24px; right: 24px; bottom: 26px; }
#hero-milestone { text-align: left; padding: 12px 0; line-height: 1.5; }
#lobby { gap: 28px; }
#lobby .homepass { gap: 22px; }
#lobby .worldmap { margin-top: 8px; }
#lobby .doors { margin-right: -4px; }
#lobby .board { flex-direction: row; gap: 14px; overflow-x: auto; scroll-snap-type: x proximity; padding: 2px 0 12px; }
#lobby .dep { flex: 0 0 250px; flex-direction: column; align-items: flex-start; scroll-snap-align: start; }
#lobby .dep-time { width: auto; flex-direction: row; align-items: baseline; gap: 8px; }
#lobby .dep-what { width: 100%; }
#lobby .dep-what .n { -webkit-line-clamp: 3; }
#lobby .dep-who { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; text-align: left; }
#lobby .dep-who .f { flex-basis: 100%; }
#lobby .dep .dep-mark { display: none; }
#lobby .dep { padding: 20px; min-height: 210px; }
#lobby .dep:not(.full):not(.mine) { border: 1px solid var(--hair); border-radius: var(--r-lg); }
.door .tile::before { opacity: .55; }
#door-title { min-height: 0; text-wrap: balance; }
.doorhead { min-height: 410px; }
.doorhead .bigmark { top: 106px; }
.doorbody > .stack-8 { gap: 14px; }
.doorbody > .spread { flex-direction: column; align-items: flex-start; gap: 12px; }
.doorbody #door-hours, .doorbody [data-t="privacy.line"] { font-size: 14px; }
#door-share { align-self: flex-start; }
#door-share-said:empty { display: none; }
/* ── /me AS CARDS (his call, 2026-09-09) ────────────────────────────────────
   *"High end elements, fully card based design, and very intuitive — right
   now it's a bunch of text."* The pass stays the reference object, centred;
   everything under it is `.mecard` — the one card, in the one rule above —
   with an icon disc or a photograph to anchor the eye, one title, one line,
   one action. The cards sit in groups (`.megroup`) that read my pass → what
   it opens next → keep it safe → the way out, and a group with nothing
   showing in it is not drawn, so the empty state is the blank card, the two
   ways forward and the door. No new keyframe, no new radius, no new picture:
   the two scenes are the lake and the mountain off `--session-art`. */
.mepage { gap: 22px; }
.mepage .freepass-me { max-width: 430px; width: 100%; align-self: center; }
.mepage .stamprow { align-self: stretch; }
.megroup { display: flex; flex-direction: column; gap: 14px; }
.megroup-title { padding: 6px 2px 0; }
/* A group that has no card showing is not on the page. The account card is
   hidden by what it holds, not by itself — linkcard.js toggles the section,
   and the card around it follows — so it is asked separately (`:has()` may
   not nest). */
.megroup:not(:has(> .mecard:not([hidden]):not(#account-card))):not(:has(> #account-card > #account-save:not([hidden]))) { display: none; }
.mecard:has(> #account-save[hidden]) { display: none; }
.mecard { padding: 20px; gap: 10px; }
.mecard > .eyebrow, .mecard > .spread .eyebrow, .mecard > .accountsave > .eyebrow { color: var(--ink); }
.mecard > .body, .mecard > .accountsave > .body { color: var(--ink-2); }
.mecard-icon {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 40px; height: 40px; margin-bottom: 4px;
  border-radius: 50%; background: var(--veil); color: var(--ink);
}
.mecard-icon .i { width: 20px; height: 20px; }
.mecard .textlink, .mecard .quiet-link { align-self: flex-start; padding-left: 0; color: var(--ink); }
.mecard .textlink::after { content: ' ›'; }
.mepage #pass-delete { color: var(--ink-3); }
.mepage #pass-delete::after { content: none; }
.mecard .deleteask .textlink::after { content: none; }
.mecard .btn, .mecard .btn-outline { width: 100%; margin-top: 4px; }
/* The account's own words flow into the card round it, left-aligned like
   every other card, and its two ways in are drawn as outline buttons. */
.mecard > .accountsave { display: contents; text-align: left; }
.mecard > .accountsave .quiet-btn {
  border: 1px solid var(--ink); border-radius: var(--r-pill); min-height: 48px; color: var(--ink);
}
.mecard > .accountsave .hint:empty { display: none; }
/* A scene card: the photograph across the top, the words under it. */
.mecard-scene { overflow: hidden; padding-top: 0; }
.mecard-scene::before {
  content: ''; display: block; height: 132px; margin: 0 -20px 10px;
  background: linear-gradient(180deg, rgba(0,0,0,.08), rgba(0,0,0,.42)), var(--session-art) center 45% / cover;
}
.mecard-held { color: var(--ink); font-weight: 600; }
/* The empty state is a card too: the pass glyph in gold, the line, the two ways. */
.passempty { padding: 24px 20px; gap: 12px; }
.passempty .mecard-icon { align-self: center; color: var(--gold); margin-bottom: 0; }
.passempty .btn { width: 100%; }
/* The ledger is a card of its rows; a new card with nothing on it says so on one. */
.ledger.mecard { padding: 8px 20px 12px; }
.ledger.mecard .lmonth:first-child .eyebrow { padding-top: 10px; }
.ledger.mecard .lrow.lit { margin: 0 -12px; padding-left: 12px; padding-right: 12px; }
#ledger-empty.mecard { padding: 20px; }
.mepage .wayout { border-top: 0; padding-top: 20px; }
.mepage .deleteask { border-radius: var(--r-md); }
.shopcard .shopcard-opens { gap: 12px; }
.shopcard > .btn, .shopcard > .shopcard-soon, .shopcard > .shopcard-held { margin-top: auto; }
.shopcard[data-kind="keep"], .shopcard[data-kind="night"] { background: var(--ground); }
.helpindex-name { padding-top: 18px; padding-bottom: 18px; }
.helpindex-item .helpsheet-lines { line-height: 1.6; }
.formcard .h1 { font-size: 28px; }
.helpsheet-card { max-width: 560px; }
.welcome-col { max-width: 500px; }
.studio-card::before {
  content: ''; height: clamp(90px, 22vh, 190px); border-radius: var(--r-lg);
  background: url('/img/studio-lake.webp') center 45% / cover; margin-bottom: 8px;
}
.studio-card .h2 { max-width: 20ch; }
.studio-card .body { max-width: 40ch; }
.firstfield { border: 1px solid var(--hair); border-radius: var(--r-xl); }
.firstfield::before {
  background: linear-gradient(180deg, rgba(0,0,0,.22), rgba(0,0,0,.88)), url('/img/studio-lake.webp') center / cover;
}
.firstfield .body, .firstfield .eyebrow, .firstfield .firstmeta { color: #FFFFFF; }
@media (min-width: 760px) {
  .page { padding-top: 40px; padding-bottom: 40px; gap: 28px; }
  #lobby, .schedulepage, .page:has(.shopcards) { max-width: 1120px; }
  .mepage { max-width: 960px; }
  .mepage > .h2 { text-align: center; }
  .megroup { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 24px; align-items: stretch; }
  .megroup > .megroup-title, .megroup > .ledger, .megroup > #ledger-more, .megroup > #ledger-empty, .megroup > .wayout { grid-column: 1 / -1; }
  .wayout > .body { max-width: 56ch; }
  .mecard { padding: 24px; }
  .mecard-scene::before { margin: 0 -24px 12px; height: 150px; }
  .mecard-scene > .textlink, #slot-block > .textlink, #pass-block > .textlink { margin-top: auto; }
  .mecard .btn, .mecard .btn-outline, .mecard > .accountsave .quiet-btn { max-width: 380px; }
  #lobby { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-content: start; gap: 28px 32px; }
  #lobby > * { grid-column: 1 / -1; }
  #lobby > .hero { height: 440px; }
  #lobby > #hero-milestone, #lobby > #hero-cta, #lobby > #nextline { max-width: 560px; }
  #lobby > #breathe { grid-column: 1; align-self: stretch; height: auto; min-height: 180px; }
  #lobby > #worldmap { grid-column: 2; margin-top: 0; }
  #lobby .hero-foot .h2 { max-width: 16ch; }
  #lobby .homepass { display: flex; }
  #lobby .weekcard { flex-direction: row; justify-content: space-between; align-items: center; }
  #lobby .weekcard > .btn { width: auto; max-width: 380px; padding-left: 24px; padding-right: 24px; }
  .shopcards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
  .shopcard { padding: 28px; gap: 24px; }
  .schedulepage .days { gap: 40px; }
  .schedulepage .dayp { padding: 24px; border: 1px solid var(--hair); border-radius: var(--r-lg); }
  .schedulepage .dayp + .dayp { margin-top: 16px; }
  .doorpage { max-width: 900px; }
  .doorhead { border-radius: 0 0 var(--r-xl) var(--r-xl); min-height: 460px; padding: 30px 40px; }
  .doorhead .bigmark { top: 130px; }
  .doorhead > .stack-12 { max-width: 540px; }
  .doorbody { padding: 32px 40px; }
  .doorbody > .stack-8, .doorbody > .stack-16, .doorbody > .band { max-width: 600px; width: 100%; align-self: center; }
  .firstpage { max-width: 680px; }
  .firstfield { min-height: 340px; }
  .welcome-col { padding-top: 32px; }
  .stage { max-width: 600px; }
  .stage .screen { max-width: 520px; margin-left: auto; margin-right: auto; }
}
@media (max-width: 380px) {
  .page { padding-left: 18px; padding-right: 18px; }
  .doorpage { padding: 0; }
  .hero { height: 380px; }
  .hero-foot { left: 18px; right: 18px; }
  .playcircle { width: 52px; height: 52px; }
  .deskfooter { gap: 0; }
  .deskfooter > a, .deskfooter > button, .footmore > summary { padding: 0 9px; }
}
@media (max-height: 700px) {
  .studio-card::before { height: 64px; }
  .studio { gap: 18px; }
}

/* A stable visual identity for each session family, shared by card and room.
   Decorative images: titles and approved marks remain the accessible identity.
   This table uses only local assets; it needs no content or database migration. */
:root { --session-art: url('/img/studio-lake.webp'); }
[data-mark="group"] { --session-art: url('/img/studio-bridge.jpg'); }
[data-mark="gap"] { --session-art: url('/img/studio-river-crossing.jpg'); }
[data-mark="rings"] { --session-art: url('/img/studio-mountain.jpg'); }
[data-mark="alone"] { --session-art: url('/img/studio-desert-caravan.jpg'); }
[data-mark="breathing"], [data-mark="plain"] { --session-art: url('/img/studio-lake.webp'); }
#lobby .dep { overflow: hidden; gap: 8px; }
#lobby .dep::before {
  content: ''; display: block; flex-shrink: 0; height: 112px;
  width: calc(100% + 40px); margin: -20px -20px 10px;
  background: var(--session-art) center / cover;
}
.door .tile::before {
  background: linear-gradient(180deg, rgba(0,0,0,.12), rgba(0,0,0,.5)), var(--session-art) center / cover;
  opacity: 1;
}
.door .tile .markbox { color: #FFFFFF; }
.doorhead::before {
  background: linear-gradient(180deg, rgba(0,0,0,.6), rgba(0,0,0,.18) 30%, rgba(0,0,0,.84)), var(--session-art) center / cover;
}
.doorhead { color: #FFFFFF; --live: var(--night-live); --warn: #F08C80; --ink-3: var(--night-ink-2); }
.doorhead .h1, .doorhead .eyebrow, .doorhead .bigmark { color: #FFFFFF; }
.doorhead .pill { background: rgba(0,0,0,.65); color: #FFFFFF; }
.doorhead .pill.solid { background: #FFFFFF; color: #000000; }
.sit:not(.sit-none) { overflow: hidden; min-height: 218px; }
.sit:not(.sit-none)::before {
  content: ''; display: block; height: 80px; flex-shrink: 0;
  margin: -14px -14px 6px; background: var(--session-art) center / cover;
}
.dayp::before, .shopdoor::before {
  content: ''; display: block; height: 150px; margin-bottom: 18px;
  border-radius: var(--r-md); background: var(--session-art) center / cover;
}
.breathe { --session-art: url('/img/studio-lake.webp'); color: #FFFFFF; }
.breathe::before { background: linear-gradient(90deg, rgba(0,0,0,.74), rgba(0,0,0,.62)), var(--session-art) center / cover; }
.breathe .faint, .breathe .body, .breathe .markleft { color: #FFFFFF; }
.reeltile::before { background: linear-gradient(180deg, rgba(0,0,0,.16), rgba(0,0,0,.88)), var(--session-art) center / cover; }
.stage::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  /* The room is the same scene as every entry (his call, 2026-09-09 evening:
     "the same kind of interface, full screen, when you are joining any
     breathing session with the timer in between — a uniform view"). The
     picture stays; the ring sits in the middle of it; the words at the foot
     stand on the dark. The rested screen is still pure black (the OLED law). */
  background: linear-gradient(180deg, rgba(0,0,0,0.62) 0%, rgba(0,0,0,0.34) 26%, rgba(0,0,0,0.48) 52%, rgba(0,0,0,0.88) 76%, #000000 100%), var(--session-art) center 40% / cover;
}
@media (min-width: 760px) {
  .schedulepage .daygroup { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; align-items: start; }
  .schedulepage .daygroup > .eyebrow { grid-column: 1 / -1; }
  .schedulepage .dayp + .dayp { margin-top: 0; }
}

/* Labelled destinations remain visible before a sitting, never during it. */
.studio-nav { grid-column: 1 / -1; grid-row-start: 3; display: flex; width: 100%; gap: 4px; padding: 4px; margin-top: 12px; border: 1px solid var(--hair); border-radius: var(--r-lg); background: var(--panel); }
.studio-nav a { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 44px; padding: 8px 4px; border-radius: var(--r-md); font-size: 12px; font-weight: 600; color: var(--ink-2); text-decoration: none; }
.studio-nav a[aria-current="page"], .studio-nav a:hover { color: var(--ink); background: var(--hair); }
#lobby .passstrip { background: transparent; border: 0; border-bottom: 1px solid var(--hair); border-radius: 0; padding: 4px 0 20px; }
.primersheet .helpsheet-card::before { content: ''; display: block; flex: 0 0 auto; width: 100%; height: clamp(100px, 24dvh, 220px); border-radius: var(--r-lg); background: url('/img/studio-lake.webp') center 45% / cover; }
.primersheet .primer-mark { display: none; }
.primersheet .helpsheet-card { gap: 18px; height: 100dvh; min-height: 0; max-height: 100dvh; overflow-y: auto; justify-content: safe center; }
@media (min-width: 760px) { .studio-nav { max-width: 480px; justify-self: center; } }

/* One quiet frame on every page, including the experience join screen. */
.deskhead {
  display:block; flex:none; width:100%; min-width:0; height:auto; min-height:0;
  position:relative; margin:0; padding:0; box-sizing:border-box;
  border:0; border-radius:0; background:transparent; color:var(--ink);
}
.deskbar {
  display:grid; grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);
  align-items:center; gap:6px; min-height:48px; width:100%;
}
.deskleft { grid-column:1; display:flex; align-items:center; min-width:0; gap:4px; }
.deskhead .backlink { flex:0 0 32px; width:32px; min-width:0; margin:0; }
.deskhead .wordmark {
  grid-column:2; grid-row:1; justify-self:center; display:flex; align-items:center;
  gap:7px; min-height:44px; padding:0; text-decoration:none; letter-spacing:.12em;
}
.deskhead .listen-brand-mark { width:23px; height:23px; flex:0 0 23px; color:var(--ink); }
.listen-brand-name { display:flex; flex-direction:column; align-items:center; font-size:11px; font-weight:750; line-height:1.2; }
.listen-brand-product { margin-top:3px; font-size:12px; font-weight:500; letter-spacing:.03em; color:var(--ink-2); }
.deskhead .livedot {
  position:static; width:auto; flex:0 1 auto; min-width:0; max-width:100%;
  justify-content:flex-start; font-size:9px; gap:3px; pointer-events:auto;
}
.deskhead .livedot-dot { width:5px; height:5px; }
.deskhead .headright { grid-column:3; grid-row:1; display:flex; justify-self:end; gap:0; }
.deskhead .headright .iconbtn { width:44px; height:44px; border-radius:50%; }
.deskhead .headright .iconbtn:hover { background:var(--hair); }
.deskhead .headright .i { width:21px; height:21px; }
.deskhead .studio-nav {
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  width:100%; max-width:none; margin:10px 0 0; padding:3px;
  border:1px solid var(--hair); border-radius:var(--r-md); background:var(--panel); gap:3px;
}
.deskhead .studio-nav a {
  min-width:0; min-height:40px; padding:6px 3px; gap:5px; border-radius:var(--r-sm);
  text-align:center; font-size:11px; line-height:1.3;
}
.deskhead .studio-nav .i { width:15px; height:15px; flex:0 0 15px; }
.page > .deskhead { margin-bottom:0; }
.stage > .roomhead { margin:0 0 24px; padding:0; flex:none; }
.stage > .screen { min-width:0; }
.gatering { flex:0 0 136px; isolation:isolate; }
.gatering::before { width:100%; max-width:280px; height:136px; }
.doorpage > .deskhead { width:calc(100% - 36px); margin:18px 18px 20px; }
.doorhead { justify-content:flex-start; gap:28px; min-height:0; padding:32px 24px; }
.doorhead .bigmark { position:static; inset:auto; flex:none; min-height:110px; }
.doorhead > .stack-12 { position:relative; width:100%; min-width:0; }
@media(min-width:760px) {
  .deskbar { gap:12px; }
  .listen-brand-name { font-size:12px; }
  .listen-brand-product { font-size:12px; }
  .deskhead .livedot { font-size:11px; }
  .deskhead .studio-nav a { font-size:12px; }
  .doorpage > .deskhead { width:calc(100% - 80px); margin:0 40px 24px; }
  .doorhead { padding:40px; gap:32px; }
}

/* ── THE ENTRY IS A SCENE (his call, 2026-09-09) ────────────────────────────
   "Anywhere you are entering into an experience on Listen, dramatize the entry
   with UI elements which make it look like people are entering an immersive
   experience — full screen, full bleed image, both for desktop and mobile, the
   same UI language as aatam.app." The homepage's hero is the reference: the
   photograph fills the viewport edge to edge, the head floats over it in
   white, the words sit at the foot on the left, the way in at the foot on the
   right, and one quiet ↓ says there is more below.
   Four surfaces carry it — the lobby's hero, a door's head, the first
   experience's threshold and the room's gate — and two layers open onto the
   same picture: the arrival's doors and the first-sitting sheet. The pictures
   are the five the desk already holds (`--session-art`, by family); nothing
   new is fetched, nothing decorative moves that did not move before. */
body { overflow-x: clip; }
.firstpage, .doorpage, #lobby { position: relative; padding-top: 0; }
/* The head floats over the picture, white, on a soft ground under the tabs. */
.firstpage > .deskhead, .doorpage > .deskhead, #lobby > .deskhead {
  position: absolute; z-index: 5; top: calc(12px + env(safe-area-inset-top));
  left: calc(50% - 50vw + 24px); width: calc(100vw - 48px); margin: 0;
  --ink: #FFFFFF; --ink-2: rgba(255,255,255,0.84); --ink-3: rgba(255,255,255,0.72);
  --panel: rgba(0,0,0,0.42); --hair: rgba(255,255,255,0.22); --veil: rgba(255,255,255,0.12);
  --live: var(--night-live); --warn: #F08C80;
  color: #FFFFFF;
}
/* The scene itself: one surface, three names. */
#lobby > .hero, .doorhead, .firstfield {
  position: relative; isolation: isolate; z-index: 0;
  width: 100vw; max-width: none; margin-left: calc(50% - 50vw); margin-right: 0;
  min-height: 100svh; height: auto;
  border: 0; border-radius: 0; box-shadow: none; background: var(--night);
  padding: calc(150px + env(safe-area-inset-top)) 24px calc(76px + env(safe-area-inset-bottom));
  /* A grid, not a stack: the mark owns whatever is left above the words, so the
     two can never meet on a tall foot or a short screen (his read, 09-09). */
  display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto auto; row-gap: 22px; align-items: end;
  color: #FFFFFF; text-align: left; overflow: hidden;
}
#lobby > .hero { order: -1; }
#lobby > .hero::before, .doorhead::before, .firstfield::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: 0; opacity: 1;
  background: linear-gradient(180deg, rgba(0,0,0,0.56) 0%, rgba(0,0,0,0.12) 24%, rgba(0,0,0,0.08) 46%, rgba(0,0,0,0.56) 70%, rgba(0,0,0,0.90) 100%), var(--session-art) center 45% / cover;
}
/* One quiet ↓ at the foot, the homepage's own cue: a hairline and an arrow. */
#lobby > .hero::after, .doorhead::after, .firstfield::after {
  content: '↓'; position: absolute; left: 50%; bottom: calc(14px + env(safe-area-inset-bottom));
  transform: translateX(-50%); padding-top: 22px;
  font-size: 13px; line-height: 1; color: rgba(255,255,255,0.78);
  background: linear-gradient(transparent, rgba(255,255,255,0.7)) no-repeat 50% 0 / 1px 16px;
  pointer-events: none;
}
/* The mark stands in the middle of the field, as it always did on the tile. */
#lobby > .hero .markbox, .doorhead .bigmark, .firstfield .firstmark {
  position: static; inset: auto; min-height: 0; padding: 0; transform: none;
  grid-row: 1; grid-column: 1 / -1; align-self: center; justify-self: center;
  display: flex; justify-content: center; align-items: center;
  color: #FFFFFF; opacity: 0.92; pointer-events: none;
}
#lobby > .hero .hero-pill { grid-row-start: 2; grid-column: 1 / -1; }
#lobby > .hero .hero-foot { grid-row-start: 3; grid-column: 1 / -1; }
.doorhead > .stack-12, .firstfield > .stack-8 { grid-row-start: 2; }
.doorhead > .scene-entry, .firstfield > .firstmeta { grid-row-start: 3; }
.firstfield .firstmark .i { width: 56px; height: 56px; }
/* The foot: the words on the left, the way in on the right. */
#lobby > .hero .hero-pill { position: static; align-self: flex-start; }
#lobby > .hero .hero-foot { position: static; display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; width: 100%; }
#lobby > .hero .hero-foot .h2 { font-size: clamp(32px, 4.2vw, 52px); font-weight: 500; line-height: 1.08; letter-spacing: -0.03em; max-width: 16ch; color: #FFFFFF; }
#lobby > .hero .hero-foot .eyebrow, #lobby > .hero .hero-foot .body, #lobby > .hero .hero-foot .entline { color: rgba(255,255,255,0.86); }
#lobby > .hero .hero-foot .body { max-width: 44ch; }
.doorhead > .stack-12, .firstfield > .stack-8 { position: relative; width: 100%; min-width: 0; align-items: flex-start; text-align: left; }
.doorhead .h1, .firstfield .h1 { font-size: clamp(32px, 4.2vw, 52px); font-weight: 500; line-height: 1.08; letter-spacing: -0.03em; max-width: 16ch; color: #FFFFFF; }
.doorhead .eyebrow, .firstfield .eyebrow, .firstfield .body, .firstfield .firstmeta { color: rgba(255,255,255,0.86); }
.firstfield .body { max-width: 46ch; font-size: 17px; }
.firstfield .firstmeta { font-size: 13px; }
#door-title { min-height: 0; }
.scene-entry { display: flex; flex-direction: column; gap: 10px; width: 100%; }
.scene-entry .body { color: rgba(255,255,255,0.86); font-size: 14px; }
.scene-entry .btn { width: 100%; }
#door-hours[hidden] { display: none; }
/* Desktop: the head spans the page like the homepage's, the tabs stay a
   compact bar, and the foot is two columns — words left, the way in right. */
@media (min-width: 760px) {
  .firstpage > .deskhead, .doorpage > .deskhead, #lobby > .deskhead {
    left: calc(50% - 50vw + 40px); width: calc(100vw - 80px); margin: 0; top: calc(16px + env(safe-area-inset-top));
  }
  .firstpage > .deskhead .studio-nav, .doorpage > .deskhead .studio-nav, #lobby > .deskhead .studio-nav { max-width: 480px; margin-left: auto; margin-right: auto; }
  #lobby > .hero, .doorhead, .firstfield {
    padding: calc(160px + env(safe-area-inset-top)) 40px calc(84px + env(safe-area-inset-bottom));
    grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: minmax(0, 1fr) auto; column-gap: 48px; row-gap: 22px;
    border-radius: 0; min-height: 100svh;
  }
  .doorhead > .stack-12, .firstfield > .stack-8 { grid-row-start: 2; grid-column: 1; max-width: 640px; }
  .doorhead > .scene-entry, .firstfield > .firstmeta { grid-row-start: 2; grid-column: 2; align-self: end; }
  .scene-entry { width: 340px; }
  .firstfield .firstmeta { padding-bottom: 6px; }
}
/* The arrival's doors open onto the picture: each half draws the whole
   photograph pinned to its own edge, so the two halves are one image until
   they part. The words ride above it on their own half. */
.arrive-half::before {
  content: ''; position: absolute; top: 0; bottom: 0; width: 100vw; z-index: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0.46), rgba(0,0,0,0.58) 55%, rgba(0,0,0,0.88)), var(--session-art) center 45% / cover;
}
.arrive-left::before { left: 0; }
.arrive-right::before { right: 0; }
.arrive-face { z-index: 1; }
/* The first-sitting sheet, asked over a door, is the same scene: the picture
   behind the whole sheet, the words at its foot. */
.primersheet .helpsheet-card {
  max-width: none; justify-content: flex-end; color: #FFFFFF;
  background: linear-gradient(180deg, rgba(0,0,0,0.56) 0%, rgba(0,0,0,0.30) 40%, rgba(0,0,0,0.88) 78%, #000000 100%), var(--session-art) center 45% / cover;
}
.primersheet .helpsheet-card::before { display: none; }
.primersheet .eyebrow, .primersheet .h2, .primersheet .body, .primersheet .faint, .primersheet .textlink, .primersheet .quiet-link { color: #FFFFFF; }
.primersheet .quiet-link, .primersheet .primer-foot { color: rgba(255,255,255,0.78); }
@media (min-width: 760px) {
  .primersheet .primer-card { padding-left: 40px; padding-right: 40px; }
  .primersheet .primer-card > * { max-width: 640px; }
}
/* The door itself — the pass, in their name — opens on the same picture. The
   layer used to start under the head on the desk's own ground; now it is the
   scene from the top, the head floating over it as on every entry, and its
   words in the night set so both themes read the same on the photograph. The
   two studio cards lose their own strip of the lake: the lake is behind them. */
.welcome {
  top: 0; padding-top: calc(118px + env(safe-area-inset-top));
  background: linear-gradient(180deg, rgba(0,0,0,0.58) 0%, rgba(0,0,0,0.50) 30%, rgba(0,0,0,0.80) 62%, rgba(0,0,0,0.94) 100%), var(--session-art) center 45% / cover;
  background-attachment: fixed;
  --ground: #000000; --panel: #111111; --panel-2: #191919;
  --ink: #FBF6EF; --ink-2: #D9D4CC; --ink-3: #AAAAAA;
  --hair: rgba(255,255,255,0.18); --hair-2: rgba(255,255,255,0.30); --veil: rgba(255,255,255,0.10);
  --btn-bg: #FBF6EF; --btn-ink: #000000; --card-edge: rgba(255,255,255,0.16);
  color: var(--ink);
}
html.welcoming #lobby > .deskhead { z-index: 45; }
.welcome .studio-card::before { display: none; }
.welcome .studio-card { background: transparent; border: 0; box-shadow: none; padding: 0; }
@media (min-width: 760px) {
  .welcome { padding-top: calc(132px + env(safe-area-inset-top)); }
  .welcome-col { padding-left: 40px; padding-right: 40px; }
}
/* Two lines at the foot, never spread up the field: the pill's own row and
   the words' row both sit at the bottom on a wide screen. */

/* The welcome's words sit at the foot of the picture, like every entry; the
   pass card, when it is up, simply fills the column. */
.welcome-col { min-height: calc(100dvh - 118px - env(safe-area-inset-top)); justify-content: flex-end; box-sizing: border-box; }
@media (min-width: 760px) { .welcome-col { min-height: calc(100dvh - 132px - env(safe-area-inset-top)); } }
/* The arrival's poster is the picture now, so the face draws no field of its own. */
.arrive-face { background: transparent; }

/* THE HERO CARRIES THE WAY IN (his read of the live site, 2026-09-09 evening:
   "the hero itself has the joining possibility … who is in … their pass on
   the desktop, without scrolling"). The play circle says Come in in words; the
   pill above the title already says Open now and how many are in; and on a
   wide screen the compact pass floats inside the scene, top right, under the
   head. Nothing new is fetched — homepass.js draws the same strip it always did. */
#lobby > .hero .playcircle { width: auto; height: 56px; padding: 0 22px 0 18px; gap: 10px; border-radius: var(--r-pill); }
#lobby > .hero .playword { font-size: 16px; font-weight: 600; white-space: nowrap; }
@media (min-width: 760px) {
  /* Bottom right, in the column of the Come in button, never floating in the sky (his read, 09-09). */
  /* The strip shares the hero's grid row, so it is anchored to the hero's own
     foot: level with the words on the left, and above Come in when there is a
     Come in (the studio may be closed — then it sits where the button would). */
  #lobby > .hero { grid-row: 1; grid-column: 1 / -1; }
  /* The hero escapes the page's 1120px box to fill the viewport; the strip must
     escape with it, or it hangs 200px in from the button on a wide screen (his
     read, 09-09 late). Percent margins on a grid item resolve against the grid
     area, so this lands its right edge exactly 40px from the viewport's. */
  #lobby > #homepass { grid-row: 1; grid-column: 1 / -1; align-self: end; justify-self: end; z-index: 2; width: 380px; margin: 0 calc(50% - 50vw + 40px) calc(84px + env(safe-area-inset-bottom)) 0; }
  #lobby:has(#hero-play:not([hidden])) > #homepass { margin-bottom: calc(156px + env(safe-area-inset-bottom)); }
}
@media (min-width: 760px) {
  #lobby > #homepass .passstrip { background: rgba(0,0,0,0.58); border: 1px solid rgba(255,255,255,0.22); border-radius: var(--r-lg); padding: 16px 18px; color: #FFFFFF; --ink: #FFFFFF; --ink-2: rgba(255,255,255,0.84); --ink-3: rgba(255,255,255,0.72); }
}
/* THE JOIN SCREEN, WIDE (his call, 2026-09-09 evening: "every join screen —
   a scheduled session, a host session, any session — has the same design
   language: the pass, who is in, everything ready for the person, high-end
   and intuitive"). On a phone the gate is the column it always was. On a wide
   screen it takes the hero's composition: the clock and who is in on the
   left, big, at the foot of the picture; the pass and the way in on the
   right. Same markup, same ids, nothing moves in the DOM. */
@media (min-width: 1000px) {
  .stage:has(#screen-join.on) { max-width: 1120px; padding-left: 40px; padding-right: 40px; }
  .stage:has(#screen-join.on) > .roomhead { margin-bottom: 0; }
  .stage:has(#screen-join.on) > #screen-join { max-width: none; margin-left: 0; margin-right: 0; }
  .stage:has(#screen-join.on) .studio-nav { max-width: 480px; margin-left: auto; margin-right: auto; }
  #screen-join.on { display: grid; grid-template-columns: minmax(0, 1fr) 480px; column-gap: 64px; row-gap: 12px; align-content: end; flex: 1 1 auto; padding: 28px 0 24px; }
  #screen-join > .gatering { grid-column: 1; grid-row: 1 / span 8; align-self: end; justify-self: center; width: 240px; height: 240px; flex-basis: 240px; margin-bottom: 40px; }
  #screen-join > .gatering .ringsvg { width: 240px; height: 240px; }
  #screen-join > .gatering::before { width: 480px; height: 480px; }
  #screen-join > .gatering .gatecount { font-size: 52px; }
  #screen-join > .gatering .gateunder { font-size: 16px; }
  #screen-join > :not(.gatering) { grid-column: 2; }
}
/* No "Demo preview" label on any screen (his call, 2026-09-09 evening). The
   demo numbers themselves are his to keep; the word goes. */
#demo-note { display: none !important; }

/* THE WAY IN WEARS HEADPHONES (his call, 2026-09-09 evening: "wherever there is
   a button which gets you into the experience, it should carry a headphone
   icon so people know"). Drawn by the stylesheet, so a button whose words are
   rewritten by a script keeps its icon. The glyph is the sprite's own. */
#join::before, #first-begin::before, #end-next-btn::before, #end-ladder-btn::before, #tour-start::before {
  content: ''; display: inline-block; width: 20px; height: 20px; margin-right: 10px; flex-shrink: 0;
  background: currentColor; vertical-align: -4px;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14v-2a8 8 0 0 1 16 0v2'/%3E%3Cpath d='M4 14h2.6a1.4 1.4 0 0 1 1.4 1.4v3.2A1.4 1.4 0 0 1 6.6 20H4Z'/%3E%3Cpath d='M20 14h-2.6a1.4 1.4 0 0 0-1.4 1.4v3.2a1.4 1.4 0 0 0 1.4 1.4H20Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14v-2a8 8 0 0 1 16 0v2'/%3E%3Cpath d='M4 14h2.6a1.4 1.4 0 0 1 1.4 1.4v3.2A1.4 1.4 0 0 1 6.6 20H4Z'/%3E%3Cpath d='M20 14h-2.6a1.4 1.4 0 0 0-1.4 1.4v3.2a1.4 1.4 0 0 0 1.4 1.4H20Z'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Three things, then the sound: one sheet, three rows, one button. */
.steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.steprow { display: flex; align-items: center; gap: 14px; }
.stepmark { width: 44px; height: 44px; flex-shrink: 0; border-radius: 50%; border: 1px solid var(--hair-2); display: flex; align-items: center; justify-content: center; color: var(--ink); }
.steps-card { gap: 20px; }
.steps-card > .btn { margin-top: 4px; }
/* A way in that cannot be taken right now is dimmed and says why, never hidden
   (his call, 2026-09-09 evening: "disable the button, never hide stuff"). */
.hero[data-off="true"] .playcircle { opacity: 0.45; }
/* THE PLAYING SCREEN, IN THE SCENE'S OWN TYPE (his read, 2026-09-09 evening:
   "see if it needs any high-end treatment — that page seems very basic").
   Nothing new is put on the screen — the room's rule stands — only the size
   of what is there: the piece's name as a title, the ring larger on a wide
   screen, and the foot spread like every other scene's. */
.roomscreen .playhead { flex-wrap: wrap; align-items: baseline; row-gap: 4px; }
.roomscreen #play-meta { flex-basis: 100%; font-size: 12px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2); }
.roomscreen #play-title { font-size: 22px; font-weight: 500; line-height: 1.15; letter-spacing: -0.02em; color: var(--ink); }
.roomscreen .playfoot { font-size: 14px; }
.roomscreen .playfoot .clock { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ink-2); }
@media (min-width: 1000px) {
  .stage:has(.roomscreen.on) { max-width: 1120px; padding-left: 40px; padding-right: 40px; }
  .stage:has(.roomscreen.on) > .roomscreen { max-width: none; margin: 0; }
  .roomscreen #play-title { font-size: 34px; letter-spacing: -0.03em; max-width: 20ch; }
  .roomscreen .playmid { margin-top: min(8vh, 72px); margin-bottom: min(10vh, 88px); }
  .roomscreen .dot { transform: scale(1.35); }
  .roomscreen .playmid::before { width: 460px; height: 460px; top: -60px; }
  .roomscreen .playmid .hint { margin-top: 44px; font-size: 16px; }
  .roomscreen .playfoot { flex-direction: column; }
}

/* THE GATE'S FOOT, QUIET AND HIGH-END (his read, 2026-09-09 late). Two standing
   facts as icon lines; the crowd as one sentence over the flags; the flags as
   a wrapped row of trust — all of it under the button, where it was. */
.gatefoot .gateline { display: flex; align-items: center; gap: 10px; }
.gatefoot .gateline::before { content: ''; width: 16px; height: 16px; flex-shrink: 0; background: currentColor; opacity: 0.9; -webkit-mask: var(--fact-icon) center / contain no-repeat; mask: var(--fact-icon) center / contain no-repeat; }
.gatefoot .gateline[data-t="privacy.line"] { --fact-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 9V5h4M20 9V5h-4M4 15v4h4M20 15v4h-4'/%3E%3Ccircle cx='12' cy='12' r='2.5'/%3E%3C/svg%3E"); }
.gatefoot #gate-late-line, .gatefoot #gate-choice-line { --fact-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5.5' width='17' height='15' rx='3'/%3E%3Cpath d='M8 3.5v4M16 3.5v4M3.5 10.5h17'/%3E%3C/svg%3E"); }
.gatefoot .gateline:has(> span:empty) { display: none; }
#join-flags { flex-wrap: wrap; justify-content: center; gap: 8px; overflow: visible; -webkit-mask-image: none; mask-image: none; margin-top: 10px; padding: 18px 16px 16px; border-top: 1px solid var(--hair); }
#join-flags .crowdline { flex-basis: 100%; margin: 0 0 6px; text-align: center; font-size: 15px; line-height: 1.5; color: var(--ink); }
#join-flags .flagchip { padding: 7px 12px; font-size: 14px; }
.flagrow .crowdline:only-child { margin: 0; }
/* On a phone the way in sits under the words, full width, in the same column
   — never beside them, squeezing the title (his read, 2026-09-09 late). */
@media (max-width: 759px) {
  #lobby > .hero .hero-foot { flex-direction: column; align-items: stretch; gap: 18px; }
  #lobby > .hero .playcircle { width: 100%; justify-content: center; }
}
/* THE ROOM'S BOTTOM STRIP (his read on the phone, 2026-09-09 late): a chip is
   a chip — never taller than its own line, whatever stands beside it — and the
   foot is a dock at the bottom of the screen: the sentence, the clock, then
   one slim scrollable row of flags. Nothing scrolls under the ? and the ×. */
.flagrow { align-items: center; }
.flagrow .flagchip { height: 32px; padding-top: 0; padding-bottom: 0; }
.roomscreen .playfoot { margin-top: auto; padding-top: 16px; }
.roomscreen .playfoot .flagrow { margin-top: 2px; }
@media (min-width: 1000px) {
  .roomscreen .playfoot .flagrow { justify-content: center; -webkit-mask-image: none; mask-image: none; flex-wrap: wrap; overflow: visible; }
}
/* The room fits the phone without a scroll (his read, 09-09 late): tighter
   rhythm between the head, the ring and the dock; the clock on one line. */
.roomscreen { padding-bottom: 8px; }
.roomscreen .playmid { margin-top: min(6vh, 56px); margin-bottom: min(8vh, 64px); }
.roomscreen .playfoot .clock { white-space: nowrap; }
.roomscreen .playfoot .spread { gap: 16px; }
.stage:has(.roomscreen.on) .foot { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }
