/* Golf Finder — mobile first, no external assets. Colours are tokens on :root. */

:root {
  color-scheme: light dark;

  --bg: #f3f5f2;
  --surface: #ffffff;
  --surface-2: #e9eee9;
  --border: #d3dbd5;
  --border-strong: #7c8a81;
  --text: #13201a;
  --text-muted: #4b5951;
  --accent: #0a6b4d;
  --accent-hover: #08573e;
  --on-accent: #ffffff;
  --accent-soft: #dbefe4;
  --on-accent-soft: #0a4c37;
  --focus: #1658c9;
  --warn-bg: #fff3d0;
  --warn-border: #d9a520;
  --warn-text: #563d00;
  --info-bg: #e5eefa;
  --info-border: #8fb0dc;
  --info-text: #1b3a66;
  --danger-text: #a01d17;
  --good-bg: #effaf0;
  --good-border: #3f9a55;
  --good-text: #16612a;
  /* "Vapautui" (ui.js freedTag): a calm violet, apart from the green, amber, blue and red of the other states */
  --freed-bg: #f3effc;
  --freed-border: #7d5bc9;
  --freed-text: #4a2b8f;
  --stale-bg: #f2f2ec;
  --stale-text: #6a4b00;
  --skeleton: #e2e8e3;
  --skeleton-hi: #f2f5f2;
  --shadow: 0 1px 2px rgb(16 32 24 / 0.07), 0 1px 3px rgb(16 32 24 / 0.05);
  /* week grid (grid.js): the cheapest price … the dearest, one hue; --text is at least 8.5:1 on each */
  --heat-0: #79c99c;
  --heat-1: #a2dabb;
  --heat-2: #c4e8d2;
  --heat-3: #e0f2e7;
  --heat-4: #f4f9f5;
  --heat-ink-strong: var(--text); /* text on --heat-0 and --heat-1 */
  --heat-ring: var(--accent); /* the week's cheapest (3.3:1 on --heat-0) */

  --pf-playtomic: #2b59d1;
  --pf-thril: #d85a12;
  --pf-cintoia: #7a3cc2;
  --pf-matchi: #178a4f;
  --pf-playfi: #cc2a4b;
  --pf-wisenet: #0f7c8c;
  --pf-asio: #a13a8a;
  --pf-slotti: #687a00;
  --pf-enkora: #7d4f2c;
  --pf-digitalbooker: #2e7d1a;
  --pf-tolobook: #0369a1;
  --pf-other: #68737a;

  --radius: 12px;
  --radius-sm: 8px;
  --gutter: 16px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0e1311;
    --surface: #161d1a;
    --surface-2: #202a25;
    --border: #2e3a35;
    --border-strong: #748a7f;
    --text: #e6ede8;
    --text-muted: #a5b2aa;
    --accent: #4fcf99;
    --accent-hover: #72dbb0;
    --on-accent: #062218;
    --accent-soft: #163a2b;
    --on-accent-soft: #a3e9c9;
    --focus: #8ab4ff;
    --warn-bg: #3a2d0a;
    --warn-border: #8f6c16;
    --warn-text: #f6da8e;
    --info-bg: #132238;
    --info-border: #3a5d8f;
    --info-text: #bcd4f5;
    --danger-text: #ff9d93;
    --good-bg: #12261a;
    --good-border: #45a463;
    --good-text: #95e3a8;
    --freed-bg: #221a33;
    --freed-border: #9d82e6;
    --freed-text: #cdbcff;
    --stale-bg: #1a1f1c;
    --stale-text: #e9c46a;
    --skeleton: #202a25;
    --skeleton-hi: #2b3731;
    --shadow: 0 1px 2px rgb(0 0 0 / 0.4);
    /* the cheap end is the light one here: dark text on it (7.3 and 5.5:1), --text on the rest (5:1 or more) */
    --heat-0: #46c08c;
    --heat-1: #3aa678;
    --heat-2: #24704f;
    --heat-3: #1f4d39;
    --heat-4: #1b3027;
    --heat-ink-strong: #062218;
    --heat-ring: #062218;

    --pf-playtomic: #7b9ef5;
    --pf-thril: #f59a5f;
    --pf-cintoia: #b98cf0;
    --pf-matchi: #5fd39a;
    --pf-playfi: #f37f97;
    --pf-wisenet: #5cc6d4;
    --pf-asio: #e78cd0;
    --pf-slotti: #c3d65a;
    --pf-enkora: #d9a57e;
    --pf-digitalbooker: #8fd16a;
    --pf-tolobook: #7dd3fc;
    --pf-other: #9aa5ad;
  }
}

/* ---------------------------------------------------------------- base */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.45 var(--font);
  overflow-x: hidden;
}

h1, h2, h3, p, ul, fieldset {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

fieldset {
  border: 0;
  padding: 0;
  min-width: 0;
}

legend {
  padding: 0;
}

a {
  color: var(--accent);
}

button,
input,
select {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
}

[hidden] {
  display: none !important;
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: -100px;
  z-index: 20;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
}

.skip-link:focus {
  top: 8px;
}

.wrap {
  width: 100%;
  max-width: 1160px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}

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

.site-header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.header-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 56px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 1.15rem;
  font-weight: 750;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.mock-badge {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--warn-bg);
  color: var(--warn-text);
  border: 1px solid var(--warn-border);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.freshness {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  color: var(--text-muted);
  font-size: 0.85rem;
  text-align: right;
}

.freshness .dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--border-strong);
}

.freshness[data-state="ok"] .dot { background: var(--pf-matchi); }
.freshness[data-state="stale"] .dot { background: var(--warn-border); }
.freshness[data-state="error"] .dot { background: var(--danger-text); }
.freshness[data-state="idle"] .dot { visibility: hidden; }
.freshness[data-state="busy"] .dot {
  background: var(--accent);
  animation: pulse 1.2s ease-in-out infinite;
}

@keyframes pulse {
  50% { opacity: 0.25; }
}

/* ---------------------------------------------------------------- layout */

.layout {
  display: grid;
  gap: 16px;
  padding-top: 16px;
  padding-bottom: 32px;
}

.layout > * {
  min-width: 0;
}

/* ---------------------------------------------------------------- filters */

.filters {
  display: grid;
  gap: 14px;
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.field {
  display: grid;
  gap: 6px;
  min-width: 0;
}

.label {
  font-size: 0.8rem;
  font-weight: 650;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}

.field .label {
  margin-bottom: 0;
}

fieldset > .label {
  margin-bottom: 6px;
}

.label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.mini-label {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-right: 2px;
}

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

.hint-error {
  color: var(--danger-text);
}

/* Under the place field: "Lähellä: Kallio · tarkkuus ±40 m  Näytä kartalla", or a warning for a coarse fix */
.place-note {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 12px;
}

.place-note .btn-link {
  padding: 0;
  font-size: inherit;
}

.place-note-warn {
  flex-basis: 100%;
  padding: 6px 10px;
  border: 1px solid var(--warn-border);
  border-radius: var(--radius-sm);
  background: var(--warn-bg);
  color: var(--warn-text);
}

.input {
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  min-width: 0;
}

.input::placeholder {
  color: var(--text-muted);
  opacity: 1;
}

.place-box {
  position: relative;
}

.place-row {
  display: flex;
  gap: 8px;
}

.place-row .input {
  flex: 1 1 auto;
  width: 100%;
}

.suggest {
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  top: calc(100% + 4px);
  max-height: 320px;
  overflow-y: auto;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
}

.suggest-item {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  text-align: left;
}

.suggest-item:hover,
.suggest-item:focus-visible {
  background: var(--accent-soft);
  color: var(--on-accent-soft);
}

.suggest-region {
  font-size: 0.8rem;
  color: var(--text-muted);
}

.saved-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.btn:disabled {
  cursor: progress;
  opacity: 0.7;
}

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

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

.btn-secondary {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border-strong);
}

.btn-secondary:hover {
  background: var(--surface-2);
}

.btn-link {
  min-height: 0;
  padding: 4px 0;
  border: 0;
  background: none;
  color: var(--accent);
  text-decoration: underline;
  font-weight: 500;
}

.geo-btn {
  flex: none;
  padding: 8px 12px;
}

/* chips */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.chip,
.chip-x {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 38px;
  padding: 6px 11px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.9rem;
  white-space: nowrap;
}

.chip:hover {
  background: var(--surface-2);
}

.chip[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}

.chip-sub {
  font-size: 0.78rem;
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
}

/* a date chip's price from only some of the halls (others not fetched for that day yet) */
.chip-sub.is-partial {
  opacity: 0.6;
}

.chip-add {
  border-style: dashed;
  color: var(--text-muted);
}

.chip-pair {
  display: inline-flex;
}

.chip-pair .chip {
  border-radius: 999px 0 0 999px;
}

.chip-pair .chip-x {
  margin-left: -1px;
  padding: 6px 10px;
  border-radius: 0 999px 999px 0;
  font-size: 1.05rem;
  line-height: 1;
}

.chip-x:hover {
  background: var(--surface-2);
}

/* a hidden hall in "Rajaukset" (app.js renderHiddenHalls): a name, not a button; a long one wraps on a phone */
.chip-static {
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
  cursor: default;
}

.chip.chip-static:hover {
  background: var(--surface);
}

.hidden-halls .chip-pair {
  max-width: 100%;
}

/* Date chips scroll sideways on phones instead of wrapping into many rows. */
.chips-scroll > * {
  flex: none;
}

/* position: relative keeps visually-hidden (absolute) text inside the scroller's clip. */
.chips-scroll {
  position: relative;
  flex-wrap: nowrap;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  padding: 3px;
  margin: -3px;
}

/* "other day": a calendar chip with the native date input invisibly on top, so a tap opens the picker */
.date-other {
  position: relative;
  flex: none;
  min-width: 44px;
  justify-content: center;
  cursor: pointer;
}

.date-other.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}

.date-other:focus-within {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.date-other-text:empty {
  display: none;
}

.date-native {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
}

/* time */

.time-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.time-dash {
  color: var(--text-muted);
}

.select {
  width: 100%;
  font-variant-numeric: tabular-nums;
}

.check {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  font-size: 0.95rem;
  cursor: pointer;
}

.check input {
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--accent);
  flex: none;
}

/* the filters under the place: folded by default on every screen, the button summarises them */

.more-toggle {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "title chevron" "summary summary";
  align-items: center;
  gap: 2px 10px;
  width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  text-align: left;
}

.more-toggle:hover {
  background: var(--surface-2);
}

.more-title {
  grid-area: title;
  font-weight: 650;
  white-space: nowrap;
}

.more-summary {
  grid-area: summary;
  min-width: 0;
  font-size: 0.85rem;
  color: var(--text-muted);
}

/* open, the controls themselves show what's set */
.more-toggle[aria-expanded="true"] .more-summary {
  display: none;
}

.more-chevron {
  grid-area: chevron;
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.15s ease;
}

.more-toggle[aria-expanded="true"] .more-chevron {
  transform: translateY(2px) rotate(-135deg);
}

.more {
  display: grid;
  gap: 14px;
}

.more.is-collapsed {
  display: none;
}

/* segmented controls */

.field-pair {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 20px;
}

.seg {
  position: relative;
  display: inline-flex;
  justify-self: start;
  max-width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface);
}

.seg-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 40px;
  min-width: 44px;
  padding: 6px 12px;
  border: 0;
  border-left: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text);
  font-size: 0.9rem;
  white-space: nowrap;
}

.seg-btn:first-child {
  border-left: 0;
}

.seg-btn:hover {
  background: var(--surface-2);
}

.seg-btn[aria-pressed="true"] {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}

.seg-btn:focus-visible {
  outline-offset: -3px;
}

/* price slider */

.price-out {
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.range {
  width: 100%;
  margin: 4px 0 0;
  accent-color: var(--accent);
  min-height: 32px;
}

/* ---------------------------------------------------------------- results toolbar */

.toolbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: grid;
  gap: 8px;
  padding: 10px 0;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

.count {
  font-weight: 700;
  font-size: 1.05rem;
}

.coverage-line {
  margin-top: 8px;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.coverage-line:empty {
  display: none;
}

.coverage-line summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  cursor: pointer;
  list-style: none;
}

.coverage-line summary::-webkit-details-marker {
  display: none;
}

.coverage-line summary::after {
  content: "";
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}

.coverage-line details[open] summary::after {
  transform: translateY(1px) rotate(-135deg);
}

/* Halls whose booking for a searched day opens later (ui.js notOpenList, PLAN §13.6) */
.coverage-line .not-open {
  margin-top: 6px;
}

.coverage-line .not-open li {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0 12px;
  border-top: 1px solid var(--border);
  overflow-wrap: anywhere;
}

.coverage-line .not-open .venue-name {
  color: var(--text);
}

/* "2 piilotettua hallia · Näytä" (app.js renderHiddenLine): right under the coverage line */
.hidden-line {
  margin-top: 2px;
}

.coverage-line:empty + .hidden-line {
  margin-top: 8px;
}

.hidden-line .btn-link {
  min-height: 28px;
  padding: 0 2px;
}

/* ---------------------------------------------------------------- day strip (above the results) */

.day-strip {
  min-width: 0;
  margin: 0 0 4px;
  padding: 0;
  border: 0;
}

/* Each day over its price, not beside it: "Koko viikko", the seven days and the calendar fit one line next to the
   filters from 1280 px on (the strip's 764 px, English and "≈ 19,90 €" on every chip included). Narrower, they
   wrap; a phone scrolls them (chips-scroll). */
.day-strip .chips {
  gap: 5px;
}

.day-strip button.chip {
  flex-direction: column;
  justify-content: center;
  gap: 0;
  padding: 3px 8px;
  line-height: 1.2;
}

.toolbar-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: space-between;
}

/* The sort hidden (the week grid, app.js): the view buttons stay at the right instead of sliding left. */
.toolbar-row > [hidden] + .seg {
  margin-left: auto;
}

.seg-small .seg-btn {
  min-height: 40px;
  min-width: 40px;
  padding: 6px 8px;
  font-size: 0.88rem;
}

@media (min-width: 560px) {
  .seg-small .seg-btn {
    padding: 6px 11px;
  }
}

.progress {
  display: grid;
  gap: 4px;
}

.progress-track {
  height: 4px;
  border-radius: 2px;
  background: var(--surface-2);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: var(--accent);
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 0.4s ease;
}

/* Exact prices still arriving: there is no done/total fraction, so the bar slides. */
.progress-track.is-indeterminate .progress-fill {
  width: 40%;
  transform: none;
  transition: none;
  animation: progress-indeterminate 1.15s ease-in-out infinite;
}

@keyframes progress-indeterminate {
  0% { transform: translateX(-120%); }
  100% { transform: translateX(280%); }
}

.progress-text {
  font-size: 0.8rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- banners */

.banners {
  display: grid;
  gap: 8px;
  margin-top: 10px;
}

.banners:empty {
  display: none;
}

.banner {
  display: flex;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid;
  font-size: 0.9rem;
}

.banner-warn {
  background: var(--warn-bg);
  border-color: var(--warn-border);
  color: var(--warn-text);
}

.banner-info {
  background: var(--info-bg);
  border-color: var(--info-border);
  color: var(--info-text);
}

.banner-icon {
  flex: none;
  font-size: 1rem;
  line-height: 1.35;
}

.banner-title {
  font-weight: 600;
}

.banner-detail {
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------- results */

.results {
  margin-top: 10px;
  transition: opacity 0.15s ease;
}

.results:focus {
  outline: none;
}

.results.is-pending {
  opacity: 0.55;
}

.results-list {
  display: grid;
  gap: 8px;
}

.slot,
.venue-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 12px 14px;
}

.slot {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px 12px;
}

.slot-main {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.slot-time {
  font-size: 1.15rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* A week search: every row's day ("pe 30.10.") before its times (ui.js dayLabel) */
.slot-day {
  display: inline-block;
  padding: 1px 7px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--on-accent-soft);
  font-size: 0.85rem;
  font-weight: 650;
  line-height: 1.5;
  vertical-align: 0.1em;
  white-space: nowrap;
}

.slot-when {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}

.slot-when .slot-day {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  vertical-align: baseline;
}

.slot-when .slot-times {
  min-width: 0;
}

/* a week row (ui.js timesByDay): a line per day, the days lined up */
.slot-days {
  display: grid;
  gap: 4px;
  margin-bottom: 4px;
}

.slot-days .slot-times {
  margin-bottom: 0;
}

.slot-days .slot-day {
  min-width: 4.9em;
  justify-content: center;
}

/* flex: the star stays beside a long name that wraps, centred on its first line */
.slot-venue {
  display: flex;
  align-items: flex-start;
  min-width: 0;
  overflow-wrap: anywhere;
}

.slot-venue .fav-btn {
  flex: none;
  margin-top: -4px;
}

.venue-text {
  min-width: 0;
}

.venue-name {
  font-weight: 600;
}

/* The name is a button (it opens the hall dialog) but it should read as the old text. */
button.venue-name {
  display: inline;
  max-width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  font: inherit;
  font-weight: 600;
  color: inherit;
  text-align: inherit;
  text-decoration: none;
  vertical-align: baseline;
  overflow-wrap: anywhere;
  cursor: pointer;
}

button.venue-name:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ☆/★ favourite toggle before a venue name: a 32 px tap target that doesn't push the text around */
.fav-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin: -6px 2px -6px -6px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--text-muted);
  font-size: 1.15rem;
  line-height: 1;
  vertical-align: middle;
  cursor: pointer;
}

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

.fav-btn.is-on {
  color: #c98a00;
}

.venue-where {
  color: var(--text-muted);
}

/* "piilotettu": a hidden hall shown for now ("Näytä"); quiet, it's no price news */
.hidden-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border: 1px dashed var(--border-strong);
  border-radius: 999px;
  color: var(--text-muted);
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: 1px;
}

.slot-meta {
  font-size: 0.85rem;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.also {
  white-space: nowrap;
}

.slot-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
  gap: 4px;
  text-align: right;
}

.price {
  font-size: 1.35rem;
  font-weight: 750;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.price.has-tip,
.tchip-price.has-tip {
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  cursor: help;
}

/* "päivittyy" next to an estimate whose exact price is still being fetched. */
.price-amount {
  white-space: nowrap;
}

.price-pending {
  display: inline-block;
  margin-left: 0.35em;
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-muted);
  vertical-align: 0.08em;
  white-space: nowrap;
}

/* Stacked under the price so a narrow phone row stays as wide as the price itself. */
.price.has-pending {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  white-space: normal;
}

.price.has-pending .price-pending {
  margin-left: 0;
  margin-top: 1px;
}

.price-sub {
  font-size: 0.8rem;
  color: var(--text-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* "kampanja": a campaign price (exact, but a time-limited discount); its own line under the price */
.price-tag {
  display: inline-block;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--on-accent-soft);
  font-size: 0.72rem;
  font-weight: 650;
  line-height: 1.5;
  white-space: nowrap;
  cursor: help;
}

/* "Edullinen · −8 €": clearly under the area's usual price (ui.js goodPriceTag); outlined, unlike "kampanja" */
.good-tag {
  border: 1px solid var(--good-border);
  background: var(--good-bg);
  color: var(--good-text);
  line-height: 1.35;
}

/* Phones: the saving would widen the price column and push a time chip to the next line; the word stays. */
@media (max-width: 559px) {
  .slot-side .good-saving {
    display: none;
  }

  /* "2 kenttää: 30 € + 34 €" (ui.js groupPriceText) wraps at the width of the Varaa button instead */
  .slot-side .group-prices {
    max-width: 7.5em;
    white-space: normal;
  }
}

/* a list row's "Edullinen" times when only some of them are; a hall card's "Edullinen" times */
.lchip.is-good,
.tchip.is-good {
  border-color: var(--good-border);
  background: var(--good-bg);
  color: var(--good-text);
}

.tchip.is-good .tchip-price {
  color: var(--good-text);
  font-weight: 650;
}

/* "Vapautui 14.05": the start time became free within the last day, usually a cancellation (ui.js freedTag,
   PLAN §13.5); outlined like "Edullinen", in its own colour */
.freed-tag {
  border: 1px solid var(--freed-border);
  background: var(--freed-bg);
  color: var(--freed-text);
  line-height: 1.35;
}

/* a list row's freed start times (ui.js timeChip): a dot on the corner, so it goes with "Edullinen" too */
.lchip.is-freed {
  position: relative;
}

.lchip.is-freed::after {
  content: "";
  position: absolute;
  top: -5px;
  right: -5px;
  width: 11px;
  height: 11px;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--freed-border);
}

/* Phones: like the saving of "Edullinen", the time would widen the price column; the dialog line has it */
@media (max-width: 559px) {
  .slot-side .freed-time,
  .tchip .freed-time {
    display: none;
  }
}

.btn-book {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 40px;
  margin-top: 4px;
  padding: 6px 14px;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 650;
  text-decoration: none;
  white-space: nowrap;
}

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

.ext {
  font-size: 0.9em;
}

.no-link {
  font-size: 0.8rem;
  color: var(--text-muted);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  vertical-align: 1px;
}

.badge-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--pf-other);
}

.pf-playtomic .badge-dot { background: var(--pf-playtomic); }
.pf-thril .badge-dot { background: var(--pf-thril); }
.pf-cintoia .badge-dot { background: var(--pf-cintoia); }
.pf-matchi .badge-dot { background: var(--pf-matchi); }
.pf-playfi .badge-dot { background: var(--pf-playfi); }
.pf-wisenet .badge-dot { background: var(--pf-wisenet); }
.pf-asio .badge-dot { background: var(--pf-asio); }
.pf-slotti .badge-dot { background: var(--pf-slotti); }
.pf-enkora .badge-dot { background: var(--pf-enkora); }
.pf-digitalbooker .badge-dot { background: var(--pf-digitalbooker); }
.pf-tolobook .badge-dot { background: var(--pf-tolobook); }

/* stale: muted surface + explicit note (text keeps AA contrast) */
.slot.is-stale,
.venue-card.is-stale {
  background: var(--stale-bg);
  border-style: dashed;
  box-shadow: none;
}

.slot.is-stale .slot-time,
.slot.is-stale .price {
  color: var(--text-muted);
}

.stale-note {
  font-size: 0.8rem;
  color: var(--stale-text);
}

/* list rows with several start times at one price (ui.js groupRow): each time opens the hall dialog */
.slot-times {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 4px;
}

.lchip,
.times-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 32px;
  padding: 2px 9px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-size: 0.95rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  white-space: nowrap;
}

.lchip:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--on-accent-soft);
}

.times-more {
  border-style: dashed;
  color: var(--text-muted);
  font-size: 0.88rem;
  font-weight: 600;
}

.times-more:hover {
  background: var(--surface-2);
  color: var(--text);
}

.slot.is-stale .lchip {
  background: transparent;
}

/* Phones: four times per line next to the price column, so 6 + "+N lisää" take two lines. A week search's day
   goes above them, so it doesn't take a time's place. */
@media (max-width: 559px) {
  .slot-times {
    gap: 5px;
  }

  .slot-when {
    flex-direction: column;
    gap: 4px;
  }

  .slot-when .slot-day {
    min-height: 0;
  }

  /* several days in one row: each day's label stays beside its times, or the row doubles in height */
  .slot-days .slot-when {
    flex-direction: row;
    gap: 4px;
  }

  .slot-days .slot-day {
    min-height: 32px;
    min-width: 4.4em;
    padding: 1px 4px;
  }

  .lchip,
  .times-more {
    padding: 2px 6px;
    font-size: 0.9rem;
  }

  .times-more {
    font-size: 0.84rem;
  }
}

/* by-venue cards */

.venue-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 12px;
  align-items: start;
}

.vc-title {
  min-width: 0;
}

.vc-title .venue-name {
  font-size: 1.05rem;
  overflow-wrap: anywhere;
}

.vc-price {
  text-align: right;
}

.time-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-self: end;
}

.tchip {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  min-width: 60px;
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-variant-numeric: tabular-nums;
}

.tchip-time {
  font-weight: 650;
}

.tchip-price {
  font-size: 0.78rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.tchip-price.has-pending {
  display: flex;
  flex-direction: column;
  align-items: center;
  white-space: normal;
}

.tchip .price-pending {
  display: block;
  margin: 1px 0 0;
  font-size: 0.68rem;
  font-weight: 550;
}

.tchip-tag {
  font-size: 0.7rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.tchip .price-tag {
  margin-top: 2px;
  padding: 0 5px;
  font-size: 0.68rem;
}

.tchip.is-stale {
  border-style: dashed;
}

/* A week search's by-hall card: each day's start times under its own line (ui.js dayHeadings) */
.tchip-day {
  flex-basis: 100%;
  margin-top: 4px;
  font-size: 0.85rem;
  font-weight: 650;
  color: var(--text-muted);
}

.tchip-day:first-child {
  margin-top: 0;
}

/* "Yleensä 32 €/h · 4. edullisin 15 hallista" (ui.js hallTypicalLine): a narrow card wraps between the halves */
.typical-part {
  white-space: nowrap;
}

.vc-book {
  align-self: end;
  justify-self: end;
}

.vc-book .btn-book {
  margin-top: 0;
}

.vc-stale {
  grid-column: 1 / -1;
}

/* skeleton */

.skeleton {
  box-shadow: none;
}

.sk {
  display: block;
  height: 14px;
  border-radius: 6px;
  background: linear-gradient(90deg, var(--skeleton) 25%, var(--skeleton-hi) 50%, var(--skeleton) 75%);
  background-size: 300% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

.sk-time { width: 110px; height: 20px; margin-bottom: 6px; }
.sk-line { width: 80%; margin-bottom: 6px; }
.sk-short { width: 55%; }
.sk-price { width: 56px; height: 22px; }
.sk-btn { width: 80px; height: 36px; margin-top: 8px; }

@keyframes shimmer {
  from { background-position: 100% 0; }
  to { background-position: 0 0; }
}

/* states */

.state {
  display: grid;
  gap: 8px;
  padding: 20px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-align: center;
}

.state .results-list {
  margin-top: 8px;
  text-align: left;
}

.state-title {
  font-weight: 700;
  font-size: 1.05rem;
}

.state-body {
  color: var(--text-muted);
  font-size: 0.92rem;
}

.state-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 6px;
}

.state-error .state-title {
  color: var(--danger-text);
}

/* unsupported */

.unsupported {
  margin-top: 14px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.unsupported summary {
  min-height: 32px;
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}

.unsupported summary::-webkit-details-marker {
  display: none;
}

.unsupported summary::before {
  content: "";
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 5px 0 5px 7px;
  border-color: transparent transparent transparent currentColor;
  transition: transform 0.15s ease;
}

.unsupported[open] summary::before {
  transform: rotate(90deg);
}

.unsupported > .state-body {
  margin: 4px 0 8px;
}

.unsupported li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-top: 1px solid var(--border);
  overflow-wrap: anywhere;
}

.link-ext {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  min-height: 36px;
  white-space: nowrap;
  font-weight: 600;
}

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

/* map.js inserts leaflet.css before this file, so these overrides win at equal specificity.
   Leaflet's own element.style writes are fine under the CSP; markup styles live here. */
.map {
  position: relative;
  z-index: 0; /* own stacking context: Leaflet's panes (z-index 400+) stay under the sticky toolbar */
  isolation: isolate;
  margin-top: 10px;
}

.map:empty,
.map.leaflet-container {
  height: 60vh;
  min-height: 300px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}

/* still loading Leaflet: the skeleton shimmer */
.map:empty {
  background: linear-gradient(90deg, var(--skeleton) 25%, var(--skeleton-hi) 50%, var(--skeleton) 75%);
  background-size: 300% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

.map.leaflet-container {
  font: 0.9rem/1.4 var(--font);
}

.map.is-pending .leaflet-marker-pane {
  opacity: 0.55;
}

/* hall marker: Leaflet places a 0×0 div on the hall; the price label sits above it, pointing down */
.map-pin-icon:focus-visible {
  outline: none;
}

.map-pin {
  position: absolute;
  left: 0;
  bottom: 4px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border: 2px solid var(--accent);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.82rem;
  font-weight: 700;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.35);
}

.map-pin::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: var(--accent);
  border-bottom: 0;
}

.map-pin-icon:focus-visible .map-pin {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.map-pin.is-best {
  background: var(--accent);
  color: var(--on-accent);
}

.map-pin.is-unknown {
  border-color: var(--border-strong);
  color: var(--text-muted);
}

.map-pin.is-unknown::after {
  border-top-color: var(--border-strong);
}

.map-pin.is-stale {
  border-style: dashed;
}

.map-pin-fav {
  color: #c98a00;
}

.map-pin.is-best .map-pin-fav {
  color: inherit;
}

/* a cluster of halls ("15 € · 3"): a second pill peeks out behind it (map.js PIN_STACK) */
.map-pin.is-cluster {
  box-shadow: 4px -4px 0 -2px var(--surface), 4px -4px 0 0 var(--accent), 0 1px 4px rgb(0 0 0 / 0.35);
}

.map-pin.is-cluster.is-unknown {
  box-shadow: 4px -4px 0 -2px var(--surface), 4px -4px 0 0 var(--border-strong), 0 1px 4px rgb(0 0 0 / 0.35);
}

.map-pin-count {
  font-weight: 600;
}

/* search centre and radius (SVG; CSS beats Leaflet's presentation attributes) */
.map .map-radius {
  stroke: var(--accent);
  stroke-opacity: 0.8;
  stroke-dasharray: 6 6;
  fill: var(--accent);
  fill-opacity: 0.05;
}

.map .map-accuracy {
  stroke: var(--focus);
  stroke-opacity: 0.9;
  fill: var(--focus);
  fill-opacity: 0.22;
}

.map .map-center {
  stroke: var(--surface);
  fill: var(--focus);
  fill-opacity: 1;
}

/* Leaflet controls and popups in the app's colours (light and dark) */
.map .leaflet-bar {
  border-color: var(--border-strong);
}

.map .leaflet-bar a {
  background: var(--surface);
  color: var(--text);
  border-bottom-color: var(--border);
}

.map .leaflet-bar a:hover,
.map .leaflet-bar a:focus {
  background: var(--surface-2);
}

.map .leaflet-bar a.leaflet-disabled {
  background: var(--surface-2);
  color: var(--text-muted);
}

.map .leaflet-control-attribution {
  background: var(--surface);
  color: var(--text-muted);
  font-size: 0.72rem;
}

.map .leaflet-control-attribution a {
  color: var(--accent);
}

.map .leaflet-popup-content-wrapper,
.map .leaflet-popup-tip {
  background: var(--surface);
  color: var(--text);
}

.map .leaflet-popup-content {
  margin: 12px 14px;
  font-size: 0.9rem;
  line-height: 1.4;
}

.map a.leaflet-popup-close-button {
  width: 32px;
  height: 32px;
  font: 20px/32px var(--font);
  color: var(--text-muted);
}

.map a.leaflet-popup-close-button:hover,
.map a.leaflet-popup-close-button:focus {
  color: var(--text);
}

.map-pop {
  display: grid;
  gap: 6px;
}

.map .map-pop p {
  margin: 0;
}

.map-pop-title {
  padding-right: 22px; /* room for the close button */
  font-size: 1rem;
  overflow-wrap: anywhere;
}

.map-pop-slot {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-top: 1px solid var(--border);
}

.map-pop-time {
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* a week search: the slot's day above its time */
.map-pop-day {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
}

.map-pop-price {
  font-weight: 750;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: normal;
}

/* under the price: the popup is too narrow for both on one line */
.map-pop-price .price-tag {
  display: block;
  width: fit-content;
  margin: 1px 0 0 auto;
}

/* "Edullinen" without the saving: the price column is about 70 px wide */
.map-pop-price .good-tag {
  padding: 0 4px;
  font-size: 0.68rem;
}

.map-pop-price .good-saving {
  display: none;
}

.map-pop-slot.is-stale .map-pop-time,
.map-pop-slot.is-stale .map-pop-price {
  color: var(--text-muted);
}

.map .map-pop .btn-book {
  min-height: 34px;
  margin-top: 0;
  padding: 4px 10px;
  color: var(--on-accent);
}

.map-pop-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 4px 12px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.map-pop-foot .btn-link {
  min-height: 32px;
}

/* a cluster's popup: its halls, scrolling when there are many (the padding keeps focus rings unclipped) */
.map-pop-heading {
  font-weight: 700;
}

.map-pop-halls {
  max-height: min(18rem, 40vh);
  margin: -4px -6px;
  padding: 4px 6px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.map-pop-hall {
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.map-pop-name {
  overflow-wrap: anywhere;
}

.map-pop-hall .map-pop-slot {
  padding: 4px 0 0;
  border-top: 0;
}

.map .map-pop-open,
.map .map-pop-back {
  min-height: 32px;
}

.map-pop-back {
  justify-self: start;
}

@media (prefers-color-scheme: dark) {
  .map .leaflet-tile-pane {
    filter: brightness(0.75) contrast(1.1);
  }
}

@media (min-width: 900px) {
  .map:empty,
  .map.leaflet-container {
    height: 70vh;
  }
}

/* rows of the hall picked with "Näytä listassa" on the map */
.slot.is-highlight,
.venue-card.is-highlight {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}

/* ---------------------------------------------------------------- week grid (grid.js) */

.wgrid {
  display: grid;
  gap: 10px;
}

/* 7 columns fit a 390 px phone; on anything narrower the grid scrolls in its own box, never the page.
   The padding keeps focus rings unclipped. */
.wgrid-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  margin: -3px;
  padding: 3px;
}

.wgrid-table {
  width: 100%;
  min-width: 300px;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 3px;
  font-variant-numeric: tabular-nums;
}

.wgrid-table caption {
  padding: 0 0 2px 3px;
  font-size: 0.82rem;
  color: var(--text-muted);
  text-align: left;
}

.wg-corner {
  width: 2rem;
}

.wg-day {
  padding: 0 0 2px;
  font-size: 0.78rem;
  font-weight: 650;
  line-height: 1.25;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
}

.wg-wd,
.wg-date {
  display: block;
}

.wg-date {
  font-weight: 500;
  color: var(--text-muted);
}

.wg-wait {
  font-weight: 400;
}

.wg-hour {
  padding-right: 3px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
  text-align: right;
}

.wgrid-table td {
  padding: 0;
}

.gcell {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 40px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 650;
  line-height: 1.1;
  white-space: nowrap;
}

button.gcell:hover {
  border-color: var(--border-strong);
}

.heat-0 { background: var(--heat-0); color: var(--heat-ink-strong); }
.heat-1 { background: var(--heat-1); color: var(--heat-ink-strong); }
.heat-2 { background: var(--heat-2); }
.heat-3 { background: var(--heat-3); }
.heat-4 { background: var(--heat-4); }

.gcell-approx {
  margin-right: 1px;
  font-size: 0.85em;
  font-weight: 500;
}

.gcell.is-empty,
.gcell.is-unknown {
  background: transparent;
  color: var(--text-muted);
  font-weight: 500;
}

/* today's hours that are over: hatched, no text */
.gcell.is-past {
  border-color: transparent;
  background: repeating-linear-gradient(135deg, transparent 0 5px, var(--border) 5px 6px);
}

/* the week's cheapest price (always --heat-0): a ring just inside the edge as well as the colour */
.gcell.is-best {
  border-color: var(--heat-0);
  box-shadow: inset 0 0 0 2px var(--heat-0), inset 0 0 0 4px var(--heat-ring);
  font-weight: 800;
}

.gcell[aria-pressed="true"] {
  outline: 2px solid var(--text);
  outline-offset: 1px;
}

.gcell:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 1px;
}

/* a day not all fresh yet (still being checked, or out of date): ⏱ by its name, dashed cells. Not faded: a cell's
   colour is its price, and a faded cheap cell reads as a dear one (a week search's later days are often 2 h old). */
.wg-day.is-partial {
  color: var(--text-muted);
}

.wgrid-table td.is-partial .gcell {
  border-style: dashed;
}

.gcell.is-loading {
  border-color: transparent;
  background: linear-gradient(90deg, var(--skeleton) 25%, var(--skeleton-hi) 50%, var(--skeleton) 75%);
  background-size: 300% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

.wgrid-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
  font-size: 0.8rem;
  color: var(--text-muted);
}

.wg-key {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}

.wg-ramp {
  display: inline-flex;
  gap: 2px;
}

.wg-swatch {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 1px solid var(--border);
  border-radius: 3px;
}

.wg-swatch.is-best {
  width: 16px;
  height: 16px;
  border-color: var(--heat-0);
  box-shadow: inset 0 0 0 1px var(--heat-0), inset 0 0 0 3px var(--heat-ring);
}

/* the picked cell's slots, cheapest first; each opens the hall dialog */
.wgrid-detail {
  display: grid;
  gap: 8px;
}

.wgrid-hint {
  font-size: 0.9rem;
  color: var(--text-muted);
}

.wgrid-detail-title {
  font-size: 1.05rem;
}

.wgrid-detail-count {
  font-weight: 500;
  color: var(--text-muted);
}

.wgrid-slots {
  display: grid;
  gap: 6px;
}

.gslot {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 2px 14px;
  width: 100%;
  min-height: 52px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  text-align: left;
  box-shadow: var(--shadow);
}

.gslot:hover {
  border-color: var(--accent);
}

.gslot-time {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.gslot-venue {
  display: grid;
  min-width: 0;
  overflow-wrap: anywhere;
}

.gslot-venue .venue-where {
  font-size: 0.85rem;
}

.gslot-price {
  display: grid;
  justify-items: end;
  gap: 2px;
  font-size: 1.1rem;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.wgrid-more {
  justify-self: start;
}

.gslot-item.is-stale .gslot {
  background: var(--stale-bg);
  border-style: dashed;
  box-shadow: none;
}

.gslot-item.is-stale .gslot-time,
.gslot-item.is-stale .gslot-amount {
  color: var(--text-muted);
}

/* Phones: the time above the hall, the price beside both; "Edullinen" without the saving */
@media (max-width: 559px) {
  .wgrid-table {
    border-spacing: 2px;
  }

  .gslot {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .gslot-time { grid-area: 1 / 1; }
  .gslot-venue { grid-area: 2 / 1; }
  .gslot-price { grid-area: 1 / 2 / 3 / 3; }

  .gslot .good-saving {
    display: none;
  }
}

@media (min-width: 560px) {
  .gcell {
    font-size: 0.95rem;
  }
}

/* ---------------------------------------------------------------- hall dialog */

.hall-dialog {
  flex-direction: column;
  inset: 0;
  width: 100%;
  max-width: 100%;
  height: fit-content;
  max-height: 92%;
  max-height: min(92dvh, 100%);
  margin: auto 0 0;
  padding: 0;
  border: 0;
  border-radius: 16px 16px 0 0;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 -8px 28px rgb(0 0 0 / 0.28);
  overflow: hidden;
}

/* The UA `dialog:modal` rule (max-width inset, overflow, display:block) ties a class
   selector and wins. Repeat the sheet geometry where it actually beats that rule.
   `display` is set only on the open dialog: any author `display` on the closed one beats the UA's
   `dialog:not([open]) { display: none }` and leaves an empty box in the middle of the page. */
dialog.hall-dialog:not([open]) {
  display: none;
}

dialog.hall-dialog[open] {
  display: flex;
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: 100%;
  height: fit-content;
  max-height: min(92dvh, 100%);
  margin: auto 0 0;
  padding: 0;
  border: 0;
  overflow: hidden;
}

.hall-dialog::backdrop {
  background: rgb(8 16 12 / 0.5);
}

.hall-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px;
  align-items: start;
  flex: none;
  padding: 12px 12px 10px 16px;
}

.hall-id {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.hall-title {
  font-size: 1.25rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

.hall-where {
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.hall-platforms {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.hall-platforms .also {
  white-space: normal;
}

/* "Piilota halli": an aside, not one of the slot's actions */
.hall-hide .btn-link {
  min-height: 32px;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.hall-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: -4px -4px 0 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text);
  font-size: 1.5rem;
  line-height: 1;
}

.hall-close:hover {
  background: var(--border);
}

.hall-panel {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  padding: 4px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.hall-slot {
  display: grid;
  gap: 2px;
  min-width: 0;
  margin-top: 4px;
}

.hall-slot .price {
  font-size: 1.45rem;
  text-align: left;
  white-space: normal;
}

.hall-slot .price.has-pending {
  align-items: flex-start;
}

.hall-slot-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
}

.hall-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.hall-actions .btn,
.hall-actions .btn-book {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  margin-top: 0;
  white-space: normal;
  text-align: center;
}

.hall-fallback {
  display: block;
  width: 100%;
  max-width: 100%;
  margin-top: 8px;
  min-height: 4.5rem;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  resize: vertical;
}

.hall-prices {
  display: grid;
  gap: 8px;
  min-width: 0;
  margin-top: 16px;
}

.hall-prices-title {
  font-size: 1.05rem;
}

.hall-prices-body {
  display: grid;
  gap: 8px;
  min-width: 0;
}

.hall-section {
  min-width: 0;
}

.hall-section-title {
  margin: 4px 0 0;
  font-size: 0.95rem;
}

/* The day is a short label. Each tier is its own item so a wrap never cuts "18–" from "18.30". */
.hall-price-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  min-width: 0;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.hall-days {
  font-weight: 650;
}

.hall-tiers {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: baseline;
  gap: 6px 1rem;
  max-width: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  font-variant-numeric: tabular-nums;
  text-align: left;
}

.hall-tier {
  display: flex;
  align-items: baseline;
  gap: 0.35em;
  max-width: 100%;
  margin: 0;
  padding: 0;
  white-space: nowrap;
}

.hall-tier-time {
  color: var(--text-muted);
  font-weight: 500;
}

.hall-tier-price {
  color: var(--text);
  font-weight: 750;
}

.hall-legend {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.hall-status {
  margin: 0;
  color: var(--text-muted);
}

.hall-note {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.8rem;
}

@media (min-width: 640px) {
  .hall-dialog,
  dialog.hall-dialog[open] {
    width: min(32rem, calc(100% - 2rem));
    max-width: min(32rem, calc(100% - 2rem));
    max-height: min(85dvh, 44rem);
    margin: auto;
    border-radius: var(--radius);
    box-shadow: 0 16px 48px rgb(0 0 0 / 0.28);
  }

  /* Centred panel: a fixed label column, tiers in the remaining width. */
  .hall-price-row {
    flex-direction: row;
    align-items: start;
    column-gap: 16px;
  }

  .hall-days {
    flex: 0 0 8.5rem;
    width: 8.5rem;
  }

  .hall-tiers {
    flex: 1 1 auto;
    min-width: 0;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .hall-dialog[open] {
    transition: transform 0.2s ease, opacity 0.2s ease;
  }

  @starting-style {
    .hall-dialog[open] {
      transform: translateY(16px);
      opacity: 0;
    }
  }
}

/* ---------------------------------------------------------------- footer, toast */

.site-footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 0.85rem;
}

.footer-inner {
  display: grid;
  gap: 8px;
  padding-top: 16px;
  padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 20px;
}

.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  z-index: 30;
  max-width: calc(100% - 2 * var(--gutter));
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  background: var(--text);
  color: var(--bg);
  font-size: 0.92rem;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.25);
  transform: translate(-50%, 150%);
  opacity: 0;
  transition: transform 0.2s ease, opacity 0.2s ease;
  pointer-events: none;
}

.toast.show {
  transform: translate(-50%, 0);
  opacity: 1;
}

/* While the hall dialog is open the toast has to live in the top layer, or the backdrop hides it. */
.hall-dialog .toast {
  position: absolute;
  z-index: 3;
}

.toast:empty {
  visibility: hidden;
}

/* ---------------------------------------------------------------- /health page (server-rendered, body.health) */

body.health main {
  max-width: 1160px;
  margin: 0 auto;
  padding: 16px var(--gutter) 32px;
  overflow-x: auto;
}

body.health h1 {
  font-size: 1.3rem;
  margin-bottom: 4px;
}

body.health main > p {
  color: var(--text-muted);
  margin-bottom: 16px;
}

body.health table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}

body.health th,
body.health td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
  overflow-wrap: anywhere;
}

body.health th {
  background: var(--surface-2);
  font-size: 0.8rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

body.health td.ok { color: var(--accent); font-weight: 650; }
body.health td.degraded { color: var(--warn-text); background: var(--warn-bg); font-weight: 650; }
body.health td.down { color: var(--danger-text); font-weight: 650; }
body.health .down { color: var(--danger-text); font-weight: 650; }
body.health .ok { color: var(--accent); font-weight: 650; }
body.health .degraded { color: var(--warn-text); font-weight: 650; }

body.health h2 {
  font-size: 1.05rem;
  margin: 24px 0 8px;
}

body.health p.empty {
  color: var(--text-muted);
}

/* ---------------------------------------------------------------- wider screens */

/* View toggle shows icons only on phones; the text stays as the accessible name. */
@media (max-width: 559px) {
  .view-text {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

@media (min-width: 560px) {
  .chips-scroll {
    flex-wrap: wrap;
    overflow: visible;
    padding: 0;
    margin: 0;
  }
}

@media (min-width: 900px) {
  .layout {
    grid-template-columns: 340px minmax(0, 1fr);
    align-items: start;
    gap: 24px;
    padding-top: 24px;
  }

  .filters {
    position: sticky;
    top: 16px;
    max-height: calc(100vh - 32px);
    overflow-y: auto;
  }

  .toolbar {
    padding-top: 0;
  }

  .footer-inner {
    grid-template-columns: 1fr auto;
    align-items: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }

  /* A full bar, still: the slide above would freeze off to the left. */
  .progress-track.is-indeterminate .progress-fill {
    width: 100%;
    transform: none;
    opacity: 0.7;
  }
}
