/* Anno XL — rooms.css
   Interiors. One overlay shell; the furniture changes per room:
   kitchen gets a paper menu card, the spa a booking board, the beach
   house a boat ticket. Light cards on dark rooms, cut corners, tape.    */

/* dvh, NOT --screen-h: this is a SCROLL CONTAINER, and its internal scroll
   ends at its own bottom edge — sized to --screen-h (lvh) in a Safari tab,
   its last ~100px sat under the toolbar, so the surface/next buttons at the
   foot of every room could never be scrolled into reach (found on-device,
   2026-08-10). 100dvh tracks what is actually visible: shorter while the
   toolbar is up, the full screen in the installed app. The look doesn't
   change — .room-bg below still bleeds the photograph to --screen-h behind
   this, and rooms only open long after boot, so the PWA cold-start dvh bug
   (see base.css) can't bite here. */
#room {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  background: var(--ink);
  opacity: 0;
  transition: opacity .42s var(--ease-soft);
  scrollbar-width: thin;
  scrollbar-color: rgba(231, 217, 189, .3) transparent;
}
#room.lit { opacity: 1; }
#room::-webkit-scrollbar { width: 9px; }
#room::-webkit-scrollbar-thumb { background: rgba(231, 217, 189, .25); }

/* --screen-h, not inset:0 — this is a photograph and would otherwise stop
   short of the screen in a standalone PWA. See base.css. */
.room-bg { position: fixed; top: 0; left: 0; right: 0; height: var(--screen-h); }
/* The TV room reads as one piece: the photo — with the television and the
   film controls sitting over it — rides UP and away with the content when you
   scroll, instead of staying pinned behind it. Anchored to the top of the
   scroll (absolute inside the scrolling #room), not the viewport, so scrolling
   down reveals the keepsakes on clean ground below the picture. */
#room[data-room='tv'] .room-bg { position: absolute; }
.room-bg img {
  width: 100%; height: 100%;
  object-fit: cover;
  animation: kenburns 14s var(--ease-soft) alternate infinite;
}
@keyframes kenburns { from { transform: scale(1); } to { transform: scale(1.07); } }
.room-scrim {
  position: absolute; inset: 0;
  background:
    radial-gradient(90% 120% at 18% 40%, rgba(7, 20, 24, .52), transparent 60%),
    linear-gradient(180deg, rgba(7, 20, 24, .42), rgba(7, 20, 24, .18) 30%, rgba(7, 20, 24, .78) 66%, #07181c 97%);
}

.room-panel {
  position: relative;
  max-width: 640px;
  padding: 16dvh 22px 44px;
  margin: 0 auto;
  color: var(--sand);
  transform: scale(1.05) translateY(12px);
  transition: transform .75s var(--ease-soft), opacity .3s;
}
#room.lit .room-panel { transform: none; }
#room.turning .room-panel { opacity: 0; transform: translateY(8px); }
@media (min-width: 980px) {
  .room-panel { margin-left: clamp(30px, 8vw, 150px); }
}

/* ---- head ------------------------------------------------------------- */

.room-head { position: relative; }
.hall-ghost {
  position: absolute;
  left: -14px; top: -11dvh;
  z-index: -1;
  font: 700 clamp(7.5rem, 24vw, 13rem)/1 var(--serif);
  color: transparent;
  -webkit-text-stroke: 1px rgba(231, 217, 189, .2);
  user-select: none;
}
.room-head h2 {
  font: 700 clamp(2.6rem, 7vw, 4.4rem)/0.98 var(--serif);
  letter-spacing: -.01em;
  color: var(--bone);
  text-shadow: 0 2px 30px rgba(4, 14, 17, .55);
}
.room-head h2 .th {
  display: block;
  font: 400 1.1rem var(--thai);
  letter-spacing: .05em;
  color: var(--gold);
  margin-top: 12px;
}
.room-head .chev {
  width: 60px;
  margin-top: 20px;
  stroke: rgba(231, 217, 189, .6); fill: none;
  stroke-width: 6.5; stroke-linecap: round; stroke-linejoin: round;
}

/* ---- shared furniture -------------------------------------------------- */

.room-lead {
  margin: 28px 0 30px;
  max-width: 56ch;
  font: 400 clamp(1.1rem, 2.3vw, 1.28rem)/1.62 var(--serif);
  color: var(--sand);
}
.room-lead::first-letter {
  font-size: 3.2em; font-weight: 700;
  float: left;
  line-height: .84;
  padding: 2px 10px 0 0;
  color: var(--gold);
}

.room-aside {
  margin: 28px 0;
  max-width: 52ch;
  padding-left: 15px;
  border-left: 2px solid rgba(230, 163, 62, .45);
  font: 400 .93rem/1.6 var(--sans);
  color: rgba(231, 217, 189, .74);
}

.polaroid {
  width: min(300px, 82%);
  padding: 10px 10px 13px;
  background: #fbf7ec;
  transform: rotate(var(--tilt, -2deg));
  box-shadow: 0 18px 44px rgba(2, 10, 12, .5);
  position: relative;
  margin: 36px auto 10px;
}
#room[data-room='kitchen'] .polaroid { margin-right: 4px; margin-left: auto; }
#room[data-room='beach'] .polaroid { margin-left: 4px; margin-right: auto; }
.polaroid img { aspect-ratio: 3 / 2; object-fit: cover; width: 100%; }
.polaroid figcaption {
  margin-top: 9px;
  text-align: center;
  font: italic 500 .9rem var(--serif);
  color: #3a4a50;
}
.polaroid .tape {
  position: absolute; top: -13px; left: 50%;
  width: 96px; height: 27px;
  transform: translateX(-50%) rotate(-3.5deg);
  background: rgba(233, 216, 180, .85);
  clip-path: polygon(0 8%, 5% 0, 95% 4%, 100% 14%, 99% 92%, 93% 100%, 4% 96%, 1% 86%);
  box-shadow: 0 2px 6px rgba(2, 10, 12, .18);
}

/* ---- kitchen: the menu card ------------------------------------------- */

.menu-card {
  position: relative;
  background: var(--paper);
  color: var(--paper-ink);
  clip-path: var(--cut-m);
  padding: 30px clamp(20px, 4.5vw, 42px) 26px;
  box-shadow: 0 26px 60px rgba(2, 10, 12, .5);
}
.menu-card::before {
  content: '';
  position: absolute; inset: 10px;
  border: 1px solid rgba(28, 48, 56, .3);
  pointer-events: none;
}
.menu-card::after {
  content: '';
  position: absolute; inset: 14px;
  border: 1px solid rgba(28, 48, 56, .12);
  pointer-events: none;
}
.menu-title {
  text-align: center;
  font: italic 700 1.35rem var(--serif);
}
.menu-title::after {
  content: '· ครัว ·';
  display: block;
  margin-top: 5px;
  font: 400 .85rem var(--thai);
  color: var(--paper-gold);
}
.menu-courses { margin: 20px 0 4px; }
.menu-courses li {
  display: flex; gap: 15px;
  padding: 15px 2px;
  border-top: 1px dotted rgba(28, 48, 56, .4);
}
.menu-courses li:first-child { border-top: 0; }
.course-n {
  font: 700 1.05rem var(--serif);
  color: var(--paper-gold);
  min-width: 2.6ch;
}
.course-body b { font: 700 1.1rem var(--serif); }
.course-detail {
  display: block;
  margin-top: 4px;
  font: 400 .92rem/1.55 var(--sans);
  color: var(--paper-mid);
}
.heat { display: inline-flex; gap: 3px; margin-left: 9px; }
.heat i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--chili);
}
.menu-foot {
  margin-top: 14px;
  padding-top: 16px;
  border-top: 1px solid rgba(28, 48, 56, .3);
  text-align: center;
  font: 400 .92rem/1.55 var(--sans);
  color: var(--paper-mid);
}
.menu-foot a { color: var(--paper-gold); font-weight: 600; }

/* ---- spa: the booking board ------------------------------------------- */

.spa-board {
  position: relative;
  background: rgba(13, 39, 47, .62);
  backdrop-filter: blur(9px);
  border: 1px solid rgba(231, 217, 189, .24);
  clip-path: var(--cut-m);
  padding: 26px clamp(18px, 3.8vw, 34px) 22px;
}
.spa-board::before {
  content: '';
  position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px;
  background: var(--lagoon);
}
.board-title { font: italic 700 1.3rem var(--serif); color: var(--bone); }
.board-title .th { font-size: 1rem; color: var(--gold); }

/* Refresh, on the boards that move while you are looking at them. Scoped to
   those two so the spa's static board keeps its plain heading. */
.gateboard .board-title,
.honours .board-title { display: flex; align-items: center; gap: 10px; }

.board-refresh {
  margin-left: auto;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: rgba(231, 217, 189, .5);
  display: grid; place-items: center;
  width: 44px; height: 44px;              /* thumb-sized target, small glyph */
  margin-right: -10px;
  transition: color .2s;
}
.board-refresh:hover { color: var(--gold); }
.board-refresh svg { width: 17px; height: 17px; }
.board-refresh.spinning { color: var(--gold); }
.board-refresh.spinning svg { animation: board-spin .75s linear infinite; }
@keyframes board-spin { to { transform: rotate(360deg); } }
/* Someone who asked for less motion still gets the colour change. */
@media (prefers-reduced-motion: reduce) {
  .board-refresh.spinning svg { animation: none; }
}
.board-rows { margin-top: 8px; }
.board-rows li {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas: 'name lead mins' 'note note note';
  column-gap: 12px;
  align-items: baseline;
  padding: 13px 0;
  border-top: 1px solid rgba(231, 217, 189, .12);
}
.board-rows li:first-child { border-top: 0; }
.slot-name { grid-area: name; font: 700 1.04rem var(--serif); color: var(--bone); }
.slot-lead { grid-area: lead; border-bottom: 1px dotted rgba(231, 217, 189, .4); transform: translateY(-4px); }
.slot-mins {
  grid-area: mins;
  font: 600 .92rem var(--sans);
  font-variant-numeric: tabular-nums;
  letter-spacing: .05em;
  color: var(--lagoon-2);
}
.slot-note {
  grid-area: note;
  margin-top: 4px;
  font: 400 .89rem/1.5 var(--sans);
  color: rgba(231, 217, 189, .66);
}

/* ---- beach house: the boat ticket ------------------------------------- */

.ticket {
  display: flex;
  margin: 30px 0 4px;
  background: var(--paper);
  color: var(--paper-ink);
  clip-path: var(--cut-m);
  box-shadow: 0 26px 60px rgba(2, 10, 12, .5);
}
.tk-main { flex: 1; position: relative; padding: 26px clamp(18px, 3.8vw, 34px) 22px; }
.tk-route {
  font: 800 1.04rem/1.35 var(--sans);
  letter-spacing: .09em; text-transform: uppercase;
  padding-right: 118px;
}
.tk-route .th {
  display: inline-block;
  margin-left: 7px;
  font: 400 .92rem var(--thai);
  text-transform: none; letter-spacing: 0;
  color: var(--paper-gold);
}
.tk-main dl { margin-top: 16px; }
.tk-line {
  display: flex; gap: 12px; align-items: baseline;
  padding: 9px 0;
  border-top: 1px dotted rgba(28, 48, 56, .35);
}
.tk-line:first-child { border-top: 0; }
.tk-line dt {
  font: 700 10px/1.4 var(--sans);
  letter-spacing: .18em; text-transform: uppercase;
  color: #55707a;
  min-width: 76px;
}
.tk-line dd { font: 600 .98rem/1.45 var(--serif); }
.tk-stamp {
  position: absolute; right: 16px; top: 15px;
  transform: rotate(-7deg);
  border: 2px solid var(--chili);
  color: var(--chili);
  font: 800 10.5px/1.3 var(--sans);
  letter-spacing: .12em; text-transform: uppercase;
  padding: 6px 10px;
  max-width: 120px;
  text-align: center;
  opacity: .85;
  mix-blend-mode: multiply;
}
.tk-stub {
  width: 116px;
  position: relative;
  border-left: 2px dashed rgba(28, 48, 56, .45);
  display: flex; flex-direction: column;
  align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 18px 12px;
  text-align: center;
}
.tk-stub::before, .tk-stub::after {
  content: '';
  position: absolute; left: -10px;
  width: 17px; height: 17px;
  border-radius: 50%;
  background: #0a2027;
}
.tk-stub::before { top: -9px; }
.tk-stub::after { bottom: -9px; }
.tk-admit {
  font: 800 10px/1 var(--sans);
  letter-spacing: .3em; text-transform: uppercase;
  color: #55707a;
}
.tk-barcode {
  width: 66px; height: 46px;
  background: repeating-linear-gradient(90deg,
    var(--paper-ink) 0 2px, transparent 2px 4px,
    var(--paper-ink) 4px 7px, transparent 7px 9px);
}
.tk-no { font: 700 .86rem var(--sans); letter-spacing: .08em; color: var(--paper-gold); }

@media (max-width: 560px) {
  .tk-route { padding-right: 0; }
  .tk-stamp {
    position: static;
    display: inline-block;
    margin-top: 14px;
    transform: rotate(-3deg);
    max-width: none;
  }
  .ticket { flex-direction: column; }
  .tk-stub {
    width: auto;
    flex-direction: row;
    border-left: 0;
    border-top: 2px dashed rgba(28, 48, 56, .45);
    padding: 13px 18px;
  }
  .tk-stub::before { top: -9px; left: -9px; }
  .tk-stub::after { top: -9px; left: auto; right: -9px; bottom: auto; }
  .tk-barcode { flex: 1; height: 30px; }
}

.kit { margin: 28px 0; }
.kit-title { font: italic 700 1.12rem var(--serif); color: var(--gold); }
.kit ul { margin-top: 6px; }
.kit li {
  position: relative;
  padding: 7px 0 7px 26px;
  font: 400 1.03rem var(--serif);
  color: var(--sand);
}
.kit li::before {
  content: '—';
  position: absolute; left: 1px;
  color: var(--gold);
}

/* ---- footer ------------------------------------------------------------ */

.room-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 46px;
  padding-bottom: env(safe-area-inset-bottom);
}
.btn-surface, .btn-next {
  font: 700 11px/1.2 var(--sans);
  letter-spacing: .16em; text-transform: uppercase;
  clip-path: var(--cut-s);
  padding: 15px 18px;
  background: rgba(9, 25, 29, .55);
  backdrop-filter: blur(5px);
  border: 1px solid;
  transition: background-color .25s, color .25s, border-color .25s;
}
.btn-surface { color: var(--gold); border-color: rgba(230, 163, 62, .6); }
.btn-surface:hover { background: var(--gold); color: #142a30; }
.btn-next {
  color: rgba(231, 217, 189, .88);
  border-color: rgba(231, 217, 189, .32);
  max-width: 60%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.btn-next:hover { border-color: rgba(231, 217, 189, .75); background: rgba(231, 217, 189, .1); }

@media (max-width: 680px) {
  .room-panel { padding-top: 13dvh; }
  .hall-ghost { top: -8dvh; }
  /* Tightened so "NEXT: LIVING ROOM" — the longest pair — fits a 375px
     screen beside "RETURN TO MAP" without the ellipsis. */
  .btn-surface, .btn-next {
    font-size: 10px;
    letter-spacing: .12em;
    padding: 13px 12px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .room-panel { transform: none !important; }
  .room-bg img { animation: none !important; }
}
html.still #room { transition: none; }
html.still #room.lit { opacity: 1; }
html.still .room-panel { transition: none; transform: none !important; }
html.still .room-bg img { animation: none !important; }

/* ======================================================================
   IV · THE GAME ROOM — the vigil and the cabinet
   The countdown never names a date or the thing it's counting to. It
   counts. That's the whole message.
   ====================================================================== */

.vigil {
  position: relative;
  background: rgba(13, 39, 47, .62);
  backdrop-filter: blur(9px);
  border: 1px solid rgba(231, 217, 189, .24);
  clip-path: var(--cut-m);
  padding: 24px clamp(18px, 3.8vw, 34px) 26px;
}
.vigil::before {
  content: '';
  position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px;
  background: var(--gold);
}
.vigil-title {
  font: italic 700 1.3rem var(--serif);
  color: var(--bone);
}
.vigil-title .th { font-size: 1rem; color: var(--gold); }

.vigil-face {
  margin-top: 18px;
  display: flex; align-items: baseline; justify-content: center;
  gap: clamp(6px, 2vw, 16px);
}
.vg-cell { display: flex; flex-direction: column; align-items: center; }
.vg-cell b {
  font: 700 clamp(1.9rem, 9vw, 3rem)/1 var(--serif);
  color: var(--bone);
  font-variant-numeric: tabular-nums;
}
.vg-cell i {
  margin-top: 9px;
  font: 400 9.5px/1 var(--sans);
  font-style: normal;
  letter-spacing: .34em; text-indent: .34em;
  text-transform: uppercase;
  color: var(--lagoon-2);
}
.vg-sep {
  font: 400 1.6rem var(--serif);
  color: var(--gold);
  animation: vgPulse 2s ease-in-out infinite;
  align-self: center;
  margin-bottom: 1.4rem;
}
.vg-sep:nth-of-type(2) { animation-delay: 1s; }
@keyframes vgPulse { 0%, 100% { opacity: 1; } 50% { opacity: .22; } }

/* No premiere hour set yet. Say so plainly rather than showing dashes. */
.vigil-unset, .vigil-done { flex-direction: column; text-align: center; gap: 8px; }
.vg-unset {
  font: italic 400 clamp(1.25rem, 4.4vw, 1.7rem)/1.3 var(--serif);
  color: var(--bone);
}
.vg-unset-note {
  font: 400 .84rem/1.55 var(--sans);
  color: rgba(231, 217, 189, .6);
  max-width: 34ch;
}

/* ---- the cabinet ---- */

.cabinet {
  margin-top: 22px;
  position: relative;
  background: rgba(7, 24, 28, .58);
  backdrop-filter: blur(9px);
  border: 1px solid rgba(230, 163, 62, .38);
  clip-path: var(--cut-m);
  padding: 0;                      /* the header and body carry their own */
}
/* Collapsed by default: the room also holds the vigil, the seed card and the
   board, and two open machines pushed all of that off a phone screen. The
   header alone tells you which game it is and what it wants from you. */
.cab-head {
  width: 100%;
  display: flex; align-items: baseline; gap: 10px 12px; flex-wrap: wrap;
  background: none; border: 0; cursor: pointer; text-align: left;
  font: inherit; color: inherit;
  padding: 18px clamp(18px, 3.8vw, 34px);
  min-height: 56px;                /* a comfortable thumb target */
}
.cab-head:focus-visible { outline: 2px solid var(--gold); outline-offset: -4px; }
.cab-hint {
  font: 400 .72rem/1 var(--sans);
  letter-spacing: .18em; text-transform: uppercase;
  color: rgba(231, 217, 189, .5);
  white-space: nowrap;
}
.cab-chev {
  margin-left: auto; flex: none;
  width: 11px; height: 11px;
  border-right: 2px solid var(--gold); border-bottom: 2px solid var(--gold);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .22s ease;
}
.cabinet.open .cab-chev { transform: rotate(-135deg) translate(-3px, -3px); }
/* Height is animated rather than display-toggled so the room doesn't jump. */
.cab-body {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .28s ease, opacity .2s ease;
  opacity: 0;
}
.cab-body > * { min-height: 0; }
.cabinet.open .cab-body { grid-template-rows: 1fr; opacity: 1; }
.cab-inner { overflow: hidden; padding: 0 clamp(18px, 3.8vw, 34px) 24px; }
@media (prefers-reduced-motion: reduce) {
  .cab-body { transition: none; }
}
.cab-title {
  font: 700 1.5rem/1.1 var(--serif);
  color: var(--gold);
  letter-spacing: -.01em;
}
.cab-title .th { font-size: .95rem; color: rgba(231, 217, 189, .7); }
.cab-art {
  display: block;
  width: 100%; height: auto;
  margin: 14px 0 2px;
}
.cab-blurb {
  margin-top: 12px;
  max-width: 48ch;
  font: 400 .95rem/1.65 var(--sans);
  color: rgba(231, 217, 189, .82);
}
/* Stands in for the play button on a desktop when the arcade is phone-only. */
.cab-locked {
  margin-top: 16px;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 14px;
  font: italic 400 .9rem/1.4 var(--sans);
  color: rgba(231, 217, 189, .62);
  border: 1px dashed rgba(231, 217, 189, .28);
  clip-path: var(--cut-s);
}
.cab-locked::before {
  content: '\1F4F1';   /* phone */
  font-style: normal; font-size: 1.05rem;
}

/* The games room runs tighter than the default rhythm — the heading was a
   long scroll from the actual game. */
#room[data-room='games'] .room-lead { margin-bottom: 12px; }
#room[data-room='games'] .vigil { padding: 14px clamp(18px, 3.8vw, 34px) 15px; }
#room[data-room='games'] .vigil-face { margin-top: 10px; }
#room[data-room='games'] .cabinet { margin-top: 10px; }
#room[data-room='games'] .cab-art { margin: 10px 0 0; }
#room[data-room='games'] .cab-blurb { margin-top: 8px; }
#room[data-room='games'] .btn-play { margin-top: 14px; }
.btn-play {
  margin-top: 20px;
  width: 100%;
  padding: 16px 0;
  background: var(--gold);
  color: #16262b;
  font: 700 11px/1 var(--sans);
  letter-spacing: .26em; text-indent: .26em;
  text-transform: uppercase;
  clip-path: var(--cut-s);
  transition: filter .2s;
}
.btn-play:hover { filter: brightness(1.1); }

/* ======================================================================
   II · LIVING ROOM — the honours board
   ====================================================================== */

.honours {
  position: relative;
  background: rgba(13, 39, 47, .62);
  backdrop-filter: blur(9px);
  border: 1px solid rgba(231, 217, 189, .24);
  clip-path: var(--cut-m);
  padding: 26px clamp(18px, 3.8vw, 34px) 24px;
}
.honours::before {
  content: '';
  position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px;
  background: var(--chili);
}
.hon-rows { margin-top: 10px; }
.hon-rows li {
  display: flex; align-items: baseline; gap: 14px;
  padding: 11px 0;
  border-top: 1px solid rgba(231, 217, 189, .14);
}
.hon-rows li:first-child { border-top: 0; }
.hon-n {
  flex: none; width: 2.2rem;
  font: 700 .85rem var(--serif);
  color: var(--gold);
  letter-spacing: .06em;
}
.hon-who { font: 700 1.06rem var(--serif); color: var(--bone); }
.hon-tag { font: italic 400 .9rem var(--serif); color: var(--gold); }
.hon-pts {
  margin-left: auto;
  font: 400 .78rem var(--sans);
  letter-spacing: .16em;
  color: var(--lagoon-2);
  font-variant-numeric: tabular-nums;
}
.hon-mine .hon-who { color: var(--gold); }
.hon-wait { opacity: .4; }

.hon-empty li { border: 0; display: block; }
.hon-empty-t {
  font: italic 400 1.28rem/1.4 var(--serif);
  color: var(--bone);
}
.hon-empty-n {
  margin-top: 8px;
  max-width: 40ch;
  font: 400 .86rem/1.6 var(--sans);
  color: rgba(231, 217, 189, .58);
}

/* ======================================================================
   III · THE TV ROOM — the film
   ====================================================================== */

.screen {
  position: relative;
  aspect-ratio: 16 / 9;
  background: #041013;
  border: 1px solid rgba(231, 217, 189, .24);
  clip-path: var(--cut-m);
  overflow: hidden;
}
.screen iframe { width: 100%; height: 100%; border: 0; display: block; }
.screen-cold {
  display: grid; place-items: center;
  padding: 24px;
}
/* The dark set, one press from lighting up. */
.tv-on {
  position: absolute; inset: 0;
  width: 100%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 16px;
  background:
    radial-gradient(60% 55% at 50% 46%, rgba(47, 143, 163, .12), transparent 70%),
    #041013;
  background-size: cover;
  background-position: center;
  transition: background-color .3s;
}
/* Solid gold at rest — it should look pressable before anyone hovers. */
.tv-glyph {
  display: grid; place-items: center;
  width: 66px; height: 66px;
  clip-path: var(--cut-s);
  border: 1px solid rgba(230, 163, 62, .6);
  background: var(--gold);
  color: #142a30;
  transition: filter .25s, transform .25s;
}
.tv-glyph svg {
  width: 24px; height: 24px; display: block;
  margin-left: 3px;                  /* optically centre the triangle */
  fill: currentColor;
}
.tv-line {
  font: 400 italic 15px/1 var(--serif);
  color: rgba(231, 217, 189, .66);
  transition: color .25s;
}
.tv-on:hover .tv-glyph { filter: brightness(1.12); transform: scale(1.06); }
.tv-on:hover .tv-line { color: var(--sand); }

/* ---- the set in the photograph ---------------------------------------- */
/* Registration matters in this room: the hotspot is projected onto the
   photo's own TV, so the Ken Burns drift is off here, and on wide screens
   the text panel moves to the right so it can't sit over the set. */
#room[data-room='tv'] .room-bg img { animation: none; }
@media (min-width: 980px) {
  #room[data-room='tv'] .room-panel {
    margin-left: auto;
    margin-right: clamp(30px, 8vw, 150px);
  }
}

/* Phones and small tablets: the text panel starts BELOW the televised set,
   so the photo's own TV — hotspot and all — works exactly as on desktop.
   The image is drawn 12% over-height and lifted, cropping empty wall off the
   top so the set rides higher; the projection maths reads the img's own
   rect, so it follows this crop automatically. The panel's padding-top then
   clears the set's bottom edge with real air before the heading.

   pointer-events: the panel's transparent padding sits OVER the set (the
   panel paints above the fixed background), and it was eating every tap on
   the television. The panel itself lets taps through; its children catch
   their own. */
@media (max-width: 980px) {
  #room[data-room='tv'] .room-bg { overflow: hidden; }
  #room[data-room='tv'] .room-bg img {
    position: absolute; left: 0; top: -12%;
    width: 100%; height: 112%;
  }
  #room[data-room='tv'] .room-panel { padding-top: 44dvh; }
  #room[data-room='tv'] .room-panel { pointer-events: none; }
  #room[data-room='tv'] .room-panel > * { pointer-events: auto; }
}

/* The set shows the film's own title frame (JS sets background-image once a
   videoId exists), dimmed under the play chip; before that, dark glass. */
#tv-hot {
  position: absolute;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 10px;
  background:
    radial-gradient(70% 60% at 50% 44%, rgba(47, 143, 163, .10), transparent 72%),
    rgba(3, 12, 15, .93);
  background-size: cover;
  background-position: center;
  border: 1px solid rgba(230, 163, 62, .4);
  box-shadow: 0 0 34px rgba(4, 14, 17, .55);
  transition: border-color .25s;
}
#tv-hot[hidden] { display: none; }
#tv-hot .tv-glyph { width: 52px; height: 52px; font-size: 16px; }
#tv-hot .tv-line { font-size: 13px; }
#tv-hot:hover { border-color: rgba(230, 163, 62, .85); }
#tv-hot:hover .tv-glyph { filter: brightness(1.12); transform: scale(1.06); }
#tv-hot:hover .tv-line { color: var(--sand); }

/* ---- the film, full screen -------------------------------------------- */
#film-max {
  position: fixed; inset: 0; z-index: 100;
  background: #02090b;
  display: grid; place-items: center;
}
#film-frame {
  width: min(100vw, calc(100dvh * 16 / 9));
  aspect-ratio: 16 / 9;
  display: grid; place-items: center;
}
#film-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.film-holding {
  max-width: 34ch; text-align: center; padding: 24px;
  font: italic 400 clamp(1rem, 3vw, 1.2rem)/1.5 var(--serif);
  color: rgba(231, 217, 189, .62);
}
/* A proper 44px target with its own solid backing, so it is unmistakably
   above the video and can't be lost against the picture or a stray hit box —
   the old bare glyph was fiddly to land on a phone. */
#film-x {
  position: absolute;
  top: max(12px, env(safe-area-inset-top));
  right: max(12px, env(safe-area-inset-right));
  z-index: 2;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  font: 400 20px/1 var(--sans);
  color: var(--bone);
  background: rgba(2, 9, 11, .55);
  border: 1px solid rgba(231, 217, 189, .35);
  border-radius: 50%;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  transition: background .2s, transform .1s;
}
#film-x:hover { background: rgba(2, 9, 11, .82); }
#film-x:active { transform: scale(.92); }
.screen-cold p {
  max-width: 34ch; text-align: center;
  font: italic 400 clamp(1rem, 3vw, 1.2rem)/1.5 var(--serif);
  color: rgba(231, 217, 189, .62);
}

/* ---- keepsakes: take your toon home ----------------------------------- */
.keepsakes {
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid rgba(231, 217, 189, .16);
}
.keep-title { font: italic 700 1.3rem var(--serif); color: var(--bone); margin: 0; }
.keep-title .th { font-size: 1rem; color: var(--gold); font-style: normal; }
.keep-note {
  margin: 6px 0 16px; max-width: 46ch;
  font: 400 .9rem/1.55 var(--sans); color: rgba(231, 217, 189, .68);
}
.keep-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
}
.keep-card {
  display: flex; flex-direction: column;
  width: 100%; padding: 0; margin: 0;
  font: inherit; color: inherit; text-align: left;
  -webkit-appearance: none; appearance: none;
  background: rgba(13, 39, 47, .55);
  border: 1px solid rgba(231, 217, 189, .18);
  clip-path: var(--cut-s);
  cursor: pointer;
  transition: border-color .2s, background .2s, transform .1s, opacity .2s;
}
.keep-card:hover, .keep-card:focus-visible {
  border-color: rgba(230, 163, 62, .55);
  background: rgba(230, 163, 62, .08);
}
.keep-card:active { transform: scale(.98); }
.keep-card.keep-busy { opacity: .5; pointer-events: none; }
.keep-thumb {
  height: 150px;
  background-repeat: no-repeat;
  background-position: center bottom;
  background-size: contain;
}
/* Group scenes are wide — centre them and let them breathe. */
.keep-group .keep-thumb { background-position: center; }
.keep-meta {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 10px 12px 11px;
  border-top: 1px solid rgba(231, 217, 189, .1);
}
.keep-meta b { font: 400 1rem var(--serif); color: var(--bone); }
.keep-meta span {
  font: 600 10px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold);
}

/* ======================================================================
   II · THE SALA — the round table
   The only circle in a build that otherwise refuses border-radius. It has
   earned it: the table really is round, and that is the whole point of it.
   ====================================================================== */

.salatable {
  position: relative;
  background: rgba(13, 39, 47, .62);
  backdrop-filter: blur(9px);
  border: 1px solid rgba(231, 217, 189, .24);
  clip-path: var(--cut-m);
  padding: 26px clamp(18px, 3.8vw, 34px) 24px;
  text-align: center;
}
.salatable::before {
  content: '';
  position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px;
  background: var(--lagoon-2);
}
.salatable .board-title { text-align: left; }

.ring {
  position: relative;
  width: min(230px, 62vw);
  aspect-ratio: 1;
  margin: 22px auto 0;
  border: 1px solid rgba(231, 217, 189, .28);
  border-radius: 50%;
}
/* the seats */
.ring i {
  position: absolute;
  width: 13px; height: 13px;
  margin: -6.5px 0 0 -6.5px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 3px rgba(230, 163, 62, .16);
}
.ring-mid {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  font: italic 400 1.15rem/1 var(--serif);
  color: rgba(231, 217, 189, .6);
  white-space: nowrap;
}
.ring-note {
  margin-top: 18px;
  font: 400 .9rem/1.6 var(--sans);
  color: rgba(231, 217, 189, .78);
}

/* ---- VIII · the Forty Gates standings --------------------------------
   Ranked by gates reached. The Living Room's honours board ranks whole-of-
   event points; these are different boards measuring different things, and
   the copy in places.js keeps them distinct. */

/* ---- the seed-card challenge ------------------------------------------ */
/* Built like the gateboard so it reads as part of the room's furniture, but
   its spine is green rather than gold — this is the garden, not the arcade. */
/* ⚠ It is also a .cabinet now, so it folds like the two machines above it and
   starts collapsed. Only the PADDING has to go — .cab-head and .cab-inner
   carry their own, and keeping this one boxed the header in a second time.
   The green identity stays: this is the garden, not the arcade, and that is
   the whole reason it does not simply look like a third machine. */
.seedbox {
  margin-top: 22px;
  position: relative;
  background: rgba(13, 39, 47, .62);
  backdrop-filter: blur(9px);
  border: 1px solid rgba(231, 217, 189, .24);
  clip-path: var(--cut-m);
  padding: 0;
}
/* Green where a cabinet is gold — the title, the chevron and the focus ring,
   so the fold reads as the same mechanism without borrowing the arcade's
   colour. */
.seedbox .cab-title { color: #7fbf6a; }
.seedbox .cab-chev { border-right-color: #7fbf6a; border-bottom-color: #7fbf6a; }
.seedbox .cab-head:focus-visible { outline-color: #7fbf6a; }
.seedbox::before {
  content: '';
  position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px;
  background: #7fbf6a;
}
/* Same type as .cab-blurb, deliberately — this block sits directly under the
   two cabinets and read as an afterthought at 13px in a different face. */
.seed-lead {
  margin-top: 10px;
  max-width: 48ch;
  font: 400 .95rem/1.65 var(--sans);
  color: rgba(231, 217, 189, .82);
}
.seed-lead b { color: var(--bone); font-weight: 700; }

/* The prize podium — three steps, the points shown not told. Green is the
   garden's colour here; the winning step and the point values borrow the
   arcade's gold so the reward still reads as a reward. */
.seed-prizes {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
}
/* All three tiers are equal — no highlighted "first". */
.sp {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 13px 6px 12px;
  background: rgba(13, 39, 47, .5);
  border: 1px solid rgba(231, 217, 189, .16);
  clip-path: var(--cut-s);
  transition: opacity .3s;
}
.sp-ic {
  width: 22px; height: 22px;
  fill: none; stroke: #7fbf6a; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.sp-pts { font: 700 1.5rem/1 var(--serif); color: var(--gold); }
/* A place already claimed: dimmed, drained of colour, struck through. What is
   left to play for is what still glows. */
.sp-taken { opacity: .4; }
.sp-taken .sp-ic { stroke: rgba(231, 217, 189, .4); }
.sp-taken .sp-pts { color: rgba(231, 217, 189, .5); text-decoration: line-through; }
.seed-prizes-note {
  margin: 8px 0 0; text-align: center;
  font: italic .8rem var(--sans); color: rgba(231, 217, 189, .58);
}

.seed-pick { position: relative; margin-top: 16px; }
#seed-input {
  width: 100%;
  padding: 14px 16px;
  /* 1rem is a floor, not a preference: iOS Safari zooms the page in on any
     input it considers smaller than 16px, which would throw the room around
     the moment a guest taps the field. */
  font: 400 1rem/1.2 var(--sans);
  color: var(--bone);
  background: rgba(4, 16, 19, .62);
  border: 1px solid rgba(231, 217, 189, .3);
  clip-path: var(--cut-s);
}
#seed-input::placeholder { color: rgba(231, 217, 189, .42); font-style: italic; }
#seed-input:focus { outline: 0; border-color: #7fbf6a; }
#seed-input:disabled { opacity: .5; }

/* The filtered list. Capped and scrollable — a thousand plants must never
   push the Submit button off the bottom of a phone. */
.seed-list {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px);
  z-index: 4;
  max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: #0b2a32;
  border: 1px solid rgba(231, 217, 189, .3);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .5);
}
.seed-list li { border-top: 1px solid rgba(231, 217, 189, .12); }
.seed-list li:first-child { border-top: 0; }
.seed-list button {
  display: block; width: 100%; text-align: left;
  padding: 13px 16px;
  background: none; border: 0; cursor: pointer;
  font: 400 .95rem/1.4 var(--sans); color: var(--bone);
}
.seed-list button:hover, .seed-list button:focus { background: rgba(127, 191, 106, .18); }

.seed-actions { display: flex; align-items: center; gap: 14px; }
.seed-actions .btn-play { flex: 1; margin-top: 16px; background: #7fbf6a; }
.seed-actions .btn-play:disabled { opacity: .4; cursor: default; filter: none; }
.seed-left {
  margin-top: 16px; flex: none;
  /* 11px/.26em to sit level with .btn-play's own lettering */
  font: 700 11px/1 var(--sans); letter-spacing: .22em; text-transform: uppercase;
  color: rgba(231, 217, 189, .55);
}
.seed-msg {
  margin-top: 12px; min-height: 1.2em;
  font: 400 .95rem/1.65 var(--sans); color: rgba(231, 217, 189, .8);
}
.seed-msg b { color: var(--bone); }
.seed-won::before { background: #b8e986; }
.seed-won .seed-msg { color: #b8e986; }
.seed-locked #seed-input { border-color: rgba(231, 217, 189, .16); }

/* The leaf beside a name on either board. */
.leaf {
  flex: none; display: inline-grid; place-items: center;
  width: 15px; height: 15px; margin-left: -4px;
}
.leaf svg { width: 100%; height: 100%; fill: #7fbf6a; }

/* Who holds each game right now. Same slot as the leaf, same size, so a name
   can carry all three without the row growing. */
.crown {
  flex: none; display: inline-grid; place-items: center;
  width: 15px; height: 15px;
}
.crown svg { width: 100%; height: 100%; fill: none; stroke-width: 2; stroke-linecap: round; }
.crown-gate svg { stroke: var(--gold); }
.crown-run svg { stroke: var(--lagoon-2, #7fccdb); }
.crown-run svg circle { fill: var(--lagoon-2, #7fccdb); stroke: none; }

/* The legend. Three short lines under the board, same marks as the rows. */
.gb-key {
  list-style: none; margin: 12px 0 0; padding: 10px 0 0;
  border-top: 1px solid rgba(231, 217, 189, .12);
  display: flex; flex-wrap: wrap; gap: 6px 18px;
}
.gb-key li {
  display: flex; align-items: center; gap: 7px;
  font: 400 .74rem/1.3 var(--sans);
  letter-spacing: .04em; color: rgba(231, 217, 189, .6);
}

/* The working-out, under an expanded row. Quiet on purpose: the row itself
   already carries the rank, the crowns and the total in colour, and repeating
   that here turned a twenty-name board into a fairground. So: no boxes, no
   fills, greyscale marks, one line. */
.gb-detail {
  flex: 0 0 100%; width: 100%; min-width: 0;
  margin: 6px 0 2px;
  display: flex; align-items: center; gap: 16px;
  overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.gb-detail::-webkit-scrollbar { display: none; }
.gbd {
  flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  white-space: nowrap;
}
.gbd b {
  font: 400 .82rem/1 var(--serif);
  color: rgba(231, 217, 189, .62);
  font-variant-numeric: tabular-nums;
}
.gbd > svg {
  width: 14px; height: 14px; flex: none;
  fill: none; stroke: rgba(231, 217, 189, .38);
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.gbd > svg circle { fill: rgba(231, 217, 189, .38); stroke: none; }
/* the seed leaf, same grey as everything else down here */
.gbd > svg path[d^="M20 4C10"] { fill: rgba(231, 217, 189, .38); stroke: none; }

/* Which pilot, said with their own face rather than an initial — they are
   both J. The little gate sits under the chin so a face means "gates". */
.gbd-toon {
  position: relative; flex: none;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: rgba(231, 217, 189, .08);
  overflow: visible;
}
.gbd-toon img {
  width: 100%; height: 100%; border-radius: 50%;
  object-fit: cover; display: block;
  filter: grayscale(1); opacity: .55;
}
/* Its own dark disc, or it reads as a smudge on the chin rather than a mark
   that means something. */
.gbd-gate {
  position: absolute; right: -5px; bottom: -4px;
  width: 13px; height: 13px;
  padding: 1.5px;
  box-sizing: border-box;
  border-radius: 50%;
  background: #0b2025;
  fill: none; stroke: rgba(231, 217, 189, .62); stroke-width: 2.8;
  stroke-linecap: round;
}
/* The one exception to the greyscale: a cleared XL is the whole scoring rule,
   so that face comes back to life. */
.gbd-cleared img { filter: none; opacity: 1; }
.gbd-cleared { box-shadow: 0 0 0 1.5px rgba(230, 163, 62, .7); }
.gbd-cleared + b { color: var(--gold); }

/* The doubling rule, stated on the cabinet itself. */
.cab-rule {
  margin-top: 12px;
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 12px;
  background: rgba(230, 163, 62, .09);
  border-left: 2px solid rgba(230, 163, 62, .55);
  font: 400 .86rem/1.55 var(--sans);
  color: rgba(231, 217, 189, .86);
}
.cab-rule-mark {
  flex: none;
  font: 700 .95rem/1 var(--serif);
  color: var(--gold);
  letter-spacing: .02em;
  padding-top: 1px;
}

.gateboard {
  margin-top: 22px;
  position: relative;
  background: rgba(13, 39, 47, .62);
  backdrop-filter: blur(9px);
  border: 1px solid rgba(231, 217, 189, .24);
  clip-path: var(--cut-m);
  /* Named, because .gb-mine has to cancel exactly this much to reach the
     panel's edges. It is a clamp, so hardcoding "10px" over there — which is
     what the first attempt did — is wrong at every viewport width. */
  --board-pad-x: clamp(18px, 3.8vw, 34px);
  padding: 26px var(--board-pad-x) 22px;
}
.gateboard::before {
  content: '';
  position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px;
  background: var(--gold);
}
.gb-rows { margin-top: 10px; }
.gb-rows li {
  /* wrap so the expanded working-out gets a line of its own; without it the
     strip was squeezed into whatever space was left beside the name. */
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 10px 0;
  border-top: 1px solid rgba(231, 217, 189, .14);
}
.gb-rows li:first-child { border-top: 0; }
/* ⚠ GOLD MEANS EXACTLY ONE THING ON THIS BOARD (2026-08-24): this changed
   since you last looked. Nothing else may use it, in any column.

   That is now literally true, and it took three passes to get there.

   First the top three totals were gold AND the top three rank numerals were
   red — so gold said "podium", which is the one thing the numeral beside it
   already said, and there was no colour left over for the thing that
   actually needed one. Moving the podium mark onto the numeral fixed the red
   but not the real problem: three gold numerals down the left still competed
   with the gold totals on the right, and a guest cannot tell two golds apart
   across a phone row. So the podium gave up its colour entirely — it is the
   top of a list that is sorted, and the order says the rest.

   Third pass: your OWN name was gold too, with "— that's you" beside it.
   Same fault, one column over. That is a row tint now (see .gb-mine).

   The board is left with exactly two marks and no legend: gold means the
   number changed, a signed chip means the place changed. Everything else
   about a row is the row. */
.gb-n {
  flex: none; width: 2.1rem;
  font: 700 .82rem var(--serif);
  color: rgba(231, 217, 189, .45);
  letter-spacing: .06em;
}
.gb-who { font: 700 1.04rem var(--serif); color: var(--bone); }
/* "— that's you" is still IN the row and still read aloud; it is just not
   drawn any more. Your own row is now marked by its background (see .gb-mine
   below), which needs no words — but a tint is invisible to a screen reader,
   and the one row a guest most needs to find is their own. So the sentence
   stays, hidden the standard way rather than deleted. */
.gb-tag {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* A ROW, not a column: the delta arrow sits beside the total, not above it.
   (It stacked because an older design put a second line of small text under
   the score. Nothing has emitted that since the breakdown moved into the
   tapped table, and the rule went with it.) */
.gb-score {
  margin-left: auto; text-align: right;
  display: flex; align-items: baseline; gap: 6px;
}
.gb-gates {
  font: 700 .95rem var(--serif);
  color: var(--sand);
  letter-spacing: .06em;
}
/* the number beside the total, only while that row's detail is open
   (round 19, item 3b) */
.gb-num {
  font: 400 .72rem var(--sans);
  color: rgba(231, 217, 189, .5);
}
/* YOUR OWN ROW, marked by the row itself rather than by anything inside it.
   It used to be a gold name plus an italic "— that's you" hanging off the end
   of it, which put a THIRD gold thing on a board where gold had just been
   reserved for "this number changed", and put words in the middle of a line
   whose whole job is to be scanned.

   A tint has no such problem: it marks the row without occupying any of it,
   it cannot be confused with a signal about scores, and the eye finds it
   before reading a single name. Deliberately NOT gold, and deliberately
   quiet — it says "you are here", not "look at this".

   The negative margin lets the tint reach the edges of the board rather than
   stopping at the text, which is what makes it read as a highlighted row and
   not as a coloured box sitting inside one. */
/* ⚠ SELECTOR MUST OUT-SPECIFY `.gb-rows li`. Written as plain `.gb-mine` the
   first time, which is 0-1-0 against that rule's 0-1-1 — so the margin landed
   (nothing else sets margin) and the padding meant to cancel it did NOT, and
   every rank numeral on this one row sat ten pixels left of the column. The
   symptom looks like a highlight bug and is a specificity bug. */
.gb-rows li.gb-mine {
  /* Out to the panel's edges and back again, so the tint spans the full box
     while the contents stay in the same column as every other row. */
  margin-inline: calc(-1 * var(--board-pad-x));
  padding-inline: var(--board-pad-x);
  background: rgba(244, 236, 218, .06);
  /* The rule above the block is this row's own border-top and is full-width.
     The rule BELOW it belongs to the next row and is inset, so the block used
     to be wide on top and narrow underneath. Give it its own bottom edge and
     take the next row's top edge away — one line each, both full-width. */
  border-bottom: 1px solid rgba(231, 217, 189, .14);
}
.gb-rows li.gb-mine + li { border-top: 0; }
/* .gb-seat — the top three — deliberately carries NO STYLE AT ALL now. See
   the note on .gb-n for why the podium gave up its colour. rooms.js still
   puts the class on the row, and it is kept on purpose: a bounty or a
   golden-hour card wanting to mark the leader has a hook waiting, and it
   costs nothing. If you are hunting for why the top three look like every
   other row — this is why, and it was the point. */
/* This total is not the same total you saw last time. The ONLY meaning gold
   has anywhere on this board. */
.gb-gates.changed { color: var(--gold); }
.gb-wait { opacity: .4; }

.gb-empty li { border: 0; display: block; }
.gb-empty-t { font: italic 400 1.24rem/1.4 var(--serif); color: var(--bone); }
.gb-empty-n {
  margin-top: 8px; max-width: 40ch;
  font: 400 .86rem/1.6 var(--sans);
  color: rgba(231, 217, 189, .58);
}
.gb-foot {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid rgba(231, 217, 189, .14);
  font: 400 .8rem/1.5 var(--sans);
  color: rgba(231, 217, 189, .5);
}

/* Gate Board tap-for-detail (villa-run join, 15 Aug) */
.gb-rows li { cursor: pointer; }
.gb-detail { flex-basis: 100%; width: 100%; margin: 6px 0 2px; font-size: 11.5px; letter-spacing: .04em; color: rgba(231, 217, 189, .68); }

/* Fallback mark for a ledger source the server has named but this build has
   no glyph for — its points still show rather than the chip rendering empty. */
.gbd-dot {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: .55; margin-right: 2px;
}


/* ═══ THE LEADERBOARD BREAKDOWN ══════════════════════════════════════════
   Ported from public/board-mock.html. One line per source the SERVER names,
   plus boost cards drawn as cards once the Sala starts paying out.

   Greyscale and unboxed on purpose: the row above it already carries the
   rank, the medals and the total, so the working-out must not compete with
   the thing it is explaining. */
.gb-medal {
  display: inline-grid; place-items: center;
  width: 17px; height: 17px; margin-left: 5px;
  vertical-align: -3px;
}
.gb-medal svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 1.7; }
.gb-medal.gates { color: var(--gold); }
.gb-medal.villa { color: var(--lagoon-2); }
.gb-medal.crash { color: #d4553f; }
.gb-medal.leaf  { color: #7a995c; }
.gb-medal.leaf svg { fill: currentColor; stroke: none; }

/* ---- the tapped detail, rebuilt ---------------------------------------
   Replaces .gb-detail's horizontal icon strip. Still deliberately quiet —
   the row above carries the colour, so this is greyscale, unboxed, and
   carries no total (that is six pixels away on the row itself). */
.bd {
  margin: 9px 0 4px;
  padding: 10px 0 2px;
  border-top: 1px solid rgba(231, 217, 189, .12);
  display: grid; gap: 1px;
}
.bd-row {
  /* Wide enough for the gate AND a face — see .mk-pair. Every other source
     draws one 15px glyph and simply centres in the same column. */
  display: grid; grid-template-columns: 30px 1fr auto;
  gap: 10px; align-items: center;
  padding: 5px 0;
  font: 400 .86rem var(--sans);
  color: rgba(231, 217, 189, .72);
}
.bd-row .i { display: grid; place-items: center; color: rgba(231, 217, 189, .5); }
.bd-row .i svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.bd-row .i img { width: 19px; height: 19px; border-radius: 50%; object-fit: cover; object-position: 50% 12%; }

/* THE GATES, AND WHICH SWIM. Forty Gates is two lines on this table, one per
   pilot, and the face alone was ambiguous — two portraits with no gate
   between them read as two different games rather than two runs at the same
   one. So both marks, the gate first because that is what the line IS, and
   the face tucked against it because that is which of them. */
.mk-pair {
  display: inline-flex; align-items: center;
  width: 30px; justify-content: center;
}
.mk-pair svg { flex: none; }
.mk-pair img {
  width: 17px; height: 17px; margin-left: -3px;
  border: 1px solid rgba(231, 217, 189, .35);
  /* The gate is line art on the room's ink; without a ground behind it the
     face's own background bleeds into the strokes it overlaps. */
  background: var(--ink, #0c2126);
}
.bd-row .l { min-width: 0; }
.bd-row .l em {
  display: block; font-style: normal;
  font: 400 .72rem var(--sans); color: rgba(231, 217, 189, .42);
}
.bd-row .v {
  font: 700 .86rem var(--serif); color: var(--sand);
  font-variant-numeric: tabular-nums;
  /* "CCCXIV (314)" is ~40px wider: the label wraps, the value never clips */
  white-space: nowrap;
}
.bd-row.neg .v { color: var(--chili); }

/* ---- a rolled-up line, and the + that itemises it ----------------------
   One cabinet pays twenty deeds. Rolled into a line per episode they stop
   burying the other rooms, and the + is how somebody who wants the twenty
   still gets them. The control is a ghost square in the site's own cut
   corner — the same shape language as every other small control here — and
   the only round thing on this board remains the medal disc. */
/* ⚠ RESERVED ON EVERY ROW, not just the ones that have a +. Scoped to the
   rolled-up row alone, the control's column pushed that row's value 36px
   left of every other value on the table and the numbers stopped lining up
   — which on a board whose whole job is numbers reads as a bug. A row with
   no + leaves the column empty. */
.bd > .bd-row,
.bd-sub .bd-row { grid-template-columns: 30px 1fr auto 26px; }
.bd-more {
  width: 26px; height: 26px; padding: 0;
  display: grid; place-items: center;
  font: 400 1rem var(--sans); line-height: 1;
  color: rgba(231, 217, 189, .6);
  background: rgba(231, 217, 189, .06);
  border: 1px solid rgba(231, 217, 189, .18);
  clip-path: var(--cut-s);
  cursor: pointer;
}
.bd-more:hover { color: var(--sand); border-color: rgba(231, 217, 189, .34); }
.bd-more[aria-expanded="true"] {
  color: var(--gold); border-color: rgba(230, 163, 62, .45);
  background: rgba(230, 163, 62, .1);
}
/* ⚠ .bd is display:grid, so its children are grid items and the UA's
   [hidden]{display:none} loses to .bd-sub's own display. Say it here, or the
   sub rows show from the first paint and the + only ever collapses. */
.bd-sub { display: grid; gap: 1px; }
.bd-sub[hidden] { display: none; }
.bd-sub-row {
  /* 90% of the line above it, indented under the LABEL rather than the
     glyph: these are the parts of one line, not lines of their own. */
  font-size: .78rem;
  padding: 3px 0 3px 12px;
  color: rgba(231, 217, 189, .58);
  border-left: 1px solid rgba(231, 217, 189, .12);
  margin-left: 8px;
}
.bd-sub-row .v { font-size: .78rem; color: rgba(231, 217, 189, .72); }
.bd-sub-row.neg .v { color: var(--chili); }

/* A cleared XL doubles that pilot; say so where the number is, or the guest
   does the sum, gets a different answer, and stops believing the board. */
/* ---- a boost card, on the row it changed -------------------------------
   A card that permanently moved somebody's total is not a "source" like the
   arcade or trivia — it is an EVENT, with a name, a person behind it and a
   number. So it is drawn as a little card rather than as another ledger
   line, and it says who did it to you. This is the row's answer to "why is
   my score different from yesterday". */
.bd-card {
  display: grid; grid-template-columns: 22px 1fr auto;
  gap: 10px; align-items: center;
  margin-top: 5px; padding: 7px 9px 7px 7px;
  background: rgba(244, 236, 218, .07);
  border: 1px solid rgba(231, 217, 189, .22);
  clip-path: var(--cut-s);
}
.bd-card .pip {
  width: 16px; height: 22px;
  background: linear-gradient(158deg, rgba(24,71,82,.95), rgba(11,38,46,.95));
  border: 1px solid rgba(230,163,62,.5);
  display: grid; place-items: center;
  font: 700 .5rem var(--serif); color: var(--gold);
}
.bd-card .nm { font: 700 .84rem var(--serif); color: var(--bone); }
.bd-card .by { display: block; font: 400 .7rem var(--sans); color: rgba(231,217,189,.45); }
.bd-card .amt { font: 700 .88rem var(--serif); font-variant-numeric: tabular-nums; color: var(--gold); }
.bd-card.neg .amt { color: var(--chili); }
.bd-card.neg .pip { border-color: rgba(212,85,58,.6); color: var(--chili); }
/* An active card that has not moved a number yet still belongs here — it is
   about to. It just has a state where the others have an amount. */
.bd-card.live .amt { font: 700 .56rem var(--sans); letter-spacing: .1em; text-transform: uppercase; color: var(--lagoon-2); }
.bd-card.live .pip { border-color: rgba(127,204,219,.6); color: var(--lagoon-2); }

/* Your own row gets one extra line: what moved, and when. With points
   arriving from eight places a total changes for reasons you did not watch
   happen, and "you are on 340" without "+50, Skim, two hours ago" is how a
   board becomes something people argue about. */
.bd-last {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px dashed rgba(231, 217, 189, .16);
  font: 400 .78rem/1.45 var(--sans); color: rgba(231, 217, 189, .55);
}
.bd-last b { color: var(--gold); font-weight: 700; }

/* ---- the board under an effect ----------------------------------------- */
/* THE FOG: places, not points. The board must actually stop showing numbers,
   or the card did nothing and everyone knows it. */
body.fx-fog .gb-score b { visibility: hidden; position: relative; }
body.fx-fog .gb-score b::after {
  content: '—'; visibility: visible;
  position: absolute; inset: 0; display: grid; place-items: center;
  color: rgba(231, 217, 189, .45);
}
/* Tapping a row under the Fog must not silently do nothing — "broken" and
   "a card is doing this to you" look identical when the answer is no
   response at all. The breakdown is replaced by a line saying why. */
body.fx-fog .bd, body.fx-fog .bd-last { display: none; }
.bd-fog {
  margin: 9px 0 2px; padding: 9px 0 2px;
  border-top: 1px solid rgba(231, 217, 189, .12);
  font: italic 400 .84rem/1.5 var(--serif); color: rgba(231, 217, 189, .55);
}
body.fx-fog .gb-foot { opacity: .35; }

/* GOLDEN HOUR: the whole board is worth double while it runs, so the board
   is lit rather than annotated. */
body.fx-golden .gateboard {
  border-color: rgba(230, 163, 62, .5);
  box-shadow: 0 0 0 1px rgba(230, 163, 62, .18), 0 0 40px rgba(230, 163, 62, .12) inset;
}

/* A BOUNTY marks one row, and it is the leader's. */

/* The one state the mock never had: a guest on the board with no points from
   anywhere. Reachable the moment somebody appears without having played. */
.bd-none {
  padding: 10px 0; margin: 0;
  font: 400 .86rem var(--sans); color: rgba(231, 217, 189, .45);
}

/* .gb-detail was a FLEX ROW of chips; it now holds one .bd block. A flex item
   shrink-wraps by default, so without this the breakdown lays itself out at
   its minimum content width — labels crushed to a couple of characters — no
   matter how much room the row actually has. */
.gb-detail > .bd { flex: 1 1 100%; min-width: 0; }

/* ═══ WHAT HAS MOVED ═════════════════════════════════════════════════════
   Points arrive from eight rooms, mostly overnight, and until now the board
   answered "why is this different from last time" with silence.

   TWO ARROWS AND A COLOUR, and WHERE the arrow sits is what it means:

     arrow + place, beside the NAME    they are in a different place
     arrow beside the SCORE            the number went up, nobody overtaken
     a gold total                      this score changed since you looked
     nothing, sand total               untouched since you were last here

   Never both arrows on one row: place movement is the bigger news and the
   name carries it. "Gold, no arrow anywhere" is impossible by construction —
   every changed score gets one or the other — which matters because a colour
   on its own cannot say which WAY a number went.

   No chips, no badges, no dots. Each of those was a fourth coloured thing
   competing with a rank, a name, three medals and a total on a phone row —
   and the working-out is one tap away, which is where the words belong. */
.gb-since {
  margin: 8px 0 0;
  font: 400 .78rem/1.4 var(--sans);
  letter-spacing: .02em;
  color: var(--gold);
}
.gb-since[hidden] { display: none; }

.gb-move {
  flex: none;
  /* Sized up from .68rem — at that size the numeral beside the arrow was
     unreadable at arm's length on a phone, which is the only way anybody
     reads this board. */
  font: 700 .82rem/1 var(--sans); letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  color: var(--gold);
  white-space: nowrap;
  vertical-align: 1px;
}
/* A slide down is the arithmetic consequence of somebody else's rise, not a
   demotion notice — the same call podiumNews() makes about what to announce.
   It is drawn, so the row can be tapped for the reason, and left uncoloured
   so nobody's evening is spent looking at it. */
.gb-move.down { color: rgba(231, 217, 189, .38); font-weight: 400; }

/* The score-side arrow. Same weight as the name-side one so the two read as
   one language, and no numeral: how far it moved is in the tapped table, and
   a second number beside the total is just something to misread as part of
   it. */
.gb-delta {
  flex: none; font-style: normal;
  font: 400 .82rem/1 var(--sans);
  color: var(--gold);
}
/* Red HERE and grey beside the name, and the difference is real: a name-side
   down-arrow means somebody else rose past you, which is nobody's fault. A
   score-side one means points were actually taken off you. */
.gb-delta.down { color: var(--chili); }

/* The differences, under the totals they explain. Same table, one rule
   between them, and the heading says where they moved TO — which is the
   sentence the notification would have used. */
.bd-moves {
  margin-top: 9px; padding-top: 8px;
  border-top: 1px dashed rgba(231, 217, 189, .16);
}
.bd-moves-h {
  margin: 0 0 3px;
  font: 600 .62rem/1 var(--sans); letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold);
}
/* Signed, so a gain and a loss are told apart before the number is read. */
.bd-moves .bd-row .v { color: var(--gold); }
.bd-moves .bd-row.neg .v { color: var(--chili); }
