/* ============================================================
   INDITHAR — Ink & Candlelight
   The screen is a continuation of a dark room.
   ============================================================ */

:root {
  --ink:        oklch(0.13 0.012 70);
  --void:       oklch(0.08 0.008 260);
  --bone:       oklch(0.90 0.02 85);
  --bone-dim:   oklch(0.72 0.02 85);
  --faded:      oklch(0.48 0.015 80);
  --candle:     oklch(0.78 0.13 78);
  --candle-deep:oklch(0.62 0.14 65);
  --hairline:   oklch(0.30 0.02 75);

  --covenant: oklch(0.70 0.02 250);
  --choir:    oklch(0.85 0.09 95);
  --pact:     oklch(0.45 0.14 25);
  --court:    oklch(0.60 0.07 240);
  --wound:    oklch(0.35 0.10 300);

  --serif: 'Spectral', Georgia, serif;
  --fell:  'IM Fell English SC', serif;
  --fell-text: 'IM Fell English', serif;

  --ease: cubic-bezier(0.22, 1, 0.36, 1); /* out-quint */
}

* { margin: 0; padding: 0; box-sizing: border-box; }

[hidden] { display: none !important; }

html { scrollbar-color: var(--hairline) var(--void); }

body {
  background: var(--void);
  color: var(--bone);
  font-family: var(--serif);
  font-weight: 300;
  line-height: 1.75;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--candle-deep); color: var(--void); }

img { max-width: 100%; display: block; }
a { color: var(--candle); text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* ---------- shared layers ---------- */

/* THE LAMP — the wall of the room, and the only light falling on it.
   This canvas sits behind every room and is the reason the paintings have
   surfaces now. See js/lamp.js. */
#lamp {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  width: 100%; height: 100%;
  opacity: 0;
  transition: opacity 900ms ease-in;
  transform: translateY(calc(var(--shock, 0) * 2px));
}
#lamp.lit { opacity: 1; }

/* THE AIR — everything between you and the wall: the flame you carry, the dust
   it catches, and the other people who are quietly in here with you. One layer,
   one loop. See js/air.js. */
#air {
  position: fixed; inset: 0; z-index: 28; pointer-events: none;
  width: 100%; height: 100%;
  mix-blend-mode: screen;
  transform: translateY(calc(var(--shock, 0) * 3px));
}

/* THE DOM OBEYS THE LIGHT — the walls are lit in the shader, but the cards and
   plates standing in front of them are HTML, and they used to ignore the candle
   entirely. One soft-light pool riding the lamp variables published by candle.js:
   black stays black, parchment and cards warm where the flame falls. It moves by
   transform only — composited, never repainted. */
#domlight {
  position: fixed; inset: 0; z-index: 26; pointer-events: none;
  mix-blend-mode: soft-light;
  overflow: hidden;
}
#domlight .pool {
  position: absolute; left: -700px; top: -700px;
  width: 1400px; height: 1400px;
  background: radial-gradient(circle closest-side,
    rgba(255, 224, 178, 0.62),
    rgba(255, 212, 156, 0.24) 55%,
    transparent 74%);
  transform: translate3d(var(--lx, 50vw), var(--ly, 62vh), 0)
             scale(calc(var(--lr, 300) / 520));
  opacity: var(--lo, 0);
  transition: opacity 1400ms ease;
  will-change: transform, opacity;
}
body.no-lamp #domlight { display: none; }   /* a software rasterizer has no blend to spare */

/* film, not video: the grain is quantised to twelve frames a second */
#grain {
  position: fixed; inset: -8%; z-index: 29; pointer-events: none;
  opacity: 0.038;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grainshift 0.5s steps(1) infinite;
}
@keyframes grainshift {
  0%     { transform: translate(0, 0); }
  16.6%  { transform: translate(-3%, 2%); }
  33.3%  { transform: translate(2%, -3%); }
  50%    { transform: translate(-2%, -2%); }
  66.6%  { transform: translate(3%, 2%); }
  83.3%  { transform: translate(-1%, 3%); }
  100%   { transform: translate(0, 0); }
}

/* living paintings: ambient video over the still */
.bg-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1600ms ease-in;
}
.bg-video.live { opacity: 1; }

/* room arrival: settle out of the dark */
.room.arrive > * { animation: settle 1100ms var(--ease) both; }
.room.arrive > *:nth-child(2) { animation-delay: 90ms; }
.room.arrive > *:nth-child(3) { animation-delay: 180ms; }
.room.arrive > *:nth-child(4) { animation-delay: 260ms; }
@keyframes settle {
  from { opacity: 0; transform: scale(1.015); filter: blur(5px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* headings ink themselves in */
.room.arrive h2 { animation: inkin 1300ms var(--ease) 200ms both; }
@keyframes inkin {
  from { opacity: 0; filter: blur(7px); letter-spacing: 0.16em; }
  to   { opacity: 1; filter: none; }
}

/* scroll reveals */
.reveal { opacity: 0; transform: translateY(26px); }
.reveal.revealed {
  opacity: 1; transform: none;
  transition: opacity 1000ms var(--ease), transform 1000ms var(--ease);
}

/* The veil is a canvas now: it does not fade, it CLOSES — a hole shrinking onto the
   exact point you touched, so the door you chose is the last thing alive on screen.
   See js/transit.js. */
#veil {
  position: fixed; inset: 0; z-index: 90;
  width: 100%; height: 100%;
  pointer-events: none;
}

/* The wordmark used to sit on the void, so it needed nothing behind it. Then the
   Atlas filled the screen with pale parchment and it vanished into the mountains.
   A scrim you cannot see in a dark room, and can in a bright one. */
#chrome::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 260%;
  background: linear-gradient(180deg, oklch(0.05 0.008 260 / 0.88), oklch(0.05 0.008 260 / 0.4) 45%, transparent);
  pointer-events: none; z-index: -1;
}

#chrome {
  position: fixed; z-index: 80; inset: 0 0 auto 0;
  display: flex; justify-content: space-between; align-items: baseline;
  padding: clamp(1rem, 3vh, 1.6rem) clamp(1.2rem, 4vw, 3rem);
  pointer-events: none;
}
#chrome > * { pointer-events: all; }

#wordmark {
  font-family: var(--fell);
  font-size: 1.05rem; letter-spacing: 0.22em; color: var(--bone-dim);
  transition: color 400ms;
}
#wordmark:hover { color: var(--candle); }

/* THE FLAME IS THE CHROME — there is no mute button; there is a small candle in
   the corner and you pinch it out. Sound and light are the same substance here,
   so the room dims a little with it (body.quenched below). */
#mute {
  display: inline-flex; align-items: center; gap: 0.55em;
  min-height: 44px; padding: 0 0.2em;
  font-variant-caps: small-caps; letter-spacing: 0.14em;
  font-size: 0.8rem; color: var(--faded); transition: color 400ms;
}
#mute:hover { color: var(--candle); }

.mute-candle { position: relative; width: 14px; height: 26px; display: block; }
.mute-wick {
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 2px; height: 9px;
  background: linear-gradient(180deg, oklch(0.30 0.02 60), oklch(0.20 0.015 60));
}
.mute-flame {
  position: absolute; left: 50%; bottom: 7px;
  width: 9px; height: 15px;
  transform: translateX(-50%);
  transform-origin: 50% 100%;
  background: radial-gradient(ellipse 60% 48% at 50% 78%, oklch(0.95 0.08 85), oklch(0.80 0.14 70) 55%, oklch(0.62 0.15 55) 82%, transparent);
  border-radius: 50% 50% 42% 42% / 68% 68% 32% 32%;
  filter: drop-shadow(0 0 6px oklch(0.78 0.13 78 / 0.65));
  animation: muteflame 2.7s ease-in-out infinite;
  transition: opacity 700ms ease, transform 700ms ease;
}
@keyframes muteflame {
  0%, 100% { transform: translateX(-50%) skewX(0deg) scaleY(1); }
  28%      { transform: translateX(-50%) skewX(3.5deg) scaleY(1.06); }
  55%      { transform: translateX(-50%) skewX(-2.5deg) scaleY(0.95); }
  80%      { transform: translateX(-50%) skewX(1.5deg) scaleY(1.03); }
}
#mute[aria-pressed="true"] .mute-flame {
  opacity: 0; transform: translateX(-50%) scale(0.2); animation: none;
}
/* the ember that remains, and one thread of smoke as it goes out */
#mute[aria-pressed="true"] .mute-wick { background: linear-gradient(180deg, oklch(0.45 0.11 35), oklch(0.20 0.015 60) 45%); }
.mute-candle::after {
  content: ''; position: absolute; left: 50%; bottom: 12px;
  width: 3px; height: 16px; transform: translateX(-50%);
  background: linear-gradient(180deg, transparent, oklch(0.6 0.01 260 / 0.5));
  border-radius: 50%;
  opacity: 0; pointer-events: none;
}
#mute[aria-pressed="true"] .mute-candle::after { animation: smokerise 2.2s ease-out both; }
@keyframes smokerise {
  0%   { opacity: 0;    transform: translateX(-50%) translateY(0) scaleY(0.4); }
  18%  { opacity: 0.75; }
  100% { opacity: 0;    transform: translateX(-52%) translateY(-15px) scaleY(1.5) rotate(-7deg); }
}

.mute-word { transition: opacity 400ms; opacity: 0; }
#mute:hover .mute-word, #mute:focus-visible .mute-word { opacity: 1; }
#mute .off { display: none; }
#mute[aria-pressed="true"] .on { display: none; }
#mute[aria-pressed="true"] .off { display: inline; }

/* pinch the flame and the room leans darker with it */
body.quenched #lamp { filter: brightness(0.86) saturate(0.94); }
body.quenched #air { opacity: 0.8; }

/* ---------- rooms ---------- */

.room {
  position: fixed; inset: 0;
  display: block;
}
.room[hidden] { display: none; }
.room-scroll { overflow-y: auto; overflow-x: hidden; }

/* ---------- placard: the atom ---------- */

.placard-label {
  font-variant-caps: small-caps; letter-spacing: 0.16em;
  font-size: 0.78rem; color: var(--faded); font-weight: 400;
}

.hairline { border: 0; border-top: 1px solid var(--hairline); }

/* said, not shown — for the visitors who are listening to this place */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}

/* ============================================================
   INTRO — the threshold
   ============================================================ */

/* The threshold is a composition, not a stack.
   It used to be `display:grid; place-items:center` with nothing sizing the rows —
   which means `align-content` fell back to STRETCH, the four in-flow children each
   took a quarter of the screen, and the first line landed centred in the TOP
   quarter. The -16vh nudge that was meant to lift it off the title then carried it
   clean off the top edge, on every screen size there is. The lines were being typed
   above the window.

   So the rows are declared, and every element is placed in one by name. The air
   between them flexes; the elements themselves never move. The Enter button owns
   its band whether or not it has appeared yet, so nothing reflows when it does. */
.intro-scene {
  position: absolute; inset: 0; overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows:
    minmax(0, 1fr)      /* air above */
    auto                /* the lines */
    minmax(0, 0.5fr)    /* the pause before the name */
    auto                /* the name */
    auto                /* the bleeding world */
    minmax(0, 1.5fr);   /* the way in */
  grid-template-areas: "air-top" "lines" "air-mid" "title" "sub" "act";
  justify-items: center;
  padding:
    calc(env(safe-area-inset-top, 0px) + 1rem)
    clamp(1rem, 5vw, 2rem)
    calc(env(safe-area-inset-bottom, 0px) + 1rem);
  background: var(--void);
}

.intro-tome {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0;
  transition: opacity 2400ms ease-in;
  filter: brightness(0.75) saturate(0.85);
}
.intro-scene.lit .intro-tome { opacity: 0.85; }
/* The living painting wears .intro-tome too — for the grade, which it should
   share with the still. But it was inheriting the still's OPACITY along with it,
   which outranks .bg-video and made the video visible before it had a frame to
   show. It keeps the grade and gets its own arrival back. */
.intro-scene.lit .bg-video { opacity: 0; }
.intro-scene.lit .bg-video.live { opacity: 0.85; }

.intro-fog {
  position: absolute; inset: -20%;
  background:
    radial-gradient(ellipse 60% 40% at 30% 80%, oklch(0.35 0.01 260 / 0.16), transparent 70%),
    radial-gradient(ellipse 50% 35% at 75% 70%, oklch(0.35 0.01 260 / 0.11), transparent 70%);
  animation: fogdrift 26s ease-in-out infinite alternate;
  opacity: 0;
  transition: opacity 3000ms ease-in;
}
.intro-scene.lit .intro-fog { opacity: 1; }
@keyframes fogdrift {
  from { transform: translateX(-3%) translateY(0); }
  to   { transform: translateX(3%) translateY(-2%); }
}

.intro-lines {
  grid-area: lines;
  position: relative; z-index: 2;
  font-family: var(--fell-text); font-style: italic;
  /* the vw term also answers to the height, or a laptop in landscape gets phone type */
  font-size: clamp(1.15rem, 2.2vw + 0.6vh, 2rem);
  letter-spacing: 0.08em;
  color: var(--bone-dim);
  line-height: 1.5;
  /* two lines of room, always — so the line that wraps does not shift the room */
  min-height: 3em;
  max-width: 24ch;
  text-align: center;
  text-wrap: balance;
  text-shadow: 0 2px 24px oklch(0.05 0 0 / 0.9);
}

.intro-title {
  grid-area: title;
  position: relative; z-index: 2;
  font-family: var(--fell);
  font-weight: 400;
  /* Height matters as much as width here: this is the one word that must never be
     taller than the screen it is arriving on — and never wider either. Tracked at
     0.18em, INDITHAR measures exactly 8.6× its own font-size, so the 10vw ceiling
     is the width of the room, expressed as type. Below it the name broke across
     two lines on a phone and read INDITHA / R. */
  font-size: min(clamp(2.2rem, 7vw + 2vh, 7.5rem), 10vw);
  line-height: 1.12;
  letter-spacing: 0.18em;
  text-indent: 0.18em; /* optically recenter tracked caps */
  white-space: nowrap;
  max-width: 100%;
  color: var(--bone);
  text-shadow: 0 0 60px oklch(0.78 0.13 78 / 0.25), 0 2px 30px oklch(0.05 0 0 / 0.95);
}
.intro-title span {
  display: inline-block;
  opacity: 0; transform: translateY(0.16em) scale(1.05); filter: blur(7px);
  transition: opacity 1100ms var(--ease), transform 1300ms var(--ease), filter 1100ms var(--ease);
  transition-delay: calc(var(--i, 0) * 95ms);
}
.intro-scene.titled .intro-title span { opacity: 1; transform: none; filter: none; }
/* no-JS / reduced fallback: the un-split text node still shows via .titled */
.intro-title:not(:has(span)) { opacity: 0; transition: opacity 2200ms var(--ease); }
.intro-scene.titled .intro-title:not(:has(span)) { opacity: 1; }

.intro-sub {
  grid-area: sub;
  position: relative; z-index: 2;
  margin-top: 0.6em;
  font-variant-caps: small-caps; letter-spacing: 0.34em; text-indent: 0.34em;
  color: var(--faded); font-size: clamp(0.72rem, 1.6vw, 1rem);
  opacity: 0; transition: opacity 2400ms ease-in 600ms;
}
.intro-scene.titled .intro-sub { opacity: 1; }

.enter-btn {
  /* its band exists from the first frame, so the threshold does not jump
     underneath the visitor at the moment the way in appears */
  grid-area: act;
  align-self: center;
  position: relative; z-index: 2;
  font-family: var(--fell);
  font-size: clamp(0.95rem, 1.2vw + 0.6vh, 1.5rem);
  letter-spacing: 0.3em; text-indent: 0.3em;
  color: var(--candle);
  padding: 0.9em 2.4em;
  max-width: 100%;
  border: 1px solid oklch(0.40 0.06 75 / 0.6);
  background: oklch(0.10 0.01 70 / 0.35);
  opacity: 0; transform: translateY(12px);
  transition: opacity 1400ms var(--ease), transform 1400ms var(--ease),
              border-color 500ms, color 500ms, box-shadow 500ms;
}
.enter-btn.shown { opacity: 1; transform: translateY(0); }
.enter-btn:hover, .enter-btn:focus-visible {
  color: var(--bone); border-color: var(--candle);
  box-shadow: 0 0 40px oklch(0.78 0.13 78 / 0.18), inset 0 0 24px oklch(0.78 0.13 78 / 0.07);
}

/* the cover — the first touch. The whole threshold is the button, and until a
   hand is laid on it nothing moves and nothing sounds: a browser will not let a
   room speak before it is touched, so the touch became part of the ritual. */
.threshold-touch {
  position: absolute; inset: 0; z-index: 3;
  display: grid; place-items: end center;
  padding: 0 clamp(1rem, 6vw, 3rem)
           calc(env(safe-area-inset-bottom, 0px) + clamp(4.5rem, 16vh, 9rem));
  cursor: pointer;
}
.threshold-touch span {
  font-family: var(--fell-text); font-style: italic;
  font-size: clamp(0.95rem, 1.6vw + 0.4vh, 1.3rem); letter-spacing: 0.1em;
  color: var(--faded);
  text-align: center; text-wrap: balance;
  text-shadow: 0 2px 18px oklch(0.05 0 0);
  animation: gatebreath 5.2s ease-in-out infinite;
}
@keyframes gatebreath {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 0.95; }
}
.threshold-touch:hover span, .threshold-touch:focus-visible span { color: var(--bone-dim); }
.threshold-touch.gone {
  opacity: 0; pointer-events: none;
  transition: opacity 900ms var(--ease);
}

.skip-note {
  /* above the cover: one touch must be enough for someone who has stood here before */
  position: absolute; z-index: 4;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 0.6rem);
  left: 50%; transform: translateX(-50%);
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 0.8em;
  font-variant-caps: small-caps; letter-spacing: 0.14em;
  font-size: 0.78rem; color: var(--faded);
  white-space: nowrap; max-width: calc(100% - 2rem);
  opacity: 0; transition: opacity 1600ms ease-in 1200ms, color 400ms;
}
.skip-note.shown { opacity: 0.85; }
.skip-note:hover { color: var(--candle); }

/* ============================================================
   HUB — the great library
   ============================================================ */

.hub-art {
  position: absolute; inset: 0; overflow: hidden;
  transform: scale(1.07);
  transition: transform 400ms linear;
}
.hub-art img {
  width: 100%; height: 100%; object-fit: cover;
  filter: brightness(0.9);
}
.hub-art .bg-video { filter: brightness(0.9); }

.hub-grade {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg, oklch(0.08 0.008 260 / 0.88) 0%, oklch(0.08 0.008 260 / 0.55) 34%, transparent 62%),
    linear-gradient(0deg, oklch(0.08 0.008 260 / 0.85) 0%, transparent 30%),
    linear-gradient(180deg, oklch(0.08 0.008 260 / 0.55) 0%, transparent 22%);
}

.hub-doors {
  position: absolute; z-index: 3;
  left: clamp(1.4rem, 6vw, 6rem);
  top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column;
  gap: clamp(0.5rem, 1.9vh, 1.15rem);
  max-width: min(560px, 88vw);
}

.hub-eyebrow {
  font-variant-caps: small-caps; letter-spacing: 0.2em;
  color: var(--faded); font-size: 0.8rem;
  margin-bottom: clamp(0.6rem, 2.4vh, 1.6rem);
}

.door { display: block; opacity: 0; transform: translateX(-14px); }
.door.lit { opacity: 1; transform: none; transition: opacity 900ms var(--ease), transform 900ms var(--ease); }

.door-name {
  display: block;
  font-family: var(--fell);
  font-size: clamp(1.5rem, 3.6vh, 2.2rem);
  letter-spacing: 0.06em;
  color: var(--bone-dim);
  transition: color 450ms, text-shadow 450ms, transform 600ms var(--ease);
}
.door-whisper {
  display: block;
  font-style: italic; font-family: var(--fell-text);
  font-size: clamp(0.85rem, 1.8vh, 1rem);
  color: transparent;
  letter-spacing: 0.04em;
  max-height: 0; overflow: hidden;
  transition: color 600ms 100ms, max-height 500ms var(--ease);
}
.door:hover .door-name, .door:focus-visible .door-name {
  color: var(--candle);
  text-shadow: 0 0 34px oklch(0.78 0.13 78 / 0.35);
  transform: translateX(6px);
}
.door:hover .door-whisper, .door:focus-visible .door-whisper {
  color: var(--faded); max-height: 2.2em;
}

.colophon-link {
  position: absolute; z-index: 3;
  right: clamp(1.2rem, 4vw, 3rem); bottom: clamp(1rem, 3vh, 1.6rem);
  font-variant-caps: small-caps; letter-spacing: 0.14em;
  font-size: 0.78rem; color: var(--faded);
  transition: color 400ms;
}
.colophon-link:hover { color: var(--candle); }

/* ============================================================
   ATLAS
   ============================================================ */

.atlas-stage { position: absolute; inset: 0; background: oklch(0.10 0.01 70); }

#atlas-canvas { position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; touch-action: none; }
#atlas-canvas.grabbing { cursor: grabbing; }

#atlas-pins { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }

.pin {
  position: absolute; pointer-events: all;
  transform: translate(-50%, -50%);
  display: grid; place-items: center;
  width: 34px; height: 34px;
  background: none; border: 0; cursor: pointer;
}
.pin::before {
  content: ''; display: block; width: 9px; height: 9px;
  background: var(--pin-tint, var(--candle));
  transform: rotate(45deg);
  box-shadow: 0 0 12px oklch(0.78 0.13 78 / 0.5);
  transition: transform 350ms var(--ease), box-shadow 350ms;
}
.pin:hover::before, .pin:focus-visible::before, .pin.active::before {
  transform: rotate(45deg) scale(1.5);
  box-shadow: 0 0 22px var(--pin-tint, var(--candle));
}
.pin-label {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  white-space: nowrap;
  font-variant-caps: small-caps; letter-spacing: 0.12em; font-size: 0.72rem;
  color: var(--bone-dim);
  text-shadow: 0 1px 8px oklch(0.05 0 0), 0 0 3px oklch(0.05 0 0);
  opacity: 0; transition: opacity 300ms;
  pointer-events: none;
}
.pin:hover .pin-label, .pin:focus-visible .pin-label, .pin.active .pin-label { opacity: 1; }
#atlas-pins.close-in .pin-label { opacity: 0.9; }

.atlas-hint {
  position: absolute; left: 50%; bottom: clamp(0.9rem, 2.4vh, 1.4rem); transform: translateX(-50%);
  font-variant-caps: small-caps; letter-spacing: 0.16em;
  font-size: 0.74rem; color: var(--faded);
  pointer-events: none;
  transition: opacity 1200ms;
}
.atlas-hint.gone { opacity: 0; }

/* side placard panel (atlas + shared) */
.placard-panel {
  position: absolute; z-index: 5;
  top: 0; right: 0; bottom: 0;
  width: min(430px, 92vw);
  background: linear-gradient(90deg, transparent, oklch(0.10 0.01 70 / 0.96) 12%);
  padding: clamp(4rem, 10vh, 6rem) clamp(1.6rem, 3vw, 2.6rem) 3rem clamp(2.6rem, 5vw, 4rem);
  overflow-y: auto;
  transform: translateX(24px); opacity: 0;
  transition: transform 600ms var(--ease), opacity 600ms var(--ease);
}
.placard-panel.open { transform: none; opacity: 1; }
.placard-panel h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(1.5rem, 3.4vw, 2rem); letter-spacing: 0.05em;
  color: var(--bone); margin: 0.35em 0 0.7em;
}
/* One mark on this map is yours. The Record asks where you are from and the map
   has always known where that is — these two facts had simply never been
   introduced. Steady, not blinking: it is a home, not a notification. */
body[data-homeland] #atlas-pins .pin[data-pin] { --home-ring: 0; }
body[data-homeland="preserved"] .pin[data-pin="preserved"],
body[data-homeland="marches"]   .pin[data-pin="marches"],
body[data-homeland="veldris"]   .pin[data-pin="veldris"],
body[data-homeland="reach"]     .pin[data-pin="reach"],
body[data-homeland="frontier"]  .pin[data-pin="frontier"],
body[data-homeland="ironhold"]  .pin[data-pin="ironhold"],
body[data-homeland="wasteland"] .pin[data-pin="wasteland"],
body[data-homeland="helmsdal"]  .pin[data-pin="helmsdal"] {
  --home-ring: 1;
}
#atlas-pins .pin::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border: 1px solid var(--pin-tint, currentColor);
  border-radius: 50%;
  opacity: calc(var(--home-ring, 0) * 0.55);
  transform: scale(calc(0.6 + var(--home-ring, 0) * 0.4));
  transition: opacity 900ms var(--ease), transform 900ms var(--ease);
  pointer-events: none;
}

/* ---- the descent: past what the parchment can hold (js/descent.js) ---- */
.descent {
  position: absolute; inset: 0; z-index: 6;
  pointer-events: none;              /* while it is only developing, the map still answers */
  overflow: hidden;
}
.descent-plate {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%;
  object-fit: cover;
  transform-origin: 50% 50%;
  will-change: transform, opacity, filter;
  opacity: 0;
}
/* The dark the plate is presented against. Invisible on a wide screen, where the
   plate covers everything — it exists for the portrait case below. */
.descent::before {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: oklch(0.055 0.008 60);
  opacity: var(--descent-scrim, 0);
}
/* A PHONE HELD UPRIGHT.
   These plates are 16:9 and a portrait screen is not, so `cover` was showing a
   quarter-width vertical slice of each one — you arrived at the Fallen Castle and
   were shown two inches of cloud. Upright, you are not facing a wall, you are
   holding a plate: so it is shown whole, sat high, with its caption underneath in
   the dark, the way a plate sits above its caption in a book. */
@media (orientation: portrait) {
  /* Laid out explicitly rather than via object-fit: the plate is simply an image
     the width of the screen, at its own aspect, sat in the upper third. Nothing
     is cropped and nothing depends on how a particular engine resolves `contain`
     against a stretched box. */
  /* Placed by flex, not by percentages. A percentage `top` resolves against the
     layer's containing block, and on a phone that box is not the viewport — which
     put the plate somewhere well below where the numbers said it was. Centring it
     removes the arithmetic from the problem entirely. */
  .descent { display: flex; align-items: center; justify-content: center; }
  .descent-plate {
    position: static;
    width: 100%; height: auto;
    object-fit: contain;
    /* The ratio must be declared, not inferred. `height: auto` on an <img> is 0
       until the file decodes, and the plate is fetched at the moment of arrival —
       so without this the descent lands on a zero-height box and you arrive at
       nothing for as long as the download takes. */
    aspect-ratio: 2200 / 1228;
    margin-bottom: 20vh;      /* leaves the lower third to the caption */
  }
  .descent-caption {
    left: 1.4rem; right: 1.4rem; top: auto;
    bottom: 12vh;
    max-width: none;
  }
  .descent-caption::before { inset: -2rem -2rem -2.5rem -2rem; }
  .descent-caption h2 { font-size: clamp(1.6rem, 8vw, 2.4rem); }
  .descent-rise { bottom: 1.6rem; right: 1.4rem; }
}
/* These plates are pale parchment and this is a dark building lit by one candle.
   Full-bleed paper would be a slap. The scrim keeps the arrival in the room's key
   and gives the caption something to sit on. It fades in with the descent. */
.descent::after {
  content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  opacity: var(--descent-scrim, 0);
  background:
    linear-gradient(to top, oklch(0.09 0.012 60 / 0.92) 0%, oklch(0.09 0.012 60 / 0.45) 22%, transparent 48%),
    radial-gradient(ellipse at 50% 45%, transparent 34%, oklch(0.07 0.01 60 / 0.72) 100%);
}
.descent.arrived .descent-plate { filter: brightness(0.86) saturate(0.96) !important; }
/* the caption only exists once you have actually arrived */
.descent-caption, .descent-rise {
  opacity: 0; transition: opacity 700ms var(--ease) 260ms;
  pointer-events: none;
}
.descent.arrived { pointer-events: auto; }
.descent.arrived .descent-caption,
.descent.arrived .descent-rise { opacity: 1; }
.descent-caption {
  position: absolute; z-index: 3;
  left: clamp(1.6rem, 5vw, 4rem); bottom: clamp(2rem, 8vh, 4.5rem);
  max-width: min(46ch, 80vw);
  isolation: isolate;
  text-shadow: 0 2px 18px rgb(0 0 0 / 0.9), 0 0 4px rgb(0 0 0 / 0.8);
}
/* The plates are pale and the caption lands wherever it lands, so it carries its
   own pool of dark rather than trusting the frame gradient to reach that high. */
.descent-caption::before {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  inset: -3.5rem -8rem -3.5rem -7rem;
  background: radial-gradient(ellipse at 32% 55%, oklch(0.05 0.008 60 / 0.88) 0%, oklch(0.05 0.008 60 / 0.55) 45%, transparent 74%);
}
.descent-caption .placard-label { color: var(--faded); }
.descent-caption .descent-body { color: var(--bone-dim); }
.descent-caption h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(1.8rem, 5vw, 3.2rem); letter-spacing: 0.04em;
  color: var(--bone); margin: 0.25em 0 0.5em;
}
.descent-caption .descent-body {
  color: var(--bone-dim); font-style: italic; font-size: 1rem; margin: 0;
}
/* bottom-right, clear of the candle in the top corner */
.descent-rise { z-index: 3;
  position: absolute; bottom: clamp(2rem, 8vh, 4.5rem); right: clamp(1.6rem, 5vw, 4rem);
  color: var(--faded); font-size: 0.82rem; letter-spacing: 0.16em; text-transform: lowercase;
  transition: color 300ms, opacity 700ms var(--ease) 260ms;
}
.descent-rise:hover { color: var(--bone); }
/* once you are there, the marks on the map have nothing more to say */
.atlas-stage.descended #atlas-pins,
.atlas-stage.descended .atlas-hint { opacity: 0; pointer-events: none; transition: opacity 500ms; }

.placard-panel p { margin-bottom: 1em; color: var(--bone-dim); font-size: 0.98rem; }
/* the atlas placards carry the cartographer's marginal drawing of the place —
   mounted like the bestiary plates, so the building keeps one way of framing art */
.placard-plate {
  margin: 0 0 1.4rem;
  border: 1px solid var(--hairline);
  background: oklch(0.10 0.01 70 / 0.6);
  padding: 6px;
}
.placard-plate img { display: block; width: 100%; }
.placard-panel em { color: var(--bone); }
.panel-close {
  position: absolute; top: 1.1rem; right: 1.2rem;
  color: var(--faded); font-size: 1rem; transition: color 300ms;
}
.panel-close:hover { color: var(--candle); }

.panel-seal {
  margin-top: 1.6rem; padding-top: 1rem; border-top: 1px solid var(--hairline);
  font-style: italic; font-family: var(--fell-text);
  color: var(--faded); font-size: 0.92rem;
}

/* ============================================================
   ROOM WALLS
   These four rooms used to hang a flat JPEG at opacity 0.30 behind the text,
   lit or not lit, which is why they all read as the same room. The wall is on
   the #lamp canvas now and it only exists where your flame falls. The rooms
   themselves are transparent, and they ride the impulse bus.
   ============================================================ */

/* one transform, carrying two things: the impulse bus, and which way this room lies
   in the building (js/transit.js writes --tx/--ty/--tz) */
.room {
  transform:
    translate(var(--tx, 0px), calc(var(--ty, 0px) + var(--shock, 0) * 3px))
    scale(var(--tz, 1));
}

/* nothing behind the reading rooms but the dark, and whatever you brought */
#room-archives, #room-library, #room-bestiary, #room-halls { background: transparent; }

/* if there is no WebGL2, we do not leave people in an empty black box */
body.no-lamp #room-archives, body.no-lamp #room-library,
body.no-lamp #room-bestiary, body.no-lamp #room-halls {
  background-size: cover; background-position: center;
  background-blend-mode: multiply;
  background-color: oklch(0.08 0.008 260 / 0.72);
}
body.no-lamp #room-archives { background-image: url('../assets/img/bg-archives.jpg?v=3'); }
body.no-lamp #room-library  { background-image: url('../assets/img/bg-library.jpg?v=3'); }
body.no-lamp #room-bestiary { background-image: url('../assets/img/bg-bestiary.jpg?v=3'); }
body.no-lamp #room-halls    { background-image: url('../assets/img/library.jpg?v=3'); }

/* ============================================================
   ARCHIVES — timeline
   ============================================================ */

#room-archives { display: flex; flex-direction: column; }

.archives-head {
  padding: clamp(4.5rem, 12vh, 7rem) clamp(1.4rem, 6vw, 6rem) 0;
  flex: 0 0 auto;
}
.archives-head h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(1.8rem, 4.5vw, 3rem); letter-spacing: 0.05em;
  margin: 0.2em 0 0.3em;
}
.archives-note { color: var(--faded); font-style: italic; font-family: var(--fell-text); max-width: 60ch; }

.timeline {
  flex: 1 1 auto;
  display: flex; align-items: stretch;
  gap: 0;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  padding: clamp(2rem, 6vh, 4rem) clamp(1.4rem, 6vw, 6rem);
  /* a snap aligns to the scrollport edge, not to the padding — so the first epoch
     snapped itself flush against the left rule of the screen and the rail lost its
     margin the moment anyone touched it */
  scroll-padding-inline: clamp(1.4rem, 6vw, 6rem);
  scrollbar-width: thin;
  outline: none;
}

.epoch {
  position: relative;
  flex: 0 0 auto;
  width: min(420px, 78vw);
  padding: 2.2rem 3.2rem 0 0;
  scroll-snap-align: start;
  border-top: 1px solid var(--hairline);
}
.epoch::before {
  content: ''; position: absolute; top: -5px; left: 0;
  width: 9px; height: 9px; transform: rotate(45deg);
  background: var(--epoch-tint, var(--faded));
}
.epoch-year {
  font-family: var(--fell); font-size: 1rem; letter-spacing: 0.14em;
  color: var(--candle);
}
.epoch h3 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(1.3rem, 2.6vw, 1.7rem); letter-spacing: 0.04em;
  margin: 0.3em 0 0.5em; color: var(--bone);
}
.epoch p { color: var(--bone-dim); font-size: 0.95rem; max-width: 46ch; }
.epoch .placard-label { margin-bottom: 0.4rem; }

.epoch.torn { color: var(--faded); }
.epoch.torn h3 { color: var(--faded); }
.epoch.torn p {
  color: var(--faded);
  -webkit-mask-image: linear-gradient(180deg, black 30%, transparent 95%);
  mask-image: linear-gradient(180deg, black 30%, transparent 95%);
}
.epoch.torn .epoch-year { color: var(--faded); }

/* ============================================================
   LIBRARY — shelf + reader
   ============================================================ */

#room-library { overflow-y: auto; }

.library-shelf {
  min-height: 100%;
  padding: clamp(4.5rem, 12vh, 7rem) clamp(1.4rem, 6vw, 6rem) 4rem;
  max-width: 1100px; margin-inline: auto;
}
.shelf-section { margin-bottom: clamp(2.5rem, 7vh, 4.5rem); }
.shelf-section h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(1.6rem, 3.6vw, 2.4rem); letter-spacing: 0.05em;
  margin: 0.2em 0 0.8em;
}
.shelf-blurb { color: var(--faded); font-style: italic; font-family: var(--fell-text); max-width: 58ch; margin-bottom: 1.8rem; }

.tome-row { display: flex; flex-wrap: wrap; gap: 1.4rem; align-items: stretch; }

.tome {
  flex: 1 1 240px; max-width: 330px;
  text-align: left;
  padding: 1.5rem 1.5rem 1.2rem;
  border: 1px solid var(--hairline);
  background: linear-gradient(160deg, oklch(0.16 0.015 60 / 0.75), oklch(0.11 0.01 70 / 0.9));
  transition: border-color 450ms, box-shadow 450ms, transform 600ms var(--ease);
}
.tome:hover, .tome:focus-visible {
  border-color: oklch(0.45 0.07 75 / 0.8);
  box-shadow: 0 8px 44px oklch(0.05 0 0 / 0.6), 0 0 30px oklch(0.78 0.13 78 / 0.06);
  transform: translateY(-3px);
}
.tome-kind { font-variant-caps: small-caps; letter-spacing: 0.14em; font-size: 0.72rem; color: var(--faded); }
.tome-title {
  display: block; font-family: var(--fell); font-size: 1.25rem; letter-spacing: 0.04em;
  color: var(--bone); margin: 0.5em 0 0.55em; line-height: 1.3;
}
.tome-desc { font-size: 0.9rem; color: var(--bone-dim); font-style: italic; font-family: var(--fell-text); }

.tome.sealed { opacity: 0.55; cursor: default; }
.tome.sealed:hover { transform: none; border-color: var(--hairline); box-shadow: none; }
.tome.sealed .tome-title { color: var(--faded); }

/* reader */
.reader {
  position: absolute; inset: 0; z-index: 6;
  background: var(--void);
  display: flex; flex-direction: column; align-items: center;
  padding: clamp(3.4rem, 8vh, 5rem) clamp(0.8rem, 3vw, 2rem) 1rem;
}
.reader-exit {
  position: absolute; top: clamp(3.2rem, 7vh, 4.2rem); right: clamp(1.2rem, 4vw, 3rem);
  z-index: 8;   /* the leaf is a positioned sibling that comes after it in the DOM */
  font-variant-caps: small-caps; letter-spacing: 0.14em; font-size: 0.8rem;
  color: var(--faded); transition: color 300ms;
}
.reader-exit:hover { color: var(--candle); }

.reader-book {
  position: relative;
  width: min(1060px, 96vw);
  flex: 1 1 auto; min-height: 0;
  display: grid; grid-template-columns: 1fr 1fr;
  background: linear-gradient(180deg, oklch(0.90 0.015 85), oklch(0.84 0.02 82));
  box-shadow: 0 30px 90px oklch(0.03 0 0 / 0.85), inset 0 0 90px oklch(0.6 0.03 80 / 0.25);
  perspective: 2200px;
}
.reader-book::before { /* gutter shadow */
  content: ''; position: absolute; inset: 0 auto 0 50%; width: 90px; transform: translateX(-50%);
  background: linear-gradient(90deg, transparent, oklch(0.35 0.02 80 / 0.35), transparent);
  pointer-events: none; z-index: 2;
}

.reader-page {
  padding: clamp(1.6rem, 4vh, 3rem) clamp(1.4rem, 3.4vw, 3rem);
  color: oklch(0.24 0.02 60);
  font-size: clamp(0.92rem, 1.35vw, 1.06rem);
  line-height: 1.72;
  overflow: hidden;
  font-weight: 400;
}
.reader-page p { margin-bottom: 0.85em; text-indent: 1.4em; }
.reader-page p.noindent { text-indent: 0; }
.reader-page .chap-label { font-variant-caps: small-caps; letter-spacing: 0.2em; text-align: center; color: oklch(0.45 0.03 60); text-indent: 0; margin: 1.2em 0 0.6em; }
.reader-page h3 {
  font-family: var(--fell); font-weight: 400; text-align: center;
  font-size: 1.7em; letter-spacing: 0.06em; margin: 0.4em 0 1.2em;
  color: oklch(0.2 0.02 60);
}
.reader-page .sep { text-align: center; text-indent: 0; letter-spacing: 0.6em; color: oklch(0.45 0.03 60); margin: 1.1em 0; }
.reader-page .torn-end {
  margin-top: 2em; text-indent: 0; text-align: center;
  font-style: italic; color: oklch(0.45 0.03 60);
}

.reader-flip {
  position: absolute; top: 0; bottom: 0; left: 50%; right: 0; z-index: 3;
  transform-origin: left center;
  background: linear-gradient(180deg, oklch(0.90 0.015 85), oklch(0.84 0.02 82));
  box-shadow: -4px 0 24px oklch(0.2 0.02 60 / 0.25);
  backface-visibility: hidden;
  display: none;
}
.reader-flip.flipping { display: block; animation: pageflip 620ms var(--ease) forwards; }
.reader-flip.flipping.back { animation: pageflipback 620ms var(--ease) forwards; }
@keyframes pageflip { from { transform: rotateY(0); } to { transform: rotateY(-178deg); } }
@keyframes pageflipback { from { transform: rotateY(-178deg); } to { transform: rotateY(0); } }

.reader-nav {
  flex: 0 0 auto;
  display: flex; align-items: baseline; gap: 2rem;
  padding: 0.9rem 0 0.4rem;
  color: var(--faded);
}
.reader-nav button { font-family: var(--fell); font-size: 1.5rem; color: var(--bone-dim); transition: color 300ms; padding: 0 0.6em; }
.reader-nav button:hover:not(:disabled) { color: var(--candle); }
.reader-nav button:disabled { opacity: 0.25; cursor: default; }
#page-num { font-variant-caps: small-caps; letter-spacing: 0.16em; font-size: 0.78rem; }
#read-aloud { margin-left: 1.4rem; }

/* ============================================================
   HALL OF KINGS
   ============================================================ */

#room-halls { scroll-snap-type: y proximity; }

.hall-intro {
  min-height: 88vh;
  display: grid; place-content: center; text-align: center;
  padding: 2rem; scroll-snap-align: start;
}
.hall-intro h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(2rem, 6vw, 3.6rem); letter-spacing: 0.08em;
  margin: 0.25em 0;
}
.hall-intro p { color: var(--bone-dim); font-style: italic; font-family: var(--fell-text); max-width: 52ch; margin-inline: auto; }
.hall-scroll-cue { margin-top: 8vh; color: var(--faded); font-variant-caps: small-caps; letter-spacing: 0.2em; font-size: 0.76rem; animation: cuebreath 5.2s ease-in-out infinite; }
@keyframes cuebreath {
  0%, 100% { opacity: 0.55; transform: translateY(0); }
  50%      { opacity: 1;    transform: translateY(4px); }
}

/* Each alcove is now the whole screen, and the portrait behind it is on the #lamp
   canvas being lit by that power's own rules. The plate of text is the only thing
   standing in the room — so it carries its own dark, or the Choir would bleach it
   off the wall entirely. */
.hall {
  min-height: 100svh;
  min-height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 34rem);
  align-content: center;
  padding: clamp(3rem, 9vh, 6rem) clamp(1.4rem, 6vw, 6rem);
  scroll-snap-align: start;
}
.hall[data-power="court"], .hall[data-power="choir"] { justify-content: end; }

.hall-plate {
  padding: clamp(1.6rem, 3vw, 2.4rem) clamp(1.6rem, 3vw, 2.6rem);
  border: 1px solid var(--hairline);
  border-left: 2px solid color-mix(in oklab, var(--hall-tint) 55%, transparent);
  background: linear-gradient(180deg, oklch(0.07 0.008 260 / 0.82), oklch(0.07 0.008 260 / 0.9));
  backdrop-filter: blur(3px);
  box-shadow: 0 30px 90px oklch(0.02 0 0 / 0.6);
}
.hall-name h3 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(2rem, 5.2vw, 3.4rem); letter-spacing: 0.05em;
  color: var(--bone); line-height: 1.15; margin: 0.2em 0 0.4em;
}
.hall-name .placard-label { color: color-mix(in oklab, var(--hall-tint) 60%, var(--faded)); }
.hall-motto { font-style: italic; font-family: var(--fell-text); color: var(--bone-dim); font-size: clamp(1rem, 2vw, 1.2rem); max-width: 40ch; }

/* the portraits are not thumbnails any more — they are the walls */

.hall-name { margin-bottom: 1.4rem; }
.hall-body p { margin-bottom: 1em; color: var(--bone-dim); font-size: 0.98rem; max-width: 58ch; }
.hall-body em { color: var(--bone); }
.hall-classes {
  margin-top: 1.4rem; padding-top: 1rem; border-top: 1px solid var(--hairline);
  display: flex; gap: 2rem; flex-wrap: wrap;
}
.hall-class { min-width: 110px; }
.hall-class b {
  display: block; font-family: var(--fell); font-weight: 400; font-size: 1.05rem;
  letter-spacing: 0.05em; color: color-mix(in oklab, var(--hall-tint) 55%, var(--bone));
}
.hall-class span { font-size: 0.82rem; color: var(--faded); font-style: italic; font-family: var(--fell-text); }

.veiled-row {
  min-height: 70vh;
  padding: clamp(3rem, 9vh, 6rem) clamp(1.4rem, 6vw, 6rem);
  border-top: 1px solid var(--hairline);
  scroll-snap-align: start;
}
.veiled-row h3 { font-family: var(--fell); font-weight: 400; font-size: clamp(1.5rem, 3.4vw, 2.2rem); letter-spacing: 0.06em; margin: 0.3em 0 1.4em; }
.veiled-figures { display: flex; flex-wrap: wrap; gap: clamp(1.2rem, 3vw, 2.6rem); }
.veiled {
  flex: 1 1 180px; max-width: 250px;
  padding: 2.4rem 1.3rem 1.2rem;
  border: 1px solid var(--hairline);
  background: linear-gradient(180deg, oklch(0.14 0.01 70 / 0.6), transparent);
  text-align: center;
}
.veiled::before {
  content: ''; display: block; margin: 0 auto 1.4rem;
  width: 54px; height: 84px;
  background: linear-gradient(180deg, oklch(0.28 0.015 75), oklch(0.16 0.012 70));
  clip-path: polygon(50% 0%, 78% 18%, 88% 100%, 12% 100%, 22% 18%);
  opacity: 0.85;
}
.veiled b { display: block; font-family: var(--fell); font-weight: 400; letter-spacing: 0.06em; color: var(--bone-dim); font-size: 1rem; }
.veiled span { font-size: 0.8rem; color: var(--faded); font-style: italic; font-family: var(--fell-text); }

/* ============================================================
   BESTIARY
   ============================================================ */

.bestiary-frame {
  position: absolute; inset: 0;
  display: grid; grid-template-columns: minmax(220px, 320px) 1fr;
  padding-top: clamp(3.6rem, 9vh, 5.4rem);
}

.bestiary-index {
  overflow-y: auto;
  padding: 1.5rem clamp(1rem, 2.5vw, 2rem) 2rem clamp(1.4rem, 4vw, 3rem);
  border-right: 1px solid var(--hairline);
  display: flex; flex-direction: column; gap: 0.15rem;
}
.bestiary-index .placard-label { margin-bottom: 0.9rem; }
.beast-link {
  text-align: left; padding: 0.5em 0.2em;
  font-family: var(--fell); font-size: 1.05rem; letter-spacing: 0.05em;
  color: var(--bone-dim);
  border-bottom: 1px solid transparent;
  transition: color 300ms, transform 500ms var(--ease);
}
.beast-link:hover { color: var(--candle); transform: translateX(4px); }
.beast-link.torn:hover { transform: none; }
.beast-link.active { color: var(--candle); }
.beast-link.torn { color: var(--faded); font-style: italic; font-family: var(--fell-text); font-size: 0.9rem; cursor: default; }

.bestiary-page {
  overflow-y: auto;
  padding: 1.5rem clamp(1.6rem, 5vw, 5rem) 4rem;
  max-width: 70ch;
}
.bestiary-page h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(1.7rem, 4vw, 2.6rem); letter-spacing: 0.05em; margin: 0.3em 0 0.2em;
}
.beast-taxon { font-style: italic; font-family: var(--fell-text); color: var(--faded); margin-bottom: 1.6rem; }
.beast-plate {
  position: relative;
  margin: 0 0 1.8rem;
  max-width: 540px;
  border: 1px solid var(--hairline);
  background: oklch(0.10 0.01 70 / 0.6);
  padding: 8px;
}
.beast-plate img {
  display: block; width: 100%;
  /* the page sits back a little so the flame has something to lift it out of */
  filter: brightness(calc(0.80 + 0.20 * var(--plate-lit, 1)));
  transition: filter 500ms var(--ease);
}
/* candlelight falling across paper: warm, soft-edged, and it slides off the sheet
   when the flame is carried away from it (js/bestiary.js sets the position) */
.beast-plate::after {
  content: ''; position: absolute; inset: 8px; pointer-events: none;
  background: radial-gradient(
    circle var(--plate-r, 300px) at var(--plate-x, 50%) var(--plate-y, 40%),
    rgb(255 224 168 / 0.34) 0%, rgb(255 214 150 / 0.13) 42%, transparent 72%);
  mix-blend-mode: soft-light;
  opacity: var(--plate-lit, 0);
}
.bestiary-page p { color: var(--bone-dim); margin-bottom: 1em; }
.bestiary-page em { color: var(--bone); }
.beast-field { margin-top: 1.4rem; padding-top: 0.9rem; border-top: 1px solid var(--hairline); }
.beast-field .placard-label { margin-bottom: 0.3rem; }
.beast-field p { margin-bottom: 0; }

/* ============================================================
   PORTAL CHAMBER
   ============================================================ */

.portal-art {
  position: absolute; inset: 0; overflow: hidden;
  transform: scale(1.045);
  transition: transform 400ms linear;
}
.portal-art img { width: 100%; height: 100%; object-fit: cover; filter: brightness(0.92); }

.portal-grade {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(0deg, oklch(0.08 0.008 260 / 0.7) 0%, transparent 26%),
              linear-gradient(180deg, oklch(0.08 0.008 260 / 0.5) 0%, transparent 18%);
}

.portal-zones { position: absolute; inset: 0; display: grid; grid-template-columns: 1fr 1fr 1fr; }

.portal-zone {
  position: relative; display: block;
  border: 0; background: none; cursor: pointer;
}
.portal-zone::before { /* bloom */
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(ellipse 46% 62% at 50% 52%, var(--bloom, transparent), transparent 72%);
  opacity: 0; transition: opacity 700ms var(--ease);
  pointer-events: none;
}
.portal-zone[data-portal="green"] { --bloom: oklch(0.55 0.12 155 / 0.22); }
.portal-zone[data-portal="red"]   { --bloom: oklch(0.5 0.16 25 / 0.22); }
.portal-zone[data-portal="black"] { --bloom: oklch(0.3 0.05 300 / 0.18); }
.portal-zone:hover::before, .portal-zone:focus-visible::before { opacity: 1; }

.portal-caption {
  position: absolute; left: 50%; bottom: clamp(2.2rem, 7vh, 4.4rem); transform: translateX(-50%) translateY(8px);
  text-align: center; white-space: nowrap;
  opacity: 0; transition: opacity 600ms var(--ease), transform 600ms var(--ease);
  pointer-events: none;
}
.portal-zone:hover .portal-caption, .portal-zone:focus-visible .portal-caption { opacity: 1; transform: translateX(-50%) translateY(0); }
.portal-caption em {
  display: block; font-style: normal;
  font-family: var(--fell); font-size: clamp(1.3rem, 3vw, 1.9rem); letter-spacing: 0.14em;
  color: var(--bone); text-shadow: 0 2px 18px oklch(0.05 0 0);
}
.portal-caption small {
  font-variant-caps: small-caps; letter-spacing: 0.16em; font-size: 0.74rem;
  color: var(--bone-dim); text-shadow: 0 1px 10px oklch(0.05 0 0);
}

.portal-zone[data-portal="black"].refused { animation: refuse 500ms ease-in-out; }
@keyframes refuse {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); } 40% { transform: translateX(4px); }
  60% { transform: translateX(-3px); } 80% { transform: translateX(2px); }
}

/* ============================================================
   COLOPHON
   ============================================================ */

.colophon {
  max-width: 62ch;
  margin-inline: auto;
  padding: clamp(5rem, 14vh, 8rem) clamp(1.4rem, 5vw, 2rem) 5rem;
}
.colophon h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(2rem, 5vw, 3rem); letter-spacing: 0.1em; margin: 0.25em 0 0.7em;
}
.colophon p { margin-bottom: 1.1em; color: var(--bone-dim); }
.colophon .hairline { margin: 2rem 0; }
.authors-note { font-style: italic; font-family: var(--fell-text); color: var(--faded); }
.fineprint { font-size: 0.82rem; color: var(--faded); }
.return {
  display: inline-block; margin-top: 1.6rem;
  font-variant-caps: small-caps; letter-spacing: 0.16em; font-size: 0.85rem;
}

/* ============================================================
   SHARPENING — type ornaments, drop caps, small ceremonies
   ============================================================ */

:focus-visible { outline: 1px solid var(--candle-deep); outline-offset: 3px; }

/* engraved flourish beneath the great room titles */
.archives-head h2::after,
.hall-intro h2::after,
.colophon h2::after {
  content: '';
  display: block;
  width: 200px; height: 12px;
  margin-top: 0.55em;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='12' viewBox='0 0 200 12'%3E%3Cpath d='M0 6h82M118 6h82' stroke='%23776a4e' stroke-width='1'/%3E%3Crect x='96' y='2' width='8' height='8' transform='rotate(45 100 6)' fill='none' stroke='%23b99a5f'/%3E%3C/svg%3E") no-repeat;
  opacity: 0.7;
}
.hall-intro h2::after { margin-inline: auto; }

/* drop caps: the first letter of a chapter is a small ceremony */
.reader-page h3 + p::first-letter {
  font-family: var(--fell);
  font-size: 3.1em;
  float: left;
  line-height: 0.78;
  padding: 0.06em 0.1em 0 0;
}
#atlas-panel-body > p:first-of-type::first-letter,
.bestiary-page .beast-taxon ~ p:first-of-type::first-letter {
  font-family: var(--fell);
  font-size: 2.5em;
  float: left;
  line-height: 0.8;
  padding: 0.04em 0.09em 0 0;
  color: var(--bone);
}

/* map pins breathe */
@keyframes pinbreath {
  0%, 100% { box-shadow: 0 0 10px oklch(0.78 0.13 78 / 0.35); }
  50%      { box-shadow: 0 0 20px oklch(0.78 0.13 78 / 0.65); }
}
.pin::before { animation: pinbreath 4.6s ease-in-out infinite; }
.pin:nth-child(2n)::before { animation-delay: 1.1s; }
.pin:nth-child(3n)::before { animation-delay: 2.3s; }

/* the enter button breathes like a flame */
@keyframes enterbreath {
  0%, 100% { box-shadow: 0 0 26px oklch(0.78 0.13 78 / 0.10), inset 0 0 18px oklch(0.78 0.13 78 / 0.05); }
  50%      { box-shadow: 0 0 46px oklch(0.78 0.13 78 / 0.22), inset 0 0 26px oklch(0.78 0.13 78 / 0.09); }
}
.enter-btn.shown { animation: enterbreath 4.4s ease-in-out infinite; }

/* the reader is a real block of pages */
.reader-book {
  box-shadow:
    0 2px 0 oklch(0.82 0.02 82),
    0 4px 0 oklch(0.76 0.025 80),
    0 6px 0 oklch(0.70 0.025 78),
    0 8px 0 oklch(0.62 0.03 75),
    0 34px 90px oklch(0.03 0 0 / 0.85),
    inset 0 0 90px oklch(0.6 0.03 80 / 0.25);
}

/* a light passes over the veiled statues, rarely */
@keyframes veilsheen {
  0%, 86% { background-position: -180% 0; }
  100%    { background-position: 280% 0; }
}
.veiled::before {
  background:
    linear-gradient(115deg, transparent 42%, oklch(0.75 0.03 85 / 0.20) 50%, transparent 58%) 0 0 / 260% 100%,
    linear-gradient(180deg, oklch(0.28 0.015 75), oklch(0.16 0.012 70));
  animation: veilsheen 11s ease-in-out infinite;
}
.veiled:nth-child(2)::before { animation-delay: 2.8s; }
.veiled:nth-child(3)::before { animation-delay: 5.4s; }
.veiled:nth-child(4)::before { animation-delay: 8.1s; }

/* the voice controls */
.listen-btn {
  font-variant-caps: small-caps; letter-spacing: 0.16em;
  font-size: 0.78rem; color: var(--candle);
  padding: 0.35em 0;
  border-bottom: 1px solid transparent;
  transition: color 300ms, border-color 300ms;
}
.listen-btn:hover { color: var(--bone); border-color: var(--candle-deep); }
.listen-btn::before { content: '◆ '; font-size: 0.6em; vertical-align: 0.22em; color: var(--candle-deep); }
.beast-listen { display: inline-block; margin: -0.6rem 0 1.2rem; }

/* thin engraved scrollbars where scrolling is part of the room */
.timeline::-webkit-scrollbar, .room-scroll::-webkit-scrollbar,
.bestiary-index::-webkit-scrollbar, .bestiary-page::-webkit-scrollbar,
#room-library::-webkit-scrollbar, .placard-panel::-webkit-scrollbar {
  width: 6px; height: 6px;
}
.timeline::-webkit-scrollbar-thumb, .room-scroll::-webkit-scrollbar-thumb,
.bestiary-index::-webkit-scrollbar-thumb, .bestiary-page::-webkit-scrollbar-thumb,
#room-library::-webkit-scrollbar-thumb, .placard-panel::-webkit-scrollbar-thumb {
  background: var(--hairline);
}
.timeline::-webkit-scrollbar-track, .room-scroll::-webkit-scrollbar-track,
.bestiary-index::-webkit-scrollbar-track, .bestiary-page::-webkit-scrollbar-track,
#room-library::-webkit-scrollbar-track, .placard-panel::-webkit-scrollbar-track {
  background: transparent;
}

/* ============================================================
   THE MARKS — glyphs that answer only the lamp
   ============================================================ */

.glyph {
  color: oklch(0.80 0.08 80);
  opacity: 0;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  cursor: default;
  filter: drop-shadow(0 0 6px oklch(0.78 0.13 78 / 0.5));
  transition: transform 500ms var(--ease);
}
.glyph[data-lit="1"] { cursor: pointer; }
.glyph[data-lit="1"]:hover { transform: scale(1.12); }
.glyph.claimed {
  animation: glyphclaim 2400ms var(--ease) forwards;
  pointer-events: none;
}
@keyframes glyphclaim {
  0%   { opacity: 1; transform: scale(1); filter: drop-shadow(0 0 10px oklch(0.78 0.13 78 / 0.9)); }
  30%  { opacity: 1; transform: scale(1.5); filter: drop-shadow(0 0 26px oklch(0.78 0.13 78 / 1)); }
  100% { opacity: 0; transform: scale(0.6); filter: drop-shadow(0 0 0 transparent); }
}

/* THE MARGINAL HAND — pencil, not ink: graphite-grey, wrong-angled, smaller than
   the text it questions. It flows with the block so the paginator measures true. */
.margin-note {
  display: block;
  margin: 0.55em 0 0.25em 1.8em;
  max-width: 34ch;
  transform: rotate(var(--mtilt, -1.8deg));
  font-family: var(--fell-text); font-style: italic;
  font-size: 0.82em; line-height: 1.45;
  letter-spacing: 0.02em;
  text-indent: 0;
  color: oklch(0.44 0.008 270 / 0.85);
}
.sealed-page { margin-top: 1.8rem; padding: 1rem 1.1rem; border: 1px solid var(--hairline); }
.sealed-page.unsealed { border-color: oklch(0.45 0.07 75 / 0.8); background: oklch(0.15 0.014 65 / 0.5); }
.sealed-page .page-text { color: var(--bone-dim); line-height: 1.65; margin-bottom: 0.7em !important; }
.sealed-page .page-text b { color: var(--candle); font-weight: 500; }
.dim-italic { color: var(--faded); font-style: italic; font-family: var(--fell-text); }

/* ============================================================
   THE RECORD — signing the ledger
   ============================================================ */

#rite {
  position: fixed; inset: 0; z-index: 87;
  display: grid; place-items: center;
  background: oklch(0.05 0.008 260 / 0.78);
  backdrop-filter: blur(4px);
  padding: 1.2rem;
}
#rite[hidden] { display: none; }

.rite-page {
  position: relative;
  width: min(460px, 94vw);
  max-height: 88svh; max-height: 88vh;
  overflow-y: auto;
  padding: clamp(1.8rem, 4vh, 2.6rem) clamp(1.6rem, 4vw, 2.4rem);
  border: 1px solid var(--hairline);
  background: linear-gradient(180deg, oklch(0.11 0.01 70 / 0.98), oklch(0.09 0.01 70 / 0.99));
  box-shadow: 0 40px 120px oklch(0.02 0 0 / 0.8);
}
.rite-page h2 {
  font-family: var(--fell); font-weight: 400;
  font-size: clamp(1.5rem, 3.4vw, 1.9rem); letter-spacing: 0.05em;
  margin: 0.3em 0 0.6em;
}
.rite-body { color: var(--bone-dim); font-size: 0.96rem; margin-bottom: 1.2rem; }
.rite-body em { color: var(--bone); }

.rite-field { display: block; margin: 1.1rem 0; }
.rite-field span {
  display: block; margin-bottom: 0.3rem;
  font-variant-caps: small-caps; letter-spacing: 0.14em;
  font-size: 0.74rem; color: var(--faded);
}
.rite-input {
  width: 100%;
  font: inherit; font-size: 1.05rem; color: var(--bone);
  background: transparent; border: 0;
  border-bottom: 1px solid var(--hairline);
  padding: 0.3em 0.1em;
  transition: border-color 300ms;
}
.rite-input:focus { outline: none; border-bottom-color: var(--candle-deep); }

.rite-act {
  display: inline-block; margin-top: 1.2rem;
  font-variant-caps: small-caps; letter-spacing: 0.16em; font-size: 0.82rem;
  color: var(--candle);
  border: 1px solid var(--hairline);
  padding: 0.7em 1.6em;
  min-height: 44px;
  transition: border-color 300ms, color 300ms, box-shadow 300ms;
}
.rite-act:hover { border-color: var(--candle-deep); box-shadow: 0 0 24px oklch(0.78 0.13 78 / 0.08); }
.rite-act.ghostly { color: var(--faded); }

.rite-row { display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }

.rite-sigil {
  display: grid; place-items: center;
  margin: 1.2rem auto; width: 128px; height: 128px;
  color: oklch(0.80 0.08 80);
  border: 1px solid var(--hairline);
  filter: drop-shadow(0 0 10px oklch(0.78 0.13 78 / 0.35));
}
.rite-sigil.small { width: 84px; height: 84px; margin: 0.8rem auto; }

.rite-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; margin-top: 0.6rem; }
.rite-choice {
  text-align: left; padding: 0.7rem 0.9rem;
  border: 1px solid var(--hairline);
  transition: border-color 300ms, background 300ms;
  min-height: 44px;
}
.rite-choice:hover { border-color: var(--candle-deep); background: oklch(0.13 0.012 70 / 0.6); }
.rite-choice b { display: block; font-family: var(--fell); font-weight: 400; letter-spacing: 0.04em; }
.rite-choice span { display: block; font-size: 0.78rem; color: var(--faded); font-style: italic; font-family: var(--fell-text); }

.rite-note { color: oklch(0.62 0.1 40); font-size: 0.85rem; font-style: italic; }
.rite-fine, .ledger-fine { font-size: 0.74rem; color: var(--faded); margin-top: 1.4rem; }

.record-line { display: flex; gap: 0.8rem; align-items: center; margin-top: 1.2rem; }
.record-sigil { color: oklch(0.80 0.08 80); flex: 0 0 auto; }
.record-line em { color: var(--candle); font-style: normal; font-variant-caps: small-caps; letter-spacing: 0.1em; }
.record-small {
  font-size: inherit; color: var(--faded);
  text-decoration: underline; text-decoration-color: var(--hairline); text-underline-offset: 3px;
  transition: color 300ms;
}
.record-small:hover { color: var(--candle); }

@media (max-width: 520px) { .rite-grid { grid-template-columns: 1fr; } }

/* ============================================================
   BLOOD MOON — the site follows the actual sky
   ============================================================ */

/* A blood moon is no longer a red sheet thrown over the screen. It is the moon,
   and the moon is the fill light — so on a blood moon the SHADOWS go red, in the
   shader, which is a considerably worse thing to be standing in. The rooms with
   no lit wall keep a hint of it. */
body.bloodmoon .hub-grade::after, body.bloodmoon .portal-grade::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 90% 70% at 50% 0%, oklch(0.35 0.13 25 / 0.16), transparent 62%),
              radial-gradient(ellipse 120% 60% at 50% 110%, oklch(0.28 0.12 25 / 0.13), transparent 70%);
  mix-blend-mode: screen;
}
body.bloodmoon .intro-sub { color: oklch(0.55 0.1 25); }
body.bloodmoon .portal-zone[data-portal="red"]::before { opacity: 0.45; }
body.bloodmoon .portal-zone[data-portal="red"]:hover::before { opacity: 1; }
body.bloodmoon #wordmark:hover { color: oklch(0.6 0.13 25); }

/* ============================================================
   THE LEDGER, THE GHOSTS, THE CLOCK
   ============================================================ */

#ledger-btn {
  position: fixed; z-index: 80;
  left: clamp(1.2rem, 4vw, 3rem); bottom: clamp(1rem, 3vh, 1.6rem);
  font-variant-caps: small-caps; letter-spacing: 0.16em;
  font-size: 0.78rem; color: var(--faded);
  transition: color 400ms;
}
#ledger-btn:hover { color: var(--candle); }
#ledger-btn::before { content: '◆ '; font-size: 0.6em; vertical-align: 0.22em; color: var(--candle-deep); }

.ledger-side {
  position: fixed; z-index: 85;
  left: 0; right: auto; top: 0; bottom: 0;
  width: min(410px, 92vw);
  background: linear-gradient(270deg, transparent, oklch(0.10 0.01 70 / 0.97) 12%);
  padding: clamp(4rem, 10vh, 6rem) clamp(2.4rem, 5vw, 4rem) 3rem clamp(1.6rem, 3vw, 2.6rem);
  transform: translateX(-24px);
}
.ledger-side.open { transform: none; }

.ledger-greeting { font-style: italic; font-family: var(--fell-text); color: var(--bone-dim); margin-bottom: 1.6rem; }
.ledger-section { margin-bottom: 1.3rem; }
.ledger-section .placard-label { margin-bottom: 0.35rem; }
.ledger-item { font-size: 0.92rem; color: var(--bone-dim); margin: 0.15em 0 !important; }
.ledger-item.dim { color: var(--faded); letter-spacing: 0.3em; }
.ledger-count { color: var(--bone); }
.ledger-whisper { font-style: italic; font-family: var(--fell-text); color: var(--faded); }
.ledger-fine { font-size: 0.74rem; color: var(--faded); margin-top: 1.6rem; font-variant-caps: small-caps; letter-spacing: 0.12em; }

.librarian-whisper {
  position: fixed; z-index: 70;
  left: 50%; bottom: clamp(3rem, 8vh, 5rem); transform: translateX(-50%) translateY(8px);
  font-style: italic; font-family: var(--fell-text);
  font-size: 1rem; color: var(--bone-dim);
  text-shadow: 0 2px 14px oklch(0.05 0 0);
  opacity: 0; pointer-events: none;
  transition: opacity 2000ms ease-in, transform 2000ms var(--ease);
}
.librarian-whisper.shown { opacity: 0.9; transform: translateX(-50%) translateY(0); }

/* nightfall: between the tenth hour and the sixth, the library dims
   (the lit rooms dim inside the shader — see uNight in js/lamp.js) */
body.nightfall .hub-art img, body.nightfall .hub-art .bg-video { filter: brightness(0.72) saturate(0.9); }
body.nightfall .intro-tome { filter: brightness(0.6) saturate(0.85); }
body.nightfall .portal-art img, body.nightfall .portal-art .bg-video { filter: brightness(0.78); }
body.nightfall #air { opacity: 0.72; }

.bleeding-counter { margin-top: 1.6rem; max-width: 60ch; }
.bleeding-counter .placard-label { display: block; margin-bottom: 0.3rem; }
.bleeding-line { display: block; color: var(--bone-dim); font-size: 0.95rem; }
.bleeding-num {
  font-weight: 500; color: var(--candle);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.03em;
}
.bleeding-note { display: block; margin-top: 0.3rem; font-style: italic; font-family: var(--fell-text); color: var(--faded); font-size: 0.9rem; }

/* ============================================================
   RESPONSIVE
   ============================================================ */

/* THE PORTAL CHAMBER, HELD UPRIGHT.
   The painting is three doors side by side in a 16:9 frame. Held upright, `cover`
   crops it to a slice of its middle — so the phone showed the RED door filling the
   screen with all three names written across it, and every label pointed at the
   wrong door. The chamber is not one picture on a phone; it is three doorways, and
   each one gets its own. Same plate, cut in thirds, one third to a band. */
@media (max-width: 760px) and (orientation: portrait) {
  .portal-zones { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr 1fr; }
  .portal-art { display: none; }
  .portal-zone {
    background-image: url('../assets/img/portals.jpg?v=3');
    background-size: 300% auto;
    background-position-y: 42%;
    background-repeat: no-repeat;
    filter: brightness(0.92);
  }
  .portal-zone[data-portal="green"] { background-position-x: 0%; }
  .portal-zone[data-portal="red"]   { background-position-x: 50%; }
  .portal-zone[data-portal="black"] { background-position-x: 100%; }
  .portal-zone + .portal-zone { border-top: 1px solid oklch(0.05 0.008 260 / 0.9); }
  /* each doorway carries its own dark so its name is legible on it */
  .portal-zone::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(0deg, oklch(0.06 0.008 260 / 0.92), transparent 68%);
  }
  .portal-caption {
    bottom: clamp(0.8rem, 3vh, 1.6rem);
    z-index: 1;
  }
  /* the bottom doorway ends where the ledger begins */
  .portal-zone[data-portal="black"] .portal-caption {
    bottom: calc(env(safe-area-inset-bottom, 0px) + clamp(3.2rem, 9vh, 4.5rem));
  }
  .portal-caption em { font-size: clamp(1.1rem, 5.5vw, 1.6rem); }
}

@media (max-width: 760px) {
  /* On a phone the plate is taller than the screen, so if it starts at the top it
     covers the alcove entirely — and the alcove is the whole point. Give the wall the
     top third and let the text begin under it. */
  .hall {
    grid-template-columns: 1fr;
    align-content: start;
    padding-top: 34svh;
    padding-bottom: 10svh;
    scroll-snap-align: none;
  }
  .hall-plate { backdrop-filter: blur(6px); }

  /* a thumb is not a mouse pointer: these all cleared the 24px minimum and none of
     them cleared 44, which is the number that decides whether a door opens on the
     first try or the third */
  #wordmark, #mute {
    display: inline-flex; align-items: center;
    min-height: 44px;
  }
  .door { padding-block: 0.4rem; }
  /* it was wrapping to two lines across the middle of the ledger button */
  .atlas-hint {
    padding: 0.5rem 0;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 3.2rem);
    font-size: 0.66rem; letter-spacing: 0.12em;
    white-space: nowrap;
  }
  .colophon-link, #ledger-btn {
    display: inline-flex; align-items: center;
    min-height: 44px;
  }
  .bestiary-frame { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .bestiary-index { flex-direction: row; flex-wrap: wrap; gap: 0.2rem 1.1rem; border-right: 0; border-bottom: 1px solid var(--hairline); padding-bottom: 0.9rem; }
  .bestiary-index .placard-label { flex-basis: 100%; margin-bottom: 0.2rem; }
  .reader-book { grid-template-columns: 1fr; }
  #page-right { display: none; }
  .reader-book::before { display: none; }

  /* THE BOOK YOU COULD NOT CLOSE.
     On a phone the leaf takes 96% of the width and starts under the top padding —
     so it printed itself straight across "return the book", and the page, being a
     positioned element later in the DOM, took every tap meant for the way out. The
     reader is a full-screen overlay: with the shelf sealed behind it and no key to
     press, a book once opened could not be shut at all.
     So the way out goes where the thumb already is — under the leaf, on the same
     line it turns pages from. */
  .reader-exit {
    position: static; order: 3; align-self: center;
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 0 1rem; margin-top: 0.2rem;
  }
  .reader-book { order: 1; }
  .reader-nav { order: 2; }
  .portal-caption { opacity: 1; transform: translateX(-50%); }

  /* The placard was a 430px column pinned to the right — which on a 390px phone left
     the continent itself a thirty-pixel sliver, and put the close button underneath
     the chrome. On a small screen the placard rises from the bottom instead, and you
     go on looking at the map while you read about it. */
  .placard-panel {
    top: auto; right: 0; left: 0; bottom: 0;
    width: 100%;
    max-height: 58vh;
    max-height: 58svh;
    border-top: 1px solid var(--hairline);
    background: linear-gradient(0deg, oklch(0.09 0.01 70 / 0.985), oklch(0.09 0.01 70 / 0.94));
    padding: 1.5rem clamp(1.2rem, 5vw, 2rem) calc(1.4rem + env(safe-area-inset-bottom));
    transform: translateY(28px);
    overscroll-behavior: contain;
  }
  .placard-panel.open { transform: none; }
  .panel-close {
    top: 0.9rem; right: 1rem;
    width: 44px; height: 44px;      /* a thumb is not a mouse pointer */
    display: grid; place-items: center;
  }
  /* The ledger sat directly on top of the last line of every scrolling room, in the
     same colour as it. It keeps its corner — the colophon owns the other one — but it
     now carries its own dark behind it, and the rooms end above it instead of under it. */
  #ledger-btn {
    bottom: calc(0.8rem + env(safe-area-inset-bottom));
    padding: 0.5rem 0.8rem 0.5rem 0.6rem;
    background: linear-gradient(0deg, oklch(0.08 0.008 260 / 0.94), oklch(0.08 0.008 260 / 0.72));
  }
  /* and when a placard or a book has risen over the room, the ledger gets out of the
     way entirely — it was printing itself across the middle of the page */
  body:has(.placard-panel.open) #ledger-btn,
  body:has(#reader:not([hidden])) #ledger-btn {
    opacity: 0; pointer-events: none;
  }
  #room-library, #room-bestiary, .bestiary-page, #room-halls, #room-colophon {
    padding-bottom: calc(4.5rem + env(safe-area-inset-bottom));
  }
  .ledger-side { width: 100%; }

  /* An epoch is taller than the rail it stands in on a phone, and the rail
     hides its overflow — so the last third of every entry was simply gone.
     Each card carries its own scroll now, and nothing is unreadable; the tail
     ends above the ledger rather than underneath it. */
  .epoch {
    overflow-y: auto;
    padding-right: 1.6rem;
    padding-bottom: calc(3rem + env(safe-area-inset-bottom, 0px));
    overscroll-behavior: contain;
  }
  /* the header was taking two thirds of a portrait phone and leaving the thread of
     time a letterbox at the bottom of it */
  .archives-head { padding-top: clamp(3.6rem, 9vh, 5rem); }
  .archives-head h2 { font-size: clamp(1.4rem, 6.4vw, 2rem); margin: 0.15em 0 0.3em; }
  .archives-head h2::after { display: none; }
  .archives-note { font-size: 0.86rem; }
  .bleeding-counter { margin-top: 1rem; }
  .bleeding-line, .bleeding-note { font-size: 0.82rem; }
}

/* ============================================================
   SHORT SCREENS — a phone on its side, a laptop with the dock out.
   Everything above sizes itself against the WIDTH; a 390-pixel-tall window is
   wide, so it got desktop type in a letterbox and the rooms overran the glass.
   ============================================================ */

@media (max-height: 560px) {
  /* The hub: six doors, a title above them and a ledger below, in 390 pixels of
     height. Stacked, the eyebrow printed itself straight through the wordmark and
     the Portal Chamber sat on top of the ledger. A wide short screen has width to
     spare and no height at all, so the doors take two columns and stop fighting
     over the one axis the window is short of. */
  .hub-doors {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: clamp(1.4rem, 5vw, 3rem);
    row-gap: 0;
    max-width: min(760px, 78vw);
  }
  .hub-eyebrow { grid-column: 1 / -1; font-size: 0.68rem; margin-bottom: 0.5rem; }
  /* and a door is still something a thumb has to hit */
  .door { display: flex; flex-direction: column; justify-content: center; min-height: 44px; }
  .door-name { font-size: clamp(1rem, 4.4vh, 1.5rem); line-height: 1.25; }
  .door-whisper { font-size: 0.78rem; }

  /* the archives: the header was the whole room, and the thread of time —
     which is the room — began below the bottom of the screen */
  .archives-head { padding: clamp(3.6rem, 14vh, 4.6rem) clamp(1.4rem, 6vw, 6rem) 0; }
  .archives-head h2 { font-size: clamp(1.3rem, 4.2vh, 2rem); margin: 0.1em 0 0.2em; }
  .epoch p, .epoch h3 { font-size: 0.88rem; }
  .epoch h3 { margin: 0.2em 0 0.35em; }
  /* the ledger sits in the corner of every room; here it sat in the middle of a
     sentence, in the same colour as the sentence */
  .epoch, .room-scroll > *:last-child { padding-bottom: 2.8rem; }
  #ledger-btn {
    padding: 0.45rem 0.9rem 0.45rem 0.7rem;
    background: linear-gradient(0deg, oklch(0.08 0.008 260 / 0.94), oklch(0.08 0.008 260 / 0.7));
  }
  .archives-head h2::after { display: none; }
  .archives-note, .bleeding-counter { display: none; }
  .timeline { padding-block: clamp(1rem, 4vh, 2rem); }
  .epoch { overflow-y: auto; padding-right: 1.6rem; overscroll-behavior: contain; }

  /* the threshold: the name must not be taller than the room it arrives in */
  .intro-scene { grid-template-rows: minmax(0,1fr) auto minmax(0,0.35fr) auto auto minmax(0,1.1fr); }
  .intro-lines { min-height: 2.4em; }
  .enter-btn { padding: 0.6em 1.8em; }
  .threshold-touch { padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 3.4rem); }

  .hall-intro { min-height: 100svh; }
  .hall-scroll-cue { margin-top: 4vh; }
  .hall { padding-block: clamp(2rem, 8vh, 4rem); }
  .veiled-row { min-height: auto; }
  .colophon { padding-top: clamp(3.4rem, 12vh, 5rem); }
  .reader { padding-top: clamp(2.6rem, 8vh, 4rem); }
  .reader-exit { top: clamp(2.4rem, 7vh, 3.4rem); }
  .bestiary-index { max-height: 42svh; }
}

/* A thumb is a thumb on a tablet too. These two were only given their 44 pixels
   inside the phone breakpoint, and a 768-wide tablet is not a phone. */
.colophon-link, .return, #ledger-btn, #wordmark, #mute {
  display: inline-flex; align-items: center; min-height: 44px;
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  .intro-fog { animation: none; }
  #grain { display: none; }
  /* the lamp stays — it is the light in the room, not an animation — but nothing
     shudders, nothing drifts, and the flame holds perfectly still */
  .room, #air, #lamp { transform: none !important; }
}
