/* ==========================================================================
   BingoCloud - owner / property / game selector
   ========================================================================== */

:root {
  --sky-deep: #16213f;
  --sky-mid: #2f5183;
  --sky-light: #6f9bd1;

  --ink: #1c2333;
  --ink-soft: #5a6478;
  --hairline: rgba(255, 255, 255, 0.55);

  --accent: #e08706;
  --accent-soft: #ffc24d;

  /* Consumed by the gradient stops inside the inlined map SVG. */
  --map-idle-a: #f7f9fb;
  --map-idle-b: #ccd4dd;
  --map-avail-a: #7cc7ff;
  --map-avail-b: #2f7fd4;
  --map-hover-a: #ffd479;
  --map-hover-b: #f0a527;
  --map-active-a: #ffc24d;
  --map-active-b: #e08706;

  --slide-duration: 260ms;
  --slide-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

* { box-sizing: border-box; }

/* Author `display` rules outrank the UA stylesheet's [hidden] rule, so the
   hidden attribute needs reinforcing or .back-button etc. stay visible. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, var(--sky-deep), var(--sky-mid) 55%, var(--sky-light));
  /* Deliberately NOT background-attachment: fixed. The page itself never
     scrolls (overflow is hidden just below), so fixed would look identical
     while costing real work: it is one of Chrome's main-thread scrolling
     reasons, and it forces the gradient to be re-rasterised whenever anything
     over it repaints - including the carousel strip sitting on top of it. */
  color: var(--ink);
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  /* The panels own the scrolling; the page itself never scrolls. */
  overflow: hidden;
}

/* ---- header -------------------------------------------------------------- */

.site-header {
  flex: 0 0 auto;
  padding: 0.9rem 1rem 0.5rem;
  text-align: center;
}

.wordmark {
  margin: 0;
  font-size: clamp(1.5rem, 3.6vmin, 2.4rem);
  font-weight: 300;
  letter-spacing: 0.06em;
  color: #fff;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
}
.wordmark span { font-weight: 700; color: var(--accent-soft); }

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

.stage {
  flex: 1 1 auto;
  min-height: 0;               /* lets the scroll containers inside actually scroll */
  display: flex;
  justify-content: center;
  gap: 1.5rem;
  padding: 0 1rem 1rem;
}

.map-column { display: none; }  /* revealed at the desktop breakpoint below */

.menu-column {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 32rem;
  display: flex;
}

/* ---- menu shell ---------------------------------------------------------- */

.menu-shell {
  flex: 1 1 auto;
  /* min-width:0 matters: the shell is a flex item on its horizontal main axis,
     so the default min-width:auto would size it to the 300%-wide rail inside. */
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 2px solid var(--hairline);
  border-radius: 1rem;
  background: rgba(255, 255, 255, 0.16);
  box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

.menu-topbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.75rem;
  padding: 0.4rem 0.7rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.35);
  background: rgba(255, 255, 255, 0.12);
}

.back-button {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.9);
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}
.back-button span { font-size: 1.15rem; line-height: 1; }
.back-button:hover { background: #fff; }

.crumbs {
  min-width: 0;
  color: #fff;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
}
.crumbs .sep { opacity: 0.55; margin: 0 0.35rem; }

/* ---- the sliding rail ----------------------------------------------------
   The rail translates; the panels never do. Each panel keeps its own native
   overflow-y, so momentum scrolling, scroll position and focus all survive
   the transition.
   ------------------------------------------------------------------------- */

.rail-viewport {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

/* Two panels: properties, then games. Width and flex-basis are a matched pair -
   the rail is N x the viewport and each panel is 1/N of the rail, so a panel
   ends up exactly one viewport wide. Changing the level count means changing
   both, and the translateX step in menu.js. */
.rail {
  display: flex;
  width: 200%;
  height: 100%;
  transform: translateX(0);
  transition: transform var(--slide-duration) var(--slide-ease);
}

.panel {
  flex: 0 0 50%;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.panel-heading {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.7rem 0.9rem 0.5rem;
}
.panel-heading h2 {
  margin: 0;
  /* Focused programmatically on each slide so screen readers announce the new
     level. It is tabindex="-1", so suppressing the ring costs no keyboard UX. */
  outline: none;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 1px 5px rgba(0, 0, 0, 0.45);
}

.panel-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;   /* stops scroll chaining to the page on iOS */
  padding: 0 0.9rem 1rem;
}

/* ---- rows ---------------------------------------------------------------- */

/* Centred label flanked by rules, matching the KenoCloud original. The rules
   run to the row edges, so the label reads as a divider rather than as text
   jammed against the left of a band.
   Deliberately not sticky: pinning it needs an opaque background to stop rows
   showing through while scrolling, and that band reads far heavier than the
   rules do. */
.section-header {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin: 0.75rem 0 0.55rem;
  padding: 0.15rem 0;
  color: #fff;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}

.section-header::before,
.section-header::after {
  content: '';
  flex: 1 1 auto;
  height: 2px;
  border-radius: 1px;
  background: rgba(255, 255, 255, 0.5);
}

.row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  margin-bottom: 0.55rem;
  padding: 0.4rem 0.55rem;
  border: 1px solid rgba(0, 0, 0, 0.55);
  border-radius: 0.7rem;
  background: linear-gradient(#f8fbfc, #ccd7de);
  box-shadow: inset 0 0 0.3rem rgba(255, 255, 255, 0.6), 0 0.2rem 0.5rem rgba(0, 0, 0, 0.4);
  font: inherit;
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  cursor: pointer;
}
.row:hover { background: linear-gradient(#fff, #dae4ea); }
.row:active { background: linear-gradient(#c6cccf, #8e989e); }
.row:focus-visible { outline: 3px solid var(--accent-soft); outline-offset: 2px; }

/* Fixed slot so rows stay aligned whatever aspect ratio the real artwork has,
   and whether the entry carries one logo or two.

   Width and height grew together, keeping the box near 1.38:1. That ratio is
   just under the narrowest real logo (Gold Coast, 1.42:1), which keeps every
   one of them width-limited - so a 50% wider box makes all 18 exactly 50%
   bigger. Widening alone would have left the eight logos under 2.08:1
   height-limited and completely unchanged, which is not what "bigger icons"
   means. */
.row-icons {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  width: 5.4rem;
  height: 3.9rem;
}

/* flex-basis 0, not auto: the two halves are equal regardless of how wide each
   source file happens to be, so a pair lines up instead of one crowding the
   other. contain (not cover) means a wide logo letterboxes rather than being
   cropped. */
.row-icon {
  flex: 1 1 0;
  min-width: 0;
  max-width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Game rows are taller. A location runs at most two games, so the list is
   never long enough to need the space back, and the artwork is the thing worth
   looking at once you have chosen where you are playing.

   Both dimensions matter, and for opposite reasons. The slot is 1.54:1, so:
     - logos wider than that are width-limited, and the extra WIDTH grows them.
       The Gila River wordmark is 3.89:1 and goes from 5.4rem across to 12.
     - logos narrower are height-limited, and the extra HEIGHT grows them.
       Super Coverall Multi-Win is 1.15:1 and doubles.
   Changing only one would have left half the artwork exactly as it was. */
#gamesBody .row-icons {
  width: min(18rem, 50%);
  height: 7.8rem;
}

.row .text { flex: 1 1 auto; min-width: 0; }
.row .title {
  display: block;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.25;
}
.row .subtitle {
  display: block;
  color: var(--ink-soft);
  font-size: 0.82rem;
  line-height: 1.3;
}
.row .chevron {
  flex: 0 0 auto;
  color: var(--ink-soft);
  font-size: 1.3rem;
  line-height: 1;
}

.empty {
  padding: 1.5rem 0.5rem;
  color: #fff;
  text-align: center;
  opacity: 0.85;
}

/* ---- state filter chip --------------------------------------------------- */

.filter-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.6rem;
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 999px;
  background: var(--accent-soft);
  color: #3a2600;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
}
.filter-chip::after { content: "\00d7"; font-size: 1rem; line-height: 1; }
.filter-chip:hover { background: #fff; }

/* ---- shortcut carousel ----------------------------------------------------
   A real horizontal scroll container, not a transformed track, so dragging and
   trackpad gestures work natively. The drift just nudges scrollLeft.
   Hidden below the desktop breakpoint, matching the original.
   -------------------------------------------------------------------------- */

.carousel-bar {
  display: none;
  flex: 0 0 auto;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 0.6rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(9, 20, 40, 0.34);
  scrollbar-width: none;                 /* the strip drifts; a bar adds noise */
}
.carousel-bar::-webkit-scrollbar { display: none; }

/* The drift is a compositor animation, not a script.
   ---------------------------------------------------------------------------
   This used to be a rAF loop assigning bar.scrollLeft every frame. That is a
   programmatic scroll on the main thread: each frame invalidated the scroller,
   re-rastered the newly exposed strip, and fired a scroll event whose handler
   read scrollWidth - a forced synchronous layout, sixty times a second. On a
   discrete GPU that showed up as ~23% utilisation for a decorative strip, and
   dropped to nothing the moment it was hovered and the loop stopped writing.

   Animating `transform` instead keeps every frame on the compositor: the track
   is rasterised once and each frame only changes its transform matrix. No
   script, no style recalc, no layout, no paint.

   --carousel-shift and the duration are set once by menu.js. The shift is the
   measured distance between the first item of each copy - not 50% of the
   track, which would be out by the difference between the track's padding and
   half a gap and would visibly jump each cycle. */
.carousel-track {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  width: max-content;                    /* let items keep their natural size */
  padding: 0 0.5rem;
  animation: carousel-drift var(--carousel-duration, 60s) linear infinite;
}

@keyframes carousel-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-1 * var(--carousel-shift, 0px)), 0, 0); }
}

/* Pausing is a CSS state change, so hovering costs one style recalc rather
   than running a JS flag through the rAF loop. focus-within keeps a keyboard
   user's target from sliding out from under them. */
.carousel-bar:hover .carousel-track,
.carousel-bar:focus-within .carousel-track {
  animation-play-state: paused;
}

/* Logo only, no label. The box is deliberately wide rather than square: these
   are wordmarks, several near 11:1 (Boulder Station is 256x24), and a square
   box would shrink them to fit the width and leave them unreadably small. A
   light plate sits behind them because most of the artwork is dark. */
.carousel-item {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 9rem;
  height: 5.4rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 0.6rem;
  background: rgba(240, 246, 250, 0.9);
  font: inherit;
  cursor: pointer;
}
.carousel-item:hover {
  background: #fff;
  border-color: var(--accent-soft);
}
.carousel-item:focus-visible {
  outline: 3px solid var(--accent-soft);
  outline-offset: 2px;
}

.carousel-icon {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* ---- map ----------------------------------------------------------------- */

.map-holder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

.us-map {
  width: 100%;
  height: 100%;
  max-height: 100%;
  filter: drop-shadow(0 0 0.6rem rgba(0, 0, 0, 0.5));
}

/* ==========================================================================
   Desktop landscape. Matches the breakpoint the Angular app uses, so portrait
   desktop is treated as mobile and gets the list on its own.
   ========================================================================== */

@media (min-aspect-ratio: 1/1) and (min-width: 1024px) {
  .map-column {
    display: block;
    flex: 1 1 60%;
    min-width: 0;
    max-width: 60rem;
  }
  /* The map column is capped at 60rem, so once the viewport is wider than
     map + menu the surplus became empty margin down the right-hand side. The
     menu takes that space instead: it is the part of this screen people
     actually use. */
  .menu-column { flex: 0 0 39rem; max-width: 39rem; }
  .carousel-bar { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  .rail { transition: none; }
  /* Still a scroll container, just never self-moving. Dropping the animation
     also drops the compositor layer it was holding. */
  .carousel-track { animation: none; }
}
