:root {
  --bg: #ffffff;
  --card: #131a33;
  --card-border: #2a3560;
  --gold: #f2c14e;
  --red: #e2384d;
  --ticket-glow: #39f2ff;
  --progress-glow: #38d16a;
  --text: #f5f6fa;
  --text-dim: #93a0c9;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: 'Segoe UI', Arial, sans-serif;
}

body {
  display: flex;
  flex-direction: column;
  position: relative;
  transition: filter .8s ease, background 1.5s ease;
}

/* Day/night/weather: app.js sets one daypart-* class (from the clock) and
   the weather-fx overlay's condition class (from Open-Meteo, Pasadena TX)
   on a timer. Sky colors live here as variables per daypart; the actual
   sun/rain/cloud animation is the #weatherFx layer below, pinned behind
   everything (z-index:-1) since the grid is usually packed edge-to-edge -
   this mostly shows in the gaps and around the header. */
body.daypart-morning {
  --bg: linear-gradient(180deg, #ffd9a0, #ffb199 60%, #ffe9d6);
  --sun-x: 18%; --sun-y: 78%; --sun-color: #fff3c4;
}

body.daypart-afternoon {
  --bg: linear-gradient(180deg, #7fc3ff, #cdeaff 70%, #eef8ff);
  --sun-x: 50%; --sun-y: 12%; --sun-color: #fffef0;
}

body.daypart-evening {
  --bg: linear-gradient(180deg, #4a3a7a, #ff8a65 65%, #ffbf8a);
  --sun-x: 85%; --sun-y: 78%; --sun-color: #ffcf8a;
}

body.daypart-night {
  --bg: linear-gradient(180deg, #05070f, #0b1020 60%, #131a33);
  --sun-x: 50%; --sun-y: 15%; --sun-color: transparent;
  filter: brightness(.8);
}

.weather-fx {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.weather-fx.clear {
  background: radial-gradient(circle at var(--sun-x, 50%) var(--sun-y, 15%), var(--sun-color, #fff3c4) 0%, transparent 55%);
  opacity: .8;
}

/* Cartoon clouds: real SVG elements injected by app.js (renderClouds), not
   CSS-only - a flat-design cloud silhouette (a cluster of circles + a base,
   drawn twice at two scales so the larger back copy peeks out as a clean
   outline with no seams between the bumps) drifting slowly across the sky.
   Reused with a darker tint for rain/storm skies too. Big + tall + spread
   across the full height (not just a few thin bands) since most of the
   grid is opaque game tiles - a cloud only shows where it crosses one of
   the few open gaps, so more/bigger clouds covering more of the vertical
   space means better odds of actually crossing one at any given moment. */
.weather-fx .cartoon-cloud {
  position: absolute;
  width: 26vw;
  min-width: 200px;
  filter: drop-shadow(0 8px 14px rgba(20,30,50,.22));
  animation: cloudDrift linear infinite;
}

.weather-fx .cartoon-cloud svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.weather-fx .cartoon-cloud.a { top: 4%;  left: -34vw; width: 24vw; animation-duration: 55s; }
.weather-fx .cartoon-cloud.b { top: 16%; left: -44vw; width: 34vw; animation-duration: 80s; animation-delay: -20s; }
.weather-fx .cartoon-cloud.c { top: 30%; left: -28vw; width: 20vw; animation-duration: 62s; animation-delay: -45s; }
.weather-fx .cartoon-cloud.d { top: 46%; left: -40vw; width: 30vw; animation-duration: 88s; animation-delay: -10s; }
.weather-fx .cartoon-cloud.e { top: 62%; left: -30vw; width: 22vw; animation-duration: 68s; animation-delay: -55s; }
.weather-fx .cartoon-cloud.f { top: 78%; left: -46vw; width: 32vw; animation-duration: 95s; animation-delay: -30s; }

@keyframes cloudDrift {
  from { transform: translateX(0); }
  to   { transform: translateX(170vw); }
}

.weather-fx.rain::before,
.weather-fx.storm::before {
  content: '';
  position: absolute;
  inset: -20% -10% -10% -10%;
  background: rgba(20,30,50,.25);
  background-image: repeating-linear-gradient(
    115deg,
    rgba(255,255,255,.4) 0px,
    rgba(255,255,255,.4) 1px,
    transparent 1px,
    transparent 14px
  );
  background-size: 100% 140px;
  animation: rainFall .5s linear infinite;
}

@keyframes rainFall {
  from { background-position: 0 0; }
  to   { background-position: -40px 140px; }
}

.weather-fx.storm::after {
  content: '';
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
  animation: lightningFlash 7s ease-in-out infinite;
}

@keyframes lightningFlash {
  0%, 92%, 100% { opacity: 0; }
  93% { opacity: .3; }
  94% { opacity: 0; }
  95% { opacity: .45; }
  96% { opacity: 0; }
}

.weather-fx.fog::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent, rgba(220,225,235,.55) 45%, rgba(220,225,235,.7) 60%, transparent);
  animation: fogDrift 40s ease-in-out infinite alternate;
}

@keyframes fogDrift {
  from { transform: translateX(-4%); opacity: .7; }
  to   { transform: translateX(4%);  opacity: 1; }
}

.weather-fx.snow::before {
  content: '';
  position: absolute;
  inset: -10% 0 0 0;
  background-image: radial-gradient(circle, rgba(255,255,255,.85) 1.5px, transparent 1.5px);
  background-size: 40px 60px;
  animation: snowFall 4s linear infinite;
  opacity: .8;
}

@keyframes snowFall {
  from { background-position: 0 0; }
  to   { background-position: 20px 220px; }
}

.topbar {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.6vh 2vw;
  background: linear-gradient(90deg, #1a2140, #0b1020);
  border-bottom: 3px solid var(--gold);
}

.brand {
  font-size: 2.8vh;
  font-weight: 800;
  letter-spacing: .08em;
  color: var(--gold);
}

.pageinfo {
  font-size: 1.9vh;
  color: var(--text-dim);
  letter-spacing: .04em;
}

.clock {
  font-size: 2.4vh;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.grid {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(var(--cols, 8), 1fr);
  grid-template-rows: repeat(var(--rows, 8), 1fr);
  gap: .6vh .5vw;
  padding: .8vh 1vw;
  min-height: 0;
  perspective: 900px;
}

.card {
  background: #000;
  border: 1px solid var(--card-border);
  border-radius: 10px;
  overflow: hidden;
  position: relative;
  animation: fadein .4s ease;
  min-height: 0;
  transform-style: preserve-3d;
}

@keyframes fadein {
  from { opacity: 0; transform: scale(.97); }
  to   { opacity: 1; transform: scale(1); }
}

.card.just-updated {
  animation: ticketFlip .9s ease-in-out;
  z-index: 20;
}

@keyframes ticketFlip {
  0%   { transform: scale(1) rotateY(0deg);   box-shadow: 0 0 0 rgba(242,193,78,0); }
  35%  { transform: scale(1.12) rotateY(180deg); box-shadow: 0 0 5vh rgba(242,193,78,.85); }
  70%  { transform: scale(1.12) rotateY(360deg); box-shadow: 0 0 5vh rgba(242,193,78,.85); }
  100% { transform: scale(1) rotateY(360deg);  box-shadow: 0 0 0 rgba(242,193,78,0); }
}

.card .ticket-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

.card.empty-slot {
  /* transparent, not a solid fill - so the weather background actually
     shows through in unplaced slots instead of every grid cell (filled
     or not) blocking it with its own opaque tile. */
  background: transparent;
  border-color: #1c2547;
}

.card.empty-slot .slot {
  background: #1c2547;
  color: var(--text-dim);
  box-shadow: none;
}

.card.empty-slot::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top right,
    transparent calc(50% - 1.5px),
    var(--card-border) calc(50% - 1.5px),
    var(--card-border) calc(50% + 1.5px),
    transparent calc(50% + 1.5px)
  );
}

.card .slot {
  position: absolute;
  top: 3px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--gold);
  color: #1a1200;
  font-weight: 900;
  font-size: clamp(13px, 3.2vh, 40px);
  line-height: 1;
  padding: .15em .5em;
  border-radius: 8px;
  letter-spacing: -.02em;
  z-index: 3;
  box-shadow: 0 2px 8px rgba(0,0,0,.45);
}

.card .price {
  position: absolute;
  bottom: 3px;
  left: 3px;
  background: var(--red);
  color: #fff;
  font-size: clamp(9px, 1.6vh, 20px);
  font-weight: 900;
  padding: .15em .4em;
  border-radius: 6px;
  z-index: 2;
}

.card .name {
  position: absolute;
  top: 3px;
  left: 3px;
  max-width: 40%;
  background: rgba(0,0,0,.6);
  color: #fff;
  font-size: clamp(6px, .8vh, 10px);
  font-weight: 600;
  padding: .2em .5em;
  border-radius: 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  z-index: 2;
}

.card .corner-badges {
  position: absolute;
  top: 3px;
  right: 3px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  z-index: 3;
}

.card .closing-badge {
  background: var(--red);
  color: #fff;
  font-size: clamp(6px, .85vh, 11px);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .02em;
  padding: .2em .5em;
  border-radius: 5px;
  white-space: nowrap;
  animation: closingPulse 1.6s ease-in-out infinite;
}

@keyframes closingPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .55; }
}

.card .new-badge {
  background: var(--red);
  color: #fff;
  font-size: clamp(7px, .95vh, 13px);
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: .2em .6em;
  border-radius: 5px;
  white-space: nowrap;
  animation: newBadgeBlink .9s steps(1, end) infinite;
}

@keyframes newBadgeBlink {
  0%, 49%   { opacity: 1; box-shadow: 0 0 1em rgba(226,56,77,.9); }
  50%, 100% { opacity: 0; box-shadow: none; }
}

/* Featured/New border: a string of gold dashes traced around the tile as a
   real SVG outline. stroke-dashoffset shifts the dash pattern along that
   fixed path at a steady rate - each dash steps into the next one's spot -
   which reads as lights marching around the perimeter without the frame
   itself rotating. A slower opacity flicker on top adds the twinkle. */
.card.featured {
  overflow: visible;
}

.card .featured-lights {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 4;
  pointer-events: none;
  overflow: visible;
}

.card .featured-lights rect {
  fill: none;
  stroke: #fff8dd;
  stroke-width: 5;
  stroke-dasharray: 7 9;
  filter: drop-shadow(0 0 .5vh rgba(242,193,78,.9));
  animation: lightsMarch 1.6s linear infinite, lightsTwinkle 1.7s ease-in-out infinite;
}

@keyframes lightsMarch {
  to { stroke-dashoffset: -16; }
}

@keyframes lightsTwinkle {
  0%, 100% { opacity: .6; }
  20%      { opacity: 1; }
  35%      { opacity: .5; }
  50%      { opacity: 1; }
  65%      { opacity: .6; }
  80%      { opacity: 1; }
}

.card .ticketwrap {
  position: absolute;
  right: 3px;
  bottom: 3px;
  display: flex;
  align-items: center;
  gap: .3em;
  background: rgba(0,0,0,.85);
  border-radius: 6px;
  padding: .2em .45em;
  z-index: 2;
  max-width: calc(100% - 6px);
}

.card .ticket-label {
  font-size: clamp(7px, .85vh, 12px);
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: .02em;
  white-space: nowrap;
}

.card .ticket-value {
  font-size: clamp(10px, 1.6vh, 19px);
  font-weight: 900;
  color: var(--ticket-glow);
  text-shadow: 0 0 .15em var(--ticket-glow), 0 0 .4em var(--ticket-glow);
  line-height: 1;
  letter-spacing: -.01em;
  flex: 0 0 auto;
}

.card .progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: rgba(0,0,0,.2);
  z-index: 1;
}

.card .progress-fill {
  height: 100%;
  background: var(--progress-glow);
  box-shadow: 0 0 .6em var(--progress-glow), 0 0 .2em var(--progress-glow);
}

/* Brief edge glow whenever the board's data refreshes (timer poll or a live
   push) - a general "just synced" heartbeat, separate from the per-tile
   flip that only plays on tiles whose ticket number actually changed. */
.refresh-glow {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 40;
}

.refresh-glow.pulse {
  animation: refreshPulse 1.4s ease-out;
}

@keyframes refreshPulse {
  0%   { box-shadow: inset 0 0 0 0 rgba(242,193,78,0); }
  12%  { box-shadow: inset 0 0 6vh 1vh rgba(242,193,78,.4); }
  100% { box-shadow: inset 0 0 0 0 rgba(242,193,78,0); }
}

.empty, .error {
  grid-column: 1 / -1;
  grid-row: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.6vh;
  color: #5a6584;
  text-align: center;
  padding: 4vh;
}

/* Scan mode indicator: a small labeled badge right under "NOW PLAYING",
   not just a color dot - a tiny dot was too easy to miss while actually
   scanning, which meant tickets sometimes got logged in the wrong mode
   without anyone noticing. Absolutely positioned off the topbar (which is
   the anchor via position:relative) so it overlays a small corner of the
   grid instead of taking layout space - the game grid's own size doesn't
   shrink to make room for it. */
.scan-mode-badge {
  position: absolute;
  top: 100%;
  left: 2vw;
  font-size: 1.05vh;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  padding: .2vh .6vh;
  border-radius: 0 0 5px 5px;
  color: #1a1200;
  background: var(--gold);
  z-index: 45;
  box-shadow: 0 2px 8px rgba(0,0,0,.4);
}

.scan-mode-badge[hidden] {
  display: none;
}

.scan-mode-badge.selling {
  background: #38d16a;
  color: #06210f;
}

.scan-toast {
  position: fixed;
  right: 8px;
  bottom: 20px;
  max-width: 40vw;
  background: rgba(0,0,0,.85);
  color: var(--text);
  font-size: 12px;
  padding: 6px 10px;
  border-radius: 6px;
  border-left: 3px solid var(--gold);
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s;
  z-index: 51;
}

.scan-toast.show {
  opacity: 1;
}

.scan-toast.err {
  border-left-color: var(--red);
}

/* Same idea as the toast, but for the rare case a scan needs a yes/no answer
   (right now: "does this drop in ticket # mean a new pack?"). Only appears
   while scan mode is on, so only whoever's standing at the screen scanning
   sees it. */
.scan-confirm {
  position: fixed;
  right: 8px;
  bottom: 20px;
  max-width: 46vw;
  background: rgba(0,0,0,.92);
  color: var(--text);
  font-size: 13px;
  padding: 12px 14px;
  border-radius: 8px;
  border-left: 3px solid var(--gold);
  z-index: 52;
  box-shadow: 0 10px 30px rgba(0,0,0,.55);
}

.scan-confirm[hidden] {
  display: none;
}

.scan-confirm-msg {
  margin-bottom: 10px;
  line-height: 1.4;
}

.scan-confirm-actions {
  display: flex;
  gap: 8px;
}

.scan-confirm-btn {
  flex: 1 1 auto;
  font-size: 13px;
  font-weight: 700;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--gold);
  background: transparent;
  color: var(--gold);
  cursor: pointer;
}

.scan-confirm-btn:hover {
  background: var(--gold);
  color: #1a1200;
}

.scan-confirm-btn.no {
  border-color: var(--text-dim);
  color: var(--text-dim);
}

.scan-confirm-btn.no:hover {
  background: var(--text-dim);
  color: #0b1020;
}

.settings-btn {
  position: fixed;
  top: 10px;
  right: 10px;
  width: 4vh;
  height: 4vh;
  min-width: 30px;
  min-height: 30px;
  border-radius: 50%;
  border: 1px solid var(--card-border);
  background: rgba(19,26,51,.8);
  color: var(--text-dim);
  font-size: 2vh;
  line-height: 1;
  cursor: pointer;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: .55;
  transition: opacity .2s, color .2s;
}

.settings-btn:hover,
.settings-btn.open {
  opacity: 1;
  color: var(--gold);
}

.settings-panel {
  position: fixed;
  top: calc(4vh + 22px);
  right: 10px;
  min-width: 240px;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: 10px;
  padding: 8px;
  z-index: 60;
  display: none;
  box-shadow: 0 10px 30px rgba(0,0,0,.55);
}

.settings-panel.open {
  display: block;
}

.settings-title {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: 4px 8px 8px;
}

.settings-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  background: transparent;
  border: none;
  color: var(--text);
  font-size: 14px;
  padding: 9px 8px;
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
}

.settings-option:hover {
  background: rgba(255,255,255,.07);
}

.settings-hint {
  font-size: 10.5px;
  color: var(--text-dim);
  font-weight: 400;
}

.settings-check {
  opacity: 0;
  color: var(--gold);
  font-weight: 900;
  flex: 0 0 auto;
}

.settings-option.active {
  color: var(--gold);
}

.settings-option[data-mode="selling"].active {
  color: #38d16a;
}

.settings-option[data-mode="selling"].active .settings-check {
  color: #38d16a;
}

.settings-option.active .settings-check {
  opacity: 1;
}
