/* ============================================================
   Replica of raunofreiberg.com landing page — styles.
   Values extracted from the live site's CSS (light theme is
   forced on the index route, so only light tokens are used).
   ============================================================ */

:root {
  --yellow: #FFFF02;
  --orange: #FF6100;
  --colors-red: #FF484C;

  /* Radix gray (light) — only the steps the landing page uses */
  --colors-gray1: hsl(0 0% 99%);
  --colors-gray4: hsl(0 0% 93%);   /* page background (#EDEDED) */
  --colors-gray9: hsl(0 0% 56.1%);
  /* 42%, matching tokens.css: 43.5% measured 4.29:1 on the #EDEDED
     ground, a hair under small text's 4.5. (The .sheet re-pin below
     keeps 43.5 — on the sheets' white it already clears at 4.86.) */
  --colors-gray11: hsl(0 0% 42%);
  --colors-gray12: hsl(0 0% 9%);
  --colors-focus: var(--colors-gray9);

  --radii-round: 9999px;
  --root-background: var(--colors-gray4);
  --fonts-body: X, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
    "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
    "Helvetica Neue", sans-serif;

  --frame-width: 1200px;
  --frame-height: 720px;

  /* tokens the shared chrome (chrome.css) reads — the subpage values */
  --red-accent: var(--colors-red);
  --colors-gray5: hsl(0 0% 90.9%);
  --colors-gray8: hsl(0 0% 78%);
  --card-bg: #ffffff;
  --shadow-dock: 0 0 0 1px var(--colors-gray5), 0 12px 32px rgba(0, 0, 0, 0.08),
    0 2px 8px rgba(0, 0, 0, 0.04);
}

/* Dark theme (P2.3): the landing joins the theme system as a gallery
   wall — the ground drops to near-black (Radix gray dark, the same
   steps tokens.css gives the subpages) while the frame sheets stay
   lit, each artwork keeping the palette it was composed in. Only
   ground-level ink flips: labels, minimap, hints, chrome. */
html.dark {
  --colors-gray4: hsl(0 0% 8.5%);    /* page background */
  --colors-gray5: hsl(0 0% 17.9%);
  --colors-gray8: hsl(0 0% 31.2%);
  --colors-gray9: hsl(0 0% 43.9%);
  --colors-gray11: hsl(0 0% 62.8%);
  --colors-gray12: hsl(0 0% 93%);
  --card-bg: hsl(0 0% 11%);
  --shadow-dock: 0 0 0 1px var(--colors-gray5), 0 12px 32px rgba(0, 0, 0, 0.5),
    0 2px 8px rgba(0, 0, 0, 0.4);
}

/* The sheets (and the white mobile-note card) are theme-invariant:
   re-pin the gray ramp inside them to the light values, so their ink
   never follows the wall */
.sheet,
.mobile-note {
  --colors-gray8: hsl(0 0% 78%);
  --colors-gray9: hsl(0 0% 56.1%);
  --colors-gray11: hsl(0 0% 43.5%);
  --colors-gray12: hsl(0 0% 9%);
}

@supports (color: color(display-p3 1 1 1)) {
  :root {
    --orange: color(display-p3 0.99 0.4 0.02);
  }
}

/* The site serves PP Neue Montreal Medium as family "X" for both weights */
@font-face {
  font-family: X;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/dd.woff2) format("woff2");
}
@font-face {
  font-family: X;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/dd.woff2) format("woff2");
}

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

html,
body {
  background: var(--root-background);
}

html {
  overscroll-behavior: none;
  touch-action: pan-x pan-y;
  scrollbar-width: none;
}
html::-webkit-scrollbar {
  display: none;
}

body {
  margin: 0;
  font-family: var(--fonts-body);
}

::selection {
  background: var(--yellow);
  color: black;
}

h1, h2, h3, h4, h5, h6, p { margin: 0; }
h1, h2, h3 { font-weight: 500; }

/* the original site sets its italic voice in Georgia */
em, i { font-family: Georgia, serif; }

a {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

button {
  padding: 0;
  margin: 0;
  background: transparent;
  font-family: var(--fonts-body);
  border: 0;
}

/* visually hidden (aria-live region) */
.vh {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  overflow: hidden;
  white-space: nowrap;
  position: absolute;
}

/* ---------------- layout ---------------- */

.ghost {
  pointer-events: none;
  user-select: none;
}

.center,
.cross {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.cross {
  mix-blend-mode: difference;
  cursor: pointer;
}

.frames {
  display: flex;
  gap: 8px;
  flex-direction: row;
  width: var(--frame-width);
  height: var(--frame-height);
  will-change: transform;
}

@keyframes fadeFrames {
  to { opacity: 1; }
}

.frame {
  outline: 0;
  user-select: none;
  -webkit-user-select: none;
}
.frame:not([data-variant="main"]) {
  opacity: 0;
  animation: fadeFrames 0.35s ease-in-out 0.8s forwards;
}
.root[data-cancel-animation="true"] .frame {
  animation-delay: 0s;
}
.frame:focus-visible [data-sheet] {
  outline: 4px solid var(--colors-focus);
  outline-offset: 4px;
}

.frame a,
.frame h1 span,
.frame p {
  font-size: 85px;
  font-weight: 400;
}
.frame p[data-size="medium"] {
  font-size: 75px;
}
/* The Projects frame's label is a real (non-decorative) heading, so it
   is an h2 — the outline must not jump h1 to h3 — in the same elephant
   type. Scoped to .contents: the Player One sheet also carries an h2
   (.p1-name, 140px) and a bare `.frame h2` out-specified it to 720px,
   bursting that sheet. */
.frame .contents h2,
.frame h3 {
  font-size: 720px;
  font-weight: 400;
  white-space: nowrap;
  user-select: none;
  -webkit-user-select: none;
}

.sheet {
  width: 100%;
  height: 100%;
  background: white;
  overflow-x: clip;
}
.sheet-yellow {
  background: var(--yellow);
}

.contents {
  display: flex;
  align-items: center;
  height: 100%;
}
.contents[data-color="yellow"] {
  background: var(--yellow);
  padding: 50px;
}
/* mantra sheet: cited question (serif italic, top left) answered in the
   site's own voice (grotesk, bottom right) — a diagonal call-and-response */
.contents[data-color="yellow"] {
  position: relative;
}
.frame p.mantra-question {
  position: absolute;
  top: 50px;
  left: 50px;
  line-height: 1.15;
}
.frame p.mantra-answer {
  position: absolute;
  right: 50px;
  bottom: 50px;
  text-align: right;
  line-height: 1.15;
}
.frame p.mantra-source {
  position: absolute;
  left: 50px;
  bottom: 68px;
  font-size: 14px;
  color: var(--colors-gray11);
}

.centerFull {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}
.centerFull > svg {
  flex-shrink: 0;
}
.centerFull > svg[data-icon="history"] {
  height: 50%;
  width: fit-content;
}
.scaleUp svg {
  scale: 1.2;
}

/* main intro sheet */
.main {
  padding: 64px;
  display: flex;
  align-items: center;
  gap: 64px;
  color: var(--colors-gray12);
}

.circle {
  border-radius: var(--radii-round);
  aspect-ratio: 1 / 1;
}
.circle-main {
  background: var(--yellow);
  height: 100%;
  position: absolute;
  display: flex;
  right: 0;
  top: 0;
}
.circle-dd {
  background: var(--orange);
  height: 100%;
}

.label {
  font-size: 14px;
  color: var(--colors-gray11);
  position: absolute;
  transform-origin: top left;
  top: -28px;
}

/* six-row hero block (rauno.me's alternating-indent sheet) at the
   frame's own 85px: six rows at 1.15 land 586.5px in the sheet's
   592px inner height. Round 16 had to mirror the indent parity for a
   1126px verdict line; round 17's AI joke shrinks the tails to 47px
   and the line to 865px, so the parity stands where it always stood
   and row two ends at x=993 of the 1200 sheet — measured, well clear
   of the drums' old x=1131 high-water mark */
.frame h1 {
  line-height: 1.15;
}

.clipReveal {
  display: block;
  overflow: hidden;
}
/* the entrance grips ONLY the static reveal span — a bare
   ".clipReveal span" would also grip the promoted drum's chars,
   ghosts, measurer and escort with an entrance translateY and an
   inline-block that belong to the reveal alone (round 11 scoping) */
.clipReveal > [data-reveal] {
  display: inline-block;
  /* one line, always: the row-two verdict must never fold, whatever
     its future length — a folded reveal breaks the six-row grid */
  white-space: nowrap;
  transform: translateY(100px);
}
.clipReveal[data-variant="offset"] {
  margin-left: 64px;
}

/* ---- the rotating role line (hero.js, staggered-text engine) ----
   chars are absolutely placed around a zero-width line so a glyph
   swapping mid-flight never reflows the settled ones; each glyph
   carries its own perspective() inside its transform, so it turns
   about itself, upright — no shared vanishing point to shear it */
.hero-role-line {
  display: block;
  position: relative;
  height: 1.15em;
  line-height: 1.15;
}
.hero-role-line[data-variant="offset"] {
  margin-left: 64px; /* the clipReveal offset rhythm, carried down */
}
.st-line {
  position: relative;
  display: block;
  width: 0;
  height: 1.15em;
}
.st-char {
  position: absolute;
  top: 0;
  left: 0;
  line-height: 1.15;
  white-space: pre;
  /* transform and opacity only: the roll's blur is transient, and
     hinting filter too would pin a filtered surface per glyph — some
     hundred across the six drums, all riding the scaling row. Safari
     pays for every one of them (round 19's raster diet). */
  will-change: transform, opacity;
  transform-origin: 50% 50%; /* the roll turns about the glyph's middle */
  backface-visibility: hidden;
}
/* the measurer: a body-level probe holding the whole word as one
   shaped text run — kerning intact — far offscreen and invisible */
.st-measure {
  position: absolute;
  left: -99999px;
  top: 0;
  visibility: hidden;
  pointer-events: none;
  white-space: pre;
}

/* ---- round 10: identity rows promoted to drums, lazily, at their
   first wildcard. Promotion releases the entrance clip, keeps the
   static for the line box (and the vertical truth of the saltires),
   and floats the drum over the same origin. */
.clipReveal[data-drummed] {
  position: relative;
  overflow: visible;
}
.clipReveal[data-drummed] > [data-reveal] {
  visibility: hidden;
}
.clipReveal .st-line {
  position: absolute;
  left: 0;
  top: 0;
  transform: none; /* escapes the entrance's translateY(100px) */
}
/* the marks' escorts: each mark leaves and returns with its word,
   wearing the drum's own pose (transform/blur/opacity set inline).
   Doubled class: it must out-rank the marks' own margins below. The
   verdict's small tails are text escorts and wear it too. */
.ams-escort.avg-brain,
.ams-escort.ai-sm-a,
.ams-escort.ai-sm-i,
.ams-escort.ams-xxx,
.ams-escort.bkk-buddha,
.ams-escort.cn-panda,
.ams-escort.gc-globe {
  position: absolute;
  margin-left: 0;
  will-change: transform, opacity; /* filter is roll-transient here too */
  transform-origin: 50% 50%;
  backface-visibility: hidden;
}
/* the city link, promoted: a quiet hitbox over the drum's word,
   retargeted city to city (Amsterdam at home, Bangkok abroad) */
.ams-hit {
  position: absolute;
  z-index: 2;
}
.ams-hit:focus-visible {
  outline: 2px solid var(--colors-focus);
  outline-offset: 4px;
  border-radius: 6px;
}

/* row two's Ams — the city abstracted to its own mark: the three
   St Andrew's saltires stacked vertically beside the word, as they
   stand on Amsterdam's coat of arms. Proportions measured from
   Tony's reference (cross 56, gap 12, stroke 16.3 — 29% weight,
   21% gaps), scaled so the ink spans exactly cap height: top tips
   at the capitals, bottom tips on the baseline (endpoints inset
   0.354×stroke so the butt caps don't overshoot the box). Since
   round 16 it waits as a hidden template like the other cities,
   cloned for escort duty when the drum turns to Ams — still
   currentColor, the word's own ink, through themes and the
   blueprint alike. */
.ams-xxx {
  width: 17.8px;
  height: 61px;
  margin-left: 14px;
}
.ams-xxx path {
  fill: none;
  stroke: currentColor;
  stroke-width: 5.2;
}
/* row two abroad — BKK's mark: the seated Buddha of Tony's reference
   abstracted to a single closed outline (needle finial, stepped
   crown, the neck's dip, folded legs on their cushion), drawn clean
   from the silhouette's measured proportions (0.7735 aspect) and
   standing the saltires' own cap height, a solid in the word's ink.
   The template hides in the markup; the drum clones it for escort
   duty at promotion. */
.bkk-buddha {
  width: 47.2px;
  height: 61px;
  margin-left: 14px;
}
/* the hidden attribute is HTML's — an svg element never hears the
   UA's [hidden]{display:none}, so the templates must be told in CSS
   or they render inline beside the static from the first paint */
.ams-xxx[hidden],
.bkk-buddha[hidden],
.cn-panda[hidden],
.gc-globe[hidden] {
  display: none;
}
.bkk-buddha path {
  fill: currentColor;
  stroke: none;
}
/* row two's third and fourth seats (round 14): the panda for "is
   Chinese by blood", the desk globe for "is a global citizen" —
   each rebuilt from Tony's reference out of measured primitives
   (clipped-circle ears flush with the face ring, egg patches leaning
   to the ears, a two-arc smile; sphere ring, tilted axis and ticks,
   three landmasses, the under-arc cradle on its stem and base),
   solids in the word's own ink at the saltires' cap height. */
.cn-panda {
  width: 64px;
  height: 61px;
  margin-left: 14px;
}
.gc-globe {
  width: 46px;
  height: 61px;
  margin-left: 14px;
}
.cn-panda path,
.gc-globe path {
  fill: currentColor;
  stroke: none;
}
/* row two's home mark (round 16): the brain beside Intelligent —
   Tony's reference mirrored by its own skeleton: the ink thresholded,
   thinned to centerlines, and every stroke re-drawn from that walk —
   the bold contour one closed path, the gyri twenty-four open
   strokes. The reference stands 638x544 (aspect 1.1728), so the box
   is 71.1x61 at the saltires' cap height; its weights (contour 28.7,
   gyri 15.3 of 980 — a 1.9 ratio) land at 4.7 and 2.7, lifted from
   the tracing's proportional 3.2/1.7 to the family's presence. Round
   caps, the word's own ink. */
.avg-brain {
  width: 71.1px;
  height: 61px;
  margin-left: 12px; /* a seat narrower than the cities' 14 — the brain hugs its word */
}
/* the AI joke (round 17): the verdict's capitals stand the frame's
   own 85 and their tails drop to 47px — chosen in review against 43
   and 52: the A and I read as initials while the tails stay words.
   Baseline-aligned by inline flow in the static; the drum's escorts
   re-pin the baseline by measured line-height at promotion. The
   doubled selector out-specifies the frame's shared 85px span rule. */
.frame h1 span.ai-sm-a,
.frame h1 span.ai-sm-i {
  font-size: 47px;
}
.avg-brain path {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.avg-brain path[data-o] {
  stroke-width: 4.7;
}
/* the reveal's overflow:hidden clips an outer ring into two floating
   bars — draw the site's ring INSIDE the link's own box instead */
.frame h1 a:focus-visible {
  outline: 2px solid var(--colors-focus);
  outline-offset: -2px;
}

h1 {
  position: relative;
}

/* contact frame */
.contact {
  --padding: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  position: relative;
}
.contact button {
  cursor: copy;
}
.contact a,
.contact button {
  color: var(--colors-gray12);
  border-radius: 12px;
}
.contact a:focus-visible,
.contact button:focus-visible {
  outline: 4px solid var(--colors-focus);
  outline-offset: 4px;
}
.contact a[data-position="top-left"] { position: absolute; top: var(--padding); left: var(--padding); }
.contact a[data-position="top-right"] { position: absolute; top: var(--padding); right: var(--padding); }
.contact a[data-position="bottom-left"] { position: absolute; bottom: var(--padding); left: var(--padding); }
.contact a[data-position="bottom-right"] { position: absolute; bottom: var(--padding); right: var(--padding); }

.email {
  display: grid;
  place-items: center;
  overflow: hidden;
}
.email > * {
  grid-area: 1 / 1;
}
.email p {
  font-size: 85px;
  font-weight: 400;
}

/* ---------------- minimap ---------------- */

.minimap {
  position: fixed;
  top: 64px;
  left: 50%;
  transform: translateX(-50%);
  pointer-events: none;
  --line-width: 1px;
  --line-height: 18px;
  --line-gap: 9px;
  --line-color: var(--colors-gray9);
  --tracker-width: 30px;
}
.minimap-inner {
  display: flex;
  align-items: flex-end;
  gap: var(--line-gap);
}
.minimap .line {
  width: var(--line-width);
  height: var(--line-height);
  background: var(--line-color);
  position: relative;
}
.minimap .tracker {
  width: var(--tracker-width);
  height: var(--line-height);
  position: absolute;
  background: transparent;
  border: var(--line-width) solid var(--line-color);
}

/* ---------------- debug (wireframe) mode: click the cross ----------------
   the wire takes the theme's ink: black lines on the light ground,
   white on the dark one — a blueprint either way, never ink-on-ink */

.root[data-debug="true"] {
  --wire: #000;
}
.dark .root[data-debug="true"] {
  --wire: #fff;
}
.root[data-debug="true"] .circle,
.root[data-debug="true"] .contents,
.root[data-debug="true"] .main {
  background: transparent;
}
.root[data-debug="true"] .circle {
  border: 1px solid var(--wire);
}
.root[data-debug="true"] [data-sheet] {
  border: 1px solid var(--wire);
  background: transparent;
}
.root[data-debug="true"] [data-sheet] svg path {
  stroke: var(--wire);
  stroke-width: 1px;
}
.root[data-debug="true"] .contact a,
.root[data-debug="true"] h1 span,
.root[data-debug="true"] h2,
.root[data-debug="true"] h3,
.root[data-debug="true"] p {
  color: transparent;
  -webkit-text-stroke: 1px var(--wire);
}
/* small type can't live as a 1px hairline — the hover captions take
   the wire as fill instead */
.root[data-debug="true"] .p1-key-cap i {
  color: var(--wire);
}
.root[data-debug="true"] [data-icon="history"] path {
  stroke: var(--wire);
  fill: var(--wire);
  stroke-width: 0px;
}
.root[data-debug="true"] .frame h1 a {
  color: transparent;
  -webkit-text-stroke: 1px var(--wire);
}
/* the crosses join the blueprint as hairlines — the brain too */
.root[data-debug="true"] .ams-xxx path,
.root[data-debug="true"] .avg-brain path {
  stroke: var(--wire);
  stroke-width: 1px;
}
.root[data-debug="true"] .bkk-buddha path {
  fill: none;
  stroke: var(--wire);
  stroke-width: 1px;
}
.root[data-debug="true"] .cn-panda path,
.root[data-debug="true"] .gc-globe path {
  fill: none;
  stroke: var(--wire);
  stroke-width: 1px;
}

/* ---------------- touch devices: one road under the thumb ----------------
   The desktop geometry serves both pointers now — the ghost is shaped
   from JS (vertical-only on coarse), scrollY drives the travel, and
   travel.js supplies the horizontal drag, the fling and the settle. */

@media (pointer: coarse) {
  html {
    /* nothing scrolls on touch — every single-finger pan belongs to
       travel.js, which turns sideways swipes into the sideways travel;
       pinch stays free — the reading escape hatch */
    touch-action: pinch-zoom;
  }
  /* the crosshair is a cursor's shadow — a screen with no cursor casts none
     (and the wireframe easter egg it toggles stays a desktop secret) */
  .cross {
    display: none;
  }
  /* corners and the email answer to thumbs */
  .contact a { padding: 20px; margin: -20px; }
}

/* ---------------- player one: the frame before the beginning ---------------- */

.playerOne {
  visibility: hidden;
}
.playerOne[data-revealed="true"] {
  visibility: visible;
}
.p1-sheet {
  position: relative;
  color: var(--colors-gray12);
  transform: scale(0.94);
  opacity: 0;
  transition: transform 0.55s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.4s ease;
}
.playerOne[data-revealed="true"] .p1-sheet {
  transform: scale(1);
  opacity: 1;
}

/* the frame speaks the site's diagonal grammar: identity top-left,
   links bottom-right, the copper key bottom-left as the signature */
.p1-top {
  position: absolute;
  top: 52px;
  left: 64px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.frame .p1-kicker {
  font-size: 22px;
  color: var(--colors-gray11);
}
.p1-kicker i {
  font-family: Georgia, serif;
}
.p1-name {
  font-size: 140px;
  font-weight: 400;
  line-height: 0.95;
  letter-spacing: -0.01em;
}
.frame .p1-text {
  font-size: 21px;
  line-height: 1.5;
  color: var(--colors-gray12);
}
.p1-links {
  position: absolute;
  right: 64px;
  bottom: 52px;
  display: flex;
  gap: 26px;
}
.frame .p1-links a {
  font-size: 18px;
  color: var(--colors-gray12);
}
.frame .p1-links a:hover,
.frame .p1-links a:focus-visible {
  background: var(--yellow);
  outline: 0;
}
/* the three keys spin in the empty band beside the kicker;
   the hovered one whispers its line in a reserved strip below them */
.p1-keys-wrap {
  position: absolute;
  left: 480px;
  top: 24px;
  width: 640px;
  height: 260px;
  pointer-events: none;
}
.p1-keys-wrap canvas {
  position: absolute;
  top: 0;
  left: 0;
}
.frame .p1-key-cap {
  position: absolute;
  top: 224px;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 14px;
  color: var(--colors-gray12);
  opacity: 0;
  transition: opacity 0.45s ease;
}
.p1-key-cap i {
  font-family: Georgia, serif;
}
.p1-key-cap[data-cap="0"] { left: 100px; }
.p1-key-cap[data-cap="1"] { left: 300px; }
.p1-key-cap[data-cap="2"] { left: 500px; }
.p1-key-cap[data-show="true"] { opacity: 1; }

/* the four anchors arrive in reading order: identity, spectrum, links, keys */
.p1-top,
.p1-spectrum,
.p1-links,
.p1-keys-wrap {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s ease, transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.playerOne[data-revealed="true"] .p1-top { transition-delay: 0.08s; }
.playerOne[data-revealed="true"] .p1-spectrum { transition-delay: 0.2s; }
.playerOne[data-revealed="true"] .p1-links { transition-delay: 0.32s; }
.playerOne[data-revealed="true"] .p1-keys-wrap { transition-delay: 0.4s; }
.playerOne[data-revealed="true"] .p1-top,
.playerOne[data-revealed="true"] .p1-spectrum,
.playerOne[data-revealed="true"] .p1-links,
.playerOne[data-revealed="true"] .p1-keys-wrap {
  opacity: 1;
  transform: none;
}

/* the spectrum: one frame-wide gesture. eight fields, each a frequency;
   under the cursor the band magnifies and turns copper (the key's metal) */
.p1-spectrum {
  position: absolute;
  left: 64px;
  right: 64px;
  top: 322px;
}
.p1-spec-labels {
  position: relative;
  height: 38px;
}
.p1-spec-labels[data-side="top"] {
  margin-bottom: 16px;
}
.p1-spec-labels[data-side="bottom"] {
  margin-top: 16px;
}
.frame .p1-spec-labels span {
  position: absolute;
  transform: translateX(-50%);
  font-size: 14px;
  line-height: 1.35;
  text-align: center;
  color: var(--colors-gray12);
  transition: background-color 0.15s ease;
  white-space: nowrap;
}
.p1-spec-labels[data-side="top"] span {
  bottom: 0;
}
.p1-spec-labels span[data-zone="0"] { left: 6.25%; }
.p1-spec-labels span[data-zone="1"] { left: 18.75%; }
.p1-spec-labels span[data-zone="2"] { left: 31.25%; }
.p1-spec-labels span[data-zone="3"] { left: 43.75%; }
.p1-spec-labels span[data-zone="4"] { left: 56.25%; }
.p1-spec-labels span[data-zone="5"] { left: 68.75%; }
.p1-spec-labels span[data-zone="6"] { left: 81.25%; }
.p1-spec-labels span[data-zone="7"] { left: 93.75%; }
.p1-spec-labels span[data-pole="true"] {
  font-weight: 500;
}
.p1-spec-labels span[data-active="true"] {
  background: var(--yellow);
}
.p1-spec-band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 96px;
  cursor: crosshair;
  touch-action: none;
}
.p1-spec-band .bar {
  width: 2px;
  background: var(--colors-gray9);
  transition: transform 0.14s ease-out, background-color 0.14s ease-out;
  will-change: transform;
}
.frame .p1-status {
  margin-top: 18px;
  min-height: 22px;
  text-align: center;
  font-size: 14px;
}
.p1-status i {
  font-family: Georgia, serif;
  color: var(--colors-gray11);
}
.p1-status i[data-hidden="true"] {
  display: none;
}
.frame .p1-status span {
  font-family: Menlo, Consolas, monospace;
  font-size: 14px;
  color: var(--colors-gray11);
}

/* ---------------- wittgenstein, silkscreened on the mantra sheet ---------------- */

.mantra-witt {
  position: absolute;
  top: 12px;
  right: 96px;
  height: calc(100% - 24px);
  pointer-events: none;
  z-index: 0;
}
.frame p.mantra-question,
.frame p.mantra-answer,
.frame p.mantra-source {
  z-index: 1;
}

/* the hint chip, left of the minimap */
.p1-hint {
  position: fixed;
  top: 58px;
  right: calc(50% + 128px);
  z-index: 20;
  border: 0;
  background: transparent;
  padding: 6px 10px;
  cursor: pointer;
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  /* visibility keeps the hidden button out of the tab order for its
     first seconds; its delay holds the fade smooth on the way out */
  visibility: hidden;
  transition: opacity 0.8s ease, transform 0.8s ease, visibility 0s 0.8s;
}
.p1-hint[data-show="true"] {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  visibility: visible;
  transition-delay: 0s;
}
.p1-hint i {
  font-family: Georgia, serif;
  font-style: italic;
  font-size: 14px;
  color: var(--colors-gray11);
  white-space: nowrap;
  transition: color 0.2s ease;
}
.p1-hint:hover i {
  color: var(--colors-gray12);
}
.p1-hint:focus-visible {
  outline: 2px solid var(--colors-focus);
  border-radius: 6px;
}

/* ---- scroll hint: the riddle's practical twin, right of the minimap ----
   A miniature ruler; the yellow tracker (the site's "you are here" mark)
   fades in at the start, holds, glides right, dissolves. */
.scroll-hint {
  position: fixed;
  top: 58px;
  left: calc(50% + 128px);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 0;
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 0.8s ease, transform 0.8s ease;
}
.scroll-hint[data-show="true"] {
  opacity: 1;
  transform: none;
}
.scroll-hint i {
  font-family: Georgia, serif;
  font-style: italic;
  font-size: 14px;
  color: var(--colors-gray11);
  white-space: nowrap;
}
.scroll-hint-demo {
  position: relative;
  width: 41px;
  height: 12px;
}
.scroll-hint-demo .sh-line {
  position: absolute;
  top: 0;
  width: 1px;
  height: 12px;
  background: var(--colors-gray8);
}
.scroll-hint-demo .sh-line:nth-child(1) { left: 0; }
.scroll-hint-demo .sh-line:nth-child(2) { left: 10px; }
.scroll-hint-demo .sh-line:nth-child(3) { left: 20px; }
.scroll-hint-demo .sh-line:nth-child(4) { left: 30px; }
.scroll-hint-demo .sh-line:nth-child(5) { left: 40px; }
.scroll-hint-demo .sh-tracker {
  position: absolute;
  top: 0;
  left: 0;
  width: 16px;
  height: 12px;
  background: var(--yellow);
  opacity: 0;
  animation: shTravel 2.8s cubic-bezier(0.45, 0.05, 0.25, 1) infinite;
}
@keyframes shTravel {
  0%   { transform: translateX(0);    opacity: 0; }
  12%  { transform: translateX(0);    opacity: 1; }
  30%  { transform: translateX(0);    opacity: 1; }
  74%  { transform: translateX(25px); opacity: 1; }
  90%  { transform: translateX(25px); opacity: 0; }
  100% { transform: translateX(25px); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .scroll-hint {
    transition: opacity 0.8s ease;
    transform: none;
  }
  .scroll-hint-demo .sh-tracker {
    animation: none;
    transform: translateX(12px);
    opacity: 1;
  }
}
/* ---- colophon: the debt at the foot of the menu ----
   whisper-set like the riddle and the ruler, one size softer; the
   line ignores the cursor — only the name itself answers it */
.colophon {
  position: fixed;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  pointer-events: none;
}
.colophon i {
  font-family: Georgia, serif;
  font-style: italic;
  font-size: 11.5px;
  color: var(--colors-gray9);
  white-space: nowrap;
}
.colophon a {
  color: var(--colors-gray11);
  text-decoration: none;
  border-bottom: 1px solid var(--colors-gray8);
  pointer-events: auto;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.colophon a:hover {
  color: var(--colors-gray12);
  border-color: var(--colors-gray9);
}
.colophon a:focus-visible {
  outline: 2px solid var(--colors-focus);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .p1-sheet,
  .p1-top,
  .p1-spectrum,
  .p1-links,
  .p1-keys-wrap,
  .p1-hint {
    transition: none;
  }
}

/* ---------------- a confession card for touch-first visitors ---------------- */

.mobile-note {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: 16px;
  z-index: 40;
  max-width: 430px;
  margin: 0 auto;
  background: white;
  color: var(--colors-gray12);
  padding: 24px 24px 22px;
  overflow: hidden;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.18);
  transform: translateY(calc(100% + 24px));
  transition: transform 0.65s cubic-bezier(0.2, 0.9, 0.3, 1.08);
}
.mobile-note[data-show="true"] {
  transform: none;
}
.mobile-note-circle {
  position: absolute;
  top: -72px;
  right: -72px;
  width: 152px;
  height: 152px;
  border-radius: var(--radii-round);
  background: var(--yellow);
}
.mobile-note-mac {
  position: absolute;
  top: 18px;
  right: 16px;
}
.mobile-note-kicker {
  font-size: 14px;
  color: var(--colors-gray11);
  margin-bottom: 10px;
}
.mobile-note-line {
  font-size: 21px;
  font-weight: 500;
  line-height: 1.25;
  max-width: 250px;
}
.mobile-note-sub {
  font-size: 14px;
  color: var(--colors-gray11);
  margin-top: 10px;
  max-width: 270px;
  line-height: 1.45;
}
.mobile-note-btn {
  margin-top: 18px;
  font-size: 15px;
  font-weight: 500;
  padding: 10px 18px;
  background: var(--colors-gray12);
  color: white;
  cursor: pointer;
}
.mobile-note-btn:active {
  background: black;
}
@media (prefers-reduced-motion: reduce) {
  .mobile-note {
    transform: none;
    opacity: 0;
    transition: opacity 0.3s ease;
  }
  .mobile-note[data-show="true"] {
    opacity: 1;
  }
}

/* ---- the rotate glyph: a card outline that turns and settles wide,
   the yellow tracker arriving once it lies down ---- */
.rotate-glyph {
  display: inline-block;
  width: 34px;
  height: 34px;
  position: relative;
  vertical-align: middle;
  margin-right: 10px;
}
.rg-frame {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 30px;
  height: 17px;
  border: 1.5px solid var(--colors-gray9);
  border-radius: 3px;
  transform: translate(-50%, -50%) rotate(90deg);
  animation: rgTurn 3.2s cubic-bezier(0.3, 0.7, 0.2, 1) infinite;
}
.rg-tracker {
  position: absolute;
  left: 3px;
  top: 3px;
  width: 9px;
  height: 8px;
  background: var(--yellow);
  opacity: 0;
  animation: rgTracker 3.2s ease infinite;
}
@keyframes rgTurn {
  0%, 22%  { transform: translate(-50%, -50%) rotate(90deg); }
  42%, 88% { transform: translate(-50%, -50%) rotate(0deg); }
  100%     { transform: translate(-50%, -50%) rotate(90deg); }
}
@keyframes rgTracker {
  0%, 40%  { opacity: 0; }
  52%, 82% { opacity: 1; }
  92%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .rg-frame { animation: none; transform: translate(-50%, -50%) rotate(0deg); }
  .rg-tracker { animation: none; opacity: 1; }
}
.mobile-note-turn {
  font-size: 14px;
  color: var(--colors-gray11);
  margin-top: 12px;
  display: flex;
  align-items: center;
}
/* the chip: quiet, bottom center, portrait only (JS gates too) */
.rotate-hint {
  position: fixed;
  bottom: 44px;
  left: 50%;
  transform: translate(-50%, 6px);
  z-index: 20;
  display: none;
  align-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.8s ease, transform 0.8s ease;
}
.rotate-hint i {
  font-family: Georgia, serif;
  font-size: 14px;
  color: var(--colors-gray11);
  white-space: nowrap;
}
@media (pointer: coarse) {
  .rotate-hint { display: flex; }
  .rotate-hint[data-show="true"] { opacity: 1; transform: translate(-50%, 0); }
  /* the ruler hint returns on touch, bottom center, above the colophon */
  .scroll-hint { top: auto; bottom: 44px; left: 50%; transform: translate(-50%, 6px); }
  .scroll-hint[data-show="true"] { transform: translate(-50%, 0); }
  /* the riddle chip comes inside the viewport on phones; hidden it must
     swallow no taps — an invisible strip over the hint band opened
     Player One from nowhere */
  .p1-hint { top: auto; right: auto; bottom: 40px; left: 16px; pointer-events: none; }
  .p1-hint[data-show="true"] { pointer-events: auto; }
  /* the paint-level law of the bottom band: whatever the choreography in
     JS says, at most ONE line ever renders. Priority runs confession
     card > open Player One > turn chip > swipe hint > riddle chip; a
     suppressed chip drops instantly (no crossfade double-paint) and
     fades back in normally once its suppressor leaves. */
  html:has(#mobileNote[data-show="true"]) #rotateHint,
  html:has(#mobileNote[data-show="true"]) #scrollHint,
  html:has(#mobileNote[data-show="true"]) #p1Hint,
  html:has(#playerOne[data-revealed="true"]) #rotateHint,
  html:has(#playerOne[data-revealed="true"]) #scrollHint,
  html:has(#rotateHint[data-show="true"]) #scrollHint,
  html:has(#rotateHint[data-show="true"]) #p1Hint,
  html:has(#scrollHint[data-show="true"]) #p1Hint {
    opacity: 0 !important;
    pointer-events: none !important;
    transition-duration: 0.01s !important;
  }
  /* Player One reads at thumb size inside its 1200x720 stage
     (this block sits after the P1 base rules — same specificity,
     the later voice carries). The name cedes height so the louder
     lines stay clear of the spectrum's top labels. */
  .p1-top { top: 40px; gap: 8px; }
  .p1-name { font-size: 112px; }
  .frame .p1-kicker { font-size: 30px; }
  .frame .p1-text { font-size: 32px; }
  .frame .p1-links { gap: 34px; }
  .frame .p1-links a { font-size: 32px; padding: 12px; margin: -12px; }
  .frame .p1-spec-labels span { font-size: 20px; }
  .frame .p1-status, .frame .p1-status span { font-size: 20px; }
  .frame .p1-key-cap { font-size: 18px; }
  .frame p.mantra-source { font-size: 20px; }
}
@media (pointer: coarse) and (max-height: 500px) {
  /* short landscape: the chrome ducks toward the edges */
  .minimap { top: 12px; }
  .p1-hint { bottom: 12px; }
  .rotate-hint, .scroll-hint { bottom: 12px; }
  .colophon { display: none; }
}
