/* ==========================================================================
   game.css - the single game page.

   The whole page is themed off one custom property, --game-theme, which
   game.js sets from the record. That is what let eleven hand-built pages
   collapse into one: the differences between them were a colour, two images
   and a paragraph.
   ========================================================================== */

/* Self-hosted so the page has no third-party requests. Oswald ships as one
   variable font now, so a single file covers the whole weight range. It is the
   same face the live pages pull from Google Fonts, and it matters: it is
   condensed, and a six-figure jackpot in anything wider will not fit inside
   the lit box the artwork draws. */
@font-face {
  font-family: 'Oswald';
  src: url('fonts/oswald.woff2') format('woff2-variations');
  font-weight: 200 700;
  font-display: swap;
}

:root {
  --game-theme: #16213f;
  --ink: #1c2333;
  --accent-soft: #ffc24d;
}

* { box-sizing: border-box; }

[hidden] { display: none !important; }

html, body { margin: 0; }

body {
  background: var(--game-theme);
  color: #fff;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  /* The theme lands a frame after the record is read; without this the page
     flashes the default navy first. */
  transition: background-color 200ms linear;
}

/* ---- top bar ------------------------------------------------------------- */

.game-topbar {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.6rem 1rem;
  background: rgba(0, 0, 0, 0.35);
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
}

.back-link {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem 0.85rem;
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  font-size: 0.9rem;
  text-decoration: none;
  white-space: nowrap;
}
.back-link:hover { background: #fff; }
.back-link span { font-size: 1.15rem; line-height: 1; }

.game-where {
  margin: 0;
  min-width: 0;
  font-size: 0.9rem;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.92);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- artwork stage ------------------------------------------------------- */

.stage-art {
  padding: 1.25rem 1rem 1.75rem;
  text-align: center;
}

.banner-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-bottom: 1rem;
}

.banner-art {
  width: 100%;
  height: auto;
}

/* The punchboard banner is a 1920x120 strip meant to run the full width. */
.banner-full { max-width: none !important; }

.meter-deck {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 0.75rem;
}

/* ---- a meter ------------------------------------------------------------
   The artwork is the layout. Fields are positioned against this box, which is
   exactly the image - no padding - so a field's `bottom` is a straight
   percentage of the artwork's height.

   container-type: inline-size is what makes the overlays safe at any width:
   the numbers are sized in cqw, so they are locked to a fraction of the
   artwork and cannot outgrow the box they sit in. The originals set a fixed
   px font against an image that scaled with the viewport, so the jackpot burst
   out of its frame on a narrow screen.
   ------------------------------------------------------------------------ */

.meter {
  container-type: inline-size;
  position: relative;
  flex: 0 1 500px;
  min-width: 0;
  margin: 0;
  line-height: 1;
}

/* Full-bleed artwork: the punchboard banner and board, and the Golden scene.
   Capped at the source width so a wide monitor does not upscale it. */
.meter-full {
  flex-basis: 100%;
  max-width: 1920px;
  margin-inline: auto;
}

.meter-art {
  display: block;
  width: 100%;
  height: auto;
}

/* A field is the lit box itself - game.js sets bottom and height from the
   record - and the text is centred inside it both ways. So the jackpot stays
   put when the font changes, and the ball-call label stops jumping sideways
   when the number goes from one digit to two.

   The originals hand-tuned a left offset per field instead, which is why the
   same jackpot sat at 42% of the width on one page and 53% on another. */
.meter-caption,
.meter-amount {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Exactly one font-size tall, so centreInk() in game.js can work out where
     the baseline lands without having to ask what "normal" resolved to. */
  line-height: 1;
  font-family: 'Oswald', 'Segoe UI', sans-serif;
  font-style: italic;
  color: #eee;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}

/* One inner item, so inline layout works normally inside the field. Putting
   text straight into the flex box makes each run an anonymous flex item and
   strips the whitespace between them - which welded "BONUS PALS" onto
   "WINNERS". */
.meter-text { display: inline; }

.meter-caption {
  font-size: 6.1cqw;
  font-weight: 500;
  letter-spacing: 0.01em;
}

.meter-amount {
  font-size: 14cqw;
  font-weight: 600;
}

/* The run the originals coloured yellow, e.g. BONUS PALS *WINNERS*. */
.hl { color: #ffe14d; }

/* Numbers we could not refresh. Dimmed rather than hidden: a stale jackpot is
   still roughly right, and blanking it would read as a jackpot of zero. */
.is-stale { opacity: 0.55; }

.updated-line {
  margin: 1rem 0 0;
  font-size: 0.95rem;
  color: rgba(255, 255, 255, 0.9);
}

/* ---- panel layout (Michigan) --------------------------------------------- */

.tier-panel {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1.25rem 1rem 1.5rem;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 1rem;
  background: rgba(255, 255, 255, 0.08);
}

.tier-strap {
  margin: 0 0 1.1rem;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--accent-soft);
}

.tier {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.25rem;
}
.tier + .tier { border-top: 1px solid rgba(255, 255, 255, 0.18); }

.tier-label {
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
}

.tier-amount {
  font-family: 'Oswald', 'Segoe UI', sans-serif;
  font-size: clamp(1.6rem, 6vw, 2.4rem);
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
}

/* ---- copy ---------------------------------------------------------------- */

.game-copy {
  max-width: 60rem;
  margin: 0 auto;
  padding: 0 1rem 3rem;
  text-align: center;
}

.game-title {
  margin: 0 0 0.35rem;
  outline: none;              /* focused programmatically; it is tabindex="-1" */
  font-size: clamp(1.4rem, 4vw, 2rem);
  font-weight: 600;
}

.game-heading {
  margin: 0 0 1rem;
  font-size: 1.05rem;
  color: var(--accent-soft);
}

.game-blurb {
  margin: 0 auto 1.5rem;
  max-width: 46rem;
  font-size: 1rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.92);
}

.game-disclaimer {
  max-width: 46rem;
  margin: 0 auto;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  font-size: 0.82rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.72);
}

/* ---- error --------------------------------------------------------------- */

.game-error {
  max-width: 34rem;
  margin: 4rem auto;
  padding: 0 1rem;
  text-align: center;
}
.game-error h1 { font-size: 1.5rem; }
.game-error .row-ish {
  display: inline-block;
  margin-top: 1rem;
  padding: 0.6rem 1.2rem;
  border-radius: 0.7rem;
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  body { transition: none; }
}

/* ==========================================================================
   Display modes

   Set by game.js from ?av=1 / ?partner=1 onto <html data-display>, before
   anything renders, so a wall screen never flashes the player chrome.

     site     full chrome. The player view; the default.
     av       Gaming Arts logo bar, blank branded footer band, no blurb.
     partner  no chrome, no padding. The property's own display.

   Both signage modes FIT the viewport instead of scrolling it. That is the
   whole requirement: these run fullscreen at 100% zoom on a screen nobody can
   scroll, so anything below the fold is simply lost.

   The mechanism is to bound the artwork by HEIGHT rather than width. A normal
   web page sizes an image to the width it is given and lets the height fall
   where it may; here the meter deck takes whatever vertical space is left over
   and the images shrink into it. That is what makes one rule serve both a
   1920x1080 screen and a 1080x1920 one.
   ========================================================================== */

:root[data-display='av'],
:root[data-display='partner'] {
  --signage-gap: 0.5rem;
}

:root[data-display='av'] body,
:root[data-display='partner'] body {
  /* dvh, not vh: a browser in kiosk mode with dynamic toolbars would otherwise
     lay out against a viewport taller than the one actually painted. */
  height: 100dvh;
  overflow: hidden;
  /* A column flexbox is what lets the leftover height reach the artwork:
     the bar and disclaimer take what they need, .game-main takes the rest. */
  display: flex;
  flex-direction: column;
}

/* Marketing copy goes; the regulatory disclaimer stays. The old pages drew
   exactly this line - hideAttractionBanner dropped the headline and blurb and
   left the disclaimer alone. */
:root[data-display='av'] .game-title,
:root[data-display='av'] .game-heading,
:root[data-display='av'] .game-blurb,
:root[data-display='partner'] .game-title,
:root[data-display='partner'] .game-heading,
:root[data-display='partner'] .game-blurb {
  display: none;
}

:root[data-display='partner'] .stage-art { padding: 0; }
:root[data-display='av'] .stage-art { padding: 0.5rem 0.5rem 0; }

:root[data-display='av'] .game-copy,
:root[data-display='partner'] .game-copy {
  flex: 0 0 auto;
  max-width: none;
  padding: 0.35rem 1rem;
}

:root[data-display='av'] .game-disclaimer,
:root[data-display='partner'] .game-disclaimer {
  max-width: none;
  padding-top: 0.35rem;
  border-top: none;
  /* Small, but it has to stay legible at the back of a bingo hall, so it
     scales with the screen rather than sitting at a fixed px size. */
  font-size: clamp(0.6rem, 0.85vh, 0.95rem);
  line-height: 1.35;
}

/* The stage takes the leftover height and hands it to the artwork. */
:root[data-display='av'] .game-main,
:root[data-display='partner'] .game-main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

:root[data-display='av'] .stage-art,
:root[data-display='partner'] .stage-art {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--signage-gap);
}

:root[data-display='av'] .banner-strip,
:root[data-display='partner'] .banner-strip {
  flex: 0 1 auto;
  min-height: 0;
  margin-bottom: 0;
  gap: var(--signage-gap);
}

/* max-width is cleared because the record's value is a player-view cap - on a
   wall screen the logo should use the room it has. The height cap only ever
   shrinks: max-width:100% already stops the image going past its natural size,
   so a banner is never upscaled into blur. */
:root[data-display='av'] .banner-art,
:root[data-display='partner'] .banner-art {
  max-width: 100% !important;
  width: auto;
  max-height: 45vh;
  object-fit: contain;
}

/* The deck's pixel size and its .is-stacked class come from sizeDeck() in
   game.js - see the comment there for why this is measured rather than
   expressed in CSS. Everything here just honours that decision. */
:root[data-display='av'] .meter-deck,
:root[data-display='partner'] .meter-deck {
  flex: 0 0 auto;
  margin: auto;
  align-items: center;
  justify-content: center;
  gap: var(--signage-gap);
  flex-wrap: nowrap;      /* wrapping here would push a meter off-screen */
}

:root[data-display='av'] .meter-deck.is-stacked,
:root[data-display='partner'] .meter-deck.is-stacked {
  flex-direction: column;
}

/* Sized off the deck, which is now definite in both axes, so the aspect ratio
   published from the decoded artwork gives the other dimension. */
:root[data-display='av'] .meter,
:root[data-display='partner'] .meter {
  flex: 0 1 auto;
  height: 100%;
  width: auto;
  aspect-ratio: var(--ar, 2 / 1);
}

:root[data-display='av'] .is-stacked .meter,
:root[data-display='partner'] .is-stacked .meter {
  height: auto;
  width: 100%;
}

:root[data-display='av'] .meter-art,
:root[data-display='partner'] .meter-art,
:root[data-display='av'] .meter picture,
:root[data-display='partner'] .meter picture {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---- av: the branded bar and band --------------------------------------- */

.game-topbar.logo-bar {
  flex: 0 0 auto;
  justify-content: center;
  padding: 0.3rem;
  background: #f6f6f6;
  border-bottom: none;
}

.ga-logo {
  height: clamp(1.6rem, 4.5vh, 2.6rem);
  width: auto;
}

/* footer_Partner_Blank_Main.html is a plain red band with nothing in it - it
   exists to close the page in Gaming Arts red. Cheaper as a border. */
:root[data-display='av'] body {
  border-bottom: 0.6rem solid #c20000;
}

/* Deliberately NOT hiding the cursor or the focus ring here.

   Both were suppressed on the assumption that a display page is never touched
   by anyone. That is wrong twice over: someone sets these screens up, and
   points at them afterwards, and a machine whose pointer has vanished reads as
   a machine that has crashed. The focus ring had a sharper failure - a
   malformed signage URL renders the error page, which has a real link on it,
   and hiding the ring left that link unreachable to anyone on a keyboard. */
