* {
  box-sizing: border-box;
}

:root {
  /* Defaults for custom properties set inline by React (player theming,
     clock progress, per-building modal theme) so they resolve even before
     JS applies them. */
  --player-color: #ffc107;
  --elapsed-angle: 0deg;
  --modal-bg: transparent;
  --building-accent: #ffc107;
  --game-scale: 1;

  /* Shared design tokens for the in-game HUD/map/modal system - keeps
     hover timing, radii and glass-panel look consistent everywhere
     instead of every component re-declaring its own values. */
  --glass-bg: rgba(0, 0, 0, 0.45);
  --glass-blur: blur(6px);
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 12px;
  --radius-pill: 999px;
  --transition-fast: 0.15s ease; /* hover micro-interactions: lift, zoom, tint, fade */
  --transition-med: 0.3s ease; /* state fills/reveals: mini-bar height, hunger icon */
  --transition-slow: 0.4s ease; /* clock hand sweep */
  --disabled-opacity: 0.45;
  --hover-lift: -2px;
  --pill-zoom: 1.08;
  --label-zoom: 1.06;
  --icon-zoom: 1.25;
  --press-scale: 0.95; /* click/tap feedback on buttons - the only thing telling you a click actually registered */

  /* the game's max "native" width - .game-main never grows past this */
  --game-w: 900px;
}

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

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: #14181d;
  color: #e9ecef;
}

#root {
  height: 100%;
}

/* Registering --player-color as a real <color> lets the browser smoothly
   interpolate it (and anything derived from it below) whenever a turn
   handoff changes the value, instead of snapping instantly. */
@property --player-color {
  syntax: '<color>';
  inherits: true;
  initial-value: #ffc107;
}

.container {
  position: relative;
  height: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: #0a0d12;
  transition: --player-color 1.4s ease;
}

/* Ambient page backdrop: soft, slow-drifting blobs tinted with the current
   player's color, sitting in the dead space around the game board. Kept
   delicate on purpose - this is atmosphere, not a focal point. */
.container::before,
.container::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.container::before {
  background:
    radial-gradient(ellipse 50% 42% at 12% 15%, color-mix(in srgb, var(--player-color) 30%, transparent), transparent 70%),
    radial-gradient(ellipse 46% 50% at 90% 88%, color-mix(in srgb, var(--player-color) 22%, transparent), transparent 72%),
    radial-gradient(ellipse 36% 36% at 88% 6%, color-mix(in srgb, var(--player-color) 14%, transparent), transparent 75%);
  filter: blur(40px);
  animation: ambient-drift 26s ease-in-out infinite alternate, ambient-breathe 7s ease-in-out infinite;
}

.container::after {
  background: radial-gradient(ellipse 85% 85% at 50% 50%, transparent 45%, rgba(0, 0, 0, 0.45) 100%);
}

@keyframes ambient-drift {
  0% { transform: translate(0, 0) scale(1); }
  100% { transform: translate(3%, -3%) scale(1.08); }
}

@keyframes ambient-breathe {
  0%, 100% { opacity: 0.65; }
  50% { opacity: 0.9; }
}

hr {
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  margin: 12px 0;
}

/* buttons */

.btn {
  padding: 6px 14px;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  margin: 2px 0;
  transition: transform var(--transition-fast);
}

.btn:active:not(:disabled) {
  transform: scale(var(--press-scale));
}

.btn-primary {
  background: #2c6e49;
  color: #fff;
}

.btn-primary:disabled {
  opacity: var(--disabled-opacity);
  cursor: default;
}

.btn-danger {
  background: #a83232;
  color: #fff;
}

.btn-danger:disabled {
  opacity: var(--disabled-opacity);
  cursor: default;
}

.btn-secondary {
  background: rgba(255, 255, 255, 0.08);
  color: #e9ecef;
  border: 1px solid rgba(255, 255, 255, 0.16);
}

.btn-secondary:hover {
  background: rgba(255, 255, 255, 0.14);
}

/* confirmation dialogs (e.g. quit-game) - a compact message plus a
   cancel/confirm pair, reusing Modal with the `confirm` tier so it stacks
   above an already-open Intro/Game Menu modal */

.confirm-message {
  margin: 0 0 18px;
  font-size: 14px;
  line-height: 1.5;
  color: rgba(233, 236, 239, 0.85);
}

/* SpendConfirmModal's price line - reuses .confirm-message's spacing rhythm
   but bigger/bolder, since the cost is the one number that actually needs
   a second look before confirming. */
.spend-confirm-price {
  margin: 0 0 18px;
  font-size: 20px;
  font-weight: 700;
}

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

.confirm-actions .btn {
  flex: 1;
  padding: 10px;
}

/* alerts */

.alert {
  position: relative;
  z-index: 1;
  padding: 10px 14px;
  border-radius: 4px;
  margin: 10px 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

.alert-warning {
  background: #fff3cd;
  color: #664d03;
  border: 1px solid #ffe69c;
}

/* layout */

/* sized in JS to the actual scaled pixel dimensions (native size * scale) -
   so the flex container always centers a box that's already the right
   size. transform: scale() alone can't do this because it never changes an
   element's layout footprint, only its painted appearance; without this
   wrapper .game-main would still reserve its full native size and could
   get clipped asymmetrically by the flex container once the viewport is
   smaller than that native size. */
.game-scale-wrap {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  overflow: hidden;
}

.game-main {
  position: relative;
  /* always rendered at its fixed native size - nothing inside reflows or
     resizes, the whole scene is one rigid picture. --game-scale is computed
     in JS (useFitScale) from the actual viewport size, picking whichever
     dimension is tighter, so it fills the screen with no scrollbars and no
     distortion at any zoom level. scaled from the top-left corner so it
     exactly fills .game-scale-wrap, which is already sized to match. */
  width: var(--game-w);
  transform: scale(var(--game-scale, 1));
  transform-origin: top left;
  border: 2px solid var(--player-color, transparent);
  border-radius: 10px;
  box-shadow: 0 0 18px -6px var(--player-color, transparent);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

/* mini stat bars (happiness/wealth, above cash) */

.mini-bars-box {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: 4px;
}

.mini-bar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.mini-bar-track {
  position: relative;
  width: 18px;
  height: 26px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.mini-bar-icon {
  position: absolute;
  top: 2px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 10px;
  line-height: 1;
  z-index: 1;
}

.mini-bar-fill {
  width: 100%;
  opacity: 0.7;
  transition: height var(--transition-med);
}

/* career bar - a hud-pill (same as the hunger/happiness/lottery badges)
   whose "icon" is a slim vertical pill-track that fills from the bottom as
   lifetimeHours climbs toward the next rung of the University -> HighTech
   career ladder. */

.career-bar-track {
  position: relative;
  width: 10px;
  height: 18px;
  background: rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-pill);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.career-bar-fill {
  width: 100%;
  background: #4fa3ff;
  transition: height var(--transition-med);
}

/* name entry */

/* Unlike the game view (deliberately non-scrolling, see html/body's
   overflow: hidden above), this page's content can genuinely be taller
   than the viewport - a short landscape phone especially - so it needs its
   own bounded height + scroll, independent of body's overflow rule.
   `align-items/justify-content: safe center` (not plain `center`) avoids a
   well-known flexbox gotcha: centered overflow is otherwise clipped on the
   *start* side with no way to scroll back up to it. The landscape media
   query further down switches the card to a two-column layout specifically
   so short phones don't *need* that scroll in the first place. */
.name-entry-page {
  /* declared here (rather than only relying on the inline style set in
     NameEntry.tsx) so static CSS analysis can resolve the property - the
     real value always comes from that inline style, which overrides this
     default via normal cascade specificity, and inherits down to the
     ::before below that actually uses it. */
  --login-map-image: none;
  --login-map-position: center;
  position: relative;
  height: 100dvh;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  display: flex;
  align-items: safe center;
  justify-content: safe center;
  padding: 24px;
  isolation: isolate;
}

/* the actual city map (see NameEntry.tsx's --login-map-image and
   --login-map-position - a random crop picked once per page load), blurred
   and slowly panning/zooming - a moody, on-theme backdrop instead of a
   generic gradient. Oversized (inset: -6%) so the pan never reveals a hard
   edge. `position: fixed` (viewport-relative), not `absolute` -
   .name-entry-page is itself the scroll container (overflow-y: auto
   above), and an *absolutely* positioned oversized layer would inflate its
   scrollable content area even though it's purely decorative, forcing a
   scrollbar that has nothing real to scroll to. Fixed-position elements
   don't contribute to an ancestor's scrollable overflow at all. */
.name-entry-page::before {
  content: '';
  position: fixed;
  inset: -6%;
  z-index: -2;
  background: var(--login-map-image) var(--login-map-position)/cover no-repeat;
  filter: blur(3px) saturate(0.7) brightness(0.42);
  animation: name-entry-pan 50s ease-in-out infinite alternate;
}

.name-entry-page::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(circle at 20% 15%, rgba(44, 110, 73, 0.4), transparent 45%),
    radial-gradient(circle at 85% 85%, rgba(87, 63, 168, 0.35), transparent 50%),
    linear-gradient(180deg, rgba(20, 24, 29, 0.55), rgba(20, 24, 29, 0.85));
}

@keyframes name-entry-pan {
  0% { transform: scale(1.06) translate(0, 0); }
  100% { transform: scale(1.14) translate(-2.5%, -2%); }
}

@media (prefers-reduced-motion: reduce) {
  .name-entry-page::before {
    animation: none;
  }
}

/* the animated ring is a classic two-layer background trick: a solid fill
   clipped to the padding-box (so it doesn't show past the border) stacked
   under a conic-gradient clipped to the border-box (so only the 1px border
   ring shows it) - cycling through the same accent colors the buildings
   use on the map, tying this screen to the game's own palette. Angle is a
   registered <angle> custom property (same technique as --player-color
   above) so the browser can animate it smoothly instead of snapping. */
@property --name-card-ring-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.name-entry-card {
  position: relative;
  width: 100%;
  max-width: 400px;
  border: 1px solid transparent;
  border-radius: 16px;
  padding: 28px 26px;
  background:
    linear-gradient(rgba(20, 24, 29, 0.82), rgba(20, 24, 29, 0.82)) padding-box,
    conic-gradient(
      from var(--name-card-ring-angle),
      #d4af37, #e0522a, #3f9142, #2e86ab, #8a2e3b, #5b3a8a, #00c2e0, #c8ced6, #d4af37
    ) border-box;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.55);
  animation: name-card-ring-spin 16s linear infinite;
}

@keyframes name-card-ring-spin {
  to { --name-card-ring-angle: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
  .name-entry-card {
    animation: none;
  }
}

.name-entry-brand {
  text-align: center;
  margin-bottom: 22px;
}

.name-entry-logo {
  font-size: 42px;
  display: inline-block;
  margin-bottom: 4px;
  animation: name-entry-dice-idle 7s ease-in-out infinite;
  transform-origin: 50% 65%;
}

@keyframes name-entry-dice-idle {
  0%, 8%, 100% { transform: rotate(0deg); }
  3% { transform: rotate(-14deg); }
  5.5% { transform: rotate(10deg); }
}

@media (prefers-reduced-motion: reduce) {
  .name-entry-logo {
    animation: none;
  }
}

.name-entry-brand h1 {
  margin: 0;
  font-size: 32px;
  font-weight: 800;
  letter-spacing: 1px;
  background: linear-gradient(135deg, #ffc107, #4fd888 55%, #63a4ff);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.name-entry-tagline {
  margin: 4px 0 0;
  font-size: 13px;
  color: rgba(233, 236, 239, 0.65);
}

.name-entry-card form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Short/wide viewports (a landscape phone especially) have height as the
   tight dimension, not width - stacking brand/locked-bar/form vertically
   is exactly what forces a scrollbar there. Grid (not flex) so the brand
   block can span both rows in column 1 without changing the JSX: the
   locked-bar and form are each still single unlabeled children, they just
   auto-place into column 2, row 1 and row 2 in turn. */
@media (orientation: landscape) and (max-height: 560px) {
  .name-entry-page {
    padding: 12px;
  }

  .name-entry-card {
    max-width: 620px;
    padding: 14px 22px;
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 22px;
    align-items: center;
  }

  .name-entry-brand {
    grid-column: 1;
    grid-row: 1 / -1;
    margin-bottom: 0;
    padding-right: 22px;
    border-right: 1px solid rgba(255, 255, 255, 0.14);
  }

  .name-entry-logo {
    font-size: 26px;
  }

  .name-entry-brand h1 {
    font-size: 19px;
  }

  .name-entry-tagline {
    display: none;
  }

  .name-locked {
    grid-column: 2;
    margin-bottom: 8px;
  }

  .name-entry-card form {
    grid-column: 2;
    gap: 8px;
  }

  .running-games ul {
    max-height: 80px;
  }
}

.name-entry-card .field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  color: rgba(233, 236, 239, 0.75);
}

.name-entry-card input {
  padding: 10px 12px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.3);
  color: #e9ecef;
  font-size: 14px;
}

.name-entry-card input:focus {
  outline: none;
  border-color: #4fd888;
}

.name-locked {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  margin-bottom: 16px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.3);
}

.name-locked-value {
  font-size: 14px;
  font-weight: 600;
  color: #e9ecef;
}

.login-error {
  margin: -6px 0 0;
  font-size: 13px;
  color: #ff8080;
}

.name-edit-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 15px;
  padding: 2px 6px;
  border-radius: 6px;
  line-height: 1;
  opacity: 0.75;
  transition: opacity var(--transition-fast), background-color var(--transition-fast);
}

.name-edit-btn:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.1);
}

/* the game list dims (but doesn't hide) once a code is typed - it's still
   there for reference, just not the thing that's about to happen. */
.running-games {
  transition: opacity var(--transition-fast);
}

.running-games-disabled {
  opacity: var(--disabled-opacity);
  pointer-events: none;
}

.running-games ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 180px;
  overflow-y: auto;
}

.running-games-empty {
  font-size: 13px;
  color: rgba(233, 236, 239, 0.55);
  margin: 4px 0;
}

.running-game-card {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 8px 12px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  color: #e9ecef;
  cursor: pointer;
  text-align: left;
  transition: background-color var(--transition-fast), border-color var(--transition-fast),
    transform var(--transition-fast), box-shadow var(--transition-fast);
}

.running-game-card:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.1);
  border-color: #4fd888;
  transform: translateY(var(--hover-lift));
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}

.running-game-card:disabled {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

.running-game-card.selected {
  background: rgba(79, 216, 136, 0.14);
  border-color: #4fd888;
  box-shadow: 0 0 0 1px #4fd888, 0 6px 16px rgba(0, 0, 0, 0.35);
}

.running-game-players {
  font-size: 13px;
  font-weight: 600;
}

.running-game-meta {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 11px;
  color: rgba(233, 236, 239, 0.6);
}

.running-game-status {
  flex-shrink: 0;
  font-weight: 600;
  color: rgba(233, 236, 239, 0.8);
}

.running-game-status-open {
  color: #4fd888;
  text-shadow: 0 0 0 rgba(79, 216, 136, 0.7);
  animation: running-game-live-pulse 1.8s ease-in-out infinite;
}

@keyframes running-game-live-pulse {
  50% {
    text-shadow: 0 0 6px rgba(79, 216, 136, 0.7);
  }
}

@media (prefers-reduced-motion: reduce) {
  .running-game-status-open {
    animation: none;
  }
}

.name-entry-submit {
  width: 100%;
  padding: 12px;
  font-size: 15px;
  font-weight: 700;
  border-radius: 8px;
  background: linear-gradient(135deg, #2c6e49, #1f8a55);
}

/* clock */

.clock-wrap {
  position: absolute;
  bottom: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
}

/* Map.tsx owns the clock and job pill. cqw keeps their dimensions tied to
   the map itself, matching the status posts and player vehicle on mobile. */
.map-wrap > .clock-wrap {
  bottom: 0.9%;
}

.map-wrap .clock-face {
  width: 8.67cqw;
  height: 8.67cqw;
  border-width: 0.33cqw;
  outline-width: 0.22cqw;
  outline-offset: -0.11cqw;
}

.map-wrap .clock-face::before {
  -webkit-mask: radial-gradient(circle, transparent calc(50% - 0.89cqw), #000 calc(50% - 0.89cqw) calc(50% - 0.56cqw), transparent calc(50% - 0.56cqw));
  mask: radial-gradient(circle, transparent calc(50% - 0.89cqw), #000 calc(50% - 0.89cqw) calc(50% - 0.56cqw), transparent calc(50% - 0.56cqw));
}

.map-wrap .clock-hand {
  width: 0.44cqw;
  height: 3.11cqw;
  margin-left: -0.22cqw;
  border-radius: 0.44cqw 0.44cqw 0.11cqw 0.11cqw;
}

.map-wrap .clock-turn {
  width: 3.33cqw;
  height: 3.33cqw;
  border-width: 0.22cqw;
  font-size: 1.44cqw;
}

.map-job-badge {
  position: absolute;
  top: 1.1%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
}

.map-wrap .hud-pill {
  gap: 0.67cqw;
  padding: 0.44cqw 1.11cqw;
  border-width: 0.11cqw;
  border-radius: 0.67cqw;
  font-size: 1.89cqw;
}

.map-wrap .hud-corner {
  gap: 0.89cqw;
}

.map-wrap .hud-top-right {
  top: 1.1%;
  right: 1.1%;
  gap: 0.67cqw;
}

.map-wrap .hud-bottom-left {
  bottom: 1.72%;
  left: 1.1%;
}

.map-wrap .hud-bottom-right {
  bottom: 1.72%;
  right: 1.1%;
}

.map-wrap .menu-btn {
  width: 4cqw;
  height: 4cqw;
  padding: 0;
  font-size: 1.89cqw;
}

.map-wrap .hunger-icon {
  font-size: 1.89cqw;
}

.map-wrap .fridge-inventory-badge {
  gap: 0.44cqw;
}

.map-wrap .icon-tooltip {
  bottom: calc(100% + 0.67cqw);
  padding: 0.33cqw 0.78cqw;
  border-radius: 0.44cqw;
  font-size: 1.22cqw;
}

.map-wrap .career-bar-track {
  width: 1.11cqw;
  height: 2cqw;
}

.map-wrap .player-stack {
  top: 1.1%;
  left: 1.1%;
  gap: 0.89cqw;
}

.map-wrap .turn-badge {
  gap: 1.11cqw;
  padding: 0.67cqw 1.56cqw 0.89cqw 0.67cqw;
  border-width: 0.11cqw;
  box-shadow: 0 0.22cqw 0.89cqw rgba(0, 0, 0, 0.4);
}

.map-wrap .turn-badge.inactive {
  padding: 0.67cqw 0.89cqw 0.89cqw 0.67cqw;
}

.map-wrap .turn-badge-avatar {
  width: 3.33cqw;
  height: 3.33cqw;
  border-width: 0.22cqw;
  font-size: 1.44cqw;
}

.map-wrap .turn-badge.you .turn-badge-avatar {
  box-shadow: 0 0 0 0.22cqw rgba(255, 193, 7, 0.5);
}

.map-wrap .turn-badge-sub {
  bottom: -0.78cqw;
  min-width: 2cqw;
  height: 1.67cqw;
  padding: 0 0.33cqw;
  border-width: 0.11cqw;
  font-size: 1cqw;
}

.map-wrap .turn-badge-progress {
  left: -0.89cqw;
}

.map-wrap .turn-badge-rank {
  right: -0.89cqw;
}

.map-wrap .turn-badge-name {
  font-size: 1.56cqw;
  letter-spacing: 0.03cqw;
}

.map-wrap .turn-badge-you {
  margin-left: 0.67cqw;
  padding: 0.11cqw 0.67cqw;
  border-radius: 0.89cqw;
  font-size: 1.11cqw;
}

.map-wrap .mini-bars-box {
  gap: 0.89cqw;
  margin-left: 0.44cqw;
}

.map-wrap .mini-bar {
  gap: 0.22cqw;
}

.map-wrap .mini-bar-track {
  width: 2cqw;
  height: 2.89cqw;
  border-radius: 0.44cqw;
}

.map-wrap .mini-bar-icon {
  top: 0.22cqw;
  font-size: 1.11cqw;
}

.clock-face {
  position: relative;
  width: 78px;
  height: 78px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.35), transparent 45%),
    conic-gradient(
      var(--player-color, #ffc107) 0deg,
      var(--player-color, #ffc107) var(--elapsed-angle, 0deg),
      rgba(255, 255, 255, 0.08) var(--elapsed-angle, 0deg),
      rgba(255, 255, 255, 0.08) 360deg
    );
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 3px solid rgba(20, 22, 26, 0.85);
  outline: 2px solid var(--player-color, rgba(255, 255, 255, 0.3));
  outline-offset: -1px;
  box-shadow:
    0 3px 12px rgba(0, 0, 0, 0.45),
    inset 0 0 0 3px rgba(0, 0, 0, 0.2),
    inset 0 2px 5px rgba(255, 255, 255, 0.25);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
}

/* hour tick marks around the rim, evenly spaced regardless of how many
   game-hours make up a day - purely decorative, not tied to day.totalHours */
.clock-face::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: repeating-conic-gradient(rgba(255, 255, 255, 0.65) 0deg 1.6deg, transparent 1.6deg 30deg);
  -webkit-mask: radial-gradient(circle, transparent calc(50% - 8px), #000 calc(50% - 8px) calc(50% - 5px), transparent calc(50% - 5px));
  mask: radial-gradient(circle, transparent calc(50% - 8px), #000 calc(50% - 8px) calc(50% - 5px), transparent calc(50% - 5px));
  pointer-events: none;
}

.clock-wrap:hover .clock-face {
  transform: scale(var(--pill-zoom));
  box-shadow:
    0 3px 14px rgba(0, 0, 0, 0.5),
    inset 0 0 0 3px rgba(0, 0, 0, 0.2),
    inset 0 2px 5px rgba(255, 255, 255, 0.3),
    0 0 14px -2px var(--player-color, transparent);
}

.clock-hand {
  position: absolute;
  bottom: 50%;
  left: 50%;
  width: 4px;
  height: 28px;
  margin-left: -2px;
  border-radius: 4px 4px 1px 1px;
  background: linear-gradient(to top, #3a4048, #14171b 85%);
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.7);
  transform-origin: bottom center;
  transition: transform var(--transition-slow);
  z-index: 1;
}

.clock-turn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: radial-gradient(circle at 33% 28%, #333a42, #14171b 75%);
  border: 2px solid rgba(255, 255, 255, 0.5);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.3px;
  line-height: 1;
  z-index: 2;
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.5),
    inset 0 1px 2px rgba(255, 255, 255, 0.2);
}

.hunger-icon {
  display: inline-block;
  font-size: 17px;
  line-height: 1;
  opacity: 0.25;
  filter: grayscale(100%);
  transition: opacity var(--transition-med), filter var(--transition-med), transform var(--transition-fast);
}

.hunger-icon.filled {
  opacity: 1;
  filter: none;
}

.hud-pill:hover .hunger-icon,
.hud-pill:focus .hunger-icon {
  transform: scale(var(--icon-zoom));
}

.fridge-inventory-badge {
  gap: 4px;
}

.fridge-inventory-tooltip {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
}

/* the single icon inside a hud-pill that has its own hover-tooltip (mood
   face, lottery ticket count...) - zooms in when the pill is hovered */
.pill-icon {
  display: inline-block;
  transition: transform var(--transition-fast);
}

.hud-pill:hover .pill-icon,
.hud-pill:focus .pill-icon {
  transform: scale(var(--icon-zoom));
}

/* shared tooltip bubble - used by any hoverable/focusable icon (inventory,
   happiness) that needs a name/label popover above it */
.icon-tooltip {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  background: rgba(0, 0, 0, 0.85);
  padding: 3px 7px;
  border-radius: var(--radius-sm);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-fast);
}

.hud-pill:hover > .icon-tooltip,
.hud-pill:focus > .icon-tooltip {
  opacity: 1;
}

.hud-corner {
  position: absolute;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 8px;
}

.hud-bottom-left {
  bottom: 14px;
  left: 10px;
}

.hud-bottom-right {
  bottom: 14px;
  right: 10px;
}

.hud-top-right {
  top: 10px;
  right: 10px;
  flex-direction: row;
  align-items: center;
  gap: 6px;
}

.hud-top-center {
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
}

.pill-cursor {
  cursor: default;
}

.hud-pill {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
  color: #fff;
  white-space: nowrap;
  background: var(--glass-bg);
  padding: 4px 10px;
  border-radius: var(--radius-md);
  border: 1px solid var(--player-color, transparent);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  transition: transform var(--transition-fast);
}

.hud-pill:hover,
.hud-pill:focus-within {
  transform: scale(var(--pill-zoom));
}

.menu-btn {
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 50%;
  font-size: 17px;
}

.menu-code {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 16px;
}

.menu-code-label {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  opacity: 0.7;
}

.menu-code-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-md);
}

.menu-code-value {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.menu-code-row button {
  flex-shrink: 0;
  width: 110px;
}

.color-swatch {
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.25);
  cursor: pointer;
  transition: transform var(--transition-fast), border-color var(--transition-fast);
}

.color-swatch:hover:not(:disabled) {
  transform: scale(1.12);
}

.color-swatch.selected {
  border-color: #fff;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.35);
}

.color-swatch:disabled {
  opacity: 0.25;
  cursor: not-allowed;
}

.add-player-form {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  margin: 0 0 16px;
}

.add-player-form .field {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  color: rgba(233, 236, 239, 0.75);
}

.add-player-form input {
  padding: 10px 12px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.3);
  color: #e9ecef;
  font-size: 14px;
}

.add-player-form input:focus {
  outline: none;
  border-color: #4fd888;
}

.add-player-form button {
  flex-shrink: 0;
  width: 110px;
}

/* the Menu's one real action (no "Start playing" CTA to sit next to here,
   unlike the Intro screen's main-menu button), so it gets the same
   substantial sizing .intro-start uses instead of the bare .btn default. */
.menu-leave {
  width: 100%;
  font-size: 15px;
  padding: 12px;
}

.quit-button {
  width: 100%;
  margin-top: 6px;
  padding: 10px 12px;
  font-size: 14px;
  font-weight: 700;
}

.intro-roster {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 16px;
}

.intro-roster-list {
  list-style: none;
  margin: 0;
  padding: 0 6px 0 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 180px;
  overflow-y: auto;
}

/* thin, dark, rounded scrollbar for scroll panels inside the glass modals -
   the browser default (chunky, light track) clashed hard with the theme */
.intro-roster-list,
.running-games ul,
.modal-body {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}

.intro-roster-list::-webkit-scrollbar,
.running-games ul::-webkit-scrollbar,
.modal-body::-webkit-scrollbar {
  width: 6px;
}

.intro-roster-list::-webkit-scrollbar-track,
.running-games ul::-webkit-scrollbar-track,
.modal-body::-webkit-scrollbar-track {
  background: transparent;
}

.intro-roster-list::-webkit-scrollbar-thumb,
.running-games ul::-webkit-scrollbar-thumb,
.modal-body::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-pill);
}

.intro-roster-list::-webkit-scrollbar-thumb:hover,
.running-games ul::-webkit-scrollbar-thumb:hover,
.modal-body::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.35);
}

.intro-roster-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius-md);
}

.intro-roster-avatar-wrap {
  position: relative;
  flex-shrink: 0;
}

.intro-roster-avatar {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  font-size: 12px;
}

.intro-roster-avatar-btn {
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

.color-picker-backdrop {
  position: fixed;
  inset: 0;
  z-index: 25;
}

/* opens to the right of the avatar, vertically centered on it - level with
   the player name rather than dropping down below the row. Single row, no
   wrap: this sits inside .intro-roster-list (a scrollable, max-height
   panel), so a popover that wraps to multiple rows can grow tall enough to
   get clipped by that scroll container instead of just floating over it. */
.color-picker-popover {
  position: absolute;
  top: 50%;
  left: calc(100% + 8px);
  transform: translateY(-50%);
  z-index: 30;
  display: flex;
  flex-wrap: nowrap;
  gap: 6px;
  width: max-content;
  padding: 8px;
  background: rgba(20, 20, 24, 0.97);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45);
}

.intro-roster-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.intro-roster-name {
  font-size: 14px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.intro-roster-controller {
  font-size: 11px;
  color: rgba(233, 236, 239, 0.55);
}

.intro-roster-you {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: #4fd888;
  border: 1px solid rgba(79, 216, 136, 0.5);
  border-radius: var(--radius-pill);
  padding: 2px 8px;
}

.intro-roster-remove {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: 50%;
  color: rgba(233, 236, 239, 0.5);
  font-size: 12px;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.intro-roster-remove:hover {
  background: rgba(168, 50, 50, 0.25);
  color: #ff8080;
}

.intro-invite-toggle {
  flex: 1;
  text-align: left;
  background: none;
  border: none;
  padding: 0;
  color: rgba(233, 236, 239, 0.55);
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
}

.intro-invite-toggle:hover {
  color: rgba(233, 236, 239, 0.85);
}

.intro-actions {
  display: flex;
  align-items: stretch;
  gap: 8px;
  margin-top: 4px;
}

/* a small square icon button, not a same-size sibling to .intro-start - it's
   a secondary navigation action (main menu, then come back later) sitting next
   to the actual primary CTA, so it shouldn't visually compete with it.
   Stretches to .intro-start's height via .intro-actions' flex default. */
.intro-main-menu-btn {
  flex-shrink: 0;
  width: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--radius-md);
  color: #e9ecef;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast);
}

.intro-main-menu-btn:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.24);
}

.intro-main-menu-btn:active {
  transform: scale(var(--press-scale));
}

.intro-start {
  flex: 1;
  font-size: 16px;
  padding: 12px;
}

.player-stack {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.turn-badge {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  padding: 6px 14px 8px 6px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
  transition: opacity var(--transition-fast), transform var(--transition-fast);
}

.turn-badge.active {
  border-color: var(--player-color, transparent);
}

.turn-badge.inactive {
  align-self: flex-start;
  padding: 6px 8px 8px 6px;
  opacity: var(--disabled-opacity);
  transform: scale(0.85);
  transform-origin: left center;
}

/* shared circular avatar look - player turn badge and the winners list both
   show an initial in a colored circle */
.avatar-circle {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  border: 2px solid rgba(255, 255, 255, 0.6);
}

.turn-badge-avatar {
  position: relative;
  width: 30px;
  height: 30px;
  font-size: 13px;
}

.turn-badge.you .turn-badge-avatar {
  border-color: #ffc107;
  box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.5);
}

.turn-badge-sub {
  position: absolute;
  bottom: -7px;
  min-width: 18px;
  height: 15px;
  padding: 0 3px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(255, 255, 255, 0.6);
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

.turn-badge-progress {
  left: -8px;
  background: #2c6e49;
  color: #fff;
}

.turn-badge-rank {
  right: -8px;
  background: #ffc107;
  color: #1b1f24;
}

.turn-badge-name {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: #fff;
  transition: transform var(--transition-fast), color var(--transition-fast);
}

.turn-badge:hover .turn-badge-name {
  transform: scale(var(--label-zoom));
  color: var(--player-color, #ffc107);
}

.turn-badge-you {
  margin-left: 6px;
  font-size: 10px;
  font-weight: 700;
  color: #1b1f24;
  background: #ffc107;
  padding: 1px 6px;
  border-radius: 8px;
}

/* new-turn summary / messages notifications */

.new-turn-lead {
  margin: 0 0 12px;
  font-size: 14px;
}

.notification-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 16px;
}

.notification-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  font-size: 13px;
  line-height: 1.4;
}

.notification-icon {
  flex-shrink: 0;
  font-size: 16px;
  line-height: 1.4;
}

.notification-text {
  flex: 1;
}

.notification-good {
  background: color-mix(in srgb, #4fd888 14%, rgba(255, 255, 255, 0.05));
  border: 1px solid color-mix(in srgb, #4fd888 45%, transparent);
}

.notification-bad {
  background: color-mix(in srgb, #ff5252 14%, rgba(255, 255, 255, 0.05));
  border: 1px solid color-mix(in srgb, #ff5252 45%, transparent);
}

.notification-neutral {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.14);
}

.rent-note {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 10px 0 8px;
  padding: 6px 10px;
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 12%, rgba(255, 255, 255, 0.05));
  border: 1px solid color-mix(in srgb, var(--modal-accent, #2c6e49) 35%, transparent);
  border-radius: var(--radius-md);
  font-size: 12px;
  font-weight: 600;
  opacity: 0.9;
}

.balance-note {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 14px;
  padding: 14px 16px;
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 14%, rgba(255, 255, 255, 0.05));
  border: 1px solid color-mix(in srgb, var(--modal-accent, #2c6e49) 40%, transparent);
  border-radius: var(--radius-md);
}

.balance-note-label {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  opacity: 0.75;
}

.balance-note-amount {
  font-size: 22px;
  font-weight: 800;
}

.bank-transfer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 10px 0;
}

.bank-transfer .modal-card {
  flex: 1;
  max-width: 140px;
}

.bank-transfer-icon {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 20%, rgba(255, 255, 255, 0.05));
  border: 1px solid color-mix(in srgb, var(--modal-accent, #2c6e49) 45%, transparent);
  font-size: 16px;
}

.autopay-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  transition: opacity var(--transition-fast);
}

.autopay-toggle:hover {
  opacity: 1;
}

/* the login screen's public/private toggle only applies when you're about
   to create a game - dimmed (not hidden, so the layout doesn't jump) once
   a code's typed or a running game's selected instead. */
.toggle-inactive {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
  pointer-events: none;
}

/* simple "Public game" on/off - the switch alone carries the state. */
.game-visibility-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: rgba(233, 236, 239, 0.85);
}

/* read-only counterpart shown in the in-game Menu (GameMenuModal) - the
   Intro screen's .game-visibility-toggle is the only place this can
   actually be changed, and only while still joinable. A pill badge, not
   a bare sentence, so it reads as a status chip rather than a stray line
   of text floating between the roster and the Pause button. */
.game-visibility-status {
  margin: 0 0 14px;
  text-align: center;
}

.game-visibility-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 14px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.16);
  font-size: 12px;
  font-weight: 600;
  color: rgba(233, 236, 239, 0.8);
}

.bank-autopay {
  justify-content: space-between;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  font-size: 13px;
  font-weight: 600;
}


.bank-hint {
  margin: 0;
  padding: 8px 10px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px dashed rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-md);
  font-size: 12px;
  font-style: italic;
  opacity: 0.8;
}

.nothing-to-do {
  margin: 16px 0;
  padding: 18px 16px;
  text-align: center;
  font-size: 13px;
  font-style: italic;
  opacity: 0.75;
}

.switch {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  width: 36px;
  height: 20px;
}

.switch input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}

.switch-track {
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-pill);
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.switch-thumb {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  transition: transform var(--transition-fast);
}

.switch input:checked ~ .switch-track {
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 55%, transparent);
  border-color: var(--modal-accent, #2c6e49);
}

.switch input:checked ~ .switch-track .switch-thumb {
  transform: translateX(16px);
}


/* card grids inside modals (buildings, inventory) */

.modal-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin: 10px 0;
}

.modal-card {
  position: relative; /* anchors the .icon-tooltip item description below */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-md);
  padding: 10px 4px;
  color: #fff;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast);
}

.modal-card:hover > .icon-tooltip,
.modal-card:focus > .icon-tooltip {
  opacity: 1;
}

/* item-effect descriptions run longer than the other one-word .icon-tooltip
   uses elsewhere, so unlike the shared base rule's nowrap default, these
   wrap normally - confined to the card's own width (not a fixed px value)
   so it can never spill past the card's left/right edges into the page,
   however narrow the grid column happens to be. */
.item-description-tooltip {
  white-space: normal;
  text-align: left;
  width: 100%;
  max-width: 100%;
}

.modal-card:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.14);
  border-color: var(--modal-accent, rgba(255, 255, 255, 0.4));
  transform: translateY(var(--hover-lift));
}

.modal-card:active:not(:disabled) {
  background: rgba(255, 255, 255, 0.22);
  transform: scale(var(--press-scale));
}

.modal-card:disabled {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

.modal-card-static {
  cursor: default;
}

.modal-card-current:disabled {
  opacity: 1;
  cursor: default;
  background: color-mix(in srgb, var(--modal-accent, #4fd888) 18%, rgba(255, 255, 255, 0.06));
  border-color: var(--modal-accent, #4fd888);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--modal-accent, #4fd888) 35%, transparent);
}

.modal-card-icon {
  font-size: 26px;
  line-height: 1;
}

.modal-card-label {
  font-size: 11px;
  font-weight: 600;
  text-align: center;
  line-height: 1.25;
}

.modal-card-sub {
  font-size: 10px;
  opacity: 0.75;
}

/* any displayed price (Card sub, ActionButton sub, university fees...) -
   red whenever the current player can't afford it, see Price.tsx */
.price-cant-afford {
  color: #ff6b6b;
  opacity: 1;
}

/* University course tree (BuildingView -> UniversityFlowchart) - 3 track
   columns (Life/Economics/STEM) laid out as rows of nodes with thin
   connector lines between tiers, instead of a flat grid of buttons. */

.uni-tree {
  display: flex;
  flex-direction: column;
  margin: 6px 0 4px;
}

.uni-tree-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.uni-tree-connector {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  height: 22px;
}

.uni-tree-line {
  width: 2px;
  height: 100%;
  margin: 0 auto;
  background: rgba(255, 255, 255, 0.18);
}

.uni-tree-connector-fork {
  display: block;
  height: 22px;
}

.uni-tree-connector-fork svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}

.uni-tree-connector-fork line {
  stroke: rgba(255, 255, 255, 0.18);
  stroke-width: 2;
}

.uni-node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 8px 4px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-top: 3px solid var(--uni-track-color, rgba(255, 255, 255, 0.15));
  border-radius: var(--radius-md);
  color: #fff;
  font-family: inherit;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast), filter var(--transition-fast);
}

.uni-node-badge {
  position: absolute;
  top: 3px;
  right: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1px 4px;
  font-size: 8px;
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
  color: #fff;
  background: rgba(20, 20, 24, 0.85);
  border: 1px solid var(--uni-track-color, rgba(255, 255, 255, 0.3));
  border-radius: var(--radius-pill);
}

.uni-node-badge:hover > .icon-tooltip,
.uni-node-badge:focus > .icon-tooltip {
  opacity: 1;
}

/* overlaps down onto the node card itself instead of floating above the
   badge - the tree sits near the top of a scrolling modal-body, so a
   tooltip that pokes upward can extend past its top edge and force a
   scrollbar to appear just from being hovered. */
.uni-node-badge .icon-tooltip {
  top: calc(100% + 4px);
  bottom: auto;
  right: 0;
  left: auto;
  transform: none;
}

.uni-node:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.14);
  transform: translateY(var(--hover-lift));
}

.uni-node:active:not(:disabled) {
  background: rgba(255, 255, 255, 0.22);
  transform: scale(var(--press-scale));
}

.uni-node:disabled:not(.uni-node-graduated):not(.uni-node-locked) {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

.uni-node-locked {
  opacity: 0.5;
  filter: grayscale(0.7);
  cursor: not-allowed;
}

.uni-node-graduated {
  cursor: default;
  background: color-mix(in srgb, var(--uni-track-color) 20%, rgba(255, 255, 255, 0.06));
  border-color: color-mix(in srgb, var(--uni-track-color) 55%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--uni-track-color) 30%, transparent);
}

.uni-node-enrollable {
  border-color: color-mix(in srgb, var(--uni-track-color) 45%, rgba(255, 255, 255, 0.15));
}

.uni-track-life {
  --uni-track-color: #4fd888;
}

.uni-track-economics {
  --uni-track-color: #ffc107;
}

.uni-track-stem {
  --uni-track-color: #4fa3ff;
}

.uni-node-icon {
  font-size: 20px;
  line-height: 1;
}

.uni-node-label {
  font-size: 10px;
  font-weight: 600;
  text-align: center;
  line-height: 1.2;
}

.uni-node-sub {
  font-size: 9px;
  opacity: 0.75;
}

/* action buttons (Rest, Pay Rent, Work, Job offers...) - the primary thing to
   do at this location right now, as opposed to item buttons (below) which are
   a grid of interchangeable choices (shop items, food, schools, jobs). */

.action-zone {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}

.action-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Straddles the modal's bottom border, 20% in/80% out (see Modal's
   `footer` prop) - currently BuildingView's Work/Ask promotion pair,
   bottom-left/bottom-right respectively (see .action-bar-fixed below). A
   sibling of .modal rather than a child of it specifically so .modal's own
   overflow:hidden (which clips header/body to the rounded corners as
   normal) doesn't also clip this - it's positioned against .modal-shell
   instead, which wraps .modal without clipping anything. `bottom: 0` lines
   its own bottom edge up with .modal's (fully inside, 0% out); each 1% of
   translateY then moves 1% of its own height past that line, so
   translateY(80%) leaves 20% above the border and 80% hanging below it. */
.modal-footer {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 0;
  transform: translateY(80%);
}

/* Work/Job offers/Ask promotion visually belong to the building modal even
   while straddling its edge: use the same theme accent as a fully opaque
   fill so the map cannot show through the part hanging below the modal. */
.modal-footer .action-btn {
  background: var(--modal-accent, #2c6e49);
}

.modal-footer .action-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 88%, white);
}

.modal-footer .action-btn:active:not(:disabled) {
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 76%, black);
}

/* Work/Ask promotion only (see WorkActionsFooter) - fixed/content-sized
   buttons instead of .action-btn-wrap's default flex:1 (which stretches a
   lone button, e.g. just Work with no offer available, to fill the whole
   row). margin-left:auto on .footer-work-btn pins Work to the row's right
   edge regardless of whether Ask promotion precedes it - the standard
   flexbox "push this item and everything after it to the far end" trick -
   so each button stays anchored to its own corner whether it's alone or
   paired with the other one. */
.action-bar-fixed .action-btn-wrap {
  flex: none;
  min-width: 0;
}

.action-bar-fixed .footer-work-btn {
  margin-left: auto;
}

.action-btn-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
  min-width: 120px;
}

.action-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 16%, rgba(255, 255, 255, 0.05));
  border: 1px solid var(--modal-accent, rgba(255, 255, 255, 0.3));
  border-radius: var(--radius-md);
  color: #fff;
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.action-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 30%, rgba(255, 255, 255, 0.08));
  transform: translateY(var(--hover-lift));
}

.action-btn:active:not(:disabled) {
  background: color-mix(in srgb, var(--modal-accent, #2c6e49) 45%, rgba(255, 255, 255, 0.12));
  transform: scale(var(--press-scale));
}

.action-btn:disabled {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

.action-icon {
  font-size: 18px;
  line-height: 1;
}

.action-label {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.3px;
  transition: transform var(--transition-fast), color var(--transition-fast);
}

.action-btn:hover:not(:disabled) .action-label {
  transform: scale(var(--label-zoom));
  color: var(--modal-accent, #4fd888);
}

.action-sub {
  font-size: 11px;
  font-weight: 700;
  opacity: 0.85;
}

/* Work's per-hour garnishment (see WorkActionsFooter/garnishedPerHour) -
   red instead of a spelled-out "debt" label, same tone .debt-badge used to
   use before it moved onto this button. */
.garnish-amount {
  color: #ff5252;
  opacity: 1;
}

/* modal (building view, new turn, etc.) */

/* every modal portals to document.body (see Modal.tsx) - true fixed,
   viewport-relative overlay, flush to the real screen edges regardless of
   confirm tier. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  padding: 20px;
  z-index: 10;
}

.modal-backdrop-top {
  z-index: 30;
}

/* one tier above modal-backdrop-top - for a confirmation prompt opened from
   inside an already-open "top" modal (Intro screen, Game Menu), so it still
   stacks above it. Darker backdrop to read as an even-more-unmissable
   "this can't be undone" prompt. */
.modal-backdrop-confirm {
  z-index: 40;
  background: rgba(0, 0, 0, 0.65);
}

/* Positioning context for .modal + its optional straddle-the-border
   .modal-footer (see Modal.tsx) - sized+scaled the same way regardless of
   confirm tier, one shared rule, not two separately-maintained ones.
   transform: scale(var(--game-scale)) is what keeps every modal in step
   with the map/pins/car/tooltip as the window resizes; --game-scale is
   mirrored onto <html> in App.tsx specifically so this reaches modals here
   even though they're portalled outside .game-main's own subtree.
   Flexbox already centers the pre-transform box, so the default center
   transform-origin keeps it centered as it scales. Lives on the shell
   rather than .modal itself so .modal-footer (a sibling, not a child of
   .modal - see below) scales and tracks position in lockstep with it. */
.modal-shell {
  position: relative;
  width: 100%;
  max-width: 380px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  transform: scale(var(--game-scale, 1));
}

.modal {
  --modal-accent: #2c6e49;
  width: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: rgba(24, 28, 33, 0.8);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--modal-accent);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55), 0 0 24px -8px var(--modal-accent);
  overflow: hidden;
}

.modal-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--modal-accent) 25%, transparent), transparent);
  border-bottom: 1px solid var(--modal-accent);
}

.modal-icon {
  font-size: 24px;
  line-height: 1;
}

.modal-heading {
  flex: 1;
}

.modal-heading h3 {
  margin: 0;
  font-size: 16px;
}

.modal-tagline {
  margin: 2px 0 0;
  font-size: 11px;
  font-style: italic;
  opacity: 0.75;
}

.modal-close {
  background: none;
  border: none;
  color: #fff;
  font-size: 14px;
  cursor: pointer;
  opacity: 0.7;
  padding: 4px 9px;
  border-radius: 4px;
}

.modal-close:hover:not(:disabled) {
  opacity: 1;
  background: rgba(255, 255, 255, 0.12);
}

.modal-close:disabled {
  opacity: var(--disabled-opacity);
  cursor: not-allowed;
}

.modal-body {
  padding: 14px 16px;
  overflow-y: auto;
  background: var(--modal-bg, transparent);
}

.modal-body p:first-child {
  margin-top: 0;
}

.modal .btn-primary {
  background: var(--modal-accent);
}

/* winners modal */

.winners-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 4px 0 16px;
}

.winner-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: color-mix(in srgb, var(--modal-accent) 14%, rgba(255, 255, 255, 0.05));
  border: 1px solid color-mix(in srgb, var(--modal-accent) 45%, transparent);
  border-radius: var(--radius-md);
}

.winner-avatar {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  font-size: 14px;
}

.winner-name {
  flex: 1;
  font-weight: 700;
  font-size: 14px;
}

.winner-stats {
  font-size: 12px;
  opacity: 0.8;
  white-space: nowrap;
}

.winners-leave {
  width: 100%;
}

/* map */

.map-wrap {
  position: relative;
  width: 100%;
  padding-top: 90.45%; /* image aspect ratio (976 / 1079) */
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
  /* lets the building tooltip below size itself in cqw - a true % of the
     map's own width, same idea as the pin signs/player-ball's %-of-
     .map-wrap sizing just above/below - so it sticks to the map's size
     the same way they do, on both desktop (rides along with .game-main's
     transform for free either way) and mobile (.map-wrap genuinely
     resizes there, with no transform involved at all). */
  container-type: inline-size;
}

.map-bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  border-radius: 8px;
  z-index: 0;
}

.map-building {
  position: absolute;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  z-index: 1;
}

/* the building's own art - one of the src/assets/buildings/*.png crops
   (see split-building-sprites.mjs) instead of one big combined image
   covering the whole map. Its box is exactly BUILDING_BOUNDS, same as the
   button itself, so `inset: 0` lines it up with no extra positioning math -
   the old single `map_layer_2.png` overlay is still the *source* those
   crops are generated from, it's just no longer loaded by the app itself. */
.map-building-sprite {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

.map-building:disabled {
  cursor: not-allowed;
}

/* the exact-silhouette hover/active highlight - map-glow is a plain colored
   layer (the building's own accent) masked down to just its opaque pixels
   (src/assets/buildings/*.png, see split-building-sprites.mjs), instead of
   lighting up the whole rectangular hit-box. Invisible until hovered/here.
   Two copies of the same mask are layered (see the inline maskImage in
   Map.tsx), the second one scaled up 10% around the center - `exclude`/
   `xor` keeps only the sliver where just one of the two overlaps, i.e. a
   thin ring along the silhouette's edge, so the building's own art shows
   through the middle instead of getting a flat color wash over the whole
   shape. The drop-shadow blur then turns that ring into a soft glow. */
.map-glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--building-accent, #ffc107);
  mask-repeat: no-repeat, no-repeat;
  mask-position: center, center;
  mask-size: 100% 100%, 104% 104%;
  mask-composite: exclude;
  -webkit-mask-repeat: no-repeat, no-repeat;
  -webkit-mask-position: center, center;
  -webkit-mask-size: 100% 100%, 104% 104%;
  -webkit-mask-composite: xor;
  mix-blend-mode: screen;
  opacity: 0;
  filter: drop-shadow(0 0 6px var(--building-accent, #ffc107)) drop-shadow(0 0 10px var(--building-accent, #ffc107));
  transition: opacity var(--transition-fast);
}

.map-building:hover:not(:disabled) .map-glow,
.map-building:focus-visible .map-glow {
  opacity: 0.85;
}

.map-building.here .map-glow {
  opacity: 0.65;
}

/* raise the hovered/focused building's glow above its neighbors so it never
   ends up peeking out from underneath a building that happens to overlap
   it further down the map. The tooltip itself is a separate top-level
   layer (see .map-building-tooltip below), not a descendant of this
   button, so it isn't capped by this z-index. */
.map-building:hover,
.map-building:focus-visible {
  z-index: 2;
}

/* the building's own nice hover/focus bubble - name plus whatever status
   badges apply (home/job/hiring/studying, shared with the pin's
   StatusSigns via buildingStatusBadges) - replacing the plain browser
   title tooltip. Shown even when the building is disabled (not your turn)
   so that's still communicated, just without the status list swapped in
   for a one-line note instead. Rendered as a top-level sibling of the
   buildings/pins/car (positioned via inline left/top computed from the
   hovered building's BUILDING_BOUNDS box in Map.tsx, since it's no longer
   a descendant of the button - see the hoveredBuilding state there) so it
   can sit above *everything* on the map, car included, rather than being
   capped by its old parent building's z-index. Sized in cqw (a % of
   .map-wrap's own width, via its container-type: inline-size above)
   rather than px, same idea as the pin signs/player-ball's %-of-.map-wrap
   sizing - it needs to stick to the *map's* size, not the surrounding
   page/HUD, so it doesn't end up looming over a shrunk-down map the way a
   fixed-px HUD pill/tooltip would. */
.map-building-tooltip {
  position: absolute;
  transform: translate(-50%, -50%);
  /* above the player ball (10) and status signs - a hovered building's
     info bubble should never be hidden underneath either of those. */
  z-index: 20;
  width: max-content;
  max-width: 20cqw;
  padding: 0.9cqw 1.2cqw;
  border-radius: 1.1cqw;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 0.11cqw solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.45);
  color: #fff;
  text-align: left;
  pointer-events: none;
}

.map-building-tooltip-name {
  display: block;
  font-size: 1.45cqw;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.map-building-tooltip-description {
  display: block;
  margin-top: 0.35cqw;
  font-size: 1.2cqw;
  line-height: 1.35;
  opacity: 0.85;
  white-space: normal;
}

.map-building-tooltip-not-your-turn {
  display: block;
  margin-top: 0.45cqw;
  font-size: 1.25cqw;
  font-weight: 600;
  color: #ff9c9c;
  white-space: nowrap;
}

.map-building-tooltip-statuses {
  display: flex;
  flex-direction: column;
  gap: 0.45cqw;
  margin-top: 0.65cqw;
  padding-top: 0.65cqw;
  border-top: 0.11cqw solid rgba(255, 255, 255, 0.16);
}

.map-building-tooltip-status {
  --status-color: #fff; /* overridden inline per-badge in Map.tsx */
  display: flex;
  align-items: center;
  gap: 0.65cqw;
  font-size: 1.25cqw;
  font-weight: 600;
  color: var(--status-color);
  white-space: nowrap;
}

.map-building-tooltip-status-icon {
  filter: drop-shadow(0 0 3px var(--status-color, transparent));
}

/* a small tilted signboard on a post, planted at the building's pin spot -
   the border color codes the status (home/job/hiring/study, see the
   per-sign `color` prop in Map.tsx) so it reads at a glance even before
   the emoji does. The name/icon signage itself is baked into the map art
   (map_layer_2.png), so this carries no background of its own.
   Positioned and sized exactly like .player-ball below - a direct % of
   .map-wrap, its containing block, since it's absolutely positioned
   straight inside it (no intermediate auto-sized wrapper, which is what
   made an earlier attempt at this collapse to ~0 on some layouts: a
   percentage can't resolve against a shrink-wrapped ancestor). 4.44%/
   6.39% matches the old 40px/52px at the game's native 900x814 canvas
   size (40/900, 52/814). --badge-index (set in Map.tsx) cascades multiple
   simultaneous badges at the same pin into a down-right fan, like a hand
   of cards, each one drawn in front of the last (see z-index below) -
   replaces an earlier attempt that centered them in a single row, which
   read as one blob rather than distinct signs once two badges' signboard
   art (which nearly fills its box - see the SVG in StatusSign.tsx)
   overlapped at anything less than a full sign-width of gap. */
.map-status-sign {
  --sign-color: #ffc107;
  --sign-w: 4.44%;
  --badge-index: 0; /* overridden inline per-badge in Map.tsx */
  position: absolute;
  width: var(--sign-w);
  height: 6.39%;
  /* cqw, not %, for the offset itself - translate()'s percentages resolve
     against this element's own (tiny) box, not .map-wrap, so a %-based
     offset here would be a fraction of a fraction and barely move at all.
     cqw resolves against .map-wrap (container-type: inline-size above),
     same unit the tooltip uses for everything including vertical spacing. */
  transform: translate(calc(-50% + var(--badge-index, 0) * 1.1cqw), calc(-50% + var(--badge-index, 0) * 0.8cqw));
  z-index: calc(4 + var(--badge-index, 0));
  filter: drop-shadow(0 0 3px var(--sign-color)) drop-shadow(0 0 6px var(--sign-color)) drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
  cursor: default;
  user-select: none;
}

.map-status-sign.dimmed {
  opacity: var(--disabled-opacity);
  filter: grayscale(60%);
}

.map-status-sign svg {
  display: block;
  width: 100%;
  height: 100%;
  cursor: default;
  user-select: none;
}

/* percentage of .map-wrap (its containing block, since it's absolutely
   positioned directly inside it), not fixed px - desktop scales the whole
   game canvas as one rigid transformed unit (see .game-main), so a fixed
   px size rides along with that for free, but mobile resizes .map-wrap's
   actual box dimensions instead (see useLayoutMode's comment below) - a
   fixed px car would stay full-size there while the map around it shrank,
   looking oversized. 2%/3.7% matches the old 18px/30px at the game's
   native 900x814 canvas size (18/900, 30/814). */
.player-ball {
  position: absolute;
  width: 2%;
  height: 3.7%;
  /* above every status sign (see their z-index, capped at 4 badge types
     i.e. max 4 + 3) so the car is never hidden underneath a stacked-up
     building's posts. */
  z-index: 10;
  pointer-events: none;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5));
}

.player-ball svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* mobile layout - swapped in below useLayoutMode's breakpoint instead of
   the desktop's whole-canvas useFitScale transform. Map and HUD each size
   independently rather than being one rigid scaled unit; see
   docs/architecture.md. Portrait stacks them (bands above/below the map);
   landscape flips to a row (sidebars beside the map) so the scarce
   dimension - height, in landscape - isn't eaten by full-width bars. */

.mobile-layout {
  position: relative;
  width: 100%;
  height: 100%;
  max-width: 480px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  box-sizing: border-box;
}

.mobile-layout.landscape {
  flex-direction: row;
  max-width: 960px;
}

/* grid, not flex space-between, so the middle item stays truly centered
   regardless of how wide the left/right groups end up (space-between only
   centers the middle child when its neighbors are equal width) - matches
   how top-center/bottom-center are independently centered on desktop. */
.mobile-band {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-md);
  padding: 8px 12px;
}

.mobile-band > *:first-child {
  justify-self: start;
}

.mobile-band > *:nth-child(2) {
  justify-self: center;
}

.mobile-band > *:last-child {
  justify-self: end;
}

.mobile-band-group {
  display: flex;
  align-items: center;
  gap: 10px;
}

.mobile-map-wrap {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  min-width: 0;
}

/* landscape's counterpart to .mobile-band - a fixed-width column instead
   of a full-width bar, split top/bottom so all 6 desktop HUD corners still
   have a home (see MobileLandscapeContent.tsx for which widget goes where). */
.mobile-sidebar {
  width: 88px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
}

.mobile-sidebar-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

/* overrides the desktop padding-top aspect-ratio hack with the modern
   property, which (unlike padding-top) can be constrained by either width
   or height. Portrait's `.mobile-map-wrap` sits in a vertical stack where
   *width* is the tighter dimension (the phone's screen width), so the map
   should size off width and let height follow - sizing off height instead
   (like the old one-size-fits-all rule below did) could compute a width
   wider than the screen, which `max-width` would then clamp without
   correspondingly shrinking the height, leaving `object-fit: cover` to
   crop the sides to fill a now too-tall box (i.e. losing buildings off the
   edges). */
.mobile-map-wrap .map-wrap {
  width: 100%;
  height: auto;
  max-height: 100%;
  aspect-ratio: 1 / 0.9045;
  padding-top: 0;
}

/* landscape flips which dimension is tight - a short/wide viewport makes
   *height* the tighter dimension instead, so here the map sizes off height
   and lets width follow. */
.mobile-layout.landscape .mobile-map-wrap .map-wrap {
  width: auto;
  height: 100%;
  max-width: 100%;
  max-height: none;
}

/* PlayerStack positions itself over the desktop map rather than relying on
   a `.hud-corner` wrapper, so it needs to be pulled back into normal flow in
   both contexts, and neither the thin portrait bands nor the narrow (88px)
   landscape sidebar have room for PlayerStack's full name+wealth-bar
   display, so both collapse it to just avatar circles - the band lays
   them out in a row (it has width, not height, to spare), the sidebar
   keeps its natural column (the opposite tradeoff). */

.mobile-band .player-stack,
.mobile-sidebar .player-stack {
  position: static;
}

.mobile-band .player-stack {
  flex-direction: row;
  gap: 6px;
}

.mobile-band .turn-badge,
.mobile-sidebar .turn-badge {
  padding: 0;
}

.mobile-band .turn-badge-name,
.mobile-band .mini-bars-box,
.mobile-sidebar .turn-badge-name,
.mobile-sidebar .mini-bars-box {
  display: none;
}

