/* Nemudoro web surface.
 *
 * The scene is built from layered gradients and SVG silhouettes rather than artwork, so it costs
 * nothing to ship and shifts with the user's local hour. Every colour it uses comes from the
 * variables at the top, which app.js rewrites as the hour changes — replacing the .scene block
 * with real artwork later means touching this file in exactly one place.
 */

:root {
  /* Phase palette, mirrored from design/UI_SPECIFICATIONS.md section 1. app.js overrides these
     with the server's ambient-tinted values so web and Discord agree on the colour of a moment. */
  --focus: #ffb7b2;
  --short_break: #89cff0;
  --long_break: #2b3a4a;
  --profile: #e6e6fa;
  --evolution: #f5d77f;
  --accent: var(--focus);

  /* Scene, rewritten per hour by app.js */
  --sky-top: #1b2340;
  --sky-mid: #3b3b63;
  --sky-bottom: #6d5277;
  --glow: #f5d77f;
  --star-opacity: 0.9;
  --city-far: #232a46;
  --city-near: #161b2e;
  --window-glow: #ffd79a;

  --ink: #f4f1ee;
  /* Secondary text. Contrast over every painted scene, day and night, is measured, not guessed:
     UI_SPECIFICATIONS.md 5.13 (WCAG 2.2 AA, 4.5:1 for text under 24px). */
  --ink-dim: #d3cddc;
  /* Dark text on the phase colour. Near-black, because the server tints the accent darker by
     night and #1a1726 then fell under 4.5:1. */
  --on-accent: #0d0a12;
  /* The frosted panels. Was 0.42, which bright day scenes shone straight through; 0.7 is the lightest that passes. */
  --panel: rgba(12, 12, 22, 0.7);
  --panel-line: rgba(255, 255, 255, 0.1);
  --blur: saturate(140%) blur(20px);
  --radius: 22px;
  --scrim: 0 2px 16px rgba(0, 0, 0, 0.6);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; }

/* Class selectors below set `display`, which outranks the UA rule behind the `hidden` attribute.
   Without this, anything toggled via `hidden` (sheets, overlays, the toast) renders on load. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: Nunito, ui-rounded, "Segoe UI", system-ui, sans-serif;
  color: var(--ink);
  background: #10121f;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

body[data-state="loading"] .app,
body[data-state="loading"] .gate { opacity: 0; }

/* ===========================================================  Scene  === */

.scene { position: fixed; inset: 0; z-index: 0; overflow: hidden; background: #10121f; }
.scene > * { position: absolute; inset: 0; }

/* Plain colour washes, for people who want somewhere quiet to look rather than a room to sit in.
   They cross-fade on the same variables the painted rooms use, so the day/night toggle behaves
   identically whichever kind of scene is showing. */
.wash { transition: opacity 3s linear, background 1.2s linear; }
.wash-day { background: var(--wash-day, none); opacity: var(--day-opacity, 0); }
.wash-night { background: var(--wash-night, none); opacity: var(--night-opacity, 1); }

/* The hour's sky: under a painted room it shows where day and night blend, and it stands in if the
   painting fails. Until the painting has arrived it would be all there is, a bright gradient flashing
   on every load, so over a painted scene it waits for the room (applyScene sets data-room-ready). */
.sky {
  background: linear-gradient(180deg, var(--sky-top) 0%, var(--sky-mid) 55%, var(--sky-bottom) 100%);
  transition: background 2.4s linear, opacity 0.6s ease;
}
body[data-state="loading"] .sky,
body[data-scene-kind="art"]:not([data-room-ready]) .sky { opacity: 0; }

.room {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  /* A slow push-in keeps a still image from feeling dead without distracting from the timer. */
  animation: breathe 48s ease-in-out infinite alternate;
  transition: opacity 3s linear;
}
.room-day { opacity: var(--day-opacity, 0); }
.room-night { opacity: var(--night-opacity, 1); }
/* The remembered room (restoreRoom in app.js) starts in its own lighting, not faded in from night. */
body[data-state="loading"] .room { transition: none; }

@keyframes breathe { from { transform: scale(1.02); } to { transform: scale(1.07); } }

/* Rain streaks over the window, matching the weather in the artwork. */
.rain {
  opacity: var(--rain-opacity, 0.5);
  background-image:
    linear-gradient(104deg, transparent 0 6px, rgba(198, 222, 255, 0.5) 6px 7px, transparent 7px 26px),
    linear-gradient(101deg, transparent 0 14px, rgba(198, 222, 255, 0.32) 14px 15px, transparent 15px 44px);
  background-size: 140px 180px, 210px 240px;
  animation: rainfall 0.85s linear infinite, rainfall 1.35s linear infinite;
  mix-blend-mode: screen;
  pointer-events: none;
}
@keyframes rainfall { from { background-position: 0 0, 0 0; } to { background-position: -46px 180px, -60px 240px; } }

/* Lamp warmth, strongest at night, so the room glows rather than sits flat. */
.warmth {
  background: radial-gradient(42% 46% at 66% 52%, color-mix(in srgb, var(--window-glow) 26%, transparent), transparent 70%);
  mix-blend-mode: screen;
  opacity: var(--night-opacity, 1);
  animation: flicker 6s ease-in-out infinite alternate;
  transition: opacity 3s linear;
}
@keyframes flicker { from { opacity: calc(var(--night-opacity, 1) * 0.8); } to { opacity: var(--night-opacity, 1); } }

/* The companion, composited into the room itself.
 *
 * The room images are object-fit: cover, so a percentage offset from the viewport does NOT track a
 * point in the artwork -- as the window changes aspect ratio the picture is cropped differently and
 * the rug moves. placeCompanion() in app.js therefore measures the rendered image box and writes
 * --companion-x/y/size in pixels; this rule only consumes them. The anchor is the character's feet,
 * hence translate(-50%, -100%).
 */
.room-companion-spot {
  inset: auto;
  left: var(--companion-x, 50%);
  top: var(--companion-y, 80%);
  width: var(--companion-size, 220px);
  height: var(--companion-size, 220px);
  transform: translate(-50%, -100%);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  pointer-events: none;
  /* No z-index: the rain, lamp warmth and vignette come later in the DOM and so paint over the
     companion, which is what puts it inside the room rather than pasted on top of it. */
  filter: drop-shadow(0 10px 14px rgba(0, 0, 0, 0.45));
  transition: left 0.4s var(--ease), top 0.4s var(--ease), width 0.4s var(--ease), height 0.4s var(--ease);
}

.room-companion-cutout {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  object-position: bottom center;
  pointer-events: auto;
  cursor: pointer;
  animation: companionCozyBreathe 5s ease-in-out infinite;
  transform-origin: bottom center;
}

.room-companion-cutout:hover {
  filter: brightness(1.08) drop-shadow(0 0 12px rgba(255, 183, 178, 0.6));
}

.room-companion-cutout:active,
.room-companion-cutout.petting {
  animation: companionPetBounce 0.55s var(--ease);
}

/* Organic breathing, subtle squash-and-stretch, and gentle rotation sway */
@keyframes companionCozyBreathe {
  0%   { transform: translateY(0px) rotate(0deg) scale(1, 1); }
  25%  { transform: translateY(-4px) rotate(-1.5deg) scale(0.99, 1.02); }
  50%  { transform: translateY(-7px) rotate(0deg) scale(1.02, 0.98); }
  75%  { transform: translateY(-3px) rotate(1.5deg) scale(0.99, 1.01); }
  100% { transform: translateY(0px) rotate(0deg) scale(1, 1); }
}

@keyframes companionPetBounce {
  0%   { transform: scale(1, 1) translateY(0); }
  30%  { transform: scale(1.16, 0.84) translateY(6px); }
  60%  { transform: scale(0.92, 1.08) translateY(-14px); }
  80%  { transform: scale(1.04, 0.96) translateY(-2px); }
  100% { transform: scale(1, 1) translateY(0); }
}

/* Visual effects (public/effects.js) paint here: over the room and the companion, under the vignette,
   so the weather is inside the room's light rather than stuck to the glass. */
.fx { width: 100%; height: 100%; pointer-events: none; }

.vignette {
  background: radial-gradient(120% 90% at 50% 45%, transparent 38%, rgba(6, 6, 14, 0.7) 100%);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .room, .rain, .warmth, .gate-egg, .room-companion-cutout { animation: none; }
}

/* =============================================================  Gate  === */

.gate { position: relative; z-index: 2; display: grid; place-items: center; min-height: 100dvh; padding: 24px; }

.gate-card {
  width: min(420px, 100%);
  padding: 40px 32px;
  text-align: center;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.45);
  animation: rise 0.7s var(--ease) both;
}

.gate-egg { font-size: 56px; animation: bob 4s ease-in-out infinite; }
.gate h1 { font-family: Fraunces, Georgia, serif; font-weight: 600; font-size: 34px; margin: 8px 0 6px; letter-spacing: -0.01em; }
.gate-lede { color: var(--ink-dim); margin: 0 0 26px; line-height: 1.5; }
.gate-note { color: var(--ink-dim); font-size: 13px; margin: 18px 0 0; line-height: 1.5; }
.gate-error { color: #ffb3b3; font-size: 13px; margin-top: 12px; }
.gate-legal { font-size: 12px; margin: 22px 0 0; color: var(--ink-dim); }
.gate-legal a { color: var(--ink-dim); text-underline-offset: 3px; }
.gate h1.offline-title { font-size: 24px; margin: 8px 0 10px; }
.gate-legal a:hover { color: var(--ink); }

@keyframes rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }

/* ==============================================================  App  === */

.app {
  position: relative; z-index: 2;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  /* minmax(0, …), not the default auto track: an auto track grows to its widest child's
     min-content, and on a phone a too-wide row then zooms the whole layout viewport out. */
  grid-template-columns: minmax(0, 1fr);
  padding: max(14px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom));
  animation: rise 0.8s var(--ease) both;
}

/* --- HUD --- */

.hud { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.hud-group { display: flex; gap: 8px; align-items: center; min-width: 0; }
/* The buttons are rigid and the readouts give way, so no number length can push the row past the
   right edge: the SP chip runs out of room first and ellipses (the full figure is one tap away, in
   the statistics panel). Without this the row is only as safe as the widths measured for the phone
   breakpoint below — a six-figure total, a long streak or a fallback font would put it back over
   the edge (UI_SPECIFICATIONS.md 5.17). */
.hud > .hud-group:last-child, .icon-btn, .chip-icon { flex: none; }
.chip { min-width: 0; }
.chip > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#btn-stats-streak { flex: none; } /* the streak is short: the long SP chip is the one that gives */

.chip, .icon-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 36px; padding: 0 13px;
  font: inherit; font-size: 14px; font-weight: 700; color: var(--ink);
  background: var(--panel); border: 1px solid var(--panel-line); border-radius: 999px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
}
.chip-icon { font-size: 13px; opacity: 0.95; }
.chip-unit { font-weight: 400; font-size: 12px; color: var(--ink-dim); }

.icon-btn { width: 36px; padding: 0; justify-content: center; cursor: pointer; transition: transform 0.18s var(--ease), background 0.18s; }
.chip-btn { cursor: pointer; transition: transform 0.18s var(--ease), background 0.18s; }
.chip-btn:hover { transform: translateY(-1px); background: rgba(255, 255, 255, 0.14); }
.icon-btn:hover { transform: translateY(-1px); background: rgba(255, 255, 255, 0.14); }

.hud-goal {
  display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 14px;
  background: var(--panel); border: 1px solid var(--panel-line); border-radius: 999px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
}
.dots { display: flex; gap: 5px; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, 0.24); transition: background 0.4s var(--ease), box-shadow 0.4s; }
.dot.on { background: var(--accent); box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 65%, transparent); }
.hud-goal-count { font-size: 12px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }

/* --- Centre --- */

.stage-area {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px;
  /* The scrim below is 108vw wide so its fade runs past the edges; unclipped, that overhang widens
     a phone's layout viewport. It is fully transparent well inside this box, so nothing is lost. */
  overflow-x: clip;
}
/* The sky can be bright at midday; this keeps the timer and its prose readable at every hour
   without darkening the scene itself. */
.stage-area::before {
  content: "";
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(760px, 108vw); height: min(680px, 96vh);
  background: radial-gradient(closest-side, rgba(8, 8, 18, 0.66), rgba(8, 8, 18, 0.34) 52%, transparent 76%);
  pointer-events: none;
}
/* A plain wash has no busy detail for the text to fight, so the scrim only needs to be a hint.
   At full strength it reads as a smudge in the middle of a flat colour. */
body[data-scene-kind="gradient"] .stage-area::before {
  background: radial-gradient(closest-side, rgba(8, 8, 18, 0.28), rgba(8, 8, 18, 0.12) 55%, transparent 78%);
}
.stage-area > * { position: relative; }

.tabs {
  position: relative; display: flex; padding: 4px;
  background: var(--panel); border: 1px solid var(--panel-line); border-radius: 999px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
}
.tab {
  position: relative; z-index: 1;
  font: inherit; font-size: 14px; font-weight: 700; color: var(--ink-dim);
  padding: 8px 20px; border: 0; background: none; border-radius: 999px; cursor: pointer;
  transition: color 0.25s var(--ease);
}
.tab[aria-pressed="true"] { color: var(--on-accent); }
.tab-glider {
  position: absolute; top: 4px; bottom: 4px; left: 4px;
  border-radius: 999px; background: var(--accent);
  transition: transform 0.42s var(--ease), width 0.42s var(--ease), background 0.6s linear;
}

.tag-picker {
  font: inherit; font-size: 14px; color: var(--ink-dim);
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 16px; border: 1px solid var(--panel-line); border-radius: 999px;
  background: var(--panel); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  cursor: pointer; transition: color 0.2s, border-color 0.2s;
}
.tag-picker:hover { color: var(--ink); border-color: rgba(255, 255, 255, 0.24); }
.tag-picker.set { color: var(--ink); font-weight: 700; }
.caret { font-size: 11px; opacity: 0.7; }

.timer-card { display: grid; justify-items: center; gap: 14px; }

.timer-face { position: relative; width: min(320px, 74vw); aspect-ratio: 1; display: grid; place-items: center; }
.ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.ring-track, .ring-fill { fill: none; stroke-width: 6; stroke-linecap: round; }
.ring-track { stroke: rgba(255, 255, 255, 0.18); }
.ring-fill {
  stroke: var(--accent);
  stroke-dasharray: 703.7; /* 2πr, r = 112 */
  stroke-dashoffset: 703.7;
  filter: drop-shadow(0 0 12px color-mix(in srgb, var(--accent) 55%, transparent));
  transition: stroke-dashoffset 0.9s linear, stroke 0.6s linear;
}

.timer-readout { text-align: center; }
.clock {
  display: block;
  font-family: Fraunces, Georgia, serif;
  font-size: clamp(56px, 15vw, 92px);
  font-weight: 500; line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 6px 40px rgba(0, 0, 0, 0.45);
}
.timer-sub-row { margin-top: 10px; display: flex; align-items: center; justify-content: center; gap: 10px; }
/* Its own frosted pill: the sub-line sits over whatever the ring frames, often a bright window. */
.timer-sub {
  font-size: 13px; color: var(--ink-dim); letter-spacing: 0.02em;
  padding: 4px 12px; border-radius: 999px;
  background: var(--panel); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
}

/* Focus length, adjustable while the Focus tab is idle. */
.len-btn {
  width: 28px; height: 28px; padding: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font: 600 17px/1 inherit; color: var(--ink); cursor: pointer;
  background: var(--panel); border: 1px solid var(--panel-line);
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  transition: background 0.2s, opacity 0.2s, transform 0.15s var(--ease);
}
.len-btn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.15); }
.len-btn:active:not(:disabled) { transform: scale(0.92); }
.len-btn:disabled { opacity: 0.3; cursor: default; }
.len-btn[hidden] { display: none; }

body[data-running="paused"] .clock { opacity: 0.55; transition: opacity 0.4s; }
body[data-running="paused"] .ring-fill { opacity: 0.45; }

.whisper {
  margin: 0; max-width: 42ch; text-align: center;
  font-size: 14px; line-height: 1.55; font-style: italic;
  color: var(--ink);
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.75);
  min-height: 2.6em;
  padding: 6px 16px; border-radius: 16px;
  animation: fade 0.9s var(--ease) both;
}
.whisper:not(:empty) {
  background: var(--panel); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.controls { display: flex; align-items: center; justify-content: center; gap: 14px; }

.ctl {
  font: inherit; font-weight: 700; cursor: pointer;
  border-radius: 999px; border: 1px solid var(--panel-line);
  transition: transform 0.18s var(--ease), box-shadow 0.18s, background 0.25s;
}
.ctl:active { transform: scale(0.96); }

.ctl-primary {
  min-width: 168px; height: 56px; font-size: 17px; letter-spacing: 0.02em;
  color: var(--on-accent); background: var(--accent); border: 0;
  box-shadow: 0 14px 40px color-mix(in srgb, var(--accent) 34%, transparent);
}
.ctl-primary:hover { transform: translateY(-2px); box-shadow: 0 18px 48px color-mix(in srgb, var(--accent) 44%, transparent); }

.ctl-ghost {
  width: 46px; height: 46px; color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--panel); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
}
.ctl-ghost:hover { background: rgba(255, 255, 255, 0.15); }
.ctl-ghost svg { width: 19px; height: 19px; }
.icon-btn svg { width: 17px; height: 17px; }

/* --- Companion --- */

.companion {
  position: fixed; left: 16px; bottom: max(16px, env(safe-area-inset-bottom)); z-index: 3;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px 10px 10px;
  background: var(--panel); border: 1px solid var(--panel-line); border-radius: 18px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  animation: rise 1s var(--ease) 0.15s both;
}
/* The same cut-out sprite that stands in the room, whole (contain, not cover), and still: the card
   is a label, so it never moves. The padding keeps the sprite off the tile's rounded edge. */
.companion-art {
  width: 56px; height: 56px; flex: none; box-sizing: border-box; padding: 4px;
  border-radius: 14px; object-fit: contain;
  background: rgba(255, 255, 255, 0.07);
}
.companion-text { min-width: 132px; }
.companion-stage { font-size: 13px; font-weight: 700; }
.companion-bar { height: 5px; margin: 7px 0 5px; border-radius: 99px; background: rgba(255, 255, 255, 0.16); overflow: hidden; }
.companion-bar span { display: block; height: 100%; width: 0; border-radius: 99px; background: var(--evolution); transition: width 0.9s var(--ease); }
.companion-next { font-size: 11px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
/* Early Supporter (5.39): a gold seal, on the companion card and in the Profile. */
.supporter-badge {
  display: inline-flex; align-items: center; gap: 5px; width: fit-content; margin-top: 5px;
  padding: 2px 9px 2px 5px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em; color: #f5d77f;
  background: rgba(245, 215, 127, 0.12); border: 1px solid rgba(245, 215, 127, 0.5);
}
.supporter-badge[hidden] { display: none; }
.supporter-badge svg { width: 14px; height: 14px; flex: none; }
.profile-hero-text .supporter-badge { margin-top: 8px; }

/* --- Statistics ---
 *
 * Forms follow the job of each number: a headline gets a stat tile rather than a chart, daily
 * intensity gets a sequential heatmap, and the weekday comparison gets bars. The heatmap ramp is
 * ONE hue stepping light to dark -- never a rainbow -- with a neutral step reserved for "nothing
 * here", so an empty day reads as empty rather than as a low value. Both the heatmap and the bars
 * are a single series, so neither needs a legend: the heading names them.
 */

/* Wide enough for the year calendar (24px labels + 53 weeks x 12px) inside the 20px side padding. */
.sheet.sheet-wide { width: min(740px, calc(100vw - 32px)); }

.stat-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 9px; }
.tile {
  padding: 12px 14px; border-radius: 14px;
  background: rgba(255, 255, 255, 0.05); border: 1px solid var(--panel-line);
}
.tile-value { font-family: Fraunces, Georgia, serif; font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; }
.tile-label { font-size: 10.5px; color: var(--ink-dim); text-transform: uppercase; letter-spacing: 0.07em; margin-top: 3px; }
.tile-note { font-size: 11px; color: var(--ink-dim); margin-top: 5px; }

.stats-stack { display: grid; gap: 18px; }
.stats-section { display: grid; gap: 9px; }
.stats-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.stats-title { font-size: 12px; font-weight: 700; letter-spacing: 0.02em; }
.stats-sub { font-size: 11px; color: var(--ink-dim); }

/* Heatmap: sequential, one hue, light to dark. --heat-0 is a neutral for "no focus", kept off the
   ramp so nothing reads as a small amount of something. */
/* The ramp is set on the section, not the grid, so the Less-More legend beside the grid can read it. */
.stats-section {
  --heat-0: rgba(255, 255, 255, 0.06);
  --heat-1: #4a3a1c;
  --heat-2: #7a5f28;
  --heat-3: #b8903c;
  --heat-4: #f5d77f;
}
.heat {
  /* A year: 53 week columns after the weekday labels, a month row above 7 day rows. */
  --cell: 10px;
  display: grid; gap: 2px;
  grid-template-columns: 24px repeat(var(--weeks, 53), var(--cell));
  grid-template-rows: 13px repeat(7, var(--cell));
  width: max-content;
}
.heat-scroll { overflow-x: auto; overflow-y: hidden; padding-bottom: 4px; }
.heat-daylabel { font-size: 9px; line-height: var(--cell); color: var(--ink-dim); }
.heat-month { font-size: 9.5px; line-height: 13px; color: var(--ink-dim); white-space: nowrap; }
.heat-cell { border-radius: 2px; background: var(--heat-0); }
.heat-cell[data-level="1"] { background: var(--heat-1); }
.heat-cell[data-level="2"] { background: var(--heat-2); }
.heat-cell[data-level="3"] { background: var(--heat-3); }
.heat-cell[data-level="4"] { background: var(--heat-4); }
.heat-cell.is-today { box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.7); }
.heat-legend { display: flex; align-items: center; gap: 4px; font-size: 10px; color: var(--ink-dim); }
.heat-legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }

/* Weekday bars: single series, anchored to the baseline, rounded data-end only. */
.bars { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; align-items: end; height: 92px; }
.bar-col { display: grid; align-content: end; justify-items: center; gap: 5px; height: 100%; }
.bar { width: 100%; min-height: 3px; border-radius: 4px 4px 2px 2px; background: var(--accent); opacity: 0.85; }
.bar.is-best { opacity: 1; }
.bar-label { font-size: 10px; color: var(--ink-dim); }
.bar-value { font-size: 10px; color: var(--ink); font-variant-numeric: tabular-nums; }

.stats-empty { padding: 26px 8px; text-align: center; color: var(--ink-dim); font-size: 13px; line-height: 1.55; }

/* --- Scene library --- */

.scene-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }

.scene-card {
  position: relative; padding: 0; cursor: pointer; overflow: hidden;
  font: inherit; text-align: left; color: var(--ink);
  border: 1px solid var(--panel-line); border-radius: 14px; background: rgba(255, 255, 255, 0.05);
  transition: border-color 0.18s, transform 0.18s var(--ease);
}
.scene-card:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.3); }
.scene-card[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.scene-card img,
.scene-card .scene-swatch { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.scene-card-text { padding: 8px 11px 10px; }
.scene-card-name { font-size: 13px; font-weight: 700; }
.scene-card-blurb { font-size: 11px; color: var(--ink-dim); line-height: 1.4; margin-top: 2px; }
/* Premium locks (5.38): the room still shows, dimmed, with a gold tag. */
.premium-lock {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  padding: 2px 8px 2px 6px; border-radius: 999px;
  font-size: 11px; font-weight: 700; color: #f5d77f;
  background: rgba(20, 18, 30, 0.78); border: 1px solid rgba(245, 215, 127, 0.45);
}
.premium-lock svg { width: 12px; height: 12px; }
.scene-card .premium-lock { position: absolute; top: 8px; right: 8px; }
.scene-card.is-locked img, .scene-card.is-locked .scene-swatch { filter: saturate(0.55) brightness(0.8); }

.scene-side { display: grid; gap: 16px; align-content: start; }

/* Companions (5.60): a card per companion, drawn at the player's stage, locks as for rooms. */
.companion-note { margin: 0 0 12px; font-size: 13px; color: var(--ink-dim); }
.companion-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.companion-card {
  position: relative; padding: 0; cursor: pointer; overflow: hidden;
  display: flex; flex-direction: column; justify-content: flex-start; /* a button centres its content */
  font: inherit; text-align: left; color: var(--ink);
  border: 1px solid var(--panel-line); border-radius: 14px; background: rgba(255, 255, 255, 0.05);
  transition: border-color 0.18s, transform 0.18s var(--ease);
}
.companion-card:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.3); }
.companion-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.companion-card[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.companion-card img {
  display: block; width: 100%; aspect-ratio: 1; object-fit: contain; padding: 12px 12px 0;
  background: radial-gradient(circle at 50% 60%, rgba(255, 244, 228, 0.12), transparent 70%);
}
.companion-card-text { padding: 8px 11px 10px; }
.companion-card-name { font-size: 13px; font-weight: 700; }
.companion-card-intro { font-size: 11px; color: var(--ink-dim); line-height: 1.4; margin-top: 2px; }
.companion-card .premium-lock { position: absolute; top: 8px; right: 8px; }
.companion-card.is-locked img { filter: saturate(0.55) brightness(0.8); }
.profile-companion { justify-self: start; }
@media (min-width: 721px) { .companion-grid { grid-template-columns: repeat(3, 1fr); } }

/* On a desktop the sheet widens to fill the screen: the gallery is 3 x 3 on the left and the
   lighting and effect settings sit in a sidebar on the right. Each column scrolls on its own, so a
   short window still reaches every scene and every setting. Phones keep the single column above. */
@media (min-width: 900px) and (min-height: 560px) {
  .sheet.sheet-scenes {
    width: min(1200px, calc(100vw - 48px)); max-height: 90dvh;
    display: flex; flex-direction: column; overflow: hidden;
  }
  .sheet-scenes .scene-layout {
    flex: 1; min-height: 0;
    grid-template-columns: minmax(0, 1fr) 280px; gap: 20px;
  }
  .sheet-scenes .scene-layout > * { min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
  /* The padding leaves room for a card's hover lift and selected ring inside the scroller. */
  /* max-content rows: the cards clip their overflow, so without it the rows would squash to fit. */
  .sheet-scenes .scene-grid { grid-template-columns: repeat(3, 1fr); grid-auto-rows: max-content; align-content: start; gap: 14px; padding: 4px; }
  .sheet-scenes .scene-side { border-left: 1px solid var(--panel-line); padding: 4px 2px 4px 20px; }
}

/* --- Music player --- */

.player {
  position: fixed; left: 16px; bottom: calc(max(16px, env(safe-area-inset-bottom)) + 92px); z-index: 3;
  display: flex; align-items: center; gap: 10px;
  width: min(300px, calc(100vw - 32px));
  padding: 8px 12px 8px 8px;
  background: var(--panel); border: 1px solid var(--panel-line); border-radius: 16px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  animation: rise 0.8s var(--ease) 0.25s both;
}

.player-btn {
  flex: none; width: 34px; height: 34px; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink); background: rgba(255, 255, 255, 0.08);
  border: 1px solid transparent; border-radius: 50%;
  transition: background 0.18s, transform 0.18s var(--ease);
}
.player-btn:hover { background: rgba(255, 255, 255, 0.18); transform: scale(1.06); }
.player-btn svg { width: 15px; height: 15px; }
.player-play { background: var(--accent); color: var(--on-accent); }
.player-play:hover { background: var(--accent); }

/* The song name is a button: it opens the queue above the player. */
.player-meta {
  flex: 1; min-width: 0; padding: 3px 6px; margin: -3px -6px;
  font: inherit; color: var(--ink); text-align: left; cursor: pointer;
  background: none; border: 0; border-radius: 10px;
  transition: background 0.18s;
}
.player-meta:hover, .player-meta:focus-visible, .player-meta[aria-expanded="true"] { background: rgba(255, 255, 255, 0.08); }
.player-meta:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* the tint alone is too faint to find (5.34) */
.player-title, .player-artist { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player-title { font-size: 12.5px; font-weight: 700; }
.player-artist { font-size: 11px; color: var(--ink-dim); }

/* The queue: a pop-up above the player, in play order from the current track. It scrolls past a
   handful of tracks rather than growing up the screen. */
.queue-pop {
  position: fixed; z-index: 8; left: 16px; bottom: calc(max(16px, env(safe-area-inset-bottom)) + 152px);
  width: min(300px, calc(100vw - 32px)); max-height: min(360px, 50dvh);
  display: flex; flex-direction: column;
  padding: 12px 8px 8px; border-radius: 18px;
  background: var(--panel); border: 1px solid var(--panel-line);
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
  animation: pop-up 0.2s var(--ease);
}
.queue-head { margin: 0 8px 8px; font-size: 13px; font-weight: 700; }
.queue-note { font-weight: 400; font-size: 11px; color: var(--ink-dim); margin-left: 4px; }
/* Padding in, margin out: room inside the scroll box for a focused row's ring, which it would clip. */
.queue-list { margin: -4px; padding: 4px; list-style: none; overflow-y: auto; overscroll-behavior: contain; display: grid; gap: 2px; }
.queue-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 7px 8px; border: 0; border-radius: 12px; cursor: pointer;
  font: inherit; color: var(--ink); text-align: left; background: none;
  transition: background 0.18s;
}
.queue-item:hover, .queue-item:focus-visible { background: rgba(255, 255, 255, 0.1); }
.queue-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.queue-item[aria-current="true"] { background: rgba(255, 255, 255, 0.08); }
.queue-pos { flex: none; width: 18px; font-size: 11px; color: var(--ink-dim); text-align: right; font-variant-numeric: tabular-nums; }
.queue-item[aria-current="true"] .queue-pos { color: var(--accent); }
.queue-text { min-width: 0; }
.queue-title, .queue-artist { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.queue-title { font-size: 12.5px; font-weight: 700; }
.queue-artist { font-size: 11px; color: var(--ink-dim); }

/* --- Soundboard: ambient sounds alongside the music, each with its own volume --- */
.icon-btn.is-on { color: var(--accent); background: rgba(255, 255, 255, 0.14); }
.soundboard {
  position: fixed; z-index: 8; top: 62px; right: 16px;
  width: min(280px, calc(100vw - 32px));
  padding: 12px 14px 10px; border-radius: 18px;
  background: var(--panel); border: 1px solid var(--panel-line);
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
  animation: pop-down 0.2s var(--ease);
}
@keyframes pop-down { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
/* Music in the soundboard: narrow screens only, where the player bar is hidden (UI_SPECIFICATIONS.md 5.32) */
.board-music { display: none; margin: 0 0 10px; padding-bottom: 10px; border-bottom: 1px solid var(--panel-line); }
.board-music-row { display: flex; align-items: center; gap: 8px; padding: 2px; }
.board-music-meta { flex: 1; min-width: 0; }
.board-music .player-vol input { width: 64px; }

/* --- Corner: Full screen, About and Settings, bottom right (UI_SPECIFICATIONS.md 5.12, 5.16) --- */
.corner {
  --corner-bottom: max(16px, env(safe-area-inset-bottom));
  position: fixed; right: 16px; bottom: var(--corner-bottom); z-index: 4;
  display: flex; gap: 8px;
}

/* --- About: the "i" pop-up with the legal links, opening upwards from the corner --- */
.info-pop {
  position: fixed; z-index: 8; right: 16px; bottom: calc(max(16px, env(safe-area-inset-bottom)) + 48px);
  width: min(260px, calc(100vw - 32px));
  padding: 12px 10px 8px; border-radius: 18px;
  background: var(--panel); border: 1px solid var(--panel-line);
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
  animation: pop-up 0.2s var(--ease);
}
@keyframes pop-up { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.info-pop .soundboard-head { margin: 0 6px 6px; }
.info-lede { margin: 0 6px 8px; font-size: 13px; line-height: 1.45; color: var(--ink-dim); }
.info-links { display: grid; gap: 2px; }
.info-link {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 8px; border-radius: 12px;
  color: var(--ink); text-decoration: none; font-weight: 600; font-size: 14px;
  transition: background 0.18s;
}
.info-link:hover, .info-link:focus-visible { background: rgba(255, 255, 255, 0.1); }
.info-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.info-link svg { width: 18px; height: 18px; flex: none; color: var(--ink-dim); }
/* In Settings the text lines up with the other rows; the hover fill reaches into the margin. */
.field-group .info-links { margin: 0 -8px; }
/* --- Visual effects: the same pop-up as the soundboard, one full-width row per effect --- */
.fx-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.fx-option {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 5px 8px 5px 4px; border: 0; border-radius: 14px; background: none;
  color: var(--ink); font: inherit; font-size: 14px; font-weight: 600; text-align: left; cursor: pointer;
  transition: background 0.18s;
}
.fx-option:hover, .fx-option:focus-visible { background: rgba(255, 255, 255, 0.08); }
.fx-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fx-option .fx-glyph {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.08); border: 1px solid var(--panel-line);
  transition: background 0.18s, box-shadow 0.18s, transform 0.18s var(--ease);
}
.fx-option svg { width: 19px; height: 19px; }
.fx-option:active .fx-glyph { transform: scale(0.94); }
.fx-option[aria-pressed="true"] .fx-glyph { color: #1b1b24; background: var(--accent); border-color: transparent; box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 25%, transparent); }
/* In the Background sheet: a wrapping row of chips. */
.fx-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.fx-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px 6px 8px;
  border-radius: 999px; border: 1px solid var(--panel-line); background: rgba(255, 255, 255, 0.06);
  color: var(--ink); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  transition: background 0.18s, border-color 0.18s;
}
.fx-chip svg { width: 16px; height: 16px; }
.fx-chip:hover { background: rgba(255, 255, 255, 0.12); }
.fx-chip[aria-pressed="true"] { color: #1b1b24; background: var(--accent); border-color: transparent; }
/* Premium effects (5.59): dimmed, with the gold tag; in a chip the lock alone. */
.fx-option .premium-lock { margin-left: auto; }
.fx-option.is-locked .fx-glyph, .fx-option.is-locked .fx-name, .fx-chip.is-locked > svg, .fx-chip.is-locked .fx-name { opacity: 0.6; }
.fx-chip .premium-lock { padding: 2px 4px; }
.fx-chip .premium-lock span { display: none; }

.soundboard-hint { font-size: 12px; line-height: 1.4; color: var(--ink-dim); margin: 8px 2px 0; }
.soundboard-head { font-size: 12px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-dim); margin: 0 2px 8px; }
.sound-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.sound-row { display: flex; align-items: center; gap: 10px; padding: 4px 2px; }
.sound-toggle {
  flex: none; width: 36px; height: 36px; padding: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  color: var(--ink); background: rgba(255, 255, 255, 0.08); border: 1px solid var(--panel-line);
  transition: background 0.2s, color 0.2s, box-shadow 0.2s, transform 0.15s var(--ease);
}
.sound-toggle svg { width: 19px; height: 19px; }
.sound-toggle:hover { background: rgba(255, 255, 255, 0.16); }
.sound-toggle:active { transform: scale(0.94); }
.sound-row.is-locked .sound-toggle, .sound-row.is-locked .sound-name { opacity: 0.6; }
.sound-row.is-locked .player-vol { opacity: 0.35; }
.sound-toggle[aria-pressed="true"] { color: #1b1b24; background: var(--accent); border-color: transparent; box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 25%, transparent); }
.sound-toggle.is-loading { opacity: 0.6; cursor: progress; }
.sound-name { flex: 1; min-width: 0; font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sound-row .player-vol input { width: 84px; }
.sound-row .player-vol input:disabled { opacity: 0.35; cursor: default; }
/* Scrolls vertically once there are many sounds; never sideways (the active ring is inset room). */
.sound-list { max-height: min(60vh, 460px); overflow-y: auto; overflow-x: hidden; padding: 4px; margin: -4px; }

.player-vol { flex: none; display: flex; align-items: center; }
.player-vol input {
  width: 58px; height: 4px; padding: 0; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background: rgba(255, 255, 255, 0.22); border: 0; border-radius: 99px;
}
.player-vol input::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none;
  width: 11px; height: 11px; border-radius: 50%; border: 0;
  background: var(--ink); cursor: pointer;
}
.player-vol input::-moz-range-thumb {
  width: 11px; height: 11px; border-radius: 50%; border: 0; background: var(--ink); cursor: pointer;
}

/* The companion card sits above the player when both are on screen. */
.player:not([hidden]) ~ .companion { bottom: max(16px, env(safe-area-inset-bottom)); }

/* --- Quests --- */

.quests {
  position: fixed; right: 16px; bottom: calc(max(16px, env(safe-area-inset-bottom)) + 48px); z-index: 3;
  width: min(290px, calc(100vw - 32px));
  padding: 16px;
  background: var(--panel); border: 1px solid var(--panel-line); border-radius: 18px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  animation: rise 0.6s var(--ease) both;
}
.quests h2 { margin: 0 0 12px; font-size: 13px; font-weight: 700; display: flex; justify-content: space-between; gap: 8px; }
.quests-sp { color: var(--evolution); }
.quest-list { display: grid; gap: 7px; }
.quest {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 11px; text-align: left; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--ink);
  background: rgba(255, 255, 255, 0.06); border: 1px solid transparent; border-radius: 12px;
  transition: background 0.18s, border-color 0.18s, transform 0.18s var(--ease);
}
.quest:hover { background: rgba(255, 255, 255, 0.13); border-color: var(--panel-line); transform: translateX(2px); }
.quest[disabled] { opacity: 0.45; cursor: default; transform: none; }
.quest-emoji { font-size: 17px; }

/* --- Sheets --- */

.sheet-backdrop { position: fixed; inset: 0; z-index: 8; background: rgba(6, 6, 14, 0.6); backdrop-filter: blur(6px); animation: fade 0.3s both; }

.sheet:focus { outline: none; } /* focused as a container when it opens; its controls keep their rings */
.sheet {
  position: fixed; z-index: 9;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(430px, calc(100vw - 32px)); max-height: 82dvh; overflow: auto;
  background: rgba(22, 22, 36, 0.92);
  border: 1px solid var(--panel-line); border-radius: var(--radius);
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.55);
  animation: pop 0.35s var(--ease) both;
}
@keyframes pop { from { opacity: 0; transform: translate(-50%, -46%) scale(0.97); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } }

.sheet-head { display: flex; align-items: center; justify-content: space-between; padding: 18px 20px 10px; }
.sheet-head h2 { margin: 0; font-family: Fraunces, Georgia, serif; font-weight: 600; font-size: 20px; }
.sheet-body { padding: 8px 20px 22px; display: grid; gap: 16px; }

.field { display: grid; gap: 6px; font-size: 14px; font-weight: 700; }
.field small { font-weight: 400; font-size: 12px; color: var(--ink-dim); line-height: 1.45; }

input[type="text"], input[type="number"], input[type="email"], select {
  font: inherit; font-size: 15px; color: var(--ink);
  padding: 11px 13px; border-radius: 12px;
  background: rgba(255, 255, 255, 0.07); border: 1px solid var(--panel-line);
}
input:focus-visible, select:focus-visible, .tab:focus-visible, .ctl:focus-visible,
.quest:focus-visible, .icon-btn:focus-visible, .tag-picker:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
select option { background: #1a1a2b; }

.account-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.account-with { font-size: 14px; color: var(--ink-dim); }
.account-row .btn { padding: 9px 18px; font-size: 14px; flex: none; white-space: nowrap; }
.account-with { overflow-wrap: anywhere; }
.link-btn {
  justify-self: start; padding: 2px 0; font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
  color: var(--ink); background: none; border: 0; text-decoration: underline; text-underline-offset: 3px;
}
.link-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.account-hint { margin-top: -6px; font-size: 12px; color: var(--ink-dim); }
.account-add { display: flex; flex-wrap: wrap; gap: 6px 16px; }
/* The ways in, each with Remove (UI_SPECIFICATIONS.md 5.51). */
.account-methods { list-style: none; margin: -4px 0 0; padding: 0; display: grid; gap: 4px; }
.account-methods li { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 13px; color: var(--ink-dim); }
.account-methods span { overflow-wrap: anywhere; }
.account-methods .link-btn { flex: none; }
/* Delete my account (UI_SPECIFICATIONS.md 5.35): set apart, and the button only works once DELETE is typed. */
.link-danger { color: #ffb3b3; }
.delete-panel { display: grid; gap: 10px; padding: 12px; border-radius: 14px; background: rgba(255, 90, 90, 0.08); border: 1px solid rgba(255, 179, 179, 0.35); }
.delete-panel .link-btn { justify-self: start; }
.btn-danger { color: #1a0d0d; background: #ffb3b3; }
.btn-danger:disabled { opacity: 0.45; cursor: not-allowed; transform: none; filter: none; }
.delete-message { margin: 0; color: var(--ink); }
.account-discord { display: none; }
.join-choice { display: grid; gap: 8px; padding: 12px; border-radius: 14px; background: rgba(255, 255, 255, 0.05); border: 1px solid var(--panel-line); }
.join-lede { margin: 0 0 4px; font-size: 13px; line-height: 1.5; color: var(--ink); }
.join-option {
  display: grid; gap: 2px; text-align: left; padding: 10px 12px; border-radius: 12px; cursor: pointer;
  font: inherit; color: var(--ink); background: rgba(255, 255, 255, 0.07); border: 1px solid var(--panel-line);
}
.join-option:hover { background: rgba(255, 255, 255, 0.13); }
.join-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.join-option strong { font-size: 14px; }
.join-option span { font-size: 12px; color: var(--ink-dim); }
.join-choice .link-btn { justify-self: start; }

/* --- Sign-in by emailed code (gate and Settings) --- */
.gate-or { display: flex; align-items: center; gap: 12px; margin: 18px 0 14px; color: var(--ink-dim); font-size: 13px; }
.gate-or::before, .gate-or::after { content: ""; flex: 1; height: 1px; background: var(--panel-line); }
.email-form { display: grid; gap: 10px; }
.email-form input { width: 100%; text-align: center; }
.email-form #code-input, .email-form #link-code-input { font-size: 22px; letter-spacing: 0.3em; font-variant-numeric: tabular-nums; }
.email-form .link-btn { justify-self: center; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.sheet-actions { display: flex; gap: 10px; justify-content: flex-end; }
.tag-recent { display: flex; flex-wrap: wrap; gap: 7px; }
.tag-recent button {
  font: inherit; font-size: 13px; color: var(--ink); cursor: pointer;
  padding: 6px 12px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.08); border: 1px solid var(--panel-line);
}
.tag-recent button:hover { background: rgba(255, 255, 255, 0.16); }

.btn {
  font: inherit; font-size: 15px; font-weight: 700; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 12px 22px; border-radius: 999px; border: 0;
  color: var(--on-accent); background: var(--accent);
  transition: transform 0.18s var(--ease), filter 0.18s;
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.06); }
.btn-quiet { color: var(--ink); background: rgba(255, 255, 255, 0.1); border: 1px solid var(--panel-line); }
.btn-discord { width: 100%; background: #5865f2; color: #fff; text-decoration: none; }

/* --- Settings groups --- */

.field-group { display: grid; gap: 11px; padding-top: 4px; }
.field-group-title {
  font-size: 10.5px; font-weight: 700; color: var(--ink-dim);
  text-transform: uppercase; letter-spacing: 0.07em;
}

.toggle { display: flex; align-items: center; gap: 10px; font-size: 14px; cursor: pointer; }
.toggle input {
  appearance: none; -webkit-appearance: none; flex: none; margin: 0; cursor: pointer;
  width: 38px; height: 22px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.14); border: 1px solid var(--panel-line);
  transition: background 0.2s var(--ease);
}
.toggle input::after {
  content: ""; display: block; width: 16px; height: 16px; margin: 2px; border-radius: 50%;
  background: var(--ink); transition: transform 0.2s var(--ease);
}
.toggle input:checked { background: var(--accent); }
.toggle input:checked::after { transform: translateX(16px); background: var(--on-accent); }

.keys { display: grid; grid-template-columns: 1fr auto; gap: 7px 12px; margin: 0; align-items: center; }
.keys dt { font-size: 13px; color: var(--ink-dim); }
.keys dd { margin: 0; }
.keys.is-off { opacity: 0.45; } /* shortcuts turned off: the list stays, greyed (UI_SPECIFICATIONS.md 5.29) */
kbd {
  display: inline-block; min-width: 26px; padding: 3px 7px; text-align: center;
  font: inherit; font-size: 11px; font-weight: 700; color: var(--ink);
  background: rgba(255, 255, 255, 0.09); border: 1px solid var(--panel-line);
  border-bottom-width: 2px; border-radius: 6px;
}

/* --- Profile sheet --- */

.stat-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.stat { padding: 13px 15px; border-radius: 14px; background: rgba(255, 255, 255, 0.06); border: 1px solid var(--panel-line); }
.stat-value { font-family: Fraunces, Georgia, serif; font-size: 23px; font-weight: 600; font-variant-numeric: tabular-nums; }
.stat-label { font-size: 11px; color: var(--ink-dim); text-transform: uppercase; letter-spacing: 0.06em; margin-top: 3px; }
.tag-bars { display: grid; gap: 9px; }
.profile-grid { display: grid; gap: 16px; }
.profile-loading, .tag-bar-time { color: var(--ink-dim); }
.stat-label.tag-heading { margin-bottom: 9px; }
.tag-bar-row { display: grid; gap: 5px; font-size: 13px; }
.tag-bar-head { display: flex; justify-content: space-between; }
.tag-bar { height: 6px; border-radius: 99px; background: rgba(255, 255, 255, 0.14); overflow: hidden; }
.tag-bar span { display: block; height: 100%; border-radius: 99px; background: var(--accent); }
.profile-hero { display: flex; align-items: center; gap: 14px; }
.profile-hero img { width: 76px; height: 76px; border-radius: 18px; object-fit: cover; background: rgba(255, 255, 255, 0.07); }
.profile-hero em { display: block; font-size: 13px; color: var(--ink-dim); margin-top: 4px; line-height: 1.45; }

/* --- Nemudoro's story (UI_SPECIFICATIONS.md 5.26) ---
 * A storybook page: the app icon with sparkles on top, the paragraphs with a Fraunces drop cap, the
 * stage art beside the paragraphs that name it, and dotted wave dividers between the scenes. The
 * doodles are the symbols in index.html, tinted from the palette: gold stars and Zzz, pink hearts,
 * lavender clouds. Decoration only (aria-hidden); the story itself is plain text.
 */
.doodle-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.sheet.sheet-story { width: min(560px, calc(100vw - 32px)); }

/* --- Premium (5.36): the support code, the three Ko-fi tiers, the way to Ko-fi --- */
.icon-premium { color: #f5d77f; }
.sheet.sheet-premium { width: min(520px, calc(100vw - 32px)); }
.premium { gap: 14px; }
.premium p { margin: 0; font-size: 14px; line-height: 1.55; }
.premium .premium-note { font-size: 12.5px; color: var(--ink-dim); }
.premium-code { display: grid; gap: 8px; padding: 14px; border-radius: 16px; background: rgba(255, 255, 255, 0.06); border: 1px solid var(--panel-line); }
.premium-code-label { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-dim); }
.premium-code-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.premium-code-value {
  font: 700 20px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.06em;
  user-select: all; -webkit-user-select: all; color: #f5d77f;
}
.premium-code-row .btn { padding: 8px 14px; font-size: 13px; }
.premium-copied { font-size: 12.5px; color: var(--ink-dim); }
.premium-tiers { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.premium-tier { display: grid; gap: 4px; padding: 12px 14px; border-radius: 14px; background: rgba(255, 255, 255, 0.04); border: 1px solid var(--panel-line); }
.premium-tier-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.premium-tier-name { font-weight: 700; font-size: 15px; }
.premium-tier-price { font-weight: 700; font-size: 15px; white-space: nowrap; }
.premium-tier-price small { font-weight: 400; font-size: 12px; color: var(--ink-dim); }
.premium-tier-limit { font-size: 12px; color: #f5d77f; }
.premium-tier p { font-size: 13px; color: var(--ink-dim); }
.premium-tier.is-yours { border-color: #f5d77f; }
.premium .premium-held { padding: 12px 14px; border-radius: 14px; background: rgba(245, 215, 127, 0.12); border: 1px solid rgba(245, 215, 127, 0.45); }
.premium-cta { text-decoration: none; }
.premium-cta svg { width: 20px; height: 20px; }
.story { --doodle-gold: #f5d77f; --doodle-pink: #f4a3b4; --doodle-lilac: #cbbcf2; gap: 14px; }
.doodle { display: inline-block; width: 16px; height: 16px; flex: none; }
.doodle.gold { color: var(--doodle-gold); }
.doodle.pink { color: var(--doodle-pink); }
.doodle.lilac { color: var(--doodle-lilac); }
.story-hero { position: relative; display: grid; place-items: center; padding: 10px 0 6px; }
.story-hero img { width: 104px; height: 104px; border-radius: 26px; box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35); }
.story-hero .doodle { position: absolute; }
.story-hero .d1 { width: 18px; height: 18px; left: calc(50% - 92px); top: 12px; }
.story-hero .d2 { width: 12px; height: 12px; left: calc(50% + 70px); top: 6px; }
.story-hero .d3 { width: 40px; height: 24px; left: calc(50% + 72px); top: 70px; }
.story-hero .d4 { width: 26px; height: 20px; left: calc(50% - 104px); top: 74px; }
.story-hero .d5 { width: 12px; height: 12px; left: calc(50% - 64px); top: 104px; }
.story p { margin: 0; font-size: 14.5px; line-height: 1.7; color: var(--ink); }
.story p:first-of-type::first-letter {
  float: left; margin: 4px 8px 0 0;
  font-family: Fraunces, Georgia, serif; font-weight: 600; font-size: 44px; line-height: 0.9;
  color: var(--doodle-gold);
}
.story-art { display: flex; justify-content: center; align-items: flex-end; gap: 14px; flex-wrap: wrap; padding: 4px 0; }
.story-art figure { margin: 0; display: grid; justify-items: center; gap: 4px; }
.story-art img {
  width: 84px; height: 84px; object-fit: contain; padding: 6px; box-sizing: border-box;
  border-radius: 20px; background: rgba(255, 255, 255, 0.06); border: 1px dashed rgba(255, 255, 255, 0.16);
}
.story-art figcaption { font-size: 11px; color: var(--ink-dim); }
.story-divider { display: flex; align-items: center; justify-content: center; gap: 8px; color: var(--ink-dim); opacity: 0.9; }
.story-divider .wave { width: 96px; height: 12px; color: var(--doodle-lilac); }
.story-end { display: flex; justify-content: center; gap: 10px; padding: 2px 0 4px; }
.story-end .doodle.moon { width: 22px; height: 22px; }
.story-end .doodle.zzz { width: 30px; height: 22px; }
@media (prefers-reduced-motion: no-preference) {
  .story-hero .doodle.gold { animation: twinkle 3.2s ease-in-out infinite alternate; }
  .story-hero .d2 { animation-delay: 1.1s; }
  .story-hero .d5 { animation-delay: 2s; }
}
@keyframes twinkle { from { opacity: 0.45; transform: scale(0.85); } to { opacity: 1; transform: scale(1.1); } }

/* --- Celebration --- */

.celebrate { position: fixed; inset: 0; z-index: 12; display: grid; place-items: center; padding: 24px;
  background: radial-gradient(circle at 50% 45%, color-mix(in srgb, var(--evolution) 22%, transparent), rgba(6, 6, 14, 0.82));
  backdrop-filter: blur(8px); animation: fade 0.5s both; }
.celebrate-card {
  width: min(400px, 100%); padding: 30px 26px; text-align: center;
  background: rgba(24, 22, 34, 0.9); border: 1px solid color-mix(in srgb, var(--evolution) 40%, transparent);
  border-radius: var(--radius); box-shadow: 0 0 90px color-mix(in srgb, var(--evolution) 28%, transparent);
  animation: pop-center 0.6s var(--ease) both;
}
@keyframes pop-center { from { opacity: 0; transform: scale(0.9) translateY(14px); } to { opacity: 1; transform: none; } }
.celebrate-art { width: 168px; height: 168px; border-radius: 22px; object-fit: cover; margin-bottom: 16px;
  box-shadow: 0 16px 50px rgba(0, 0, 0, 0.5); }
.celebrate-card h2 { font-family: Fraunces, Georgia, serif; font-weight: 600; font-size: 24px; margin: 0 0 10px; color: var(--evolution); }
.celebrate-card p { font-size: 14px; line-height: 1.6; color: var(--ink-dim); margin: 0 0 22px; }

/* --- Toast --- */

.toast {
  position: fixed; left: 50%; bottom: 110px; transform: translateX(-50%); z-index: 14;
  max-width: min(440px, calc(100vw - 32px));
  padding: 13px 20px; text-align: center; font-size: 14px; line-height: 1.45;
  background: rgba(22, 22, 36, 0.94); border: 1px solid var(--panel-line); border-radius: 16px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
  animation: toast-in 0.45s var(--ease) both;
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 14px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* --- The system asks for more contrast or less transparency: panels go nearly opaque, secondary
   text goes full ink --- */
@media (prefers-contrast: more), (prefers-reduced-transparency: reduce) {
  :root { --panel: rgba(8, 8, 16, 0.9); --ink-dim: var(--ink); --panel-line: rgba(255, 255, 255, 0.35); }
  .whisper { text-shadow: none; }
}

/* Music but no sounds: the soundboard holds only the Music row, which is for narrow screens (5.32) */
@media (min-width: 721px) { #btn-sounds.narrow-only { display: none; } }

/* --- Narrow screens --- */

@media (max-width: 720px) {
  .player, .queue-pop { display: none; } /* the timer and companion come first on a small screen */
  .board-music { display: block; } /* ...so the music moves into the soundboard pop-up (5.32) */
  /* The fixed controls row below needs the whole width for a running timer's buttons, so the
     corner stands upright beside the companion card (and the quests card) instead. */
  /* The companion card and the controls are pinned over the bottom 176 px: keep the timer and the
     whisper out from under them (the whisper used to sit behind the card, unreadable). */
  .app { padding-bottom: 176px; }
  /* The upright corner (Premium, full screen, story, About, Settings) can reach the whisper's height on a
     short phone. The whisper keeps 60 px clear on both sides, the lane the companion card also
     leaves free for the corner, so it stays centred and never runs under the buttons. */
  .whisper { max-width: min(42ch, calc(100vw - 120px)); }
  .companion { left: 16px; right: 60px; bottom: 92px; }
  .quests { right: 60px; bottom: 92px; width: min(290px, calc(100vw - 76px)); }
  .corner { bottom: 92px; flex-direction: column; gap: 6px; }
  .info-pop { bottom: 176px; }
  .controls { position: fixed; left: 0; right: 0; bottom: max(16px, env(safe-area-inset-bottom)); }
  .hud-goal { display: none; }
}

/* A phone's HUD: two chips and four buttons must fit in 375 - 32px. The number carries the chip
   (the icon says what it counts), so the unit words go first, then the spacing tightens. */
@media (max-width: 480px) {
  .hud { gap: 8px; }
  .hud-group { gap: 6px; }
  .chip { padding: 0 11px; }
  /* Visually hidden, still read aloud: "1200 SP", not a bare "1200". */
  .chip { position: relative; }
  .chip-unit { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .icon-btn { width: 34px; height: 34px; }
  .timer-sub { padding: 3px 9px; font-size: 12px; white-space: nowrap; }
}

/* The smallest phones (320 px wide, and the 360 px Androids): the 480 px row above still needs
   298 px of a 288 px header, so the two groups touch and the buttons run off the edge. The spacing
   tightens one more notch rather than a chip or a button leaving — every readout and every control
   stays (UI_SPECIFICATIONS.md 5.17). Measured at 320x640, 360x740 and 400x800. */
@media (max-width: 360px) {
  .hud { gap: 6px; }
  .hud-group { gap: 4px; }
  .chip { padding: 0 8px; gap: 4px; font-size: 13px; }
  .icon-btn { width: 32px; height: 32px; }
}

/* The effects button is the HUD's second of six: below 500 px the row runs off the right edge, so
   there the effects are found in the Background sheet instead (UI_SPECIFICATIONS.md 5.15).
   Measured with the row forced visible when it still had six buttons: they needed 501 px, and under
   500 px the effects button left. Full screen has since moved to the bottom-right corner (5.16), so
   the row has a button to spare there; the breakpoint is kept as it is, on the safe side. */
@media (max-width: 499px) {
  .icon-btn#btn-fx, .fx-board { display: none; }
  /* The same for Companions (5.60): on a phone the Profile leads to the picker instead. */
  .icon-btn#btn-companions { display: none; }
}

/* Full screen: expand arrows, turning into collapse arrows once the page is full screen. */
#btn-fullscreen .i-exit,
#btn-fullscreen[aria-pressed="true"] .i-enter { display: none; }
#btn-fullscreen[aria-pressed="true"] .i-exit { display: inline; }
/* It sits in the bottom-right corner, before About (5.16), where there is room at every width, so
   unlike the HUD buttons it never has to leave for space. app.js still hides it wherever the
   Fullscreen API is refused (iPhone Safari, Discord's frame). */

/* --- Inside Discord (the Activity): Discord signs the user in, so there is nothing to sign in or
   out of here. The gate only shows if connecting failed, with its error line. --- */
body[data-surface="discord"] .btn-discord,
body[data-surface="discord"] #account { display: none; }
/* Downloading and deleting your data are the website's (UI_SPECIFICATIONS.md 5.35): say where. */
body[data-surface="discord"] .account-discord { display: block; }

/* A short window: Discord's picture-in-picture (a small 16:9 tile over a call), or a phone on its
   side. Only the timer and its controls stay, and the ring is sized by height, not width. */
@media (max-height: 480px) {
  .app { padding-top: 8px; padding-bottom: 8px; }
  .tabs, .tag-picker, .whisper, .companion, .quests, .player, .hud-goal, .room-companion-spot { display: none !important; }
  .stage-area { gap: 10px; }
  .timer-card { gap: 8px; }
  .timer-face { width: min(54vh, 74vw); }
  .clock { font-size: clamp(30px, 15vh, 72px); }
  .timer-sub { white-space: nowrap; }
  .controls { position: static; }
  .corner { bottom: max(16px, env(safe-area-inset-bottom)); flex-direction: row; }
  .info-pop { bottom: calc(max(16px, env(safe-area-inset-bottom)) + 48px); }
}
/* The smallest tiles: the clock and its buttons, nothing else. */
@media (max-height: 300px) {
  .hud, .timer-sub-row, .corner, .info-pop { display: none; }
  .timer-face { width: min(62vh, 74vw); }
}
