/* ═══════════════════════════════════════════════════════════
   The Long Watch — HUD: topbar, dock, shop, abilities, inspector
   ═══════════════════════════════════════════════════════════ */

/* ── Sound, on every screen ────────────────────────────────
   Fixed rather than in the topbar, because the topbar only exists during a
   night and the music starts on the title. z-index clears the screens (20) and
   the Keep's drawer; insets keep it off a notch in landscape, where the right
   inset is 59px on a phone on its side. */
#btn-mute {
  position: fixed;
  top: calc(10px + var(--sat));
  right: calc(10px + var(--sar));
  z-index: 60;
}
/* Muted reads as muted at a glance, rather than only in the tooltip. */
#btn-mute.off { opacity: .45; }
#btn-mute.off::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 20px; height: 1.5px; background: currentColor;
  transform: translate(-50%, -50%) rotate(-45deg);
}

/* ── Top bar ───────────────────────────────────────────── */

#topbar {
  grid-area: top;
  min-width: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 14px;
  height: var(--topbar-h);
  padding: 0 14px;
  padding-top: var(--sat);
  height: calc(var(--topbar-h) + var(--sat));
  background: linear-gradient(180deg, rgba(10,13,23,.96), rgba(10,13,23,.72));
  border-bottom: 1px solid var(--panel-edge);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.brand { display: flex; align-items: center; gap: 9px; min-width: 0; }
.brand-mark { width: 20px; height: 20px; fill: var(--gold); filter: drop-shadow(0 0 8px rgba(255,180,80,.6)); flex: none; }
.brand-text {
  font-family: var(--font-display); font-size: .95rem; letter-spacing: .06em;
  color: var(--ink); white-space: nowrap;
}

.stats { display: flex; gap: 8px; margin-left: auto; }

.stat {
  display: flex; align-items: baseline; gap: 6px;
  padding: 5px 11px;
  border-radius: 999px;
  background: rgba(8,11,20,.75);
  border: 1px solid var(--panel-edge);
  transition: box-shadow .25s, border-color .25s, transform .18s;
}
.stat-ico { font-size: .8rem; opacity: .85; }
.stat-val { font-family: var(--font-display); font-size: 1rem; font-variant-numeric: tabular-nums; line-height: 1; }
.stat-sub { font-size: .68em; opacity: 0.72; }
.stat-lbl { font-size: .6rem; text-transform: uppercase; letter-spacing: .13em; color: var(--ink-mute); }

#stat-lives .stat-ico, #stat-lives .stat-val { color: var(--danger); }
#stat-gold  .stat-ico, #stat-gold  .stat-val { color: var(--gold); }
#stat-wave  .stat-ico, #stat-wave  .stat-val { color: var(--frost); }

.stat.bump { animation: statBump .42s cubic-bezier(.34,1.7,.5,1); }
@keyframes statBump { 0% { transform: none } 32% { transform: scale(1.16) } 100% { transform: none } }

.stat.alarm {
  border-color: rgba(255,95,116,.6);
  box-shadow: 0 0 20px rgba(255,95,116,.35), inset 0 0 14px rgba(255,95,116,.14);
  animation: alarmPulse 1s ease-in-out infinite;
}
@keyframes alarmPulse { 50% { box-shadow: 0 0 30px rgba(255,95,116,.55), inset 0 0 20px rgba(255,95,116,.25) } }

.topbar-actions { display: flex; gap: 6px; }

.icon-btn {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 9px;
  background: rgba(255,255,255,.05);
  border: 1px solid var(--panel-edge);
  color: var(--ink-dim);
  font-size: .95rem;
  transition: background .18s, color .18s, border-color .18s;
}
.icon-btn:hover { background: rgba(255,255,255,.11); color: var(--ink); border-color: var(--panel-edge-hi); }
.icon-btn.off { color: var(--ink-mute); opacity: 0.72; }
.icon-btn.ghost { background: none; border: none; font-size: 1.5rem; }

/* ── Board overlay bits ────────────────────────────────── */

#wave-banner {
  position: absolute;
  top: 16%; left: 50%;
  transform: translateX(-50%);
  padding: 9px 26px;
  border-radius: 999px;
  background: rgba(8,11,20,.82);
  border: 1px solid rgba(255,199,102,.4);
  box-shadow: 0 0 34px rgba(255,170,70,.28);
  font-family: var(--font-display);
  font-size: clamp(.85rem, 2.6vw, 1.15rem);
  letter-spacing: .1em;
  color: var(--gold);
  white-space: nowrap;
  animation: bannerIn 2.9s cubic-bezier(.16,1,.3,1) both;
}
#wave-banner.boss { border-color: rgba(255,95,116,.6); color: #ffb9c3; box-shadow: 0 0 44px rgba(255,60,90,.45); }
@keyframes bannerIn {
  0%   { opacity: 0; transform: translate(-50%, -14px) scale(.9) }
  12%  { opacity: 1; transform: translate(-50%, 0) scale(1) }
  78%  { opacity: 1; transform: translate(-50%, 0) scale(1) }
  100% { opacity: 0; transform: translate(-50%, -10px) scale(.98) }
}

/* ── First Watch ───────────────────────────────────────────
   One line at a time, pinned low so it never sits over the
   board, and a Skip that is always reachable. The caption is
   support for what is happening on the ground, not a substitute
   for it — so it stays short and it never blocks a tap. ── */

#tutorial { position: absolute; inset: 0; pointer-events: none; z-index: 6; }

/* One voice at a time: the caption sits exactly where these live. */
#app.tutoring #zoom-hint,
#app.tutoring #dawn-track { display: none; }

#tut-caption {
  position: absolute;
  left: 50%; bottom: 26px;
  transform: translateX(-50%);
  max-width: min(30rem, calc(100% - 32px));
  padding: 9px 18px;
  border-radius: 12px;
  background: rgba(8,11,20,.9);
  border: 1px solid rgba(255,199,102,.34);
  box-shadow: 0 6px 30px rgba(0,0,0,.6), 0 0 26px rgba(255,170,70,.14);
  font-size: clamp(.76rem, 2.5vw, .95rem);
  line-height: 1.4;
  color: #f0e6d2;
  text-align: center;
  text-wrap: balance;
  animation: tutIn .45s cubic-bezier(.16,1,.3,1) both;
}
#tut-caption.urgent {
  border-color: rgba(255,120,90,.6);
  box-shadow: 0 6px 30px rgba(0,0,0,.6), 0 0 30px rgba(255,110,60,.3);
  color: #ffd9c4;
  animation: tutIn .45s cubic-bezier(.16,1,.3,1) both, tutPulse 1.5s ease-in-out .45s infinite;
}
@keyframes tutIn {
  from { opacity: 0; transform: translate(-50%, 10px) }
  to   { opacity: 1; transform: translate(-50%, 0) }
}
@keyframes tutPulse {
  0%, 100% { box-shadow: 0 6px 30px rgba(0,0,0,.6), 0 0 22px rgba(255,110,60,.24) }
  50%      { box-shadow: 0 6px 30px rgba(0,0,0,.6), 0 0 40px rgba(255,110,60,.5) }
}

/* Top LEFT: the pause/speed/zoom cluster owns the top right. */
#btn-tut-skip {
  position: absolute;
  top: 10px; left: 10px;
  pointer-events: auto;
  height: 34px;
  padding: 0 14px;
  border-radius: 9px;
  background: rgba(8,11,20,.72);
  border: 1px solid var(--panel-edge);
  font-size: .72rem;
}

#dawn-track {
  position: absolute;
  left: 10px; right: 10px; bottom: 8px;
  height: 3px;
  border-radius: 2px;
  background: rgba(255,255,255,.07);
  overflow: visible;
}
#dawn-fill {
  height: 100%; width: 0%;
  border-radius: 2px;
  background: linear-gradient(90deg, #3a5aa8, var(--gold));
  box-shadow: 0 0 12px rgba(255,180,80,.55);
  transition: width .7s cubic-bezier(.16,1,.3,1);
}
#dawn-star {
  position: absolute; top: 50%; left: 0;
  transform: translate(-50%, -50%);
  font-size: .8rem; color: var(--gold);
  text-shadow: 0 0 10px rgba(255,180,80,.9);
  transition: left .7s cubic-bezier(.16,1,.3,1);
}

#zoom-hint {
  position: absolute;
  left: 50%; bottom: 20px;
  transform: translateX(-50%);
  padding: 5px 13px;
  border-radius: 999px;
  background: rgba(8,11,20,.8);
  border: 1px solid var(--panel-edge);
  font-size: .66rem;
  color: var(--ink-mute);
  white-space: nowrap;
  pointer-events: none;
  transition: opacity .6s ease;
  animation: fadeIn .8s ease .6s both;
}

#float-controls {
  position: absolute; top: 10px; right: 10px;
  display: flex; gap: 7px;
}
.fc-btn {
  min-width: 38px; height: 34px;
  padding: 0 10px;
  display: grid; place-items: center;
  border-radius: 9px;
  background: rgba(8,11,20,.82);
  border: 1px solid var(--panel-edge);
  color: var(--ink-dim);
  font-size: .78rem; font-weight: 600;
  backdrop-filter: blur(6px);
  transition: background .18s, color .18s, border-color .18s;
}
.fc-btn:hover { background: rgba(20,26,44,.92); color: var(--ink); }
.fc-btn.on { color: var(--gold); border-color: rgba(255,199,102,.5); }

#dock-toggle { display: none; }

/* ── Dock ──────────────────────────────────────────────── */

#dock {
  grid-area: dock;
  z-index: 15;
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  background: linear-gradient(180deg, rgba(10,13,23,.90), rgba(8,10,18,.97));
  border-left: 1px solid var(--panel-edge);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

#dock-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
#dock-scroll::-webkit-scrollbar { width: 6px; }
#dock-scroll::-webkit-scrollbar-thumb { background: rgba(140,165,220,.22); border-radius: 3px; }

.panel-title {
  font-size: .66rem;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--ink-mute);
  margin-bottom: 9px;
  display: flex; align-items: baseline; gap: 8px;
}
.panel-title .hint {
  font-family: var(--font-ui); font-size: .62rem; letter-spacing: .02em;
  text-transform: none; color: rgba(120,135,165,.75); font-weight: 400;
  margin-left: auto;
}

/* ── Shop ──────────────────────────────────────────────── */

#shop { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }

.tcard {
  position: relative;
  display: flex; align-items: center; gap: 9px;
  padding: 8px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.72);
  border: 1px solid var(--panel-edge);
  text-align: left;
  overflow: hidden;
  transition: border-color .16s, background .16s, transform .16s;
}
.tcard::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(70% 100% at 0% 50%, var(--tint, transparent) 0%, transparent 70%);
  opacity: .16; pointer-events: none;
}
.tcard:hover:not(.locked) { border-color: var(--panel-edge-hi); transform: translateY(-2px); }
.tcard.on {
  border-color: var(--tint, var(--gold));
  background: rgba(28,36,58,.85);
  box-shadow: 0 0 0 1px var(--tint, var(--gold)), 0 0 22px -6px var(--tint, var(--gold));
}
.tcard.poor { opacity: .42; }
.tcard.poor .tcard-cost { color: var(--danger); }

.tcard-icon { width: 40px; height: 40px; flex: none; border-radius: 8px; }
.tcard-body { min-width: 0; flex: 1; }
.tcard-name {
  font-family: var(--font-display); font-size: .78rem; line-height: 1.15;
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tcard-cost { font-size: .72rem; color: var(--gold); font-variant-numeric: tabular-nums; margin-top: 2px; }
.tcard-key {
  position: absolute; top: 4px; right: 5px;
  font-size: .56rem; color: var(--ink-mute);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 4px; padding: 0 3px; line-height: 1.35;
}

/* ── Abilities ─────────────────────────────────────────── */

#abilities { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }

.ability {
  position: relative;
  padding: 9px 10px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.72);
  border: 1px solid var(--panel-edge);
  text-align: left;
  overflow: hidden;
  transition: border-color .16s, transform .16s;
}
.ability:hover:not(.cooling) { border-color: var(--panel-edge-hi); transform: translateY(-2px); }
.ability.ready { border-color: rgba(255,199,102,.45); }
.ability.armed { border-color: var(--gold); box-shadow: 0 0 20px -4px var(--gold); }
.ability.cooling { opacity: .55; cursor: not-allowed; }

.ability-fill {
  position: absolute; left: 0; bottom: 0; height: 100%; width: 0%;
  background: linear-gradient(90deg, rgba(255,199,102,.20), rgba(255,199,102,.07));
  pointer-events: none;
  transition: width .12s linear;
}
.ability-name {
  position: relative;
  font-family: var(--font-display); font-size: .76rem; color: var(--ink);
  display: flex; align-items: center; gap: 5px;
}
.ability-ico { display: flex; flex: none; }
.ability-ico svg { width: 14px; height: 14px; color: var(--gold); }
.ability-desc { position: relative; font-size: .63rem; color: var(--ink-mute); line-height: 1.4; margin-top: 3px; }
.ability-cd {
  position: relative; font-size: .66rem; color: var(--gold);
  font-variant-numeric: tabular-nums; margin-top: 3px;
}

/* ── Wave panel ────────────────────────────────────────── */

#wave-panel {
  flex: none;
  padding: 11px 12px calc(12px + var(--sab));
  border-top: 1px solid var(--panel-edge);
  background: rgba(8,11,19,.9);
}

.wave-line {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: .72rem; color: var(--ink-dim); margin-bottom: 6px;
}
#wave-label { font-family: var(--font-display); letter-spacing: .05em; color: var(--ink); }
#wave-timer { font-variant-numeric: tabular-nums; color: var(--ink-mute); font-size: .68rem; }

#wave-comp { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 9px; min-height: 20px; }
.comp-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 7px 2px 5px;
  border-radius: 999px;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.09);
  font-size: .63rem; color: var(--ink-dim);
}
.comp-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; box-shadow: 0 0 6px currentColor; }
.comp-chip.boss { border-color: rgba(255,95,116,.5); color: #ffb9c3; }

.primary-btn {
  width: 100%;
  padding: 11px 16px;
  border-radius: var(--radius-s);
  background: linear-gradient(180deg, rgba(255,199,102,.95), rgba(200,132,44,.95));
  color: #251603;
  font-weight: 700;
  font-size: .88rem;
  letter-spacing: .02em;
  box-shadow: 0 6px 18px -6px rgba(255,170,70,.6), inset 0 1px 0 rgba(255,255,255,.4);
  transition: transform .14s, box-shadow .18s, filter .18s;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
}
.primary-btn:hover { transform: translateY(-2px); box-shadow: 0 10px 26px -6px rgba(255,170,70,.75), inset 0 1px 0 rgba(255,255,255,.5); }
.primary-btn:active { transform: translateY(0); }
.primary-btn.big { padding: 14px 30px; font-size: 1rem; width: auto; min-width: 220px; margin: 0 auto; }
.primary-btn:disabled { filter: grayscale(.75) brightness(.6); cursor: default; transform: none; box-shadow: none; }
.primary-btn.running {
  background: linear-gradient(180deg, rgba(60,74,110,.9), rgba(36,46,72,.9));
  color: var(--ink-dim);
  box-shadow: none;
}
.btn-sub { font-size: .64rem; font-weight: 500; opacity: .8; letter-spacing: .04em; }
.btn-sub:empty { display: none; }

.secondary-btn {
  padding: 11px 22px;
  border-radius: var(--radius-s);
  background: rgba(255,255,255,.06);
  border: 1px solid var(--panel-edge-hi);
  color: var(--ink);
  font-weight: 600; font-size: .85rem;
  transition: background .16s, transform .14s;
}
.secondary-btn:hover { background: rgba(255,255,255,.12); transform: translateY(-2px); }

/* ── Inspector ─────────────────────────────────────────── */

/*
 * The inspector sits INSIDE the stage, so the board bounds it and it can never
 * cover the dock. Selecting a tower used to bury the palette and the wave
 * button under this sheet — reported from play as "keep the bottom bar visible
 * so levels can be continued". Anchoring it to a JS-measured dock height was
 * the first attempt and it raced: the dock grows when a wave starts, and a
 * synchronous read beat the resize observer.
 */
#inspector {
  position: absolute;
  z-index: 40;
  width: 292px;
  padding: 13px;
  border-radius: var(--radius);
  background: rgba(11,15,26,.96);
  border: 1px solid var(--panel-edge-hi);
  box-shadow: var(--shadow-lift);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  animation: riseIn .22s cubic-bezier(.16,1,.3,1) both;
}

.insp-head { display: flex; align-items: center; gap: 10px; margin-bottom: 11px; }
.insp-icon { width: 44px; height: 44px; flex: none; border-radius: 9px; background: rgba(0,0,0,.35); }
.insp-title { flex: 1; min-width: 0; }
.insp-title h3 { font-size: .95rem; color: var(--ink); line-height: 1.15; }
.insp-level { font-size: .66rem; color: var(--gold); letter-spacing: .1em; text-transform: uppercase; margin-top: 2px; }

.insp-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 5px 10px; margin-bottom: 10px; }
.istat { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; font-size: .71rem; }
.istat-k { color: var(--ink-mute); }
.istat-v { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 600; }
.istat-up { color: var(--leaf); font-size: .64rem; margin-left: 3px; }

.insp-perf {
  display: flex; gap: 12px;
  padding: 7px 9px; margin-bottom: 10px;
  border-radius: 8px;
  background: rgba(255,255,255,.04);
  font-size: .66rem; color: var(--ink-mute);
}
.insp-perf b { color: var(--ink-dim); font-variant-numeric: tabular-nums; }

#insp-actions { display: flex; flex-direction: column; gap: 6px; }

.act-btn {
  width: 100%;
  padding: 9px 11px;
  border-radius: 9px;
  border: 1px solid var(--panel-edge);
  background: rgba(255,255,255,.05);
  text-align: left;
  display: flex; align-items: center; gap: 8px;
  font-size: .76rem;
  transition: background .15s, border-color .15s, transform .14s;
}
.act-btn:hover:not(:disabled) { background: rgba(255,255,255,.11); border-color: var(--panel-edge-hi); transform: translateX(2px); }
.act-btn:disabled { opacity: .4; cursor: default; }
.act-btn .act-cost { margin-left: auto; color: var(--gold); font-variant-numeric: tabular-nums; font-weight: 600; }
.act-btn:disabled .act-cost { color: var(--danger); }
.act-btn.upgrade { border-color: rgba(143,214,122,.35); }
.act-btn.ascend {
  border-color: rgba(179,137,255,.45);
  background: linear-gradient(180deg, rgba(179,137,255,.14), rgba(179,137,255,.04));
}
.act-btn.sell { border-color: rgba(255,95,116,.28); }
.act-title { font-weight: 600; color: var(--ink); }
.act-sub { display: block; font-size: .64rem; color: var(--ink-mute); font-weight: 400; margin-top: 1px; line-height: 1.35; }
.act-maxed { text-align: center; font-size: .7rem; color: var(--gold); padding: 8px; letter-spacing: .08em; }

/* ── Toasts ────────────────────────────────────────────── */

#toasts {
  position: absolute;
  left: 50%; bottom: 84px;
  transform: translateX(-50%);
  z-index: 55;
  display: flex; flex-direction: column-reverse; align-items: center; gap: 6px;
  pointer-events: none;
}
.toast {
  padding: 7px 15px;
  border-radius: 999px;
  background: rgba(10,14,24,.94);
  border: 1px solid var(--panel-edge-hi);
  font-size: .76rem;
  color: var(--ink);
  white-space: nowrap;
  box-shadow: var(--shadow-lift);
  animation: toastIn .28s cubic-bezier(.16,1,.3,1) both;
}
.toast.bad  { border-color: rgba(255,95,116,.55); color: #ffc2cb; }
.toast.good { border-color: rgba(143,214,122,.5);  color: #cdf3bf; }
.toast.out  { animation: toastOut .3s ease forwards; }
@keyframes toastIn  { from { opacity: 0; transform: translateY(12px) scale(.94) } to { opacity: 1; transform: none } }
@keyframes toastOut { to { opacity: 0; transform: translateY(-8px) scale(.97) } }

/* ── v2 additions ──────────────────────────────────────── */
#stat-oil .stat-ico, #stat-oil .stat-val { color: #ffb347; }
#wave-timer.urgent { color: var(--danger); font-weight: 700; }
.tcard.lantern { --edge: rgba(255,190,90,.35); }
.tcard.lantern::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--tint); opacity: .8;
}
.tcard.tower::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--tint); opacity: .35;
}
.tcard.locked { opacity: .3; filter: grayscale(.8); pointer-events: none; }
.pick-head {
  font-size: .64rem; text-transform: uppercase; letter-spacing: .18em;
  color: var(--ink-mute); margin: 0 0 .5rem; text-align: left;
}
.map-select { grid-template-columns: repeat(3, minmax(0,1fr)); }
@media (max-aspect-ratio: 1/1) { .map-select { grid-template-columns: repeat(2, minmax(0,1fr)); } }

/* ══ THE KEEP ══════════════════════════════════════════════
   The between-nights layer. It has to visibly grow: stars
   filling in, grounds opening, the Tree lighting up. ══ */

.screen-inner.keep { max-width: 54rem; width: 100%; }

.purses {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin: 4px 0 14px;
}
.purse {
  padding: 8px 6px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.6);
  border: 1px solid var(--panel-edge);
  text-align: center;
}
.purse-val { display: block; font-family: var(--font-display); font-size: 1.15rem; color: var(--gold); }
.purse-sub { font-size: .7em; opacity: 0.72; }
.purse-lbl { display: block; font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; opacity: 0.72; }

.keep-tabs { display: flex; gap: 6px; margin-bottom: 12px; justify-content: center; flex-wrap: wrap; }
.tab-btn {
  padding: 6px 15px;
  border-radius: 999px;
  background: rgba(16,21,36,.6);
  border: 1px solid var(--panel-edge);
  font-size: .76rem;
  color: inherit;
}
.tab-btn.on { border-color: rgba(255,199,102,.55); color: var(--gold); background: rgba(255,180,80,.08); }
.tab-btn:disabled { opacity: .32; }

#keep-body { overflow-y: auto; overscroll-behavior: contain; }
.keep-head {
  font-family: var(--font-display); font-size: .8rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-dim); margin: 10px 0 8px; text-align: left;
}
/* .keep-actions margin lived here; the Keep's bottom band spaces itself now. */

.grounds { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); gap: 8px; }
.ground {
  text-align: left;
  padding: 10px 12px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.66);
  border: 1px solid var(--panel-edge);
  color: inherit;
  transition: border-color .16s, background .16s;
}
.ground.on { border-color: rgba(255,199,102,.6); background: rgba(255,180,80,.09); }
.ground.locked { opacity: .42; }
.ground-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.ground-name { font-family: var(--font-display); font-size: .95rem; color: #f0e6d2; }
.ground-stars { letter-spacing: .08em; font-size: .85rem; color: rgba(255,255,255,.58); white-space: nowrap; }
.gstar.on { color: var(--gold); text-shadow: 0 0 8px rgba(255,180,80,.7); }
.ground-note { font-size: .72rem; color: var(--ink-dim); margin-top: 3px; line-height: 1.35; }
.ground-meta { font-size: .64rem; color: var(--ink-mute); margin-top: 4px; letter-spacing: .05em; }
.gprize { font-size: .66rem; margin-top: 5px; color: #9fd8ff; opacity: .85; }

.tree { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); gap: 12px; }
.branch-head { font-family: var(--font-display); font-size: .95rem; color: color-mix(in srgb, var(--tint) 62%, #fff); margin-bottom: 2px; }
.branch-tag { font-size: .62rem; opacity: 0.72; letter-spacing: .1em; text-transform: uppercase; }
.branch-blurb { font-size: .68rem; color: var(--ink-dim); margin-bottom: 8px; line-height: 1.35; }
.tnode {
  display: block; width: 100%; text-align: left;
  padding: 8px 10px; margin-bottom: 6px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.66);
  border: 1px solid var(--panel-edge);
  color: inherit;
}
.tnode.buyable { border-color: color-mix(in srgb, var(--tint) 55%, transparent); }
.tnode.owned { background: color-mix(in srgb, var(--tint) 10%, rgba(16,21,36,.66)); border-color: color-mix(in srgb, var(--tint) 45%, transparent); }
.tnode:disabled:not(.owned) { opacity: .45; }
.tnode-top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.tnode-name { font-weight: 600; font-size: .82rem; }
.tnode-cost { font-size: .7rem; color: var(--gold); white-space: nowrap; }
.tnode-cur { opacity: 0.72; font-size: .85em; }
.tnode-desc { font-size: .68rem; color: var(--ink-dim); margin-top: 3px; line-height: 1.35; }
.tnode-why { font-size: .64rem; margin-top: 4px; color: #ffb9c1; }

.ledger-row { padding: 8px 0; border-bottom: 1px solid var(--panel-edge); text-align: left; }
.ledger-name { font-family: var(--font-display); font-size: .85rem; }
.ledger-sub { font-size: .64rem; opacity: 0.72; margin-left: 8px; font-family: var(--font-body); }
.ledger-goals { list-style: none; margin: 4px 0 0; padding: 0; }
.ledger-goals li { font-size: .68rem; color: var(--ink-mute); line-height: 1.6; }
.ledger-goals li.on { opacity: .9; color: var(--gold); }

/* ── Dawn ─────────────────────────────────────────────── */

.dawn-pay { display: flex; align-items: baseline; justify-content: center; gap: 6px; margin: 6px 0 12px; flex-wrap: wrap; }
.dawn-embers, .dawn-cinders { font-family: var(--font-display); font-size: 1.6rem; color: var(--gold); }
.dawn-cinders { color: #9fd8ff; margin-left: 10px; }
.dawn-lbl { font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; opacity: 0.72; }

.dawn-prize {
  padding: 10px 14px; margin-bottom: 12px;
  border-radius: var(--radius-s);
  background: rgba(255,180,80,.08);
  border: 1px solid rgba(255,199,102,.4);
}
/* Only the relic prize has a painting; the others (gold, stars, a Warden
   level) are numbers. It only becomes a row when there is something to put
   in the first column — an unpainted relic falls back to the plain block
   above, unchanged. */
.relic-prize { display: flex; align-items: center; gap: 12px; text-align: left; }
.dawn-prize-body { flex: 1; min-width: 0; }
.dawn-prize-art {
  width: 64px; height: 64px; flex: none;
  border-radius: var(--radius-s);
  object-fit: cover;
  border: 1px solid rgba(255,199,102,.3);
  box-shadow: 0 4px 18px rgba(0,0,0,.6);
}
@media (max-height: 430px) { .dawn-prize-art { width: 44px; height: 44px; } }
.dawn-prize-head { font-size: .62rem; letter-spacing: .16em; text-transform: uppercase; opacity: 0.72; }
.dawn-prize-name { font-family: var(--font-display); font-size: 1.05rem; color: var(--gold); margin: 2px 0; }
.dawn-prize-note { font-size: .7rem; opacity: 0.72; line-height: 1.4; }

.dawn-stars { display: grid; gap: 4px; margin-bottom: 12px; text-align: left; }
.dawn-star { display: flex; align-items: center; gap: 8px; font-size: .74rem; opacity: 0.72; }
.dawn-star.on { opacity: .95; }
.dawn-star-mark { color: rgba(255,255,255,.3); font-size: .95rem; }
.dawn-star.on .dawn-star-mark { color: var(--gold); text-shadow: 0 0 8px rgba(255,180,80,.7); }
.dawn-star.new { animation: starPop .7s cubic-bezier(.16,1,.3,1) both; }
.dawn-star-new {
  font-size: .56rem; letter-spacing: .14em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 999px;
  background: rgba(255,180,80,.18); color: var(--gold);
}
@keyframes starPop {
  0%   { opacity: 0; transform: translateX(-8px) }
  60%  { opacity: 1; transform: translateX(2px) }
  100% { opacity: .95; transform: none }
}

/* ── Threat ────────────────────────────────────────────────
   Stated plainly on the way in. The night growing as you do
   IS the progression; hiding it reads as the game cheating. ── */

.threat {
  padding: 9px 12px;
  margin-bottom: 12px;
  border-radius: var(--radius-s);
  background: rgba(30,14,20,.5);
  border: 1px solid rgba(255,95,116,.24);
  text-align: left;
}
.threat-top { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.threat-name { font-size: .8rem; }
.threat-name b { color: #ff9aa6; font-family: var(--font-display); letter-spacing: .04em; }
.threat-nums { font-size: .66rem; opacity: 0.72; white-space: nowrap; }
.threat-bar {
  height: 3px; margin: 6px 0 5px;
  border-radius: 2px;
  background: rgba(255,255,255,.07);
  overflow: hidden;
}
.threat-fill {
  height: 100%;
  background: linear-gradient(90deg, #7a4a5a, #ff5f74);
  box-shadow: 0 0 10px rgba(255,95,116,.5);
  transition: width .7s cubic-bezier(.16,1,.3,1);
}
.threat-note { font-size: .62rem; color: var(--ink-mute); line-height: 1.4; }

/* ── The Warden's button ───────────────────────────────────
   Bottom-left of the board: reachable with a thumb, and clear
   of the pause/speed cluster and the inspector sheet. ── */

#btn-warden {
  position: absolute;
  left: 10px; bottom: 10px;
  z-index: 8;
  min-width: 78px;
  padding: 7px 13px;
  border-radius: 11px;
  display: grid;
  gap: 1px;
  background: rgba(8,11,20,.86);
  border: 1px solid var(--panel-edge);
  color: var(--ink-dim);
  text-align: left;
  backdrop-filter: blur(6px);
  transition: border-color .2s, color .2s, box-shadow .2s;
}
#warden-name { font-family: var(--font-display); font-size: .76rem; letter-spacing: .04em; }
#warden-cd { font-size: .58rem; letter-spacing: .12em; text-transform: uppercase; opacity: 0.72; }
#btn-warden.ready {
  color: var(--tint, var(--gold));
  border-color: color-mix(in srgb, var(--tint, #ffc766) 55%, transparent);
  box-shadow: 0 0 22px color-mix(in srgb, var(--tint, #ffc766) 26%, transparent);
  animation: wardenReady 2.2s ease-in-out infinite;
}
@keyframes wardenReady {
  50% { box-shadow: 0 0 34px color-mix(in srgb, var(--tint, #ffc766) 45%, transparent) }
}

.warden { text-align: left; }
.warden-card {
  display: block; width: 100%; text-align: left;
  padding: 10px 12px; margin-bottom: 8px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.66);
  border: 1px solid var(--panel-edge);
  color: inherit;
}
.warden-card.on { border-color: color-mix(in srgb, var(--tint) 60%, transparent); background: color-mix(in srgb, var(--tint) 9%, rgba(16,21,36,.66)); }
.warden-card.locked { opacity: .68; }
.warden-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.warden-nm { font-family: var(--font-display); font-size: .92rem; color: color-mix(in srgb, var(--tint) 60%, #fff); }
.warden-lvl { font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; opacity: 0.72; }
.warden-fantasy { font-size: .68rem; opacity: 0.88; font-style: italic; margin: 2px 0 5px; }
.warden-line { font-size: .7rem; opacity: .94; line-height: 1.4; margin-bottom: 2px; }
.warden-line b { color: color-mix(in srgb, var(--tint) 55%, #fff); font-weight: 600; }
.warden-xp { height: 3px; margin-top: 6px; border-radius: 2px; background: rgba(255,255,255,.07); overflow: hidden; }
.warden-xp-fill { height: 100%; background: var(--tint); box-shadow: 0 0 8px color-mix(in srgb, var(--tint) 60%, transparent); }

/* ── Talents ───────────────────────────────────────────────
   The unspent choice is a PANEL above the roster, not a badge
   on a card that already does something else when pressed.
   Levelling a Warden exists for this moment; burying it inside
   a button whose job is "take this one tonight" would throw
   the moment away and mis-fire the tap half the time. ── */

.warden-talents { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.warden-talents span {
  font-size: .62rem; padding: 2px 7px; border-radius: 999px; line-height: 1.5;
  border: 1px solid rgba(255,255,255,.10);
}
.talent-has  { color: color-mix(in srgb, var(--tint) 62%, #fff);
               background: color-mix(in srgb, var(--tint) 14%, transparent);
               border-color: color-mix(in srgb, var(--tint) 40%, transparent) !important; }
.talent-owed { color: #ffd98f; background: rgba(255,201,102,.14);
               border-color: rgba(255,201,102,.45) !important; }
.talent-soon { color: #9aa6c4; opacity: .72; }

.talent-pick {
  margin: 0 0 12px; padding: 11px 12px 12px; border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--tint) 42%, transparent);
  background: color-mix(in srgb, var(--tint) 8%, rgba(16,21,36,.72));
}
.talent-head {
  font-size: .78rem; font-weight: 600; margin-bottom: 9px;
  color: color-mix(in srgb, var(--tint) 55%, #fff);
}
.talent-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); gap: 8px; }
.talent-opt {
  text-align: left; padding: 9px 11px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.13); background: rgba(10,14,26,.6);
  color: inherit; font: inherit;
}
.talent-opt:hover, .talent-opt:focus-visible {
  border-color: color-mix(in srgb, var(--tint) 65%, transparent);
  background: color-mix(in srgb, var(--tint) 12%, rgba(10,14,26,.6));
}
.talent-nm { font-size: .78rem; font-weight: 600; color: color-mix(in srgb, var(--tint) 58%, #fff); }
.talent-desc { font-size: .7rem; opacity: .85; line-height: 1.45; margin-top: 3px; }

/* ── Relics ────────────────────────────────────────────── */

.slots { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); gap: 8px; }
.slot {
  position: relative;
  padding: 9px 11px;
  border-radius: var(--radius-s);
  border: 1px dashed color-mix(in srgb, var(--tint) 45%, transparent);
  background: rgba(16,21,36,.5);
  text-align: left;
}
.slot.full { border-style: solid; background: color-mix(in srgb, var(--tint) 8%, rgba(16,21,36,.6)); }
/* What you are actually carrying into the night, so the painting is bigger
   here than in the Found list below it. Only a filled AND painted slot
   becomes a row — an empty slot has nothing for the first column, and would
   otherwise sit differently from the full slot beside it in the grid. */
.slot.painted { display: flex; align-items: flex-start; gap: 10px; }
.slot-body { flex: 1; min-width: 0; }
.slot-art {
  width: 56px; height: 56px; flex: none; margin-top: 2px;
  border-radius: var(--radius-s);
  object-fit: cover;
  border: 1px solid color-mix(in srgb, var(--tint) 40%, transparent);
}
.slot-lbl { font-size: .58rem; letter-spacing: .16em; text-transform: uppercase; opacity: 0.72; }
.slot-name { font-family: var(--font-display); font-size: .86rem; color: color-mix(in srgb, var(--tint) 70%, #fff); margin-top: 2px; }
.slot-desc { font-size: .66rem; opacity: 0.72; line-height: 1.35; margin-top: 2px; }
.slot-empty { font-size: .74rem; opacity: 0.72; font-style: italic; margin-top: 4px; }
.slot-clear {
  margin-top: 6px; padding: 3px 9px;
  border-radius: 999px; font-size: .6rem;
  background: rgba(255,255,255,.06); border: 1px solid var(--panel-edge); color: inherit;
}

.relics { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 8px; }
.relic {
  text-align: left; padding: 9px 11px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.62);
  border: 1px solid var(--panel-edge);
  color: inherit;
  /* A row, so the painting sits beside the text. With no painting there is
     simply one child and the text takes the whole width — the card does not
     change height either way, which is what lets the twelve arrive in any
     order without the drawer reflowing under the player. */
  display: flex; align-items: flex-start; gap: 10px;
}
.relic-body { flex: 1; min-width: 0; }
/* The still lifes are lit from inside the frame and are busier than the
   creature busts, so they get slightly more room and a squarer corner —
   they should read as objects on a table, not as another portrait. */
.relic-art {
  width: 48px; height: 48px; flex: none; margin-top: 1px;
  border-radius: 4px;
  object-fit: cover;
  border: 1px solid color-mix(in srgb, var(--tint) 35%, transparent);
}
.relic.worn { border-color: color-mix(in srgb, var(--tint) 60%, transparent); background: color-mix(in srgb, var(--tint) 9%, rgba(16,21,36,.62)); }
.relic-top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.relic-name { font-weight: 600; font-size: .8rem; color: color-mix(in srgb, var(--tint) 70%, #fff); }
.relic-rarity { font-size: .58rem; letter-spacing: .12em; text-transform: uppercase; opacity: 0.72; white-space: nowrap; }
.relic-desc { font-size: .68rem; opacity: .72; line-height: 1.4; margin-top: 3px; }
.relic-mark { font-size: .62rem; color: var(--gold); opacity: .85; margin-top: 3px; }
.relic-worn { font-size: .6rem; opacity: 0.72; margin-top: 3px; letter-spacing: .08em; text-transform: uppercase; }
.relic-none { font-size: .74rem; opacity: 0.72; font-style: italic; }

/* ── Oaths ─────────────────────────────────────────────── */

.oath-total { float: right; color: var(--gold); letter-spacing: .06em; text-transform: none; opacity: .9; }
.oaths { display: grid; gap: 6px; }
.oath {
  text-align: left; padding: 8px 11px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.6);
  border: 1px solid var(--panel-edge);
  color: inherit;
}
.oath.on {
  border-color: rgba(255,95,116,.55);
  background: rgba(60,20,30,.4);
  box-shadow: inset 0 0 20px rgba(255,95,116,.08);
}
.oath-top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.oath-name { font-family: var(--font-display); font-size: .82rem; }
.oath.on .oath-name { color: #ff9aa6; }
.oath-pay { font-size: .68rem; color: var(--gold); white-space: nowrap; }
.oath-desc { font-size: .67rem; color: var(--ink-dim); line-height: 1.35; margin-top: 2px; }

/* Destroying asks twice — armed state for the confirm button. */
.act-btn.armed {
  border-color: rgba(255,95,116,.75);
  background: rgba(70,20,30,.55);
  color: #ffd8dd;
  animation: armedPulse 1.1s ease-in-out infinite;
}
.act-btn.armed .act-title { color: #ff9aa6; }
@keyframes armedPulse {
  50% { box-shadow: 0 0 22px rgba(255,95,116,.4) }
}

/* ── First sighting ────────────────────────────────────────
   One sentence about how the thing attacks the mechanic, the
   moment it first walks in. Never a stat block, never a wall of
   monsters before you have met one. ── */

#encounter {
  position: absolute;
  left: 50%; top: 14%;
  transform: translateX(-50%);
  z-index: 9;
  display: flex; align-items: center; gap: 11px;
  /* WIDTH, not max-width. An absolutely positioned box shrink-to-fits, so a
     max-width alone is only ever a ceiling it never reaches: measured on a
     375px phone this card sat at 183px with 347px available, wrapping one
     sentence of lore into six lines. It looked like a deliberately narrow
     card rather than a broken one, which is why it survived this long. */
  width: min(26rem, calc(100% - 28px));
  padding: 10px 15px;
  border-radius: 13px;
  background: rgba(8,11,20,.94);
  border: 1px solid var(--panel-edge-hi);
  box-shadow: 0 10px 40px rgba(0,0,0,.7);
  animation: encIn .4s cubic-bezier(.16,1,.3,1) both;
}
@keyframes encIn {
  from { opacity: 0; transform: translate(-50%, -12px) scale(.96) }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1) }
}
/* The mark and the painting are the SAME SIZE, deliberately, so a card looks
   identical in shape whether a creature has art or not. The set arrived one
   creature at a time and a twelfth creature can ship before its portrait
   does; a card that changed shape when the painting landed would make the
   half-finished state obvious to a player.

   54px, not the 30px they started at. 30 was sized for a coloured dot, and a
   dot is legible at any size — a painting is not. At 30 the moth was a smudge
   and the Cinder King was an orange fleck. This is a card the night pauses
   for, once per creature ever; it can afford the room. */
.enc-mark {
  width: 54px; height: 54px; flex: none;
  border-radius: 50%;
  background: var(--body);
  border: 2px solid var(--rim);
  box-shadow: 0 0 16px color-mix(in srgb, var(--rim) 55%, transparent);
}
/* Square rather than round — these are paintings, not tokens. `cover` is
   belt-and-braces: publish-art.py already writes them square, so there is
   nothing left for it to crop. */
.enc-art {
  width: 54px; height: 54px; flex: none;
  border-radius: 6px;
  object-fit: cover;
  border: 1px solid rgba(255, 199, 102, .28);
  box-shadow: 0 0 16px rgba(0, 0, 0, .5);
}
/* Short landscape — an iPhone held sideways leaves about 245px of page, and
   the card is anchored to the board, which is square and height-limited, so
   it is only ~195px wide there. At 54px the portrait pushed the card to 134px
   tall: over half the screen, covering the fight it is telling you about.
   The painting is worth less than seeing what just walked in. */
@media (max-height: 430px) {
  .enc-mark, .enc-art { width: 34px; height: 34px; }
}
.enc-head { font-size: .56rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-mute); }
.enc-name { font-family: var(--font-display); font-size: .95rem; color: var(--ink); margin: 1px 0 2px; }
.enc-line { font-size: .72rem; color: var(--ink-dim); line-height: 1.35; }

/* ── The Chronicle ─────────────────────────────────────── */

.chrons { display: grid; gap: 7px; }
.chron {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 11px;
  border-radius: var(--radius-s);
  background: rgba(16,21,36,.6);
  border: 1px solid var(--panel-edge);
  border-left: 2px solid var(--rim);
  text-align: left;
}
.chron.unknown { border-left-color: rgba(140,165,220,.18); }
.chron.unknown .chron-name, .chron.unknown .chron-line { color: var(--ink-mute); font-style: italic; }
.chron-mark {
  width: 44px; height: 44px; flex: none; margin-top: 2px;
  border-radius: 50%; background: var(--body); border: 2px solid var(--rim);
}
.chron.unknown .chron-mark { background: #1a1f2e; border-color: rgba(140,165,220,.2); }

/* The Chronicle is where the paintings actually live. The sighting card shows
   one for two and a half seconds, once, in the middle of a fight you are
   losing; this is the page you scroll when you want to look at them. Same box
   as the mark it replaces, for the reason above.

   A creature you have NOT met keeps its blank dot. Showing eleven portraits
   to a player who has met three would hand them the whole bestiary before
   they had earned a line of it. */
.chron-art {
  width: 44px; height: 44px; flex: none; margin-top: 2px;
  border-radius: var(--radius-s);
  object-fit: cover;
  border: 1px solid color-mix(in srgb, var(--rim) 40%, transparent);
}
.chron-name { font-family: var(--font-display); font-size: .85rem; color: var(--ink); }
.chron-line { font-size: .7rem; color: var(--ink-dim); line-height: 1.4; margin-top: 2px; }
.chron-stats { font-size: .62rem; color: var(--ink-mute); margin-top: 3px; letter-spacing: .04em; }

/* ── Ascension ─────────────────────────────────────────────
   Not a bigger number: a fork, taken once. ── */

.asc-head {
  font-size: .58rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-mute); margin: 6px 0 4px;
}
.act-btn.ascend {
  border-color: color-mix(in srgb, var(--tint) 45%, transparent);
  background: color-mix(in srgb, var(--tint) 8%, rgba(16,21,36,.7));
}
.act-btn.ascend .act-title { color: color-mix(in srgb, var(--tint) 62%, #fff); }
.act-btn.ascend:hover:not(:disabled) { border-color: color-mix(in srgb, var(--tint) 75%, transparent); }

/* ── Your Watch: where the save lives ──────────────────── */
.watch-card {
  text-align: left; padding: 12px 14px; border-radius: var(--radius-s);
  background: rgba(16,21,36,.66); border: 1px solid var(--panel-edge);
}
.watch-where {
  font-family: var(--font-display); font-size: .92rem; color: var(--ink);
  margin-bottom: 6px;
}
.watch-where b { color: var(--gold); }
.watch-line { font-size: .76rem; color: var(--ink-dim); line-height: 1.5; margin-top: 4px; }
.watch-line.dim { color: var(--ink-mute); }
.watch-note { font-size: .78rem; color: var(--ink-dim); padding: 10px 2px; }
.watch-status {
  font-size: .66rem; color: var(--gold); letter-spacing: .06em; margin-top: 8px; opacity: .85;
}
.watch-warn {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--radius-s);
  border: 1px solid rgba(255,199,102,.35); background: rgba(255,180,80,.08);
  font-size: .74rem; color: var(--ink-dim); line-height: 1.5; text-align: left;
}
.watch-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0 6px; }
.watch-form { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; text-align: left; }
.watch-label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-dim);
}
.watch-label input {
  font: inherit; font-size: .9rem; color: var(--ink);
  padding: 9px 11px; border-radius: var(--radius-s);
  background: rgba(8,11,20,.7); border: 1px solid var(--panel-edge);
}
.watch-label input:focus { outline: none; border-color: rgba(255,199,102,.55); }
.watch-err { font-size: .74rem; color: #ff9d8a; min-height: 1em; line-height: 1.4; }

/* ── The Keep, drawn ───────────────────────────────────────
   DESIGN §8: the hub must visibly grow. The canvas is the
   reward; the caption underneath exists because a picture
   nobody can read the meaning of is a mystery, not a prize —
   and because a screen reader gets nothing from a canvas. ── */

#keep-art-wrap { margin: 2px 0 6px; text-align: center; display: flex; justify-content: center; align-items: center; }
#keep-art {
  display: block; margin: 0 auto; border-radius: 10px;
  border: 1px solid rgba(150,180,230,.14);
  background: #0a0c18;
  box-shadow: 0 6px 22px rgba(0,0,0,.45), inset 0 0 40px rgba(255,170,80,.03);
  max-width: 100%;
}
.keep-art-cap {
  margin-top: 6px; font-size: .66rem; line-height: 1.45;
  color: #9aa6c4; opacity: .78; padding: 0 6px;
}

/* ── Painted art ───────────────────────────────────────────
   Optional by construction: js/data/art.js only emits an <img>
   for something that actually has a painting, so a ground with
   no art simply draws the card it always drew. These rules do
   nothing at all when the element is absent. ── */

.ground, .warden-card { position: relative; overflow: hidden; }

/*
 * Let the picture be seen.
 *
 * The first pass masked these so hard that the detail carrying the character —
 * the ice cracks, the Lamplighter's face — was the first thing thrown away,
 * which is a strange way to treat art you commissioned. The words stay legible
 * because of the SCRIM below, a flat dark wash under the text only, rather
 * than by dimming the whole image toward nothing.
 */
.ground-art {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: .78;
  /* Holds almost full strength across the picture, then drops away under the
     last band where the note and the stats sit. */
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 58%, rgba(0,0,0,.35) 88%, transparent);
  mask-image: linear-gradient(180deg, #000 0%, #000 58%, rgba(0,0,0,.35) 88%, transparent);
  pointer-events: none;
  transition: opacity .3s ease, transform .5s cubic-bezier(.16,1,.3,1);
}
.ground:hover .ground-art { opacity: .92; transform: scale(1.04); }
.ground.on .ground-art { opacity: .95; }

/* The scrim: a dark wash under the words only, so text contrast comes from
   here and not from hiding the painting. */
.ground::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(6,8,15,.10) 0%, rgba(6,8,15,.34) 46%, rgba(6,8,15,.86) 100%);
  pointer-events: none;
}
.ground > *:not(.ground-art) { position: relative; z-index: 1; }

.warden-art {
  position: absolute; top: 0; right: 0;
  height: 100%; width: 58%;
  object-fit: cover; object-position: top center;
  opacity: .82;
  -webkit-mask-image: linear-gradient(270deg, #000 0%, #000 34%, rgba(0,0,0,.4) 72%, transparent);
  mask-image: linear-gradient(270deg, #000 0%, #000 34%, rgba(0,0,0,.4) 72%, transparent);
  pointer-events: none;
  transition: opacity .3s ease;
}
.warden-card:hover .warden-art, .warden-card.on .warden-art { opacity: .95; }
.warden-card::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(6,8,15,.88) 0%, rgba(6,8,15,.62) 46%, rgba(6,8,15,.08) 100%);
  pointer-events: none;
}
.warden-card > *:not(.warden-art) { position: relative; z-index: 1; }

/* The title scene, behind the menu.
   NOT a blanket rule on the children. #screen-title holds a <canvas> as well
   as .screen-inner, and `#screen-title > * { position: relative }` overrode
   that canvas's absolute positioning — it became a grid item in a
   place-items:center grid, pushed the menu off the bottom of the screen, and
   .screen does not scroll. The title screen was unusable on a phone.
   .screen-inner already carries position:relative and z-index:2, so it sits
   above this without any help. */
/*
   FULL STRENGTH. The beacon is the subject of the picture and the premise of
   the game — "they are drawn to flame" — and the first cut had the whole
   painting at 30% with a radial mask fading the top, which is exactly where
   the fire is. It faded the one thing nothing should ever fade.

   Readability is the scrim's job (see .screen-inner below), not the image's.
   Same lesson as the ground and Warden cards; it just took being told twice. */
#screen-title.has-art::before {
  content: '';
  position: absolute; inset: 0;
  background: var(--title-art) center/cover no-repeat;
  opacity: 1;
  /* Only the very bottom edge softens, into the scrim under the words. */
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 72%, rgba(0,0,0,.85) 100%);
  mask-image: linear-gradient(180deg, #000 0%, #000 72%, rgba(0,0,0,.85) 100%);
  pointer-events: none;
  z-index: 0;
}

@media (prefers-reduced-motion: reduce) {
  .ground-art, .warden-art { transition: none; }
  .ground:hover .ground-art { transform: none; }
}

/* ── Tonight's Watch ───────────────────────────────────────
   Two steps: the ground, then the night. Wide-open spacing on
   purpose — this screen replaced a scroll that had three
   decisions and a progress report fighting each other, and the
   fix is not a tidier version of that, it is fewer things on
   screen at once. ── */

.orders-head {
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: 8px; margin-bottom: 14px;
}
.orders-head .game-title { margin: 0; }
.orders-head .link-btn { justify-self: start; }
.orders-step {
  justify-self: end; font-size: .66rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-mute);
}
.orders-actions { display: flex; gap: 10px; justify-content: center; margin-top: 16px; }

/* The ground list: compact, so the briefing below it is the star. */
.ogrid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr));
  gap: 6px; margin-bottom: 14px;
}
.og {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 8px 10px; border-radius: 10px;
  background: rgba(10,14,26,.72); border: 1px solid var(--panel-edge);
  color: var(--ink-dim); text-align: left;
  transition: border-color .18s, background .18s, color .18s;
}
.og:hover:not(.locked) { border-color: var(--panel-edge-hi); color: var(--ink); }
.og.on { border-color: rgba(255,199,102,.62); background: rgba(255,199,102,.10); color: var(--gold); }
.og.locked { opacity: .42; }
.og-name { font-size: .78rem; font-weight: 600; }
.og-stars { font-size: .64rem; opacity: .8; }

/* The briefing: the painting, and what this ground is. */
.obrief {
  position: relative; overflow: hidden;
  border-radius: 14px; border: 1px solid var(--panel-edge);
  background: rgba(8,11,20,.72);
  min-height: 172px;
}
.obrief-art {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: .9; pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 42%, rgba(0,0,0,.35) 82%, transparent);
  mask-image: linear-gradient(180deg, #000 0%, #000 42%, rgba(0,0,0,.35) 82%, transparent);
}
.obrief::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(6,8,15,.12) 0%, rgba(6,8,15,.62) 44%, rgba(6,8,15,.94) 100%);
}
.obrief-in { position: relative; z-index: 1; padding: 92px 14px 14px; text-align: left; }
.obrief-name { font-family: var(--font-display); font-size: 1.3rem; color: var(--gold); margin: 0 0 3px; }
.obrief-note { font-size: .82rem; color: var(--ink); opacity: .95; margin: 0 0 8px; line-height: 1.5; }
.obrief-rec { font-size: .68rem; color: var(--ink-mute); margin-bottom: 9px; }
.obrief-stars { list-style: none; padding: 0; margin: 0 0 8px; }
.obrief-stars li { font-size: .72rem; color: var(--ink-mute); line-height: 1.7; }
.obrief-stars li.has { color: var(--gold); }
.obrief-cap { font-size: .66rem; color: var(--ink-mute); opacity: .8; }
.obrief-prize {
  margin-top: 10px; padding: 8px 10px; border-radius: 9px;
  background: rgba(255,199,102,.09); border: 1px solid rgba(255,199,102,.28);
  font-size: .72rem; line-height: 1.5;
}
.obrief-prize b { color: var(--gold); }
.obrief-prize span { display: block; opacity: .75; margin-top: 2px; }

/* Step two: the night. */
.oassign {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 12px;
  padding: 10px 12px; margin-bottom: 14px;
  border-radius: 11px; background: rgba(10,14,26,.6); border: 1px solid var(--panel-edge);
  text-align: left;
}
.oassign-lbl { font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-mute); align-self: center; }
.oassign-val { font-size: .84rem; color: var(--ink); font-weight: 600; }

.ohead {
  font-family: var(--font-display); font-size: .95rem; color: var(--ink);
  margin: 0 0 8px; text-align: left; display: flex; align-items: baseline; gap: 10px;
}
.ohead-sub { font-family: var(--font-body); font-size: .66rem; color: var(--ink-mute); font-weight: 400; }

.odiffs { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 9.5rem), 1fr)); gap: 7px; margin-bottom: 16px; }
.odiff {
  display: flex; flex-direction: column; gap: 2px; text-align: left;
  padding: 10px 12px; border-radius: 11px;
  background: rgba(10,14,26,.7); border: 1px solid var(--panel-edge); color: var(--ink-dim);
  transition: border-color .18s, background .18s;
}
.odiff:hover { border-color: var(--panel-edge-hi); }
.odiff.on { border-color: rgba(255,199,102,.6); background: rgba(255,199,102,.10); }
.odiff-name { font-size: .88rem; font-weight: 700; color: var(--ink); }
.odiff.on .odiff-name { color: var(--gold); }
.odiff-desc { font-size: .7rem; line-height: 1.45; }
.odiff-meta { font-size: .64rem; color: var(--ink-mute); }

.ooaths { display: grid; gap: 7px; margin-bottom: 14px; }
.ooath {
  display: flex; flex-direction: column; gap: 3px; text-align: left;
  padding: 9px 12px; border-radius: 11px;
  background: rgba(10,14,26,.7); border: 1px solid var(--panel-edge); color: var(--ink-dim);
  transition: border-color .18s, background .18s;
}
.ooath:hover { border-color: var(--panel-edge-hi); }
.ooath.on { border-color: rgba(255,120,90,.55); background: rgba(255,120,90,.10); }
.ooath-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.ooath-top b { font-size: .82rem; color: var(--ink); }
.ooath.on .ooath-top b { color: #ffc0a8; }
.ooath-top em { font-style: normal; font-size: .74rem; color: var(--gold); }
.ooath-desc { font-size: .7rem; line-height: 1.45; }

.opay {
  padding: 9px 12px; border-radius: 10px; text-align: center;
  background: rgba(255,199,102,.07); border: 1px solid rgba(255,199,102,.2);
  font-size: .76rem; color: var(--ink-dim);
}
.opay b { color: var(--gold); }

.keep-foot { font-size: .68rem; color: var(--ink-mute); text-align: center; margin: 12px 0 0; line-height: 1.5; }
.ground.record { cursor: default; }

@media (max-width: 560px) {
  .obrief-in { padding-top: 74px; }
  .obrief { min-height: 152px; }
}

/* ── Tonight's Watch: the ground, as accordions ────────────
   The Keep's cards brought over whole, because they already
   read well. The detail opens INSIDE the card so the thing you
   are reading and the thing you are choosing are one object. ── */

.orders-head {
  position: sticky; top: 0; z-index: 3;
  /* auto 1fr auto, not 1fr auto 1fr: the back link is a fixed-size thing and
     giving it a fraction squeezed it to three wrapped lines on a phone, where
     it then collided with the title. */
  grid-template-columns: auto 1fr auto;
  padding: 6px 0 10px;
  background: linear-gradient(180deg, rgba(6,8,15,.98) 72%, rgba(6,8,15,0));
}
.orders-head .link-btn {
  grid-column: 1; white-space: nowrap; font-size: .74rem; padding: 4px 2px;
}
.orders-head .game-title { grid-column: 2; font-size: clamp(1.1rem, 4.6vw, 1.7rem); }

.ogcs { display: grid; gap: 8px; }
.ogc {
  border-radius: 13px; overflow: hidden;
  border: 1px solid var(--panel-edge);
  background: rgba(8,11,20,.72);
  transition: border-color .2s, background .2s;
}
.ogc.on { border-color: rgba(255,199,102,.6); background: rgba(255,199,102,.07); }
.ogc.locked { opacity: .5; }

.ogc-head {
  position: relative; overflow: hidden;
  display: block; width: 100%; padding: 0;
  background: none; border: 0; color: inherit; text-align: left; cursor: pointer;
  min-height: 76px;
}
.ogc-art {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: .72; pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 40%, rgba(0,0,0,.3) 88%, transparent);
  mask-image: linear-gradient(90deg, #000 0%, #000 40%, rgba(0,0,0,.3) 88%, transparent);
  transition: opacity .25s;
}
.ogc:hover .ogc-art { opacity: .88; }

/* The chosen ground is lit. Full strength AND a lighter scrim — brightening
   the picture while leaving a heavy wash over it only makes a brighter murk,
   which is the mistake the title screen already taught. */
.ogc.on .ogc-art { opacity: 1; filter: brightness(1.16) saturate(1.06); }

.ogc-head::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, rgba(6,8,15,.42) 0%, rgba(6,8,15,.72) 52%, rgba(6,8,15,.93) 100%);
  transition: background .25s ease;
}
/* Enough wash to hold the words, and no more. The name is large and gold, so
   it survives on far less than an unselected card needs. */
.ogc.on .ogc-head::after {
  background: linear-gradient(90deg, rgba(6,8,15,.16) 0%, rgba(6,8,15,.44) 54%, rgba(6,8,15,.78) 100%);
}
.ogc-in { position: relative; z-index: 1; display: block; padding: 12px 14px; }
.ogc-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.ogc-name { font-family: var(--font-display); font-size: 1.02rem; color: var(--ink); }
.ogc.on .ogc-name { color: var(--gold); }
.ogc-stars {
  font-size: .8rem; letter-spacing: .08em; color: var(--ink-mute);
  /* These sit on the painting, which on the chosen card is bright. */
  text-shadow: 0 1px 4px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.7);
}
.ogc-stars .gstar.on { color: var(--gold); }
.ogc-sub { display: block; margin-top: 3px; font-size: .68rem; color: var(--ink-mute); }
.og-pay { color: #8fd67a; }
.og-pay.low { color: #ffb36b; }

.og-detail {
  padding: 2px 14px 14px; text-align: left; cursor: pointer;
  animation: ogOpen .28s cubic-bezier(.16,1,.3,1) both;
}
@keyframes ogOpen { from { opacity: 0; transform: translateY(-5px) } to { opacity: 1; transform: none } }
.og-note { font-size: .8rem; color: var(--ink); opacity: .95; margin: 6px 0 9px; line-height: 1.5; }
.og-goals { list-style: none; padding: 0; margin: 0 0 9px; }
.og-goals li { font-size: .72rem; color: var(--ink-mute); line-height: 1.75; }
.og-goals li.has { color: var(--gold); }
.og-rec { font-size: .68rem; color: var(--ink-mute); margin-bottom: 8px; }
.og-tax {
  font-size: .7rem; line-height: 1.5; padding: 8px 10px; border-radius: 9px;
  background: rgba(255,179,107,.09); border: 1px solid rgba(255,179,107,.26);
  color: var(--ink-dim); margin-bottom: 8px;
}
.og-tax b { color: #ffb36b; }
.og-prize {
  font-size: .72rem; line-height: 1.5; padding: 8px 10px; border-radius: 9px;
  background: rgba(255,199,102,.09); border: 1px solid rgba(255,199,102,.28);
}
.og-prize b { color: var(--gold); }
.og-prize span { display: block; opacity: .75; margin-top: 2px; }

/* The call to action belongs to the card that is open, not to a footer that
   is always there whether or not you have chosen anything. */
.og-go {
  display: block; width: 100%; margin-top: 12px;
  padding: 11px 18px; font-size: .9rem;
}

/* Locked grounds keep their painting — a place out in the dark you have not
   earned is a better hook than a row of question marks — but darker, drained
   of colour, and behind a padlock. The promise stays; the detail does not. */
/* Locked, and it has to LOOK like somewhere rather than like a gap. The first
   pass sat the art at .34 opacity, greyed and darkened, underneath a wash that
   already runs to 93% — the picture was there and nobody could see it. Now it
   is plainly visible and plainly out of reach: cool, softened, behind a lock. */
.ogc.locked { opacity: 1; border-style: dashed; border-color: rgba(150,180,230,.22); }
.ogc.locked .ogc-art {
  opacity: .8;
  filter: grayscale(.5) brightness(.8) blur(1.6px);
}
.ogc.locked .ogc-head::after {
  background: linear-gradient(90deg, rgba(6,8,15,.30) 0%, rgba(6,8,15,.58) 54%, rgba(6,8,15,.84) 100%);
}
.ogc-lock {
  width: 1.15rem; height: 1.15rem; display: block;
  color: rgba(214,228,255,.9);
  filter: drop-shadow(0 1px 4px rgba(0,0,0,.9));
}
.ogc.locked .ogc-sub { color: rgba(214,228,255,.72); }
.ogc.locked .ogc-head { cursor: default; }

/* The one thing inside an open card that does not close it. */
.og-go { cursor: pointer; }

/* ── The Keep: three bands, and none of them scroll ────────
   Seven tabs wrapped to three rows and pushed Take the Watch
   clean off the bottom of a phone — the one button the screen
   exists for was below the fold. So: six tabs in two rows that
   scale to fit, and everything a tab used to show comes up in a
   drawer instead of replacing the screen.

   Then the screen itself was still one long scroller: the art
   slid away the moment you opened a tab, "The Keep" was cut in
   half by the notch, and the drawer floated over the tab row it
   had just come out of. So the layout is fixed now — the Keep
   pinned on top, the way out pinned at the bottom, and the
   drawer owning the whole gap between them. Only the drawer's
   own contents scroll.

   That is also why the drawer can no longer land on top of
   anything: it has its own grid band, and a band cannot overlap
   its neighbours. ── */

#screen-keep .screen-inner {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 8px;
  width: min(620px, 100vw);
  height: var(--app-h);
  max-height: var(--app-h);
  overflow: hidden;                  /* the drawer scrolls; the screen does not */
  /* All four insets, not just the two that show up in portrait. A notched
     phone on its side reports 59px of left AND right inset, and this screen
     ignored both — the Keep ran under the notch and under the rounded corner.
     --sal and --sar were declared in main.css and used by nothing at all. */
  padding: calc(8px + var(--sat)) calc(14px + var(--sar))
           calc(8px + var(--sab))  calc(14px + var(--sal));
}

#keep-top { min-height: 0; transition: none; }

/* The art yields rather than taking whatever it likes — this is what keeps the
   middle band from collapsing to nothing. */
#keep-top #keep-art-wrap { max-height: 32vh; }

/* ...and the CANVAS has to yield with it. `drawKeep()` sets the canvas size
   itself, from the wrapper's height budget at the moment it draws — so a cap
   that changes AFTER that draw (opening a drawer swaps 32vh for 18vh) leaves a
   canvas larger than the box it lives in. The wrapper centres its child, so
   the excess went half above: the painting hung 2px off the top of a 1024x768
   screen. Belt to the braces — the canvas can never exceed its wrapper, no
   matter which redraw won the race. */
#keep-top #keep-art-wrap > canvas { max-height: 100%; width: auto; }

/* ── Open, the drawer takes the room it needs ───────────────
   The drawer IS the 1fr band, so on a roomy screen it already grows into
   whatever is going spare and the Keep stays above it. No rule needed for
   that case, and none written — a layout tuned to one phone's pixels is
   wrong on the next screen it meets.

   The tight case did need a rule. On the viewport iPhone Safari actually
   leaves — 375×500, not the 375×844 the spec sheet claims — the art, its
   caption and the purse took 272px and the two button rows took 134, so the
   drawer got 45: a strip you could not read one row in. Christine drew an
   arrow from that strip straight up to under the title.

   So when there is not enough height to have both, the Keep stands aside and
   the drawer runs from just below the words down to the buttons. KEYED TO
   HEIGHT, NOT TO A DEVICE: a phone in landscape is short and gets the same
   treatment, a desktop window dragged short gets it too, and a tall screen
   never triggers it at all. Nothing is lost either way — the picture is one
   tap back.

   The purse WOULD be lost, and the Tree is a shop, so the embers ride in the
   drawer's own header rather than vanishing while you spend them. ── */
@media (max-height: 760px) {
  #screen-keep.drawered #keep-art-wrap,
  #screen-keep.drawered .keep-art-cap,
  #screen-keep.drawered #keep-purse { display: none; }
  #screen-keep.drawered .game-title.small { margin-bottom: 2px; }
}

/* ── ...and the TALL case needed one after all ──
   The claim above — "on a roomy screen the drawer already grows into whatever
   is going spare, no rule needed" — is false, and measuring is what showed it.
   With the Ledger open (782px of content) the drawer band was:

       1024x768   139px      1280x800   160px      1440x900  228px
       375x500    231px      1920x1080  400px

   The laptop gets LESS than the phone. The art takes its height from the
   available WIDTH (max-height: 32vh, but 32vh of a tall window is a lot), so
   the wider the screen the more the header eats and the less is left over.
   The drawer is nominally the 1fr band and 1fr of nothing is nothing.

   Above the threshold where the art is dropped outright, it instead stands
   down to 20vh WHILE A DRAWER IS OPEN — shut, it keeps the full 32vh and the
   Keep still looks like a place. Same bargain as the short case, one size up:
   when you have asked to read something, the thing you are reading wins.

   `page.mjs` asserts the drawer keeps at least 30% of the height, so this
   cannot quietly regress again. ── */
@media (min-height: 761px) {
  /* `height`, not just `max-height`. A percentage max-height on the canvas
     resolves against the parent's HEIGHT, and a parent bounded only by
     max-height still has height:auto — so `max-height: 100%` on the canvas
     computed to `none` and the painting kept the size drawKeep() gave it,
     hanging 2px off the top of a 1024x768 screen. A definite height here is
     what makes the canvas rule above bite. */
  #screen-keep.drawered #keep-top #keep-art-wrap { height: 18vh; max-height: 18vh; }
  /* The caption goes with it, as it does in the short case above: it describes
     the picture, and while a drawer is open the picture is not what you are
     looking at. 20vh alone left 1024x768 — the commonest laptop of all — at
     28%, still under the floor. */
  #screen-keep.drawered .keep-art-cap { display: none; }
}

.drawer-purse {
  font-family: var(--font-ui);
  font-size: .72rem; letter-spacing: .02em;
  color: var(--ink-mute);
}
.drawer-purse b { color: var(--gold); font-weight: 600; }

/* ── Landscape: stop fighting for height that isn't there ───
   A phone on its side leaves about 270px of usable height once Safari has
   taken its bars. Stacking a picture, a purse, two rows of buttons and a
   drawer into that is not a spacing problem to be tuned — there is no
   arrangement of those five things that fits, and trying produced a screen
   where the tab rows were painted straight through the art.

   Landscape has the opposite surplus, so the layout turns ninety degrees with
   the phone: the Keep and its buttons on the left, the drawer taking the
   whole right-hand side. Shut, the right column collapses to nothing and the
   Keep has the room to itself. ── */
@media (min-aspect-ratio: 1/1) and (max-height: 560px) {
  #screen-keep .screen-inner {
    width: min(1100px, 100vw);
    grid-template-columns: minmax(0, 1fr) 0;
    grid-template-rows: minmax(0, 1fr) auto;
    grid-template-areas:
      "keep   drawer"
      "bottom bottom";
    column-gap: 0;
    transition: grid-template-columns .26s cubic-bezier(.16,1,.3,1);
  }
  #screen-keep.drawered .screen-inner {
    grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
    column-gap: 10px;
  }
  #screen-keep #keep-top {
    grid-area: keep; overflow: hidden;
    display: flex; flex-direction: column;
    /* `safe`, not a bare `center`. Centring a flex column TALLER than its box
       splits the overflow evenly and puts half above the top edge, where
       `overflow: hidden` destroys it and no scroll can reach it — scrollTop
       has no negative side. On an iPhone in landscape (812x245 real, not the
       nominal 844x390) that was 24px over, and what it ate was the words
       "The Keep" and then the purse. `safe` falls back to flex-start the
       moment content would overflow, so anything cut is cut off the BOTTOM,
       where it is at least visible that there is more. */
    justify-content: safe center;
  }
  #keep-drawer { grid-area: drawer; }

  /* ONE row, not two. The bottom band runs the full width down here, so six
     tabs and the action fit on a single line, and every pixel that saves goes
     to the two things that actually need height. */
  #screen-keep #keep-bottom {
    grid-area: bottom;
    flex-direction: row; align-items: center; gap: 10px;
  }
  #screen-keep #keep-bottom .keep-tabs {
    flex: 1 1 auto;
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  /* Six tabs and the action on ONE row leaves roughly 75px a tab, and the
     shared clamp — clamp(.62rem, 2.9vw, .8rem) — pins to its .8rem MAXIMUM at
     these widths, because 2.9vw of 700px is 20px. So the type never scaled and
     the labels just ellipsised instead: "Watch T...", "Your Wa...". A tab you
     cannot read is not a tab. Smaller ceiling, width-proportional middle, and
     the letter-spacing goes — it buys nothing at this size and costs a
     character. `page.mjs` now fails on any truncated label, so this cannot
     quietly come back. */
  #screen-keep #keep-bottom .keep-tabs .tab-btn {
    font-size: clamp(.56rem, 1.5vw, .72rem);
    letter-spacing: 0;
    padding: 9px 2px;
  }
  #screen-keep #keep-bottom .keep-actions { flex: 0 0 auto; }
  #screen-keep .primary-btn.big { min-width: 0; padding: 10px 22px; }

  /* The picture is still the thing you are looking at, so it keeps the left
     column rather than being hidden the way it is in short portrait. */
  #screen-keep.drawered #keep-art-wrap { display: block; max-height: 42vh; }
  #screen-keep.drawered .keep-art-cap { display: none; }
}

/* ── Landscape in a BROWSER, which is shorter still ─────────
   Standalone gets the whole screen. Safari in landscape keeps its bars and
   leaves around 240px — at which point half of that, half as wide, showed one
   and a half rows of the Watch Tree. Christine: "landscape is a mess in a
   mobile browser, less horrid in a web app."

   So below the height where two columns are affordable they stop being two
   columns: while a drawer is open it takes the whole area above the button
   row, and the Keep comes straight back when you shut it. The same bargain as
   short portrait, for the same reason — at this size the screen can show one
   thing properly or two things badly. ── */
@media (min-aspect-ratio: 1/1) and (max-height: 400px) {
  #screen-keep.drawered .screen-inner {
    grid-template-columns: 0 minmax(0, 1fr);
    column-gap: 0;
  }
  #screen-keep.drawered #keep-top { display: none; }

  /* With NO drawer open the header stays — and at this height it does not fit.
     Measured at 812x245: 198px of content in a 174px box. `safe center` above
     stops the top being eaten, but the honest fix is to shed, in the order the
     screen can most afford:

       the caption (15px + 6px margin) — the picture says it better anyway, and
         the drawered rule already drops it for exactly this reason;
       the purse's bottom margin (14px -> 4px) — pure spacing.

     31px freed against 24px needed, so the title AND the purse both survive.
     Do not add height back here without re-running `node tools/page.mjs 812 245`. */
  #screen-keep:not(.drawered) .keep-art-cap { display: none; }
  #screen-keep:not(.drawered) #keep-purse { margin-bottom: 4px; }

  /* Every row counts now, so the drawer stops spending vertical space saying
     which drawer it is — the highlighted tab already says that. */
  #screen-keep.drawered .drawer-grip {
    flex-direction: row; justify-content: center; gap: 10px;
    padding: 4px 14px 2px;
  }
  #screen-keep.drawered .drawer-bar { width: 26px; }
  #screen-keep.drawered .drawer-title { font-size: .78rem; }
}

/* ── Shorter than a phone in landscape has any right to be ──
   Safari's chrome takes an iPhone's 375 CSS px of landscape height down to
   about 245. At 268 the header fits exactly; at 245 it is 16px over, and the
   thing that then gets cut is the purse — "EMBERS", "CINDERS", "STARS",
   "NIGHTS" sliced through their labels.

   The art yields, because it is the one element here that reads fine smaller
   and the only one that is decoration rather than information. 20vh of 245 is
   49px, which buys 29px against the 16px owed. This is deliberately scoped to
   280px so that 844x390 — a phone in a standalone web app, with no browser
   chrome — keeps the bigger picture it already had. ── */
@media (min-aspect-ratio: 1/1) and (max-height: 280px) {
  #screen-keep:not(.drawered) #keep-top #keep-art-wrap { max-height: 20vh; }
}

#keep-bottom { display: flex; flex-direction: column; gap: 8px; min-height: 0; }

.keep-tabs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  margin-top: 4px;
}
.keep-tabs .tab-btn {
  width: 100%; min-width: 0;
  padding: 9px 4px;
  /* Scales down rather than wrapping to a third row. */
  font-size: clamp(.62rem, 2.9vw, .8rem);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The action sits in the bottom band now, not floating over the screen, and
   the band is packed tight: there was 34px of nothing above the one button
   this screen exists for, and another cap holding the drawer short of the tab
   row. Christine's note was "kill the blank space at the bottom". */
#screen-keep .keep-actions {
  display: flex; justify-content: center;
  margin: 0; padding: 0;
}
#screen-keep .keep-tabs { margin-top: 0; }
/* The drawer fills its band. The old 50%-of-screen cap was written when this
   was a panel inside a scrolling page. */
.keep-drawer #keep-body { max-height: none; }

/* ── The drawer: the middle band ───────────────────────────
   It no longer floats. It IS the space between the Keep and
   the buttons, which is why nothing can paint over it and it
   can never cover the one button the screen exists for. ── */

.keep-drawer {
  min-height: 0;
  display: flex; flex-direction: column;
  background: rgba(9,12,21,.98);
  border: 1px solid rgba(255,199,102,.28);
  border-radius: 16px;
  box-shadow: 0 -12px 44px rgba(0,0,0,.6);
  overflow: hidden;
  /* Shut: no height, no border, nothing to see. The middle band is simply
     empty and the Keep breathes. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .22s ease, transform .26s cubic-bezier(.16,1,.3,1),
              visibility 0s linear .26s;
}
.keep-drawer.open {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition: opacity .22s ease, transform .26s cubic-bezier(.16,1,.3,1),
              visibility 0s;
}

/* The whole top of the drawer is the close target, not a 6px handle. */
.drawer-grip {
  flex: none;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  width: 100%; padding: 10px 14px 8px;
  background: none; border: 0; color: inherit; cursor: pointer;
}
.drawer-bar {
  width: 42px; height: 4px; border-radius: 2px;
  background: rgba(214,228,255,.34);
}
.drawer-title {
  font-family: var(--font-display); font-size: .9rem; letter-spacing: .06em;
  color: var(--gold);
}
.drawer-grip:hover .drawer-bar { background: rgba(255,199,102,.6); }

.keep-drawer #keep-body {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 0 14px calc(18px + var(--sab));
  -webkit-overflow-scrolling: touch;
}

@media (max-width: 380px) {
  .keep-tabs .tab-btn { padding: 8px 3px; letter-spacing: 0; }
}
