/* GetLuck Rooms (GL-157) — poker-lobby layer.
 *
 * Amal, 2026-09-03: three rooms are three ROWS of one table, not three
 * tiles. Metric labels live once in the column head; the row carries
 * numbers only. Ticket price and round bank are the only two "large"
 * numbers per row — everything else is deliberately smaller.
 *
 * Theme-safe by construction: every neutral comes from the site's own
 * custom properties (--surface-1/--surface-2/--bone/--ash/--gold/
 * --violet/--void/--hairline). --surface-lift is NOT used: it is a dark
 * gradient the light theme never overrides, so it turns rows black.
 *
 * Metals are literal hex because they ARE the brand signal, but they are
 * squeezed to a 3px rail, one number and one progress fill — a thin line
 * of saturated colour reads on any background, a large tint does not.
 * As TEXT the metals get a darkened twin (--metal-ink) in the light
 * theme, because #e0a913 on #fdfcf8 is 2.08:1 and unreadable.
 */

#roomsWidget[hidden] { display: none !important; }

/* ---------- the table ---------- */

.rooms-lobby {
  /* One template shared by the column head and every row, so numbers
     sit under their label without a second source of truth.
     name | ticket | bank | entries | ends in | progress | action */
  /* Minimums must exist (otherwise names and banks get squeezed to a
     few pixels) but they must also fit. The seven-column table needs
     ~700px of real estate; below that the two-line mobile fold is used
     instead — see the media query at the bottom of this file, which is
     why the breakpoint is 700px and not 560px. */
  /* Column widths sized for numbers the game can actually produce, not
     for the happy path. A bank can reach six digits plus decimals and
     an entry count five digits; if the track is narrower, the figure
     gets an ellipsis and the player reads the wrong amount. Money must
     never be truncated — better a tighter progress bar. */
  --cols: minmax(72px, 1.05fr) 100px minmax(112px, 1fr) 74px 64px 56px 104px;
  width: 100%;
  /* Three rows stretched over --page-max look empty; 720px keeps the
     list dense enough to scan in one glance. */
  max-width: 720px;
  min-width: 0;
  border: 1px solid var(--hairline, rgba(255, 255, 255, .12));
  border-radius: var(--radius-card, 16px);
  background: var(--surface-1, #0b0c0d);
  overflow: hidden;
}

/* ---------- column head: every metric label, exactly once ---------- */

.rooms-head {
  display: grid;
  grid-template-columns: var(--cols);
  gap: 12px;
  align-items: center;
  padding: 9px 14px 9px 18px;
  background: var(--surface-2, #101113);
  border-bottom: 1px solid var(--hairline, rgba(255, 255, 255, .12));
}

.rooms-head span {
  font: 400 10px/1.2 var(--font-mono, "JetBrains Mono", monospace);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ash, #a1a4a5);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- row ---------- */

.room {
  --metal: #e0a913;
  --metal-ink: var(--metal);
  --metal-soft: rgba(224, 169, 19, .18);
  position: relative;
  display: grid;
  grid-template-columns: var(--cols);
  gap: 12px;
  align-items: center;
  padding: 10px 14px 10px 18px;
  min-height: 64px;
  border-top: 1px solid var(--hairline, rgba(255, 255, 255, .12));
  /* No transform on hover: a row that jumps inside a list reads as a
     rendering defect, unlike a tile that lifts. */
  transition: background-color .18s ease;
}

.rooms-list .room:first-child { border-top: 0; }

/* Metal rail: the room's identity, 3px wide, full row height. */
.room::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--metal);
  transition: width .18s ease;
}

.room.is-clickable { cursor: pointer; }
.room.is-clickable:hover { background: rgba(255, 255, 255, .045); }
.room.is-clickable:hover::before { width: 5px; }

/* Light theme highlights by darkening; lightening a near-white row
   does nothing visible. */
body.layout-b.theme-light .room.is-clickable:hover { background: rgba(0, 0, 0, .04); }

/* "You are here": the whole row is flooded, so it is recognised before
   a single word is read.
   Two layers on purpose — a metal tint alone over the near-white light
   surface was a 1.05:1 step and invisible. --surface-2 supplies the
   value step (the row visibly darkens), the tint supplies the hue. */
.room.is-active,
body.layout-b.theme-light .rooms-list .room.is-active {
  background-color: var(--surface-2, #101113);
  background-image: linear-gradient(var(--metal-soft), var(--metal-soft));
}

.room.is-active::before { width: 5px; }

/* The live theme-light.css styles `.room` as a CARD:
   `body.layout-b.theme-light .room { background: var(--surface-1);
   box-shadow: 0 8px 26px ... }`. That selector is (0,3,1) — it beat
   `.room.is-active` (0,2,0) and silently erased the active fill, and its
   per-card shadow bands a stacked list. Both are neutralised here with a
   selector that wins on specificity, not on file order, because load
   order differs between the preview and production. */
body.layout-b.theme-light .rooms-list .room {
  box-shadow: none;
  border-color: var(--hairline, #ddd5c2);
}

body.layout-b.theme-light .rooms-list .room:not(.is-active) {
  background: transparent;
}

/* "Opening soon": every number drops to --ash (LOBBY-SPEC §3.6). The
   price loses its metal too — a room you cannot enter should not
   advertise its tier with the loudest colour in the row. The rail stays,
   so the list still reads as three metals. */
.room.is-soon .room-name,
.room.is-soon .room-price b,
.room.is-soon .room-bank b,
.room.is-soon .room-entries b,
.room.is-soon .room-timer b { color: var(--ash, #a1a4a5); }

/* Metals. Literal on purpose: a copper room must look copper in both
   themes. Ink twins are darkened for the light theme only. */
.room--copper { --metal: #c87f4e; --metal-soft: rgba(200, 127, 78, .18); }
.room--silver { --metal: #9aa6b5; --metal-soft: rgba(154, 166, 181, .18); }
.room--gold   { --metal: #e0a913; --metal-soft: rgba(224, 169, 19, .18); }

/* Measured with theme/audit_lobby.py against #fdfcf8 / #eee8da and the
   composited active fill — every one clears 4.5:1. */
body.layout-b.theme-light .room--copper { --metal-ink: #8e5a37; --metal-soft: rgba(200, 127, 78, .16); }
body.layout-b.theme-light .room--silver { --metal-ink: #5f6770; --metal-soft: rgba(154, 166, 181, .16); }
body.layout-b.theme-light .room--gold   { --metal-ink: #82620b; --metal-soft: rgba(224, 169, 19, .16); }

/* The active row sits on a darker fill, so its ink drops one more step.
   Gold is the tightest pair (4.61:1) and the reason this block exists. */
body.layout-b.theme-light .room--copper.is-active { --metal-ink: #825333; }
body.layout-b.theme-light .room--silver.is-active { --metal-ink: #585f67; }
body.layout-b.theme-light .room--gold.is-active   { --metal-ink: #795907; }
body.layout-b.theme-light .room.is-active .room-bank b { color: #795907; }

/* ---------- cells ---------- */

.room-name-cell { min-width: 0; }

.room-name {
  display: block;
  font: 600 13px/1.25 var(--font-ui, Inter, system-ui, sans-serif);
  color: var(--bone, #f0f0f0);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Ticket price — the only real difference between the rooms, therefore
   the largest thing in the row. */
.room-price {
  display: flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
}

.room-price b {
  /* clamp() lets a 5-digit price shrink its own type instead of being
     cut off. 12500 is not a realistic ticket, but a room priced in
     thousands must still read as a number, not as "125...". */
  font: 700 clamp(19px, 1.9vw, 28px)/1 var(--font-ui, Inter, system-ui, sans-serif);
  letter-spacing: -.02em;
  color: var(--metal-ink);
  /* A four- or five-digit ticket price used to run straight over the
     bank column. Numbers must shrink inside their own cell, never lean
     on the neighbour: the columns are the only thing telling the player
     which number is which. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.room-price span { flex: 0 0 auto;
  font: 400 10px/1 var(--font-mono, "JetBrains Mono", monospace);
  letter-spacing: .06em;
  color: var(--ash, #a1a4a5);
}

/* Desktop: the mobile line-2 wrapper must not exist as a box. */
.room-meta { display: contents; }

.room-bank,
.room-entries,
.room-timer { min-width: 0; }

/* nowrap keeps a number on one line, but without overflow control it
   simply runs into the next column. Clip inside the cell instead. */
.room-bank b {
  font: 600 20px/1.1 var(--font-mono, "JetBrains Mono", monospace);
  color: var(--gold, #ffca16);
  white-space: nowrap;
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

.room-entries b {
  font: 500 16px/1.1 var(--font-mono, "JetBrains Mono", monospace);
  color: var(--bone, #f0f0f0);
  white-space: nowrap;
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* One activity marker, icon-sized: a live dot in the room's metal.
   Drawn in CSS so the translator has no text node to chew on. */
.room-entries b::before {
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  margin-right: 5px;
  border-radius: 50%;
  background: var(--metal);
  vertical-align: middle;
}

.room.is-soon .room-entries b::before { background: var(--hairline, #292d30); }

.room-timer b {
  font: 500 15px/1.1 var(--font-mono, "JetBrains Mono", monospace);
  color: var(--bone, #f0f0f0);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.room-timer.is-urgent b { color: var(--red, #ff9592); }

/* ---------- progress: a hint, not a metric ---------- */

.room-prog { min-width: 0; }

.room-prog .prog-track {
  position: relative;
  height: 4px;
  border-radius: 3px;
  background: var(--surface-2, #101113);
  overflow: hidden;
}

body.layout-b.theme-light .room-prog .prog-track { background: var(--hairline, #ddd5c2); }

.room-prog .prog-fill {
  height: 100%;
  border-radius: 3px;
  background: var(--metal);
  transition: width .4s ease;
}

/* Full bar: the rail glows and a CSS-drawn tick appears. No text. */
.room-prog.is-full .prog-track { box-shadow: 0 0 0 1px var(--metal); }

.room-prog.is-full::after {
  content: "";
  display: block;
  width: 5px;
  height: 9px;
  margin: 4px 0 0 auto;
  border: solid var(--metal);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ---------- action ---------- */

.room-act { display: flex; justify-content: flex-end; }

.room .enter {
  width: 100%;
  border: none;
  border-radius: var(--radius-ctl, 9px);
  background: var(--violet, #9281f7);
  color: var(--void, #000);
  font: 500 13px/1 var(--font-ui, Inter, system-ui, sans-serif);
  padding: 10px 8px;
  cursor: pointer;
  white-space: nowrap;
}

.room .enter:hover { filter: brightness(1.08); }

/* The default UA ring is a 1px black outline that vanishes on the
   violet button and on the dark surface behind it. Keyboard users need
   to see where they are, so the widget draws its own. */
.room .enter:focus-visible,
.room.is-clickable:focus-visible {
  outline: 2px solid var(--bone, #f0f0f0);
  outline-offset: 2px;
  border-radius: var(--radius-ctl, 9px);
}

.room .enter.locked {
  background: transparent;
  color: var(--ash, #a1a4a5);
  font: 400 11px/1 var(--font-mono, "JetBrains Mono", monospace);
  letter-spacing: .06em;
  text-transform: uppercase;
  cursor: not-allowed;
  padding: 10px 0;
  text-align: right;
}

/* "You are here" — a pill, not a button: there is nothing left to press. */
.room .here {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid var(--metal);
  background: transparent;
  font: 500 10px/1 var(--font-mono, "JetBrains Mono", monospace);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--metal-ink);
  white-space: nowrap;
}

.room .here i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--metal);
  flex: 0 0 auto;
}

/* ---------- footnote: the 10% fee, said once instead of three times ---------- */

.rooms-foot {
  max-width: 720px;
  margin: 10px 0 0;
  font: 400 11px/1.5 var(--font-mono, "JetBrains Mono", monospace);
  color: var(--ash, #a1a4a5);
}

/* ============================================================
   Mobile: the row folds into two lines. It never becomes a tile
   again. Hard budget: <= 84px per row, so all three rooms plus the
   section heading fit on a 430px screen with no scrolling.
   ============================================================ */

/* ============================================================
   The two-line fold.

   Driven by CONTAINER width, not viewport width. On production the
   lobby sits in a 592px sidebar column inside a 1280px page: a
   viewport media query said "desktop", the seven-column table did not
   fit its 592px box, room names were clipped to "Copper R…" and the
   Enter button hung 81px outside. Container queries ask the only
   question that matters — how much room does this widget actually
   have. The viewport media query below stays as a fallback for
   browsers without container query support.
   ============================================================ */

.rooms-lobby { container-type: inline-size; container-name: lobby; }

@container lobby (max-width: 700px) {
  .rooms-head { display: none; }

  .room {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name  price"
      "meta  act";
    gap: 4px 10px;
    padding: 12px 14px;
    align-items: center;
  }

  .room-name-cell { grid-area: name; min-width: 0; }
  .room-price { grid-area: price; justify-self: end; }
  .room-price b { font-size: 22px; }

  .room-meta {
    grid-area: meta;
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    overflow: hidden;
    flex-wrap: nowrap;
  }

  .room-bank,
  .room-entries,
  .room-timer {
    display: inline-flex;
    align-items: baseline;
    white-space: nowrap;
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
  }

  .room-meta > * + *::before {
    content: "\00b7";
    margin-right: 6px;
    color: var(--ash, #a1a4a5);
  }

  .room-bank::before {
    content: attr(data-label);
    margin-right: 5px;
    font: 400 10px/1 var(--font-mono, "JetBrains Mono", monospace);
    letter-spacing: .06em;
    color: var(--ash, #a1a4a5);
  }

  .room-bank b { font-size: 13px; }
  .room-entries b,
  .room-timer b { font-size: 12px; color: var(--ash, #a1a4a5); }
  .room-entries b::before { width: 4px; height: 4px; margin-right: 4px; }

  /* Progress becomes a hairline along the bottom edge of the row. */
  .room-prog {
    grid-area: prog;
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 0;
  }

  .room { position: relative; grid-template-areas: "name price" "meta act"; }
  .room-prog .prog-track { height: 2px; border-radius: 0; width: 100%; }

  .room-act { grid-area: act; justify-self: end; }

  .room .enter {
    width: 36px;
    height: 36px;
    padding: 0;
    border-radius: 50%;
    font-size: 0;
    position: relative;
  }

  .room .enter::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 8px;
    height: 8px;
    margin: -4px 0 0 -5px;
    border: solid var(--void, #000);
    border-width: 2px 2px 0 0;
    transform: rotate(45deg);
  }

  .room .enter::before {
    content: "";
    position: absolute;
    left: -4px;
    top: -4px;
    right: -4px;
    bottom: -4px;
  }

  .room .enter.locked {
    width: auto;
    height: auto;
    border-radius: 0;
    font-size: 10px;
    padding: 0;
  }

  .room .enter.locked::after,
  .room .enter.locked::before { display: none; }

  .room .here { padding: 5px 10px; }
}

/* The two-line fold. Breakpoint is 700px, not 560px: between those two
   widths the seven-column table does not fit its own box — the row
   overflowed and pushed the Enter button outside the lobby. The fold is
   the honest layout for anything narrower than a real table. */
@media (max-width: 700px) {
  .rooms-lobby { max-width: none; }

  /* Three rows x two lines carry their own context; the head would
     wrap at 430px and cost more height than it explains. */
  .rooms-head { display: none; }

  .room {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name  price"
      "meta  act";
    gap: 4px 10px;
    align-items: center;
    padding: 8px 12px 9px 16px;
    min-height: 0;
  }

  .room-name-cell { grid-area: name; align-self: end; }
  .room-price { grid-area: price; justify-self: end; align-self: end; }
  .room-price b { font-size: 22px; }

  /* Line 2: one mono strip, dot-separated. */
  .room-meta {
    grid-area: meta;
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    overflow: hidden;
    /* Never break the second line: a wrap tore "BANK" away from its
       number and left the label sitting above a bare digit, so the
       player could not tell which figure was the bank. */
    flex-wrap: nowrap;
  }

  /* Each metric is one unbreakable unit: label, separator and value
     travel together or not at all. */
  .room-bank,
  .room-entries,
  .room-timer {
    display: inline-flex;
    align-items: baseline;
    white-space: nowrap;
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
  }

  .room-meta > * + *::before {
    content: "\00b7";
    margin-right: 6px;
    color: var(--ash, #a1a4a5);
  }

  /* Three bare numbers in a row need one anchor word; the rest is
     self-describing. Generated content, so the translator cannot
     reach it. */
  .room-bank::before {
    content: attr(data-label);
    margin-right: 5px;
    font: 400 10px/1 var(--font-mono, "JetBrains Mono", monospace);
    letter-spacing: .06em;
    color: var(--ash, #a1a4a5);
  }

  .room-bank b { font-size: 13px; }
  .room-entries b,
  .room-timer b { font-size: 12px; color: var(--ash, #a1a4a5); }
  .room-entries b::before { width: 4px; height: 4px; margin-right: 4px; }

  /* Progress becomes a loading-style hairline on the bottom edge. */
  .room-prog {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
  }

  .room-prog .prog-track { height: 2px; border-radius: 0; }
  .room-prog.is-full .prog-track { box-shadow: none; }
  .room-prog.is-full::after { display: none; }

  .room-act { grid-area: act; justify-self: end; }

  /* The whole row is the tap target on mobile; the chevron only shows
     direction. It still carries its own 44x44 hit area. */
  .room .enter {
    width: 36px;
    height: 36px;
    padding: 0;
    border-radius: 50%;
    font-size: 0;
    position: relative;
  }

  .room .enter::after {
    content: "";
    position: absolute;
    left: 13px;
    top: 13px;
    width: 8px;
    height: 8px;
    border: solid var(--void, #000);
    border-width: 2px 2px 0 0;
    transform: rotate(45deg);
  }

  .room .enter::before {
    content: "";
    position: absolute;
    left: -4px;
    top: -4px;
    right: -4px;
    bottom: -4px;
  }

  .room .enter.locked {
    width: auto;
    height: auto;
    border-radius: 0;
    font-size: 10px;
    padding: 0;
  }

  .room .enter.locked::after,
  .room .enter.locked::before { display: none; }

  .room .here { padding: 5px 9px; font-size: 9px; }

  .rooms-foot { max-width: none; font-size: 10px; }
}

/* ============================================================
   Ticket art follows the room (unchanged, /tickets side).
   ============================================================ */

body[data-room-tier="copper"] .tk-main {
  background:
    radial-gradient(120% 160% at 8% 0%, #f6d7bb 0%, #e2a877 32%, #c87f4e 68%, #9c5c30 100%);
  border-color: #7d4a26;
}

body[data-room-tier="copper"] .tk-back {
  background: linear-gradient(135deg, #6b4526, #3f2814);
  border-color: rgba(200, 127, 78, .3);
}

/* The stub is a separate gradient in home-b.css — repaint it too, or the
   ticket ends up copper with a gold tail. */
body[data-room-tier="copper"] .tk-stub {
  background: linear-gradient(180deg, #e9b98c, #c87f4e);
  border-left-color: rgba(93, 47, 8, .55);
}

body[data-room-tier="silver"] .tk-main {
  background:
    radial-gradient(120% 160% at 8% 0%, #ffffff 0%, #e6ecf3 34%, #b9c4d1 70%, #93a0b0 100%);
  border-color: #778392;
}

body[data-room-tier="silver"] .tk-back {
  background: linear-gradient(135deg, #55606d, #333b45);
  border-color: rgba(154, 166, 181, .3);
}

body[data-room-tier="silver"] .tk-stub {
  background: linear-gradient(180deg, #eef2f7, #b9c4d1);
  border-left-color: rgba(46, 55, 66, .5);
}

/* Gold is the shipped default; declared for completeness so switching
   rooms always repaints instead of relying on the base stylesheet. */
body[data-room-tier="gold"] .tk-main {
  background:
    radial-gradient(120% 160% at 8% 0%, #ffe38a 0%, #ffca16 38%, #e2a812 72%, #c98f0b 100%);
  border-color: #8a6a08;
}

body[data-room-tier="gold"] .tk-back {
  background: linear-gradient(135deg, #6b5618, #46390f);
  border-color: rgba(255, 202, 22, .25);
}

body[data-room-tier="gold"] .tk-stub {
  background: linear-gradient(180deg, #f7ce53, #dfa90f);
  border-left-color: rgba(93, 71, 8, .55);
}

/* ============================================================
   THE ROOM-LESS TICKET (GL-188)

   The base ticket in home-b.css is gold — and the Gold Room repaints it
   to *the same* gold. So a player standing in no room at all saw a gold
   ticket while being charged the 5 USDT house price: the visual said
   "Gold Room, 50 USDT", the money said otherwise. Measured, not guessed:
   both states render the identical radial-gradient(#ffe38a…#c98f0b).

   Silver already owns the pale/bone ticket, so the room-less ticket
   cannot be bone either. It gets its own identity: brand violet with a
   warm foil edge. Now every surface answers "where am I?" the same way —
   copper, silver, gold, or the house table — and none can be mistaken
   for another. Scoped to :not([data-room-tier]) so entering a room wins.
   ============================================================ */

body:not([data-room-tier]) .tk-main {
  background:
    radial-gradient(120% 160% at 8% 0%, #6f63c8 0%, #5a4fb4 38%, #453a91 74%, #332b6d 100%);
  border-color: #2a2359;
}

body:not([data-room-tier]) .tk-back {
  background: linear-gradient(135deg, #2f2860, #1d1840);
  border-color: rgba(122, 108, 214, .28);
}

body:not([data-room-tier]) .tk-stub {
  background: linear-gradient(180deg, #7d70d6, #5546a8);
  border-left-color: rgba(24, 19, 56, .55);
}

/* The violet field is dark, so ink that reads well on gold would vanish.
   The base ticket paints every line in near-black browns (#241a03,
   #6b5309, #5d4708) — measured on the live page, not assumed. Each of
   those needs its own light twin, or the text drops to ~1.5:1 and the
   ticket becomes an unreadable violet slab. */
body:not([data-room-tier]) .tk-main .tk-title,
body:not([data-room-tier]) .tk-main b {
  color: #ffffff;
}

body:not([data-room-tier]) .tk-main .tk-serial,
body:not([data-room-tier]) .tk-main small,
body:not([data-room-tier]) .tk-main span {
  color: rgba(255, 255, 255, .82);
}

body:not([data-room-tier]) .tk-main .tk-star {
  color: #ffd166;
}

/* The stub is a lighter violet than the body, so its ink is lifted
   separately rather than inherited. */
body:not([data-room-tier]) .tk-stub,
body:not([data-room-tier]) .tk-stub b,
body:not([data-room-tier]) .tk-stub small,
body:not([data-room-tier]) .tk-stub span {
  color: #ffffff;
}

/* The shimmer sweep is tuned for a bright gold face; at full strength it
   washes the violet out. Halve it rather than remove it — the ticket
   should still catch the light. */
body:not([data-room-tier]) .tk-main::after {
  background: linear-gradient(115deg,
    transparent 30%, rgba(255, 255, 255, .16) 46%, transparent 60%);
}

/* Room badge on the buy card: second answer to "which room am I in?" */
.room-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--room-metal, var(--gold, #ffca16));
  background: var(--room-metal-soft, rgba(255, 202, 22, .14));
  font: 500 11px/1 var(--font-mono, "JetBrains Mono", monospace);
  color: var(--bone, #f0f0f0);
  letter-spacing: .04em;
  /* GL-188: localised room names are longer than "Gold Room" ("Серебряная
     комната"), and the badge wrapped mid-phrase — "Silver Room · 20" on
     one line, "USDT" on the next, which read as a broken price. Keep the
     badge on one line and let it shrink instead. */
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.room-tag i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--room-metal, var(--gold, #ffca16));
}

body[data-room-tier="copper"] { --room-metal: #c87f4e; --room-metal-soft: rgba(200, 127, 78, .16); }
body[data-room-tier="silver"] { --room-metal: #9aa6b5; --room-metal-soft: rgba(154, 166, 181, .16); }
body[data-room-tier="gold"]   { --room-metal: #e0a913; --room-metal-soft: rgba(224, 169, 19, .16); }

/* ============================================================
   TICKET STAGE — FILLING THE DEAD AIR (GL-188)

   Measured on the live page, 1280px desktop: the stage is 871px tall
   (it stretches to match the buy column beside it) and holds 284px of
   content — a 188px ticket and a 96px mascot. 588px of nothing, with
   the widest band directly under the ticket.

   REJECTED: adding a fact list here. The page already ends with the
   same three claims ("Fixed within your room / One confirmed ticket
   wins / 90% of the bank to the winner"). Repeating them one screen
   apart is worse than empty space: the reader assumes two different
   lists and hunts for the difference.

   TAKEN: give the space to the thing the column is actually about.
   The ticket is the hero of this page and it was rendering at 40% of
   its column. Scaling it up and letting the mascot breathe fills the
   band with the subject itself rather than with filler.
   ============================================================ */

.game-stage {
  /* GL-193: NOT `center`. This rule sits below the anti-jump block and
     would win by source order, re-introducing the 253px drop it fixes.
     The stage stretches to the row's height, so centring here means the
     ticket moves every time the neighbouring column grows. */
  justify-content: flex-start;
  gap: 40px;
}

/* Same rule at matching weight, or home-b.css re-centres the stage. */
body.layout-b[data-page="tickets"] .game-stage,
body.layout-b .game-stage {
  justify-content: flex-start;
  gap: 40px;
}

/* The ticket scales with the column instead of sitting at a fixed size.
   Clamped so it can neither shrink below legibility nor outgrow the
   stage on a tall screen. */
.game-stage .tstack {
  width: 100%;
  max-width: 560px;
  transform: scale(clamp(1, calc(0.86 + 0.14 * (100vh / 900)), 1.22));
  transform-origin: center center;
}

.game-stage .stage-mascot img {
  width: clamp(96px, 12vh, 148px);
  height: auto;
}

.game-stage .stage-mascot small {
  font-size: 12px;
  line-height: 1.45;
}

/* Mobile keeps the original scale: the column is not tall there, so
   there is no dead air to fill and a scaled ticket would overlap the
   next section. */
@media (max-width: 900px) {
  .game-stage { gap: 22px; }
  .game-stage .tstack { transform: none; max-width: none; }
  .game-stage .stage-mascot img { width: 96px; }
}

/* ============================================================
   RECENT WINNERS — DEPTH (GL-189)

   Measured: the panel painted rgb(11,12,13) on a page background of
   rgb(11,12,13) — literally the same colour — with a 1px hairline and
   box-shadow: none. With no payouts yet, the empty state read as a
   disabled control rather than as a part of the page that is waiting.

   Fix is depth, not decoration: lift the surface off the page, give it
   a real shadow, and treat the empty message as a deliberate placeholder
   (centred, its own inset field) instead of a stray comment.
   ============================================================ */

.winner-list {
  background: var(--surface-1, #101114);
  border: 1px solid var(--border, rgba(255, 255, 255, .09));
  border-radius: 14px;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, .04) inset,
    0 10px 28px rgba(0, 0, 0, .45);
}

/* The empty state is a message, not a row: centre it and give it an
   inset field so it reads as "nothing here yet", not "broken". */
.winner-list:empty::before,
.winner-list .winner-empty {
  content: attr(data-empty);
  display: block;
  margin: 4px;
  padding: 22px 18px;
  border: 1px dashed var(--border, rgba(255, 255, 255, .12));
  border-radius: 10px;
  background: rgba(255, 255, 255, .015);
  text-align: center;
  font: 400 12px/1.55 var(--font-ui, system-ui, sans-serif);
  color: var(--ash, #a1a4a5);
}

/* Rows sit on the lifted surface, so their separators must be lighter
   than the old near-black hairline or they disappear. */
.panel-b .winner-list > * {
  border-bottom-color: var(--border, rgba(255, 255, 255, .07));
}

.panel-b .winner-list > *:hover {
  background: rgba(255, 255, 255, .025);
}

/* ============================================================
   LIGHT THEME REPAIRS (GL-190)

   Amal: "смотри как дневная тема снова портит все". Measured on the
   live page in light mode rather than argued from the screenshot:

   1. The partner banner computes `background: rgba(0,0,0,0)` — it has
      no fill at all. On the dark page an unfilled block reads as a
      deliberate outline; on ivory it dissolves into the page, which is
      exactly the "beige mush" in the screenshot.

   2. The wheel card is also transparent AND borderless (0px), so the
      whole hero has no edge in light mode.

   3. My own winners panel (GL-189) shipped dark-theme values: a white
      inset highlight and a black 45%-opacity shadow. On ivory the
      highlight is invisible and the shadow is a grey smudge.

   Every other panel (.mini-b, .panel-b) already does the right thing in
   light mode — solid #fdfcf8, a warm border, a soft brown shadow. These
   rules bring the three stragglers in line with that established
   language instead of inventing a new one.
   ============================================================ */

body.layout-b.theme-light .promo-b,
body.layout-b.theme-light [class*="partner"] {
  background: linear-gradient(180deg, #fffdf5, #f7f0dd);
  border-color: rgba(138, 101, 8, .28);
  box-shadow: 0 6px 20px rgba(60, 45, 10, .07);
}

body.layout-b.theme-light .game-card-b,
body.layout-b.theme-light [class*="wheel"] {
  background: #fdfcf8;
  border: 1px solid rgb(221, 213, 194);
  box-shadow: 0 8px 26px rgba(60, 45, 10, .07);
}

/* The winners panel must follow the light-theme palette, not carry the
   dark one over. Same border and shadow as .panel-b beside it. */
body.layout-b.theme-light .winner-list {
  background: #fdfcf8;
  border-color: rgb(221, 213, 194);
  box-shadow: 0 8px 26px rgba(60, 45, 10, .07);
}

body.layout-b.theme-light .winner-list .winner-empty {
  border-color: rgba(138, 101, 8, .22);
  background: rgba(138, 101, 8, .03);
  color: #6b6152;
}

body.layout-b.theme-light .panel-b .winner-list > *:hover {
  background: rgba(60, 45, 10, .035);
}

/* ============================================================
   MOBILE: LANGUAGE FLAGS + TICKET PROPORTIONS (GL-190)

   Two separate complaints from the same screenshot.

   FLAGS: the drawer rendered a bare "EN" while the desktop header shows
   "🇬🇧 EN", and the opener row named the section without ever saying
   which language was active. Markup added in _mobile_flags.py; these are
   its styles.

   TICKET: measured at 430px — the ticket is 406px wide and 211px tall
   and its bottom edge lands at y=576 while the rooms section starts at
   y=594. Eighteen pixels of clearance between a shadowed, rotated stack
   of cards and a heading is not clearance at all: the back cards
   (rotated 3.5deg, translated 12px) visually cross into the heading.
   The stack also keeps its desktop proportions, so on a phone it reads
   as a wide slab rather than a ticket.
   ============================================================ */

.gl-dr-lang-flag {
  font-size: 15px;
  line-height: 1;
  margin-right: 8px;
  /* Emoji flags carry their own colour; stop the row's colour rules and
     any grayscale filter from touching them. */
  filter: none;
}

.gl-dr-lang-current {
  margin-left: auto;
  margin-right: 8px;
  font: 500 12px/1 var(--font-mono, "JetBrains Mono", monospace);
  letter-spacing: .04em;
  color: var(--ash, #a1a4a5);
  white-space: nowrap;
}

@media (max-width: 900px) {
  /* Real breathing room under the ticket.

     First attempt put padding-bottom on .game-stage — measured result:
     no change, still 18px. Reason: on mobile the stage computes to
     height 0 (its children are positioned out of flow), so its padding
     pushes nothing. The gap has to come from the element that actually
     follows in the document. */
  .rooms-section {
    margin-top: 34px;
  }

  /* Give the stack a ticket's proportions instead of the desktop slab,
     and rein in the back cards so their rotation stays inside the
     stage rather than reaching for the next section. */
  .game-stage .tstack {
    width: min(360px, 88vw);
    margin-inline: auto;
  }

  .game-stage .tk-back.tk-b1 {
    transform: rotate(-3.5deg) translate(-6px, 4px);
  }

  .game-stage .tk-back.tk-b2 {
    transform: rotate(2.2deg) translate(7px, -3px);
  }

  /* The stub is a fixed 86px on desktop; at 360px that is a quarter of
     the ticket. Let it scale so the ticket keeps its shape. */
  .game-stage .tk-stub {
    flex-basis: 64px;
  }

  .game-stage .tk-main .tk-title {
    font-size: clamp(24px, 7vw, 32px);
  }
}

/* ============================================================
   THE LOADING JUMP (GL-193)

   Amal filmed it: on load the ticket visibly drops, the rooms flash in,
   the page re-settles. Traced with a per-frame probe instead of guessing:

        74ms   ticket top=384   card=0     rooms=0
       110ms   ticket top=360   card=0     rooms=0
       246ms   ticket top=371   card=0     rooms=3   <- rooms mount
       288ms   ticket top=624   card=423   rooms=3   <- +253px in one frame
       403ms   ticket top=549   card=423   rooms=3
       766ms   ticket top=557   card=439   rooms=3

   MECHANISM: `.game-play-grid` is a two-column grid with
   `align-items: center`. The right column is built by JavaScript — rooms
   (356px) then the buy card (501px) — so its height goes 0 -> 857px after
   first paint. Centring means the LEFT column re-centres inside the taller
   row, and the ticket slides down by half the growth. 501/2 ≈ 253. The
   number matches the measurement exactly, which is how we know this is
   the cause and not a coincidence.

   FIX 1 — stop coupling the columns. `align-items: start` makes the
   ticket's position independent of how tall its neighbour becomes. The
   ticket then sits level with the rooms list, which is also where the eye
   expects it: the two things you compare are on the same line.

   FIX 2 — reserve the rooms row's height before it renders, so the buy
   card below it does not get pushed down when the rooms arrive. The
   reserve is released the moment we know there are no rooms to show
   (flag off, empty list, error), so a hidden section never leaves a hole.
   ============================================================ */

.game-play-grid {
  align-items: start;
}

/* FIX 1 was necessary but NOT sufficient — measured after deploying it:

       75ms   ticket=384  stage top=384  stage h=334
      418ms   ticket=624  stage top=360  stage h=861   <- +253px
      979ms   ticket=557  stage top=360  stage h=876

   The stage's OWN top never moves (360). What moves is the ticket
   INSIDE it: `.game-stage` is a flex column with `justify-content:
   center`, it stretches to match the row, and when the row grows from
   334px to 876px the centred ticket slides down by half the difference.
   Un-coupling the grid columns fixed the row; the ticket was still
   centring inside its own stretched box.

   So the stage stops centring too. The ticket is pinned to the top of
   its column and simply stays where it was first painted.

   SPECIFICITY, measured after two failed deploys: home-b.css wins with
   `body.layout-b[data-page="tickets"] .game-stage` (0,3,1). A bare
   `.game-stage` (0,1,0) loses no matter where it sits in the file — the
   probe kept reporting jc=center while the rule "was applied". Match the
   weight instead of guessing at ordering. */
body.layout-b[data-page="tickets"] .game-stage,
body.layout-b .game-stage {
  justify-content: flex-start;
}

/* Held only while the rooms section is still deciding whether it has
   anything to show. `rooms.js` removes the attribute in reveal() and in
   every early-return path. */
.rooms-section[data-reserving] {
  display: block !important;
  min-height: 356px;
}

.rooms-section[data-reserving] > * {
  visibility: hidden;
}

/* ============================================================
   MASCOT: NO POP-IN, NO SHIFT (GL-194)

   Measured on the live page:
       473ms  108x0    top=645   complete=false   <- no height yet
       506ms  108x108  top=628   complete=true    <- pops in, shifts 17px
      1453ms  108x108  top=604                    <- shifts again, 24px
      2190ms  room pose finally swaps

   The <img> has a width/height attribute pair but no CSS box, so between
   "element exists" and "SVG decoded" it occupies zero vertical space and
   everything under it slides when the image lands. Reserving the box
   removes the shift; the swap-time fix is in rooms-active.js.

   Also: crossfade the pose change. Switching src repaints instantly and
   reads as a glitch — a 140ms fade reads as a deliberate change.
   ============================================================ */

.stage-mascot img {
  /* Hold the space before decode. aspect-ratio keeps it correct if the
     clamp above changes the width on another breakpoint. */
  aspect-ratio: 1 / 1;
  min-height: 96px;
  transition: opacity .14s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .stage-mascot img { transition: none; }
}
