/* Cinzel: the classical Roman capitals the wordmark speaks in, used for every
   label the HUD and the Esc menu set in caps. Falls back to the page serif when
   the font does not load (offline), so nothing depends on it. */
@import url("https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600&display=swap");

:root {
  --ink: #e9e2d0;
  --cream: #eddcb4;
  --gold: #ffd27f;
  --gold-dim: #b98d3f;
  --dim: #8a8577;
  --bg: #0a0c12;
  --display: "Cinzel", "Iowan Old Style", Georgia, "Times New Roman", serif;
}

* { box-sizing: border-box; }

/* The hidden attribute must win over any class that sets display, or a
   chip styled display:flex stays on screen while hidden reads true (the
   push-to-talk chip did, 2026-09-09; same trap as the dashboard's comment
   bubble). Declared once, here, for every page that loads this sheet. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: "Iowan Old Style", Georgia, "Times New Roman", serif;
  overflow: hidden;
}

#xr { display: block; width: 100vw; height: 100vh; }

.hud {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 10;
  transition: opacity 0.25s ease;
}
.hud.menu-open { opacity: 0; }

/* connection notice: only offline / session-ended states are shown (hud.js) */
.hud-conn {
  position: absolute;
  top: 14px;
  left: 16px;
  font-family: var(--display);
  font-size: 10px;
  letter-spacing: 0.26em;
  color: var(--dim);
  text-transform: uppercase;
}
.hud-conn[hidden] { display: none !important; }

/* the current objective, top right: one line, a hollow gold lozenge before
   it; on completion the lozenge fills, the line dims and fades, and the next
   step rises (hud.js refreshObjective) */
.hud-objective {
  position: absolute;
  top: 18px;
  right: 22px;
  display: flex;
  align-items: center;
  flex-direction: row-reverse;
  gap: 12px;
  padding: 8px 14px 8px 26px;
  white-space: nowrap;
  font-family: var(--display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(237, 220, 180, 0.9);
  background: radial-gradient(ellipse at 70% 50%, rgba(6, 7, 10, 0.55), rgba(6, 7, 10, 0) 72%);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.95), 0 0 18px rgba(0, 0, 0, 0.8);
  transition: opacity 0.9s ease, transform 0.9s ease, color 0.6s ease;
}
.hud-objective-mark {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border: 1px solid var(--gold);
  transform: rotate(45deg);
  box-shadow: 0 0 8px rgba(255, 210, 127, 0.45);
  transition: background 0.4s ease, box-shadow 0.4s ease;
}
.hud-objective.done { color: var(--dim); opacity: 0; transform: translateY(-6px); }
.hud-objective.done .hud-objective-mark { background: var(--gold); box-shadow: 0 0 12px 2px rgba(255, 210, 127, 0.7); }
.hud-objective.done .hud-objective-text { text-decoration: line-through; text-decoration-color: rgba(255, 210, 127, 0.5); }
.hud-objective.rise { animation: objective-rise 0.7s ease; }
@keyframes objective-rise {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

/* The reveal gauge (hud.js setRevealProgress): a gold line under the step the
   reveal measures ("Ask Curie about her life", objectives.js `gauge`), from
   the words to the lozenge. Its length is --reveal (0..1), set on the
   objective from the worker's reveal_progress each turn; the line and the
   lozenge brighten as it grows, and the faint track stays up for the whole
   step. A slow shimmer and a glint at the tip keep it kindling. Fired: it
   runs to the lozenge and fills it, then the step checks off and takes the
   gauge with it. It replaced a thread along the top edge that did not say
   what it measured (her run, 2026-09-13). */
.hud-objective.gauged { padding-bottom: 12px; }
.hud-reveal {
  display: none;
  position: absolute;
  left: 26px; /* the objective's left padding, where the words start */
  right: 17px; /* under the middle of the lozenge */
  bottom: 5px;
  height: 2px;
  background: rgba(237, 220, 180, 0.16);
}
.hud-objective.gauged .hud-reveal { display: block; }
.hud-reveal-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: calc(var(--reveal, 0) * 100%);
  background: linear-gradient(90deg,
    rgba(255, 210, 127, 0.2) 0%,
    rgba(255, 210, 127, 0.8) 60%,
    var(--gold) 100%);
  background-size: 200% 100%;
  opacity: calc(0.45 + var(--reveal, 0) * 0.55);
  box-shadow: 0 0 calc(2px + var(--reveal, 0) * 10px) rgba(255, 210, 127, 0.6);
  transition: width 1.4s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 1.4s ease, box-shadow 1.4s ease;
  animation: reveal-shimmer 3.2s linear infinite;
}
.hud-reveal-fill::after {
  /* the glint at the tip, once there is a tip */
  content: "";
  position: absolute;
  right: -3px;
  top: -2px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff6dc 0%, var(--gold) 45%, rgba(255, 210, 127, 0) 72%);
  animation: reveal-glint 1.7s ease-in-out infinite;
}
.hud-objective.reveal-empty .hud-reveal-fill::after { display: none; }
.hud-objective.gauged .hud-objective-mark {
  box-shadow: 0 0 calc(4px + var(--reveal, 0) * 12px) rgba(255, 210, 127, 0.6);
}
.hud-objective.gauged.reveal-fired .hud-reveal-fill {
  opacity: 1;
  box-shadow: 0 0 14px 2px rgba(255, 210, 127, 0.85);
  transition-duration: 0.9s;
  animation-duration: 0.9s;
}
.hud-objective.gauged.reveal-fired .hud-objective-mark {
  background: var(--gold);
  box-shadow: 0 0 14px 3px rgba(255, 210, 127, 0.8);
}
@keyframes reveal-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: 0% 0; }
}
@keyframes reveal-glint {
  0%, 100% { transform: scale(0.7); opacity: 0.55; }
  50%      { transform: scale(1.25); opacity: 1; }
}

/* the one clickable call: a line of capitals between two hairlines, like the
   title screen's enter line */
.hud-present {
  pointer-events: auto;
  position: absolute;
  left: 50%;
  bottom: 8%;
  transform: translateX(-50%);
  padding: 12px 30px;
  font: inherit;
  font-family: var(--display);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--cream);
  background: none;
  border: none;
  cursor: pointer;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.9), 0 0 24px rgba(0, 0, 0, 0.7);
  transition: color 0.25s ease, bottom 0.25s ease;
  animation: rise 0.25s ease;
}
.hud-present::before,
.hud-present::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 64px;
  height: 1px;
  background: linear-gradient(90deg, rgba(255, 210, 127, 0), rgba(255, 210, 127, 0.75));
  transition: width 0.3s ease;
}
.hud-present::before { right: 100%; }
.hud-present::after { left: 100%; background: linear-gradient(270deg, rgba(255, 210, 127, 0), rgba(255, 210, 127, 0.75)); }
.hud-present:hover { color: var(--gold); }
.hud-present:hover::before,
.hud-present:hover::after { width: 96px; }

/* the quiet menu hint, bottom-left; Esc opens the menu (hud.js / menu.js) */
.hud-menu-hint {
  pointer-events: auto;
  position: absolute;
  left: 18px;
  bottom: 16px;
  padding: 4px 0;
  font: inherit;
  font-family: var(--display);
  font-size: 10px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--dim);
  opacity: 0.75;
  background: none;
  border: none;
  cursor: pointer;
  transition: color 0.2s ease, opacity 0.2s ease;
}
.hud-menu-hint:hover { color: var(--gold); opacity: 1; }
.hud-menu-hint[hidden] { display: none !important; }

/* a passing line ("+1, the hall remembers"): text on a soft dark halo, no box */
.hud-toast {
  position: absolute;
  left: 50%;
  bottom: 22%;
  transform: translateX(-50%);
  padding: 10px 28px;
  font-size: 16px;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: var(--cream);
  background: radial-gradient(ellipse at center, rgba(6, 7, 10, 0.55), rgba(6, 7, 10, 0) 72%);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.9), 0 0 22px rgba(0, 0, 0, 0.7);
  animation: rise 0.25s ease;
}

@keyframes rise {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}
@keyframes gatepulse {
  0%, 100% { opacity: 0.7; }
  50% { opacity: 1; }
}

/* push-to-talk: a breathing gold lozenge and small capitals while Space is held */
.hud-mic {
  position: absolute;
  left: 50%;
  bottom: 20%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 10px;
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--gold);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.9), 0 0 22px rgba(0, 0, 0, 0.7);
  animation: rise 0.2s ease;
}
.hud-mic::before {
  content: "";
  width: 7px;
  height: 7px;
  background: var(--gold);
  transform: rotate(45deg);
  box-shadow: 0 0 10px 2px rgba(255, 210, 127, 0.6);
  animation: gatepulse 1.4s ease-in-out infinite;
}

/* context-action line, "[E] Take the notebook": the key as a small keycap,
   the action in cream serif, a soft dark halo instead of a box (hud.js
   wraps the bracketed key in <kbd>) */
.hud-prompt {
  position: absolute;
  left: 50%;
  bottom: 14%;
  transform: translateX(-50%);
  padding: 10px 30px;
  font-size: 17px;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: var(--cream);
  background: radial-gradient(ellipse at center, rgba(6, 7, 10, 0.6), rgba(6, 7, 10, 0) 72%);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.9), 0 0 22px rgba(0, 0, 0, 0.7);
  animation: rise 0.25s ease;
}
.hud-prompt kbd,
.hud-toast kbd {
  display: inline-block;
  margin: 0 6px 0 0;
  padding: 3px 8px 2px;
  font-family: var(--display);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  vertical-align: 2px;
  color: var(--gold);
  border: 1px solid rgba(255, 210, 127, 0.55);
  border-radius: 2px;
  text-shadow: none;
  box-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
}
.hud-prompt kbd:not(:first-child) { margin-left: 6px; }

/* While the caption sheet is up (hud.js _liftForSheet sets --sheet-h to its
   height), the lines that share the bottom of the screen rise by that height
   and keep their spacing, so no instruction sits under the paper. Her clip of
   2026-09-15: the sheet, 7% up and about 174px tall, covered the prompt at
   14%, and the present call, the push-to-talk line and a toast sit in the
   same band. */
.hud-prompt,
.hud-mic,
.hud-toast { transition: bottom 0.25s ease; }
.hud.captioned .hud-present { bottom: calc(8% + var(--sheet-h, 0px)); }
.hud.captioned .hud-prompt { bottom: calc(14% + var(--sheet-h, 0px)); }
/* ...unless it is the line telling the visitor to take a turn that is still
   his. That one tucks along the foot, below the sheet's own 7%: on screen the
   whole time he speaks, out of the way of what he is saying. */
.hud.captioned .hud-prompt.tucked {
  bottom: 14px;
  padding: 4px 18px;
  font-size: 13px;
  opacity: 0.72;
}
.hud.captioned .hud-prompt.tucked kbd { font-size: 9px; padding: 2px 6px 1px; }
.hud.captioned .hud-mic { bottom: calc(20% + var(--sheet-h, 0px)); }
.hud.captioned .hud-toast { bottom: calc(22% + var(--sheet-h, 0px)); }

/* live captions: the figure's line from the worker's transcript events, two
   lines at a time (caption-pacer.js fitChunks, hud.js _fitCaption), on aged
   parchment (reference: her 2026-09-08 "Elder Mara" board). The sheet is one
   size for every line, her ask of 2026-09-15: a fixed width, and a text area
   two lines tall at each face (.hud-caption-lines below). Every chunk of the
   line is stacked in one grid cell with only the current one showing, so
   nothing moves but the words. hud.js measures against this width, 880px,
   and the paddings below: change them together. */
.hud-caption {
  position: absolute;
  left: 50%;
  bottom: 7%;
  transform: translateX(-50%);
  width: min(880px, 88vw);
  padding: 34px 72px 36px;
  font-size: 24px;
  font-weight: 500;
  line-height: 1.38;
  text-align: center;
  color: #2a1a0c;
  animation: rise 0.25s ease;
  transition: opacity 0.3s ease;
}
.hud-caption.leaving { opacity: 0; }
/* Caption size, her pick in the Esc menu (menu.js, CAPTION_SIZES in
   caption-pacer.js): the face the sheet takes for each. "large" is the 24px
   the captions shipped with, eight words at a time; the smaller faces carry
   sixteen and twenty-six words a chunk, so the sheet holds a sentence or two
   and changes less often. The medium face is the default since her first
   full run (2026-09-13) read eight words as too slow to follow. */
.hud-caption[data-size="medium"] { font-size: 20px; line-height: 1.4; }
.hud-caption[data-size="small"] { font-size: 16px; line-height: 1.42; }
.hud-caption[data-size="small"] .hud-caption-speaker { font-size: 12px; margin-bottom: 7px; }
/* two lines tall at each face (its line-height times two), so a one-line
   chunk sits on the same sheet as a two-line one */
.hud-caption-lines { display: grid; height: 2.76em; }
.hud-caption[data-size="medium"] .hud-caption-lines { height: 2.8em; }
.hud-caption[data-size="small"] .hud-caption-lines { height: 2.84em; }
.hud-caption-lines > span {
  grid-area: 1 / 1;
  align-self: center; /* a one-line chunk sits mid-sheet when another wraps */
  opacity: 0;
  transition: opacity 0.2s ease;
}
.hud-caption-lines > span.on { opacity: 1; }

/* The paper is one sheet: her board with the sample text taken out
   (scripts/parchment_plate.py, assets/parchment.webp, 2357x603, drawn at
   half size), cut to the box rather than tiled. Four pieces, each pinned to
   its own corner of the sheet, meet in the middle through soft seams, so the
   deckle, the inner rule and its notched corners stay whole and nothing
   repeats at any size up to the sheet's own, 1178x301 (max-width keeps the
   box inside it). Each half's right piece lies under its left, and the
   bottom half under the top, so every seam fades over opaque paper. */
.hud-caption-paper { display: none; }
.hud-caption.has-paper .hud-caption-paper {
  --seam: 64px;
  display: block;
  position: absolute;
  inset: 0;
  z-index: -1;
  filter: drop-shadow(0 14px 20px rgba(0, 0, 0, 0.62)) drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5));
}
.hud-caption-paper > span {
  position: absolute;
  left: 0;
  right: 0;
  height: calc(50% + var(--seam) / 2);
}
.hud-caption-paper > .bottom { bottom: 0; }
.hud-caption-paper > .top {
  top: 0;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--seam)), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - var(--seam)), transparent);
}
.hud-caption-paper i {
  position: absolute;
  top: 0;
  bottom: 0;
  width: calc(50% + var(--seam) / 2);
  background: url("./assets/parchment.webp") no-repeat;
  background-size: 1178.5px 301.5px;
}
.hud-caption-paper i.r { right: 0; }
.hud-caption-paper i.l {
  left: 0;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--seam)), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - var(--seam)), transparent);
}
.hud-caption-paper .top i.l { background-position: left top; }
.hud-caption-paper .top i.r { background-position: right top; }
.hud-caption-paper .bottom i.l { background-position: left bottom; }
.hud-caption-paper .bottom i.r { background-position: right bottom; }
.hud-caption.has-paper { padding: 42px 84px 46px; }
.hud-caption.has-paper::before,
.hud-caption.has-paper::after { display: none; } /* the sheet carries its own rule */

/* Until the sheet loads (or if it never does), a drawn paper stands in: a
   ::before layer of warm tan with soft mottling, run through the SVG filter
   hud.js injects (#hoh-parchment), which deckles the edge with fine noise and
   multiplies in a light grain, and an ::after inner rule. */
.hud-caption::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(75% 95% at 28% 28%, rgba(226, 204, 164, 0.7), rgba(226, 204, 164, 0) 62%),
    radial-gradient(55% 75% at 80% 72%, rgba(150, 118, 76, 0.28), rgba(150, 118, 76, 0) 60%),
    radial-gradient(130% 150% at 50% 50%, rgba(0, 0, 0, 0), rgba(96, 68, 34, 0.32) 100%),
    linear-gradient(180deg, #cdb389 0%, #c3a67c 55%, #b89b70 100%);
  filter: url("#hoh-parchment") drop-shadow(0 14px 20px rgba(0, 0, 0, 0.62)) drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5));
}
.hud-caption::after {
  /* the inner rule, set in from the deckle */
  content: "";
  position: absolute;
  inset: 13px 16px;
  border: 1px solid rgba(58, 36, 14, 0.62);
  pointer-events: none;
}
.hud-caption[hidden] { display: none !important; }
.hud-caption-speaker {
  display: block;
  margin-bottom: 10px;
  font-family: var(--display);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: #3e2810;
}

/* walk-mode crosshair */
.hud-crosshair {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: rgba(233, 226, 208, 0.85);
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.6);
}

/* room-transition fade (flow-state.transition). The page's own colour, the
   one the next room waits in behind .room-veil, so a door is one dark moment. */
.fade-veil {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--bg);
  opacity: 0;
  transition: opacity 0.45s ease;
  pointer-events: none;
}
.fade-veil.on { opacity: 1; }

/* arrival veil (room-reveal.js): each room waits here until its splat has
   drawn and its objects are in, then rises whole. Over the canvas and under
   the HUD, so the objective and the first toast read on the dark; clicks pass
   through to the canvas. The fade is FADE_MS in room-reveal.js. */
.room-veil {
  position: fixed;
  inset: 0;
  /* Under the HUD (10), deliberately: room-reveal.test.mjs pins it, and a
     visitor watching an empty veil with no chrome at all has nothing to look
     at. Measured 2026-09-16: during a hall load the objective, the room name
     and the menu hint sit on the dark field for about 3 s. Whether that reads
     as company or as clutter is hers to say; do not flip it on taste alone. */
  z-index: 5;
  background: var(--bg);
  opacity: 1;
  transition: opacity 0.6s ease;
  pointer-events: none;
}
.room-veil.lifting { opacity: 0; }
/* The room you are walking into, out of focus, so the wait is a place rather
   than an empty screen. Her note of 2026-09-16: the hall "goes all grey and
   then things load in ... not a good user experience", and a full title card
   would be "equally as jarring". As the veil lifts the still sharpens and
   settles into the live room behind it, so arriving is a focus pull.
   A page that sets no --veil-still paints nothing here and keeps the flat
   colour it had before. */
.room-veil::before {
  content: "";
  position: absolute;
  inset: -5%; /* over-scan: a blur with no soft edge at the frame */
  background: var(--veil-still, none) center / cover no-repeat;
  filter: blur(24px) brightness(0.5) saturate(0.85);
  transform: scale(1.04);
  transition: filter 0.6s ease, transform 0.6s ease;
}
.room-veil.lifting::before {
  filter: blur(5px) brightness(0.85) saturate(1);
  transform: scale(1);
}
/* a slow first load: a hollow gold lozenge breathes just below the centre */
.room-veil.slow::after {
  content: "";
  position: absolute;
  left: 50%;
  top: calc(50% + 34px);
  width: 7px;
  height: 7px;
  margin-left: -4px;
  border: 1px solid var(--gold);
  transform: rotate(45deg);
  box-shadow: 0 0 8px rgba(255, 210, 127, 0.45);
  animation: gatepulse 1.4s ease-in-out infinite;
}
