/* ==================================================
   0. Theme Tokens
   --------------------------------------------------
   Every colour, font, corner radius and shadow used on the page is a
   CSS custom property ("variable") defined here, and the rules below use
   var(--name). A theme only has to redefine these properties (for
   example under html[data-theme="..."]) to restyle the whole page.

   The --map-* properties are read by main.js (readMapColors) for the
   things Leaflet draws with JavaScript: street highlights, the area hint
   circle, connector lines, intersection dots and solution circles.
   Values must be plain colours (no var() inside) so JavaScript can read
   them.

   This block is the Paper theme (warm cream paper, brown ink, a serif for
   headings), the game's look since 2026-11-02 (owner). The old charcoal
   theme was removed then; three alternatives (transit, nordic, blueprint)
   stay in css/themes.css and apply with <html data-theme="...">.
================================================== */

:root {
  /* Text */
  --color-text: #2b2620;
  --color-text-secondary: #463f35;
  --color-text-muted: #6b604f;
  --color-text-faint: #7a6e5a; /* darkened 2026-11-02 for 4.5:1 */

  /* Page and surfaces */
  --color-page-bg: #e6dcc6;
  --color-page-bg-top: #efe7d6;     /* top of the page gradient */
  --color-card-bg: #fbf7ee;         /* the game card */
  --color-surface: #f7f1e3;         /* header, modals, banner */
  --color-surface-sunken: #f2ebdb;  /* table rows, hint box, inputs */
  --color-border: #d5c8ad;
  --color-row-border: #e1d6bf;      /* guess-table rows */
  --color-placeholder: #c9bb9f;     /* dashes in empty rows */
  --color-divider: rgba(70, 50, 20, 0.12);
  --color-overlay: rgba(60, 45, 20, 0.28); /* dimmed page behind a modal */

  /* Buttons and controls */
  --color-button-border: #c6b697;
  --color-button-hover: #efe6d2;
  --color-accent: #1f5f7a;          /* primary buttons, active states */
  --color-accent-hover: #174b61;
  --color-on-accent: #ffffff;         /* text on accent-coloured buttons */
  --color-accent-tint: rgba(31, 95, 122, 0.10);
  --color-accent-tint-strong: rgba(31, 95, 122, 0.14);
  --color-accent-border-soft: rgba(31, 95, 122, 0.5);
  --color-direction-arrow: #1f5f7a;
  --color-toggle-off: #948467; /* darkened 2026-11-02 for 3:1 */
  --color-toggle-knob: #ffffff;
  --color-bar: #e4d9c3;             /* stats distribution bars */

  /* Meaning */
  --color-success: #237a35;         /* green always means correct */
  --color-success-text: #22702f;
  --color-success-tint: rgba(35, 122, 53, 0.12);
  --color-success-border: rgba(35, 122, 53, 0.55);
  --color-danger: #c92a2a;

  /* Guess markers (HTML markers on the map, styled by CSS) */
  --color-marker-fill: #3b342b;
  --color-marker-outline: #fffaf0;
  --color-marker-text: #fffaf0;
  --color-marker-text-dark: #2b2620;
  --color-band-near: #f0b429;       /* < 500 m */
  --color-band-mid: #f0802f;        /* < 1 km */
  --color-band-far: #c92a2a;        /* < 3 km */
  --color-band-near-text: #2b2620;  /* numbers on each band colour */
  --color-band-mid-text: #2b2620;
  --color-band-far-text: #fffaf0;
  --color-marker-text-success: #fffaf0;
  --color-pending-fill: rgba(59, 52, 43, 0.5);
  --color-pending-glow: rgba(59, 52, 43, 0.3);

  /* Map overlays drawn by Leaflet (read by main.js, see above) */
  --map-street-1: #0b5fa5;          /* first street: blue */
  --map-street-2: #b04a00;          /* second street: burnt orange (Okabe–Ito vermilion #d55e00, darkened 2026-11-02 for 4.5:1 as text and 3:1 on the map) */
  --map-area-hint: #7b3fa0;         /* violet area-hint circle */
  --map-district: #a61e4d;          /* raspberry district-hint outline */
  /* Practice by district: the map outside the chosen districts is washed
     out like the outside of the city (same fill and line colours), but
     more lightly */
  --map-district-fade-opacity: 0.4;  /* a number, not a colour */
  --map-connector: #3b342b;         /* dotted line guess -> intersection */
  --map-dot-fill: #fffaf0;          /* intersection dot at a connector end */
  --map-dot-outline: #3b342b;
  --map-preview-fill: #3b342b;      /* preview dots */
  --map-preview-outline: #fffaf0;
  --map-solution-stroke: #237a35;   /* win-zone circle */
  --map-solution-fill: #51cf66;
  /* Outside the city boundary the map is washed out with its own land
     colour, so the surroundings look faint and the city stands out */
  --map-outside-fill: var(--basemap-land);
  --map-outside-stroke: #6b604f;    /* the boundary line */
  --map-outside-opacity: 0.68;      /* a number, not a colour */

  /* Basemap: the self-hosted vector map (js/basemap.js reads these and
     draws the tiles with them, so each theme gets a matching map) */
  --basemap-land: #efe6d2;
  --basemap-water: #a9c8cc;
  --basemap-park: #cfdcb2;
  --basemap-wood: #bfd0a0;
  --basemap-plaza: #e8dcc3;         /* pedestrian areas, platforms, beaches */
  --basemap-building: #ddd0b6;
  --basemap-road-casing: #cdbd9c;   /* thin outline around roads */
  --basemap-road-minor: #fbf6ea;
  --basemap-road-major: #f6e7c4;
  --basemap-path: #c8b892;          /* footways and service roads */
  --basemap-rail: #8c7c66;
  --basemap-label: #3b342b;         /* station names */
  --basemap-label-halo: #f7f1e3;    /* outline around map text */
  --basemap-water-label: #2f5f6b;
  --basemap-park-label: #4a5e2c;

  /* Page background */
  --page-pattern: radial-gradient(circle at 25% 0%, rgba(255, 255, 255, 0.55), transparent 55%); /* a soft light spot, like paper under a lamp */
  --border-width-card: 1px;         /* game card, map, modals, header line */

  /* Fonts: self-hosted in fonts/ (fonts.css), see AGENTS.md */
  --font-body: "Source Sans 3", system-ui, sans-serif;
  --font-heading: "Fraunces", Georgia, serif; /* prompt, modal headings */
  --font-title: "Fraunces", Georgia, serif;   /* "Crossroadle" in the header */
  --font-marker: "Source Sans 3", system-ui, sans-serif;
  --font-control: "Source Sans 3", system-ui, sans-serif; /* buttons, text boxes */
  --title-size: 1.7rem;
  --title-weight: 700;
  --title-letter-spacing: 0;
  --title-transform: none;

  /* Corner radii */
  --radius-card: 6px;              /* game card, modals */
  --radius-map: 4px;               /* map, banner, confirm bar */
  --radius-panel: 6px;             /* hint box, city options, segmented control */
  --radius-control: 4px;            /* buttons, table rows */
  --radius-bar: 2px;                /* stats bars */
  --radius-pill: 20px;              /* toggle switch */
  --radius-marker: 50%;             /* round markers and dots */

  /* Shadows */
  --shadow-card: 0 1px 0 rgba(70, 50, 20, 0.08), 0 10px 28px rgba(70, 50, 20, 0.14);
  --shadow-modal: 0 20px 50px rgba(60, 45, 20, 0.28);
  --shadow-floating: 0 6px 18px rgba(60, 45, 20, 0.25); /* banner, confirm bar */
  --shadow-marker: 0 1px 4px rgba(40, 30, 15, 0.45);
}


/* ==================================================
   1. Global Reset & Base Theme
   --------------------------------------------------
   Base typography and page background
================================================== */

/* Buttons and text boxes don't inherit the page font by default */
button,
input,
select,
textarea {
  font-family: var(--font-control);
}

body {
  margin: 0; /* remove default browser margin */
  font-family: var(--font-body);
  color: var(--color-text); /* light text */
  background-color: var(--color-page-bg); /* fallback if gradient fails */
  /* Optional theme pattern on top of a vertical gradient */
  background:
    var(--page-pattern),
    linear-gradient(180deg, var(--color-page-bg-top) 0%, var(--color-page-bg) 100%);

  /* Full-viewport column layout: header on top, game fills the rest.
     This lets the map shrink/grow so everything fits without scrolling. */
  height: 100vh;
  display: flex;
  flex-direction: column;
}


/* ==================================================
   2. Layout Containers
   --------------------------------------------------
   Controls spacing of main content below header
================================================== */

/* Wrapper for main content (add this later in HTML if needed) */
.main-content {
  padding: 1.5rem;
}

.app-container {
  display: flex;
  justify-content: center; /* horizontal centering */
  padding: 12px 16px;
  flex: 1;        /* take all height below the header */
  min-height: 0;  /* allow shrinking inside the body flex column */
}

/* Card that contains the game */
.game-card {
  width: 100%;
  max-width: 1000px; /* adjust to taste */
  background-color: var(--color-card-bg);
  border: var(--border-width-card) solid var(--color-border);
  border-radius: var(--radius-card);
  padding: 16px 24px; /* kept small so the map gets more height */
  box-shadow: var(--shadow-card);

  /* Stack prompt, status, map and table vertically; the map (flex: 1)
     takes whatever height is left. On very short screens the card
     scrolls instead of squashing the map below its min-height. */
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
}

/* New-round button followed by the prompt text. No wrapping, so the
   button always stays on the left; a long prompt wraps inside the h2. */
.prompt-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
}

#prompt {
  margin: 0;
  font-family: var(--font-heading);
}

/* Street names in the prompt (colour set inline from STREET_COLORS) */
.prompt-street {
  font-weight: 600;
}

/* Circular-arrow "new round" button (also uses .icon-btn) */
#new-game-btn {
  flex-shrink: 0; /* never squash the button when the prompt is long */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
}

/* The id rule above beats the shared .hidden class, so repeat it here
   (the button is hidden in daily mode) */
#new-game-btn.hidden {
  display: none;
}

#new-game-btn:hover {
  color: var(--color-text);
}

#new-game-btn svg {
  transition: transform 0.3s ease;
}

/* Small playful hint that the button "spins" the round */
#new-game-btn:hover svg {
  transform: rotate(90deg);
}

/* "Hints" box on its own row below the prompt, left-aligned.
   Same dark fill and border as the guess-table rows, so it reads as
   its own panel. */
.hint-box {
  /* .game-card is a flex column, which would stretch the box to full
     width; flex-start keeps it only as wide as its contents, on the left. */
  align-self: flex-start;
  margin-bottom: 6px;
  display: flex;
  flex-wrap: wrap; /* buttons wrap inside the box on very narrow screens */
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 4px 6px 4px 12px;
  background-color: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-panel);
}

/* Small uppercase label, styled like the table headers */
.hint-box-label {
  margin-right: 4px;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}

.hint-btn {
  background: none;
  border: 1px solid var(--color-button-border);
  color: var(--color-text);
  padding: 3px 10px;
  border-radius: var(--radius-control);
  font-size: 0.85rem;
  cursor: pointer;
  transition: background 0.2s ease, opacity 0.2s ease;
}

.hint-btn:hover:not(:disabled) {
  background-color: var(--color-button-hover);
}

/* Shorter labels ("Arrows", the table's "Intersection") only on phones,
   see Phone Layout */
.hint-label-short,
.label-short {
  display: none;
}

/* Used hints, and all hints before/after a round */
.hint-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Small, muted line below the hints: the round label plus the
   instruction or a hint's text. Always exactly one line (cut off with
   "…" if too long): if it wrapped, the map below would move and shrink
   in the middle of the camera's flight to the hint. */
#feedback {
  margin: 0;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A hint's text in the feedback line (e.g. "District: Oud-Zuid"), in the
   normal text colour so it stands out from the muted label */
.feedback-hint-note {
  color: var(--color-text);
}


/* ==================================================
   3. Header (Top Bar)
   --------------------------------------------------
   Fixed top navigation band
================================================== */

/* Three columns: left buttons | title | right buttons. The two outer
   columns are "1fr" (equal shares of the leftover width), which keeps the
   title exactly centred however wide the buttons are. */
.app-header {
  color: var(--color-text); /* lets a theme give the header its own colours */
  min-height: 60px;
  box-sizing: border-box;
  flex-shrink: 0; /* keep full height in the body flex column */
  background-color: var(--color-surface);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center; /* vertical centering */
  column-gap: 12px;
  padding: 0 16px;
  border-bottom: var(--border-width-card) solid var(--color-border);
}

.header-left,
.header-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.header-right {
  justify-content: flex-end;
}

/* Daily / Practice / Stats buttons */
.mode-nav {
  display: flex;
  gap: 6px;
}

.mode-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  background: none;
  border: 1px solid var(--color-button-border);
  border-radius: var(--radius-control);
  color: var(--color-text-secondary);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.1s ease;
}

.mode-btn:hover {
  background-color: var(--color-button-hover);
}

.mode-btn:active {
  transform: scale(0.95);
}

/* The current mode */
.mode-btn.active {
  border-color: var(--color-accent);
  background-color: var(--color-accent-tint-strong);
  color: var(--color-text);
}

/* Narrower screens: the mode buttons get their own row under the title
   (the grid gets a second row that spans all three columns). */
@media (max-width: 820px) {
  .app-header {
    padding: 8px 16px;
    row-gap: 8px;
  }

  .header-left {
    display: contents; /* its children become direct grid items */
  }

  #how-to-btn {
    grid-column: 1;
    grid-row: 1;
  }

  .mode-nav {
    grid-column: 1 / -1;
    grid-row: 2;
    justify-content: center;
  }

  .mode-btn {
    flex: 1;
    max-width: 160px;
    justify-content: center;
  }
}

/* City picker button: map-pin icon + current city name */
.city-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  min-width: 0; /* allow the name to shrink and get an ellipsis */
  padding: 0 12px;
  background: none;
  border: 1px solid var(--color-button-border);
  border-radius: var(--radius-control);
  color: var(--color-text);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.1s ease;
}

.city-btn:hover {
  background-color: var(--color-button-hover);
}

.city-btn:active {
  transform: scale(0.95);
}

.city-btn svg {
  flex-shrink: 0;
  color: var(--color-text-muted);
}

#city-btn-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* On narrow screens show only the pin icon, so the title still fits */
@media (max-width: 560px) {
  #city-btn-name {
    display: none;
  }

  .city-btn {
    padding: 0 9px;
  }
}

.header-center {
  grid-column: 2;
  grid-row: 1;
  text-align: center;
}

.header-right {
  grid-column: 3;
  grid-row: 1;
}

.app-title {
  margin: 0;
  font-family: var(--font-title);
  font-size: var(--title-size);
  font-weight: var(--title-weight);
  letter-spacing: var(--title-letter-spacing);
  text-transform: var(--title-transform);
}


/* ==================================================
   4. Map Container
   --------------------------------------------------
   Main game map styling
================================================== */

/* The wrapper takes the map's place in the card layout. It is
   position: relative so the banner can be positioned on top of it. */
.map-wrap {
  position: relative;
  flex: 1;           /* fill remaining height in .game-card */
  min-height: 260px; /* but never get uncomfortably small */
  margin-top: 12px;
}

/* Shown where there are no tiles (outside the city's extract) */
#map.leaflet-container {
  background: var(--basemap-land);
}

#map {
  height: 100%;     /* fill the wrapper */
  width: 100%;
  box-sizing: border-box; /* include the border in the 100% height */
  border-radius: var(--radius-map);
  border: var(--border-width-card) solid var(--color-border);
  overflow: hidden; /* keeps rounded corners clean */
}

/* Overlays drawn by Leaflet (street highlights, hint outlines and circles,
   guess lines, the fade outside the city, preview dots, guess and solution
   markers) each get their own GPU layer ("will-change: transform"). While
   the map zooms, Leaflet moves and scales them with CSS transforms every
   frame. Without their own layer the browser has to redraw them for each
   frame, which on a real screen can lag a few frames behind the map tiles
   (owner, 2026-11-01). With it, the GPU just moves the finished picture,
   like it does for the tiles. Leaflet itself does this only during an
   animated zoom (.leaflet-zoom-anim), not during a pinch. */
#map .leaflet-pane > svg,
#map .leaflet-pane > canvas,
#map .leaflet-marker-icon {
  will-change: transform;
}

/* End-of-round banner floating at the top-centre of the map */
.round-banner {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1001; /* above Leaflet's map panes and controls (z-index 1000) */

  display: flex;
  align-items: center;
  gap: 10px;
  max-width: calc(100% - 120px); /* leave room for the zoom buttons */
  padding: 10px 12px 10px 16px;

  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-text-muted); /* accent colour set by .won / .lost */
  border-radius: var(--radius-map);
  box-shadow: var(--shadow-floating);
  font-weight: 600;

  animation: bannerSlideIn 0.25s ease-out;
}

/* Problem with the map background (missing tiles, server without Range
   support); shown at the bottom centre of the map by checkBasemapTiles() */
.map-notice {
  position: absolute;
  bottom: 32px; /* above the attribution line */
  left: 50%;
  transform: translateX(-50%);
  z-index: 1001; /* above Leaflet's panes and controls, like the banner */
  width: max-content;
  max-width: calc(100% - 32px);
  padding: 10px 14px;
  background-color: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-danger);
  border-radius: var(--radius-map);
  box-shadow: var(--shadow-floating);
  font-size: 0.9rem;
  line-height: 1.4;
}

.map-notice code {
  font-size: 0.85em;
}

.map-notice.hidden {
  display: none;
}

/* .round-banner sets display: flex, so repeat .hidden here to be sure it wins */
.round-banner.hidden {
  display: none;
}

.round-banner.won  { border-left-color: var(--color-success); }
.round-banner.lost { border-left-color: var(--color-danger); }

.round-banner .primary-btn,
.round-banner .secondary-btn {
  flex-shrink: 0;
}

/* Message and (daily) countdown stacked on the left of the banner */
.round-banner-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.round-banner-sub {
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums; /* digits don't jiggle as they change */
}

/* .round-banner-sub has no display rule, but be explicit like .round-banner */
.round-banner-sub.hidden {
  display: none;
}

@keyframes bannerSlideIn {
  from {
    opacity: 0;
    transform: translate(-50%, -10px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

/* "Guess here" button + cancel, centred at the bottom of the map
   (touch screens only; shown while a pin waits for confirmation) */
.confirm-bar {
  position: absolute;
  bottom: 28px; /* above the attribution line */
  left: 50%;
  transform: translateX(-50%);
  z-index: 1001; /* above Leaflet's panes and controls */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-map);
  box-shadow: var(--shadow-floating);
}

.confirm-bar.hidden {
  display: none;
}

.confirm-bar .primary-btn {
  padding: 10px 22px; /* a comfortable target for a finger */
  font-size: 1rem;
  white-space: nowrap;
}

/* Numbered guess markers (created with L.divIcon in main.js) */
.guess-marker {
  box-sizing: border-box; /* border counts inside Leaflet's iconSize */
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-marker-fill);
  color: var(--color-marker-text);
  border: 2px solid var(--color-marker-outline);
  border-radius: var(--radius-marker);
  font-size: 12px;
  font-weight: 700;
  font-family: var(--font-marker);
  box-shadow: var(--shadow-marker);
}

/* Colour by distance band (getDistanceBand in rules.js), matching the
   share-text squares: 🟨 < 500 m, 🟧 < 1 km, 🟥 < 3 km, ⬛ further.
   Yellow and orange use dark numbers, which are easier to read there. */
.guess-marker.band-near {
  background-color: var(--color-band-near);
  color: var(--color-band-near-text);
}

.guess-marker.band-mid {
  background-color: var(--color-band-mid);
  color: var(--color-band-mid-text);
}

.guess-marker.band-far {
  background-color: var(--color-band-far);
  color: var(--color-band-far-text);
}

/* band-very-far keeps the default marker fill */

.guess-marker.correct {
  background-color: var(--color-success);
  color: var(--color-marker-text-success);
}

/* Pin waiting for "Guess here" on touch screens: see-through, dashed
   ring, gently pulsing */
.guess-marker.pending {
  background-color: var(--color-pending-fill);
  border-style: dashed;
  animation: pendingPulse 1.2s ease-in-out infinite;
}

@keyframes pendingPulse {
  50% {
    box-shadow: 0 0 0 6px var(--color-pending-glow);
  }
}

/* Solution "target" marker shown at the end of a round
   (created in revealTarget() in utils.js): green dot, white ring,
   with a small white center drawn by ::after */
.solution-marker {
  box-sizing: border-box;
  background-color: var(--color-success);
  border: 2px solid var(--color-marker-outline);
  border-radius: var(--radius-marker);
  box-shadow: var(--shadow-marker);
}

.solution-marker::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px; /* center the 6px dot */
  background-color: var(--color-marker-outline);
  border-radius: var(--radius-marker);
}


/* ==================================================
   4b. Guess Table
   --------------------------------------------------
   History of guesses shown below the map
================================================== */

.guess-table-wrap {
  margin-top: 8px;
}

.guess-table {
  width: 100%;
  table-layout: fixed;      /* column widths come from the header */
  border-collapse: separate;
  border-spacing: 0 3px;    /* vertical gap between row "pills" */
  font-size: 0.9rem;
}

.guess-table th {
  text-align: left;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  padding: 0 12px 2px;
}

.guess-table .col-number    { width: 44px; text-align: center; }
.guess-table .col-distance  { width: 100px; }
.guess-table .col-direction { width: 140px; }

.guess-table td {
  padding: 5px 12px; /* kept small so the map gets more height */
  background-color: var(--color-surface-sunken);
  border-top: 1px solid var(--color-row-border);
  border-bottom: 1px solid var(--color-row-border);
  /* Keep every row one line tall so the layout never jumps */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Rounded ends on each row */
.guess-table td:first-child {
  border-left: 1px solid var(--color-row-border);
  border-radius: var(--radius-control) 0 0 var(--radius-control);
}

.guess-table td:last-child {
  border-right: 1px solid var(--color-row-border);
  border-radius: 0 var(--radius-control) var(--radius-control) 0;
}

.guess-number {
  text-align: center;
  font-weight: 700;
  color: var(--color-text-muted);
}

/* A target street in a wrong guess's row (colour set inline, from
   STREET_COLORS in main.js, to match the prompt) */
.street-match {
  font-weight: 700;
}

.guess-distance {
  font-variant-numeric: tabular-nums; /* digits line up vertically */
}

.direction-arrow {
  display: inline-block;
  width: 1.4em;
  color: var(--color-direction-arrow);
  font-weight: 700;
}

.guess-table .muted {
  color: var(--color-text-muted);
  font-style: italic;
}

/* Unused rows: faded with dashed outline */
/* Only dash the outline of the row. Setting `border-style` on all four
   sides would also give the inner cell edges a (default 3px) border,
   which shows up as thick lines between columns. */
.guess-row.empty td {
  background-color: transparent;
  border-top-style: dashed;
  border-bottom-style: dashed;
  color: var(--color-text-faint);
}

.guess-row.empty td:first-child {
  border-left-style: dashed;
}

.guess-row.empty td:last-child {
  border-right-style: dashed;
}

/* Thin line used as a placeholder in empty cells */
.placeholder-dash {
  display: inline-block;
  width: 18px;
  height: 1px;
  vertical-align: middle;
  background-color: var(--color-placeholder);
}

/* The row the next guess will fill */
.guess-row.next td {
  border-color: var(--color-accent-border-soft);
}

/* Winning guess */
.guess-row.correct td {
  background-color: var(--color-success-tint);
  border-color: var(--color-success-border);
}

.guess-row.correct .guess-direction {
  color: var(--color-success-text);
  font-weight: 600;
}


/* ==================================================
   4c. Phone Layout
   --------------------------------------------------
   Narrow screens: less padding, a smaller prompt, a tighter guess
   table (so the street column keeps some width) and the end-of-round
   banner across the bottom of the map instead of squeezed at the top.
================================================== */

@media (max-width: 560px) {
  .app-container {
    padding: 8px;
  }

  .game-card {
    padding: 12px;
    border-radius: var(--radius-map);
  }

  #prompt {
    font-size: 1.1rem;
  }

  /* Keep all hint buttons on one row: a shorter label for "Directions"
     (owner, 2026-10-31), and tighter spacing */
  .hint-label-long,
  .label-long {
    display: none;
  }

  .hint-label-short,
  .label-short {
    display: inline;
  }

  .hint-box {
    gap: 4px;
    padding: 4px 4px 4px 8px;
  }

  .hint-box-label {
    margin-right: 0;
    font-size: 0.6rem;
  }

  .hint-btn {
    padding: 4px 7px;
    font-size: 0.78rem;
  }

  .guess-table {
    font-size: 0.8rem;
  }

  .guess-table th,
  .guess-table td {
    padding-left: 6px;
    padding-right: 6px;
  }

  /* One-line headers; the streets column says just "Intersection"
     (.label-short), since "Nearest intersection" doesn't fit */
  .guess-table th {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .guess-table .col-number    { width: 24px; }
  .guess-table .col-distance  { width: 60px; }
  .guess-table .col-direction { width: 86px; }

  /* Map credits on one line: smaller text, without the Leaflet credit
     (it is on about.html) and with "© OpenStreetMap" instead of
     "© OpenStreetMap contributors" (.attribution-long, VECTOR_ATTRIBUTION
     in main.js) */
  .leaflet-container .leaflet-control-attribution {
    font-size: 10px;
  }

  .leaflet-control-attribution a[href*="leafletjs.com"],
  .leaflet-control-attribution a[href*="leafletjs.com"] + span,
  .leaflet-control-attribution .attribution-long {
    display: none;
  }

  .round-banner {
    top: auto;
    bottom: 24px; /* above the attribution line */
    left: 8px;
    right: 8px;
    max-width: none;
    transform: none;
    font-size: 0.9rem;
    animation-name: bannerSlideUp;
  }
}

@keyframes bannerSlideUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}


/* ==================================================
   5. Icon Buttons (Header)
================================================== */

.icon-btn {
  background: none;
  border: 1px solid var(--color-button-border);
  color: var(--color-text);
  width: 36px;
  height: 36px;
  border-radius: var(--radius-control);
  cursor: pointer;
  font-size: 1rem;
  transition: background 0.2s ease, transform 0.1s ease;
}

.icon-btn:hover {
  background-color: var(--color-button-hover);
}

.icon-btn:active {
  transform: scale(0.95);
}

/* ==================================================
   Modal Overlay
================================================== */

.modal-overlay {
  position: fixed;
  inset: 0; /* top, right, bottom, left = 0 */
  background-color: var(--color-overlay); /* dim background */
  backdrop-filter: blur(2px); /* subtle blur */
  display: flex;
  justify-content: center;
  align-items: center;
  /* Above Leaflet controls (1000) and the end-of-round banner (1001) */
  z-index: 2000;
}

/* Hidden state */
.hidden {
  display: none;
}


/* ==================================================
   Modal Window
================================================== */

.modal-window {
  /* border-box: the width and height limits below include the padding and
     border. Without it the window was 50 px taller than max-height, so on a
     phone its top and bottom were cut off. */
  box-sizing: border-box;
  width: calc(100% - 32px); /* a gap to the screen edges on phones */
  max-width: 650px;
  background-color: var(--color-surface);
  border-radius: var(--radius-card);
  padding: 24px;
  border: var(--border-width-card) solid var(--color-border);
  box-shadow: var(--shadow-modal);
  animation: modalFadeIn 0.15s ease-out;
  /* On short screens, scroll inside the modal instead of overflowing it */
  max-height: calc(100vh - 32px);
  /* On phones 100vh is the height with the browser's address bar hidden, so
     with the bar showing the window was too tall and its top was cut off.
     100dvh is the height actually visible; old browsers skip this line. */
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
}

@keyframes modalFadeIn {
  from {
    transform: translateY(10px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

.modal-window.closing {
  animation: modalFadeOut 0.1s ease-in forwards;
}

@keyframes modalFadeOut {
  from {
    transform: translateY(0);
    opacity: 1;
  }
  to {
    transform: translateY(10px);
    opacity: 0;
  }
}


/* ==================================================
   Modal Layout
================================================== */

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.modal-header h2 {
  margin: 0;
  font-family: var(--font-heading);
  font-size: 1.3rem;
}

.modal-content {
  margin-bottom: 24px;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
}


/* ==================================================
   Primary Button (Used in Modal)
================================================== */

.primary-btn {
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  border: none;
  padding: 8px 18px;
  border-radius: var(--radius-control);
  cursor: pointer;
  font-weight: 500;
  transition: background 0.2s ease;
}

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

/* Less prominent button next to a primary one (e.g. "Practice" in the
   daily banner, next to "Share") */
.secondary-btn {
  background: none;
  color: var(--color-text);
  border: 1px solid var(--color-button-border);
  padding: 7px 16px; /* 1px less than .primary-btn to make up for the border */
  border-radius: var(--radius-control);
  cursor: pointer;
  font-weight: 500;
  transition: background 0.2s ease;
}

.secondary-btn:hover {
  background-color: var(--color-button-hover);
}


/* ==================================================
   Settings Sections
================================================== */

.settings-section {
  margin-bottom: 24px;
}

.settings-section h3 {
  margin: 0 0 12px 0;
  font-family: var(--font-heading);
  font-size: 1rem;
  font-weight: 600;
  opacity: 0.85;
}

/* Small, muted explanation text inside a settings section */
.setting-description {
  margin: 0 0 12px 0;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--color-text-muted);
}

/* Description of the selected difficulty (below the buttons) */
.setting-description.selected-description {
  margin: 10px 0 0 0;
  color: var(--color-text-secondary);
}

/* Note shown in daily mode under the difficulty intro */
.setting-description.daily-note {
  color: var(--color-text-secondary);
}

/* Text after a toggle row */
.toggle-row + .setting-description {
  margin: 8px 0 0 0;
}

/* ---- Practice districts (checkbox list in Settings) ---- */

.district-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
}

/* Small outlined buttons (All / None), like the hint buttons */
.small-btn {
  background: none;
  border: 1px solid var(--color-button-border);
  color: var(--color-text);
  padding: 3px 10px;
  border-radius: var(--radius-control);
  font-size: 0.8rem;
  cursor: pointer;
}

.small-btn:hover:not(:disabled) {
  background-color: var(--color-button-hover);
}

.district-summary {
  margin-left: 4px;
  font-size: 0.8rem;
  color: var(--color-text-secondary);
}

/* Two columns of districts; one column on phones */
.district-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px 14px;
}

@media (max-width: 560px) {
  .district-list {
    grid-template-columns: 1fr;
  }
}

.district-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
  font-size: 0.88rem;
  cursor: pointer;
}

.district-option input {
  margin: 0;
  accent-color: var(--color-accent);
}

.district-option-name {
  flex: 1;
  min-width: 0;
}

/* Number of possible targets in the district at the current difficulty */
.district-option-count {
  font-size: 0.75rem;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- How to Play content ---- */

/* How to Play has a lot of text: on bigger screens keep it to a comfortable
   reading size and let the text scroll, instead of filling the screen.
   Phones keep the general .modal-window limits. */
@media (min-width: 560px) {
  #howto-modal {
    max-width: 540px;
    max-height: min(calc(100dvh - 32px), 640px);
  }
}

.howto-welcome {
  margin: 0 0 6px 0;
  font-size: 1.05rem;
  font-weight: 600;
}

.howto-intro {
  margin: 0 0 20px 0;
  line-height: 1.5;
}

.howto-list {
  margin: 0;
  padding-left: 20px;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--color-text-secondary);
}

.howto-list li + li {
  margin-top: 6px;
}

/* ---- City picker content ---- */

.city-modal {
  max-width: 420px;
}

.city-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* One button per city (created in renderCityList() in main.js) */
.city-option {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  background-color: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-panel);
  color: var(--color-text);
  font: inherit; /* buttons don't inherit the page font by default */
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.city-option:hover:not(:disabled) {
  border-color: var(--color-accent);
}

/* The city currently being played */
.city-option.active {
  border-color: var(--color-accent);
  background-color: var(--color-accent-tint);
}

/* Cities without data files yet */
.city-option:disabled {
  opacity: 0.45;
  cursor: default;
}

.city-option-name {
  font-weight: 600;
}

/* Name plus, for a city played in an inner area, that area below it */
.city-option-title {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.city-option-area {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.city-option-detail {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

/* ---- Stats content ---- */

.stats-modal {
  max-width: 440px;
}

/* Four big numbers in a row */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 20px;
  text-align: center;
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.stat-value {
  font-size: 1.8rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.stat-label {
  font-size: 0.75rem;
  color: var(--color-text-muted);
}

/* Guess distribution: label + bar per row */
.stats-distribution {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.dist-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.dist-label {
  width: 1em;
  text-align: center;
  font-weight: 600;
  color: var(--color-text-muted);
}

.dist-bar {
  box-sizing: border-box;
  padding: 2px 6px;
  border-radius: var(--radius-bar);
  background-color: var(--color-bar);
  font-size: 0.8rem;
  font-weight: 600;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Today's result */
.dist-bar.today {
  background-color: var(--color-success);
}

.stats-hints {
  margin-top: 12px;
}

.stats-today {
  text-align: center;
}

.stats-countdown {
  margin: 0 0 12px 0;
  font-variant-numeric: tabular-nums;
}

.stats-today .setting-description {
  margin: 10px 0 0 0;
}

/* Box with the share text when the clipboard is not available */
.share-fallback {
  box-sizing: border-box;
  width: 100%;
  margin-top: 8px;
  padding: 8px;
  background-color: var(--color-surface-sunken);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: 0.85rem;
  resize: none;
}

/* ---- Difficulty Segmented Control ---- */

.settings-section:not(:last-child) {
  padding-bottom: 20px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--color-divider);
}



/* ==================================================
   Segmented Control
================================================== */

.segmented-control {
  display: inline-flex;
  background-color: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-panel);
  overflow: hidden;
}

.segmented-control button {
  background: none;
  border: none;
  padding: 8px 18px;
  cursor: pointer;
  color: var(--color-text);
  transition: background 0.1s ease;
}

.segmented-control button.active {
  background-color: var(--color-accent);
  color: var(--color-on-accent);
}


/* ==================================================
   Toggle Switch
================================================== */

.toggle-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.switch {
  position: relative;
  width: 44px;
  height: 24px;
}

.switch input {
  display: none;
}

.slider {
  position: absolute;
  inset: 0;
  background-color: var(--color-toggle-off);
  border-radius: var(--radius-pill);
  transition: background 0.2s ease;
}

.slider::before {
  content: "";
  position: absolute;
  height: 18px;
  width: 18px;
  left: 3px;
  top: 3px;
  background-color: var(--color-toggle-knob);
  border-radius: var(--radius-marker);
  transition: transform 0.2s ease;
}

.switch input:checked + .slider {
  background-color: var(--color-accent);
}

.switch input:checked + .slider::before {
  transform: translateX(20px);
}

/* Disabled toggle (preview in daily mode) */
.switch input:disabled + .slider {
  opacity: 0.4;
  cursor: not-allowed;
}


/* ==================================================
   Feedback window, footer and link buttons (SPEC 8.4)
================================================== */

/* A button that looks like a link (footer "Feedback", banner "Report a
   problem"): a <button> because it opens a window instead of a page */
.link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--color-accent);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.link-btn:hover {
  color: var(--color-accent-hover);
}

/* The small line of links below the game card (and on the other pages) */
.site-footer {
  flex: none; /* the game page is a full-height column: keep this one line */
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  padding: 0 16px 8px;
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.site-footer a,
.site-footer .link-btn {
  color: var(--color-text-secondary);
}

.site-footer a:hover,
.site-footer .link-btn:hover {
  color: var(--color-accent);
}

/* "Report a problem with this puzzle" under the banner text */
.round-banner-report {
  align-self: flex-start;
  margin-top: 2px;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--color-text-secondary);
}

.feedback-modal {
  max-width: 520px;
}

.feedback-types {
  margin: 0 0 14px;
  padding: 0;
  border: none;
}

.feedback-types legend,
.feedback-label {
  display: block;
  margin-bottom: 6px;
  font-weight: 600;
  font-size: 0.9rem;
}

.feedback-optional {
  font-weight: 400;
  color: var(--color-text-muted);
}

/* The four types as a column of radio rows */
.feedback-type-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.feedback-type {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: 1px solid var(--color-row-border);
  border-radius: var(--radius-control);
  background-color: var(--color-surface-sunken);
  cursor: pointer;
}

.feedback-type:has(input:checked) {
  border-color: var(--color-accent);
  background-color: var(--color-accent-tint);
}

.feedback-type input,
.feedback-check input {
  margin: 0;
  accent-color: var(--color-accent);
}

.feedback-fields {
  display: flex;
  flex-direction: column;
  margin-bottom: 12px;
}

.feedback-fields.hidden,
.feedback-check.hidden,
.feedback-error.hidden {
  display: none;
}

.feedback-fields .feedback-label:not(:first-child) {
  margin-top: 10px;
}

.feedback-input {
  box-sizing: border-box;
  width: 100%;
  padding: 7px 9px;
  background-color: var(--color-surface-sunken);
  color: var(--color-text);
  border: 1px solid var(--color-button-border);
  border-radius: var(--radius-control);
  font-size: 1rem; /* 16 px or more: phones don't zoom in on focus */
}

textarea.feedback-input {
  resize: vertical;
  min-height: 80px;
}

.feedback-input:focus {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* A checkbox with its text; the text wraps next to the box */
.feedback-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 12px;
  font-size: 0.9rem;
  cursor: pointer;
}

.feedback-check input {
  margin-top: 3px;
}

/* Honeypot: off screen for people (not display: none, which some bots skip) */
.feedback-hp {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.feedback-preview {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  white-space: pre-wrap;
}

.feedback-error {
  margin: 8px 0 0;
  color: var(--color-danger);
  font-weight: 600;
  font-size: 0.9rem;
}

.feedback-status:empty {
  display: none;
}

.feedback-footer {
  gap: 8px;
  flex-wrap: wrap;
}


/* ==================================================
   About Page (about.html)
================================================== */

/* The game's body is a fixed-height column (no page scrolling); the
   About page is ordinary text that scrolls */
body.about-page {
  height: auto;
  min-height: 100vh;
  display: block;
  padding: 24px 16px;
  box-sizing: border-box;
}

.about-card {
  max-width: 720px;
  margin: 0 auto;
  background-color: var(--color-card-bg);
  border: var(--border-width-card) solid var(--color-border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 20px 28px 24px;
  line-height: 1.55;
}

.about-title {
  font-family: var(--font-title, var(--font-heading));
  margin: 8px 0 16px;
}

.about-card h2,
.about-card h3 {
  font-family: var(--font-heading);
}

.about-card h2 {
  margin: 28px 0 8px;
  font-size: 1.25rem;
}

.about-card h3 {
  margin: 18px 0 6px;
  font-size: 1rem;
}

.about-card a {
  color: var(--color-accent);
}

.about-updated {
  margin-top: -6px;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

/* The localStorage table on privacy.html; scrolls sideways inside the
   card on a narrow phone instead of widening the page */
.about-table-wrap {
  overflow-x: auto;
}

.about-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.about-table th,
.about-table td {
  text-align: left;
  vertical-align: top;
  padding: 6px 8px;
  border-bottom: 1px solid var(--color-row-border);
}

.about-table code {
  font-size: 0.85em;
  word-break: break-all; /* long key names may wrap on phones */
}

.about-data-list li {
  margin-bottom: 4px;
}

/* The footer below the card on the About, Privacy and 404 pages */
body.about-page .site-footer {
  padding-top: 16px;
}

.about-back {
  font-size: 0.9rem;
}

/* Link to the About page in the How to Play window */
.howto-about {
  margin-right: auto; /* pushes "Got it" to the right */
  align-self: center;
  font-size: 0.85rem;
  color: var(--color-text-secondary);
}

@media (max-width: 560px) {
  .about-card {
    padding: 16px;
  }
}
