/* ═══════════════════════════════════════════════════════════════════════
   The Table Remembers — station styles

   Written for a projector in a room with other things going on, which drives
   nearly every choice here:

   · Type is enormous. The smallest thing on screen is 15px and almost nothing
     is that small; the question is set in viewport units so it fills the wall
     whatever the projector is doing.
   · Contrast is high and the ground is near-black. Projectors wash out, and a
     mid-grey that looks refined on a laptop turns to fog on a wall.
   · No pure white. #ece5d6 against #06060a is the archive's own bone-on-ink,
     and it stops the type from blooming and smearing at projector gamma.
   · Nothing is hover-only. One person drives, everyone else watches, and a
     hover state is invisible to a room.

   Fonts load from fonts/ if the optional fetch step was run, and fall back to
   a system serif stack that is deliberately close in colour and width. The
   station must never wait on a network font to draw its first frame.
   ══════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Cormorant Station';
  src: url('../fonts/cormorant-garamond-300.woff2') format('woff2');
  font-weight: 300; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Cormorant Station';
  src: url('../fonts/cormorant-garamond-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Mono Station';
  src: url('../fonts/dm-mono-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  --void:    #06060a;
  --void-2:  #0b0b12;
  --bone:    #ece5d6;
  --bone-dim:#a49c8c;
  --gold:    #e0b765;
  --gold-dim:#8f7434;
  --ember:   #c25555;

  /* The fallbacks are ordered by how close they sit to Cormorant's high-
     contrast, small-x-height colour, so an un-vendored build reads as a
     deliberate choice rather than as a font that failed to load. */
  --serif: 'Cormorant Station', 'Cormorant Garamond', 'Iowan Old Style',
           'Palatino Linotype', Palatino, 'Book Antiqua', Georgia, serif;
  --mono:  'Mono Station', ui-monospace, 'SF Mono', 'Cascadia Mono',
           'DejaVu Sans Mono', Consolas, Menlo, monospace;

  /* Set per question from the theme palette; everything accent-coloured on
     screen reads from this one variable so the whole room shifts at once. */
  --accent: #e0b765;

  --edge: clamp(1.2rem, 2.4vw, 3rem);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  background: var(--void);
  color: var(--bone);
  font-family: var(--serif);
  overflow: hidden;
  /* A station runs for hours untouched; never let a stray drag select text. */
  user-select: none;
  -webkit-user-select: none;
}

body { cursor: default; }
body.driving { cursor: grabbing; }
body.idle    { cursor: none; }

.sr-only {
  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;
}

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

/* ── the field ──────────────────────────────────────────────────────── */

#field {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  display: block;
  z-index: 1;
}

/* Film grain. A perfectly clean gradient bands badly on a projector; a little
   noise breaks the bands up and reads as texture rather than as an artefact. */
#grain {
  position: fixed; inset: -50%;
  z-index: 2; pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain 6s steps(6) infinite;
}
@keyframes grain {
  0%,100% { transform: translate(0,0) }
  20% { transform: translate(-4%,3%) }
  40% { transform: translate(3%,-5%) }
  60% { transform: translate(-3%,-3%) }
  80% { transform: translate(5%,4%) }
}

#vignette {
  position: fixed; inset: 0;
  z-index: 3; pointer-events: none;
  background:
    radial-gradient(ellipse 78% 68% at 50% 46%,
      transparent 40%, rgba(6,6,10,.45) 78%, rgba(6,6,10,.92) 100%);
}

/* ── the opening ────────────────────────────────────────────────────── */

#intro {
  position: fixed; inset: 0; z-index: 90;
  display: grid; place-items: center;
  background:
    radial-gradient(ellipse 60% 50% at 50% 42%, #14121c 0%, var(--void) 68%);
  transition: opacity 1.6s ease, visibility 1.6s ease;
  padding: var(--edge);
}
#intro.gone { opacity: 0; visibility: hidden; pointer-events: none; }

.intro-inner { max-width: 46rem; text-align: center; }

.intro-place {
  font-family: var(--mono);
  font-size: clamp(.72rem, 1.05vw, .95rem);
  letter-spacing: .34em; text-transform: uppercase;
  color: var(--gold-dim); margin-bottom: 2.2em;
}

#intro h1 {
  font-size: clamp(2.6rem, 8vw, 7.5rem);
  font-weight: 300; line-height: .95;
  letter-spacing: .02em; color: var(--gold);
  margin-bottom: .28em;
}

.intro-blurb {
  font-size: clamp(1.05rem, 1.9vw, 1.6rem);
  font-weight: 300; line-height: 1.58;
  color: var(--bone); opacity: .82;
  max-width: 34em; margin: 0 auto 1.6em;
}
.intro-now { color: var(--bone); opacity: .6; font-style: italic; }

.intro-counts {
  list-style: none;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: .4em clamp(1.4rem, 3.4vw, 3.4rem);
  margin: 0 0 2.2em;
}
.intro-counts li {
  font-family: var(--mono);
  font-size: clamp(.68rem, .95vw, .84rem);
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--bone-dim);
}
.intro-counts b {
  display: block;
  font-family: var(--serif); font-weight: 600;
  font-size: clamp(1.7rem, 3.4vw, 2.9rem);
  letter-spacing: 0; color: var(--gold);
  text-transform: none; margin-bottom: .08em;
}
.intro-counts .loading { color: var(--gold-dim); letter-spacing: .18em; }

#enter {
  font-family: var(--mono);
  font-size: clamp(.76rem, 1.05vw, .92rem);
  letter-spacing: .28em; text-transform: uppercase;
  background: none; border: 1px solid var(--gold-dim);
  color: var(--gold); padding: 1.15em 3.6em;
  cursor: pointer; transition: background .5s, color .5s, border-color .5s;
}
#enter:hover:not(:disabled), #enter:focus-visible:not(:disabled) {
  background: var(--gold); color: var(--void); border-color: var(--gold);
}
#enter:disabled { opacity: .3; cursor: progress; }

.intro-hint {
  font-family: var(--mono); font-size: .72rem;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold-dim); margin-top: 2em; min-height: 1.4em;
}

/* ── the question ───────────────────────────────────────────────────── */

#ask {
  position: fixed; z-index: 20;
  top: var(--edge); left: 50%; transform: translateX(-50%);
  width: min(92vw, 62rem);
  text-align: center; pointer-events: none;
  opacity: 0; transition: opacity 1.1s ease;
}
#ask.on { opacity: 1; }

.ask-meta {
  display: flex; justify-content: center; align-items: center;
  gap: 1.1em; margin-bottom: .75em;
  font-family: var(--mono);
  font-size: clamp(.62rem, .88vw, .8rem);
  letter-spacing: .3em; text-transform: uppercase;
}
#ask-theme { color: var(--accent); transition: color .8s ease; }
#ask-num   { color: var(--gold-dim); }

/* Four sizes, chosen by character count in station.js. The questions were
   crowdsourced from 50,000 people and were never written to a length: the
   shortest is 15 characters, the longest 343. One size cannot serve both — at
   the size that makes "Who owns water?" land on a wall, the long ones ran to
   ten lines and covered the video panel completely. */
#ask[data-len="s"]  #ask-text { font-size: clamp(1.6rem,  3.5vw,  3.4rem); }
#ask[data-len="m"]  #ask-text { font-size: clamp(1.4rem,  2.75vw, 2.7rem); }
#ask[data-len="l"]  #ask-text { font-size: clamp(1.2rem,  2.15vw, 2.05rem); }
#ask[data-len="xl"] #ask-text { font-size: clamp(1.05rem, 1.75vw, 1.7rem); }

#ask-text {
  font-size: clamp(1.5rem, 3.5vw, 3.4rem);
  font-weight: 300; line-height: 1.16;
  letter-spacing: .005em; color: var(--bone);
  text-wrap: balance;
  text-shadow: 0 2px 24px rgba(6,6,10,.9), 0 0 60px rgba(6,6,10,.7);
}

.ask-count {
  margin-top: .85em;
  font-family: var(--mono);
  font-size: clamp(.6rem, .85vw, .76rem);
  letter-spacing: .24em; text-transform: uppercase;
  color: var(--bone-dim); opacity: .7;
}

/* ── the centre stage ───────────────────────────────────────────────── */

#stage {
  position: fixed; z-index: 18;
  /* Centred on 56%, not 50%. The stage shares the frame with a question above
     it and two bars below, and centring on the viewport put its top edge at
     199px — under a question that reaches 250px even after the size tiers. */
  left: 50%; top: 56%; transform: translate(-50%, -50%);
  width: min(86vw, 54rem);
  text-align: center; pointer-events: none;
  opacity: 0; transition: opacity .9s ease;
}
#stage.on { opacity: 1; }
/* On the wall, the mosaic is the picture. The centre panel steps back to a
   caption at the foot of the frame so the quote survives but the video does
   not compete with a hundred playing cells. */
/* Clears the lens strip. At 4.4rem the speaker's name overlapped it by 18px in
   every arrangement that stands the stage down. */
#stage.stood-down { top: auto; bottom: calc(var(--edge) + 6.2rem); transform: translateX(-50%); }
#stage.stood-down #stage-media { display: none; }
#stage.stood-down #stage-quote { font-size: clamp(1.05rem, 2vw, 1.85rem); }

#stage-media {
  position: relative;
  width: min(44vw, 26rem);
  aspect-ratio: 16 / 9;
  margin: 0 auto clamp(1rem, 2.2vh, 2rem);
  background: #000;
  border: 1px solid rgba(224,183,101,.16);
  box-shadow: 0 0 90px rgba(0,0,0,.85), 0 0 140px -30px var(--accent);
  overflow: hidden;
  opacity: 0; transform: scale(.985);
  transition: opacity .8s ease, transform .8s ease, box-shadow 1s ease;
}
#stage.media-on #stage-media { opacity: 1; transform: none; }

#stage-video, #stage-still {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .7s ease;
}
#stage-still { filter: saturate(.72) contrast(1.06); }
#stage-media.showing-still  #stage-still { opacity: 1; }
#stage-media.showing-video  #stage-video { opacity: 1; }

/* Keeps the quote legible when it sits under the picture. */
#stage-scrim {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(6,6,10,.15) 0%, transparent 34%, transparent 62%, rgba(6,6,10,.72) 100%);
}

#stage-quote {
  font-size: clamp(1.15rem, 2.5vw, 2.35rem);
  font-weight: 300; font-style: italic; line-height: 1.36;
  color: var(--bone); text-wrap: balance;
  max-width: 26em; margin: 0 auto;
  text-shadow: 0 2px 30px rgba(6,6,10,.95);
}
#stage-quote:empty { display: none; }
/* Real quotation marks, not the ones a browser guesses at. */
#stage-quote::before { content: '\201C'; }
#stage-quote::after  { content: '\201D'; }

#stage-who {
  margin-top: clamp(.7rem, 1.6vh, 1.3rem);
  display: flex; flex-direction: column; gap: .3em;
}
#stage-name {
  font-family: var(--mono);
  font-size: clamp(.74rem, 1.05vw, .92rem);
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--accent); transition: color .8s ease;
}
#stage-role {
  font-size: clamp(.85rem, 1.2vw, 1.05rem);
  font-style: italic; color: var(--bone-dim);
  max-width: 30em; margin: 0 auto;
}

/* The one piece of information the archive alone cannot carry: this person is
   no longer alive to be asked again. */
#stage-gone {
  margin-top: .8em;
  font-family: var(--mono);
  font-size: clamp(.6rem, .82vw, .74rem);
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--ember); opacity: .85;
}
#stage-gone:empty { display: none; }

/* ── quote blooms ───────────────────────────────────────────────────── */

#blooms { position: fixed; inset: 0; z-index: 14; pointer-events: none; }

.bloom {
  position: absolute;
  width: clamp(13rem, 19vw, 22rem);
  transform: translate(-50%, -50%) scale(.94);
  opacity: 0;
  transition: opacity 1.1s ease, transform 1.1s cubic-bezier(.2,.7,.3,1);
  text-align: left;
}
.bloom.on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.bloom.right { text-align: right; }

.bloom q {
  display: block;
  font-size: clamp(.92rem, 1.28vw, 1.2rem);
  font-style: italic; font-weight: 300; line-height: 1.4;
  color: var(--bone);
  text-shadow: 0 1px 14px var(--void), 0 0 34px rgba(6,6,10,.95);
}
.bloom cite {
  display: block; margin-top: .5em;
  font-family: var(--mono); font-style: normal;
  font-size: clamp(.56rem, .76vw, .68rem);
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--bloom-accent, var(--gold));
  text-shadow: 0 1px 10px var(--void);
}

/* ── labels pinned to the scene ─────────────────────────────────────── */

#labels { position: fixed; inset: 0; z-index: 13; pointer-events: none; }

.label {
  position: absolute; top: 0; left: 0;
  text-align: center; white-space: nowrap;
  opacity: 0; transition: opacity .9s ease;
}
.label.on { opacity: 1; }

.label b {
  display: block;
  font-family: var(--mono); font-weight: 400;
  font-size: clamp(.56rem, .78vw, .7rem);
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--bone);
  text-shadow: 0 1px 10px var(--void), 0 0 22px var(--void);
}
.label i {
  display: block; margin-top: .25em;
  font-family: var(--mono); font-style: normal;
  font-size: clamp(.5rem, .68vw, .6rem);
  letter-spacing: .16em;
  color: var(--gold-dim);
  text-shadow: 0 1px 10px var(--void);
}
/* Cluster titles and axis ends carry more weight than a person's name — they
   name a whole region of the field rather than one seat in it. */
/* The one label that is a sentence rather than a name. */
.label.note {
  width: min(52rem, 76vw); white-space: normal;
}
.label.note b {
  font-family: var(--serif); font-size: clamp(.92rem, 1.4vw, 1.2rem);
  font-style: italic; letter-spacing: .02em; text-transform: none;
  color: var(--bone-dim);
}
.label.note i {
  margin-top: .6em; letter-spacing: .2em; text-transform: uppercase;
  font-size: clamp(.56rem, .78vw, .68rem); color: var(--ember);
  line-height: 1.9;
}

.label.big b {
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(1rem, 1.7vw, 1.6rem);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent);
}

/* ── the lens strip ─────────────────────────────────────────────────── */

#lenses {
  position: fixed; z-index: 22;
  left: 50%; bottom: calc(var(--edge) + 3.4rem);
  transform: translateX(-50%);
  display: flex; gap: clamp(.3rem, .9vw, .9rem);
  opacity: 0; transition: opacity 1s ease;
}
#lenses.on { opacity: 1; }

.lens {
  font-family: var(--mono);
  font-size: clamp(.58rem, .8vw, .72rem);
  letter-spacing: .22em; text-transform: uppercase;
  background: rgba(6,6,10,.7);
  border: 1px solid rgba(224,183,101,.18);
  color: var(--bone-dim);
  padding: .8em 1.5em; cursor: pointer;
  backdrop-filter: blur(10px);
  transition: color .35s, border-color .35s, background .35s;
  white-space: nowrap;
}
.lens:hover { color: var(--bone); border-color: rgba(224,183,101,.42); }
.lens[aria-pressed="true"] {
  color: var(--void); background: var(--accent);
  border-color: var(--accent); font-weight: 400;
}
.lens .k {
  display: inline-block; margin-right: .8em;
  opacity: .5; font-size: .9em;
}

/* ── the driver's panel ─────────────────────────────────────────────── */

#hud {
  position: fixed; z-index: 22;
  left: var(--edge); right: var(--edge); bottom: var(--edge);
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 2rem; pointer-events: none;
  opacity: 0; transition: opacity 1s ease;
}
#hud.on { opacity: 1; }
/* The panel is for the driver. When nobody has touched anything for a while
   the room is just watching, so it gets out of the picture. */
body.idle #hud, body.idle #lenses { opacity: .16; }

.hud-left { display: flex; flex-direction: column; gap: .3em; }
.hud-title {
  font-family: var(--mono); font-size: .68rem;
  letter-spacing: .3em; text-transform: uppercase; color: var(--gold-dim);
}
.hud-sub {
  font-size: .92rem; font-style: italic; color: var(--bone-dim); opacity: .75;
}

.hud-keys {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: .35em 1.5em; max-width: 46rem;
}
.hud-keys span {
  font-family: var(--mono); font-size: .64rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--bone-dim);
  opacity: .6; white-space: nowrap;
}
.hud-keys kbd {
  font-family: var(--mono); font-size: .95em;
  color: var(--gold); background: none; border: none;
  padding: 0 .35em 0 0; letter-spacing: .1em;
}

.hud-state {
  font-family: var(--mono); font-size: .64rem;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--gold-dim); text-align: right; white-space: nowrap;
}
.hud-state.live { color: var(--accent); }

/* The whole HUD is pointer-events: none so a drag over it still turns the
   room. The one control living in it has to opt back in. */
.hud-right {
  display: flex; flex-direction: column; align-items: flex-end; gap: .55em;
}
.hud-actions {
  display: flex; flex-direction: column; align-items: flex-end; gap: .5rem;
}
/* Collapses to nothing when neither offer applies, so the HUD does not carry a
   gap on the machines — most of them — where there is no sensor and no
   headset. :has() is unsupported on older engines, where the worst case is a
   half-rem of empty space nobody will see. */
.hud-actions:not(:has(button:not([hidden]))) { display: none; }

#tilt, #vr {
  pointer-events: auto;
  font-family: var(--mono); font-size: .82rem;
  letter-spacing: .1em; text-transform: uppercase;
  background: rgba(6,6,10,.72);
  border: 1px solid rgba(224,183,101,.28);
  color: var(--bone-dim);
  padding: .72em 1.1em; min-height: 2.75rem;
  cursor: pointer; white-space: nowrap;
  transition: color .35s, border-color .35s, background .35s;
}
#tilt[hidden], #vr[hidden] { display: none; }
#tilt[aria-pressed="true"] {
  color: var(--void); background: var(--accent); border-color: var(--accent);
}
#tilt:disabled, #vr:disabled { opacity: .45; cursor: default; }

/* The headset is the louder invitation of the two — it is the thing somebody
   walking up to this station has never been offered before. */
#vr {
  color: var(--gold); border-color: rgba(224,183,101,.5);
}
#vr:hover:not(:disabled) {
  color: var(--void); background: var(--gold); border-color: var(--gold);
}

/* While a headset is presenting, this screen is a monitor for whoever is
   running the room — not the piece. The furniture stays, the performance
   stops competing for attention. */
body.in-vr #grain, body.in-vr #vignette { opacity: .25; }
body.in-vr #stage { opacity: .35; }

/* ── the question index ─────────────────────────────────────────────── */

#index {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  background: rgba(4,4,7,.9);
  backdrop-filter: blur(14px);
}
#index[hidden] { display: none; }

.index-panel {
  width: min(92vw, 52rem); max-height: 78vh;
  display: flex; flex-direction: column;
  background: var(--void-2);
  border: 1px solid rgba(224,183,101,.2);
  padding: clamp(1.2rem, 2.4vw, 2.2rem);
}

#index-search {
  font-family: var(--serif); font-size: clamp(1.1rem, 2vw, 1.6rem);
  font-weight: 300; color: var(--bone);
  background: none; border: none;
  border-bottom: 1px solid rgba(224,183,101,.25);
  padding: .4em 0 .6em; margin-bottom: 1.2em;
  outline: none; width: 100%;
}
#index-search::placeholder { color: var(--gold-dim); opacity: .7; }
#index-search:focus { border-bottom-color: var(--gold); }

#index-list {
  list-style: none; overflow-y: auto; flex: 1;
  scrollbar-width: thin; scrollbar-color: var(--gold-dim) transparent;
}
#index-list li {
  display: grid; grid-template-columns: 3.4rem 1fr auto;
  gap: 1rem; align-items: baseline;
  padding: .68em .8em; cursor: pointer;
  border-left: 2px solid transparent;
}
#index-list li[aria-selected="true"] {
  background: rgba(224,183,101,.09);
  border-left-color: var(--gold);
}
#index-list .n {
  font-family: var(--mono); font-size: .74rem;
  color: var(--gold-dim); letter-spacing: .1em;
}
#index-list .q {
  font-size: clamp(.95rem, 1.4vw, 1.15rem);
  font-weight: 300; color: var(--bone); line-height: 1.35;
}
#index-list .t {
  font-family: var(--mono); font-size: .58rem;
  letter-spacing: .16em; text-transform: uppercase;
  white-space: nowrap;
}
.index-hint {
  margin-top: 1.1em; padding-top: 1em;
  border-top: 1px solid rgba(224,183,101,.12);
  font-family: var(--mono); font-size: .64rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--gold-dim);
}

/* ── small screens ──────────────────────────────────────────────────── */
/*
   The wall is still the piece this was composed for. But it is now also
   handed round on phones at the event and taken home afterwards, and a phone
   is not a small wall — it is a different composition of the same material.

   The rule followed here: nothing is removed that carries meaning. The field,
   the question, the quote, the name, whether that person is still alive, and
   all seven arrangements survive at every size. What goes is the operator's
   furniture — the key hints, the second and third simultaneous quote, the
   driver's status line — because those serve the person running the room, and
   on a phone the person running the room is the person holding it.
*/

@media (max-width: 860px) {
  :root { --edge: clamp(.85rem, 3.4vw, 1.4rem); }

  #blooms { display: none; }          /* one quote at a time on a small screen */
  .hud-keys { display: none; }

  /* The vignette is tuned to a wall's proportions; at phone aspect it closes
     in far enough to eat the faces at the edges of the ring. */
  #vignette {
    background: radial-gradient(ellipse 92% 78% at 50% 46%,
      transparent 44%, rgba(6,6,10,.4) 82%, rgba(6,6,10,.9) 100%);
  }

  /* The question is the one thing that must never be cropped: it is what the
     answers are answers *to*. It gets the top of the screen and as many lines
     as it needs. */
  #ask { width: calc(100% - var(--edge) * 2); top: max(var(--edge), env(safe-area-inset-top)); }
  #ask-text { font-size: clamp(1.15rem, 5.2vw, 1.7rem); line-height: 1.22; }
  .ask-meta { gap: .7em; margin-bottom: .5em; flex-wrap: wrap; }
  .ask-count { margin-top: .6em; }

  /* The centre stage stops being centred. On a phone the middle of the screen
     is where the thumb drags to look around, and a panel sitting in it is
     something to fight rather than something to read — so the voice moves to
     the foot of the screen as a caption, and the field gets the whole frame. */
  #stage {
    top: auto; bottom: calc(var(--edge) + 7.4rem);
    transform: translateX(-50%);
    width: calc(100% - var(--edge) * 2);
  }
  /* The wall lens stands the stage down to a caption near the floor. That
     clearance was measured against a wall's furniture; here it would put the
     quote straight through the lens strip. */
  #stage.stood-down { bottom: calc(var(--edge) + 7.4rem); }
  #stage-media { width: min(58vw, 15rem); margin-bottom: .9rem; }
  /* The quote is the work. It never drops below 17px on any screen. */
  #stage-quote { font-size: clamp(1.08rem, 4.6vw, 1.35rem); max-width: none; }
  #stage-name { font-size: 1rem; letter-spacing: .13em; }
  #stage-gone { font-size: .95rem; letter-spacing: .1em; opacity: 1; }
  #stage-role { display: none; }      /* the name and the dates are the point */

  /* Everything the wall sets in small mono caps. On a projector these are read
     from four metres and 10px is a deliberate whisper; in a hand they are just
     small. None of them carry meaning the piece depends on, but "how many of
     the table answered this" is worth being able to read. */
  .ask-meta { font-size: .95rem; letter-spacing: .12em; }
  .ask-count { font-size: .95rem; letter-spacing: .1em; opacity: .9; }

  /* Label sizes are overridden at the foot of this file, after the .label.plate
     rules they have to beat — a media query adds no specificity, so setting
     them here would lose to a later rule of equal weight. */

  /* Thumb-sized, and scrollable rather than squeezed: seven arrangements do
     not fit across a phone at a tappable size, and shrinking them until they
     do is how The Gone becomes a 9px target nobody hits. */
  #lenses {
    bottom: calc(var(--edge) + 2.9rem);
    left: 0; right: 0; transform: none;
    max-width: 100%; width: 100%;
    justify-content: flex-start;
    padding-inline: var(--edge);
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  #lenses::-webkit-scrollbar { display: none; }
  /*
     Held at reading distance rather than read across a room, so the floor this
     stylesheet sets for itself at the top of the file — nothing meaningful
     under 15px — has to hold here too. Shrinking these to fit seven across was
     the wrong trade: the strip scrolls, so there is no width to fit into, and
     a 10px label on a control somebody has to tap is a barrier to the one
     lens they might be looking for. Letter-spacing comes down instead, which
     buys most of the width back at no cost to legibility.
  */
  .lens {
    scroll-snap-align: center;
    padding: .7em 1.05em;
    min-height: 2.9rem;               /* a real target, not a decoration */
    font-size: .94rem; letter-spacing: .09em;
  }
  .lens .k { margin-right: .55em; }

  #hud {
    left: var(--edge); right: var(--edge);
    bottom: max(var(--edge), env(safe-area-inset-bottom));
    align-items: center;
  }

  /*
     On a wall the tilt button is one more item in the driver's panel. On a
     phone it is a 44px target inside a 22px row, which grew the HUD until it
     sat on top of the lens strip — so here it leaves that row and takes its
     own line above the lenses, where a thumb reaches it without covering the
     arrangement buttons.
  */
  .hud-actions {
    position: fixed; z-index: 23;
    right: var(--edge);
    bottom: calc(max(var(--edge), env(safe-area-inset-bottom)) + 6.2rem);
  }
  #tilt, #vr { font-size: .95rem; letter-spacing: .08em; }

  /* Only reserve room for offers that are actually on screen — an empty gap
     on every laptop would be worse than the buttons. */
  body.tilt-offer #stage,
  body.xr-offer #stage { bottom: calc(var(--edge) + 11rem); }
  body.tilt-offer.xr-offer #stage { bottom: calc(var(--edge) + 15.2rem); }
  .hud-sub { display: none; }
  .hud-title { font-size: .72rem; letter-spacing: .16em; }
  .hud-state { font-size: .7rem; letter-spacing: .12em; }

  .index-panel { width: calc(100% - var(--edge) * 2); max-height: 86vh; }
  #index-list li { grid-template-columns: 2.6rem 1fr; gap: .6rem; }
  #index-list .t { display: none; }
}

/* Portrait phones: the tightest case there is, and the one most people at the
   event will actually be holding. */
@media (max-width: 860px) and (orientation: portrait) {
  /* Video is the first thing to go. A 16:9 panel, a quote, a name and seven
     lens buttons do not coexist above the fold on a 375×812 screen — and of
     those, the panel is the one whose absence the piece was already designed
     to survive. The portrait still shows on the field itself. */
  #stage-media { display: none; }
  #stage { bottom: calc(var(--edge) + 7.8rem); }
  body.tilt-offer #stage,
  body.xr-offer #stage { bottom: calc(var(--edge) + 11.4rem); }
  body.tilt-offer.xr-offer #stage { bottom: calc(var(--edge) + 15.6rem); }
  #stage-quote { font-size: clamp(1.02rem, 4.6vw, 1.3rem); }
}

/* Landscape phones — a 375px-tall viewport with browser chrome in it. The
   question and the voice cannot both have room, so the question keeps it and
   the voice narrows to a single line beside the field. */
@media (max-height: 460px) and (orientation: landscape) {
  #ask-text { font-size: clamp(1rem, 3.2vw, 1.4rem); }
  .ask-count, #stage-gone { display: none; }
  #stage { bottom: calc(var(--edge) + 5.6rem); width: min(70%, 34rem); }
  #stage-media { display: none; }
  #stage-quote {
    font-size: clamp(.92rem, 2.6vw, 1.1rem);
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
  }
  #lenses { bottom: calc(var(--edge) + 2.5rem); }
  .lens { min-height: 2.3rem; padding: .6em 1em; }
}

/* Coarse pointers at any size — a touchscreen kiosk is not a phone, but a
   finger is still a finger. Backdrop blur behind a scrolling strip is also
   the single most expensive thing on the page on mobile GPUs. */
@media (pointer: coarse) {
  .lens { min-height: 2.75rem; }
  /* The digit on each lens is a keyboard shortcut. On a device with no
     keyboard it is decoration that costs width on the one control strip that
     is short of it — and it was the smallest type on the screen. */
  .lens .k { display: none; }
  .lens:hover { color: var(--bone-dim); border-color: rgba(224,183,101,.18); }
  .lens[aria-pressed="true"] { color: var(--void); border-color: var(--accent); }
}

body.tier-lean .lens,
body.tier-lean #index { backdrop-filter: none; }
body.tier-lean #grain { display: none; }

/* Standing inside the ring, the vignette is no longer a frame around a scene —
   it is a hood over the viewer's own eyes. It opens up. */
body.inside #vignette {
  background: radial-gradient(ellipse 104% 96% at 50% 50%,
    transparent 58%, rgba(6,6,10,.34) 88%, rgba(6,6,10,.72) 100%);
}

/* ── motion ─────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  #grain { animation: none; }
  * { transition-duration: .01ms !important; }
}

/* Theme names sitting on the wall. They have to survive being laid over a
   hundred moving faces, which no amount of text-shadow does on its own — so
   they get a plate of their own colour to sit on. */
.label.plate b {
  display: inline-block;
  padding: .45em 1.1em .4em;
  background: rgba(6,6,10,.74);
  border-left: 2px solid currentColor;
  backdrop-filter: blur(6px);
  font-size: clamp(.66rem, 1.05vw, .95rem);
  letter-spacing: .22em;
}
.label.plate i { display: none; }

/* The two-line prompt for whoever walks up. Bigger and warmer than the old
   key list — it is read from standing distance by someone who has not been
   told anything, not scanned by an operator who already knows. */
.hud-keys span {
  font-family: var(--mono);
  font-size: clamp(.66rem, .92vw, .8rem);
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--bone); opacity: .78;
}
.hud-keys kbd {
  color: var(--accent); padding-right: .55em;
  transition: color .8s ease;
}
.hud-keys .hud-rest {
  font-family: var(--serif); font-style: italic; text-transform: none;
  letter-spacing: .01em; font-size: clamp(.86rem, 1.15vw, 1.05rem);
  color: var(--bone-dim); opacity: .7;
}

/* ── labels on a small screen ───────────────────────────────────────── */
/*
   Last in the file on purpose. Every rule here overrides a `.label…` rule of
   identical specificity defined above it, and a media query contributes
   nothing to specificity — so source order is the only thing deciding this,
   and moving this block up the file silently reverts it.

   All of these clamp against viewport width, which is the right instinct on a
   wall and bottoms out hard on a phone: the theme names over the mosaic landed
   at 10.6px, which is a caption you can see is there and cannot read.
*/
@media (max-width: 860px) {
  .label.plate b {
    font-size: .95rem; letter-spacing: .09em;
    padding: .4em .7em .35em;
  }
  .label b { font-size: .88rem; letter-spacing: .1em; }
  .label i { font-size: .8rem; letter-spacing: .08em; }
  .label.big b { font-size: 1.05rem; letter-spacing: .07em; }
  .label.note b { font-size: 1rem; }
  .label.note i { font-size: .88rem; letter-spacing: .1em; line-height: 1.7; }
}
