/*
  styles/components.css — díon UI Overhaul, Phase 23 (full Stitch restyle).

  Owns the NEW component surfaces the warm & clean Stitch design adds on top of
  the Phase-22 chrome: the responsive card-sheet / side-panel shell, the per-mode
  panel show/hide skeleton, the restyled-header (UIX-07 de-retro), and — in later
  waves — the legend-switch rules. stíl.css keeps the existing chrome it already
  token-ised; this file is the legible diff surface for the restyle.

  CONSUME ONLY var(--ui-*) — never a hardcoded hex (Pitfall 4, the Phase-22
  invert-removal lesson: a bare colour strands in dark because body.dark-mode only
  re-points the --ui-* tokens). The amber score-accent token is the 0–10 SCORE
  VALUE's signature ONLY (THEME-03) and is never spent here — score surfaces land
  in Plan 04.

  Load AFTER tokens.css + stíl.css in index.html — cascade order is the contract.
  No build step; 2-space indent; vanilla CSS custom properties.
*/

/* ── Restyled header chrome (UIX-07) — warm-clean pill/icon, retro 3D GONE ─────
   Kills the .button-presser / .a-presser stacked box-shadow + uppercase +
   letter-spacing + 3px border on every VISIBLE header control. The script.js
   wiring (menu/mode/lang/legend/theme) is untouched — only the look changes.
   EVERY surface reads --ui-* so body.dark-mode re-points it (no UA-default grey
   strand — the Phase-22 lesson, threat T-23-04). */

.menu-toggle,
.panel-toggle,
.lang-toggle,
.mode-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: var(--ui-space-sm) var(--ui-space-md);
  border: 1px solid transparent;
  border-radius: var(--ui-radius-pill);
  background-color: transparent;
  color: var(--ui-on-surface);
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  line-height: var(--ui-line-height-label);
  letter-spacing: normal;
  text-transform: none;
  text-decoration: none;
  box-shadow: none;
  cursor: pointer;
}

.menu-toggle:hover,
.panel-toggle:hover,
.lang-toggle:hover,
.mode-toggle:hover,
.menu-toggle:focus-visible,
.panel-toggle:focus-visible,
.lang-toggle:focus-visible,
.mode-toggle:focus-visible {
  border-color: var(--ui-primary);
  background-color: var(--ui-primary-container);
}

.menu-toggle:active,
.panel-toggle:active,
.lang-toggle:active,
.mode-toggle:active {
  /* no retro 5px nudge — stay put, clean */
  top: 0;
  left: 0;
  box-shadow: none;
}

/* The díon wordmark: Plus Jakarta Sans heading, lowercase. */
.logo {
  font-family: var(--ui-font-display);
  font-weight: var(--ui-font-weight-heading);
  color: var(--ui-on-surface);
  text-transform: none;
  letter-spacing: normal;
}

/* The hamburger menu rows (Mód · Teanga · Eochair · Faoi) keep their Phase-22
   token pill styling from stíl.css (.ui-menu__mode / .ui-menu__action). With the
   .a-presser class dropped in the markup, no retro override is needed here — the
   stíl.css pill rules now apply cleanly. This block only neutralises any residual
   retro signature should a row still inherit it. */
.ui-menu__mode,
.ui-menu__action {
  box-shadow: none;
  text-transform: none;
  letter-spacing: normal;
}

/* ── Responsive card SHELL (UIX-03 / UIX-06, SPEC Pattern 3) ───────────────────
   The place/area card is a native <dialog> opened NON-MODAL (.show()) so the GL
   map stays interactive beside/under it (deploy-first #1-risk coexistence, 2026-06-22
   UAT). Mobile (< 768px): a bottom-sheet anchored to the bottom edge with rounded
   top corners. Desktop (≥ 768px): the SAME DOM reflows to a calm top-right
   side-panel card. A non-modal dialog renders no ::backdrop, so there is no
   full-viewport scrim to freeze the map. Plan 03 fills the card BODY; shell only. */
.dion-card {
  position: fixed;
  inset: auto 0 0 0;
  /* A non-modal <dialog> (.show()) does NOT enter the top layer the way showModal()
     does — this explicit z-index keeps the screen-anchored card above the map panes,
     Leaflet controls (~1000) and the criteria panel (960) while the GL map stays
     interactive (UIX-03 deploy-first coexistence). */
  z-index: 1200;
  border: none;
  border-radius: var(--ui-radius-lg) var(--ui-radius-lg) 0 0;
  box-shadow: var(--ui-elev-soft);
  background: var(--ui-surface);
  color: var(--ui-on-surface);
  padding: var(--ui-space-lg);
}

.dion-card::backdrop {
  background: var(--ui-scrim);
}

/* The mobile bottom-sheet grab handle (the small pill at the top of the sheet). */
.dion-card__handle {
  width: 36px;
  height: 4px;
  margin: 0 auto var(--ui-space-md);
  border-radius: var(--ui-radius-pill);
  background: var(--ui-sheet-handle);
}

@media (min-width: 768px) {
  /* Desktop reflow: a floating side-panel card top-right, no scrim. */
  .dion-card {
    inset: var(--ui-space-xl) var(--ui-space-xl) auto auto;
    max-width: 360px;
    border-radius: var(--ui-radius-md);
  }

  .dion-card::backdrop {
    background: transparent;
  }

  /* The grab handle is a mobile-sheet affordance only. */
  .dion-card__handle {
    display: none;
  }
}

/* ── Card body — Stitch place + area card internals (UIX-03, SPEC §2/§3) ───────
   The card body slot (.dion-card__body) is filled VERBATIM with the frozen
   popup.js builder output (buildPlaceCard / buildAreaCard) — so these rules
   restyle the SAME class hooks the builders already emit (.popup-name,
   .place-card-addr, .place-card-cat, .place-card-actions + the four
   .place-card-*-btn buttons, .place-card-modes, .area-card-class) plus the
   panel.js-added .area-card-footer / .area-card-share-btn. Token-only (--ui-*),
   so body.dark-mode re-points every surface (no hardcoded hex, the Phase-22
   invert-removal discipline). The cards are left-aligned in the sheet; the
   frozen builders set inline `text-align: center` which these rules override for
   the Stitch look without touching popup.js. */

/* The ✕ dismiss control — a 44px round touch target top-right of the sheet. */
.dion-card__close {
  position: absolute;
  top: var(--ui-space-md);
  right: var(--ui-space-md);
  min-width: 44px;
  min-height: 44px;
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

/* Heading row — the place name / area class (Heading 20/700). */
.dion-card__body .popup-name {
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-heading);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-heading);
  color: var(--ui-on-surface);
  text-align: left;
  margin: 0 var(--ui-space-2xl) var(--ui-space-sm) 0;
}

/* De-box the heading focus ring (24-09 UAT tweak). The heading carries tabindex=-1
   and is focused on open for a11y (openCardSheet focus management), but the UA
   default `outline: auto` painted a dark rectangle that read like a text input.
   Suppress the default outline and, ONLY for keyboard focus, paint a subtle
   token ring (never a heavy input-looking box). Mouse/programmatic open-focus
   shows nothing, so the heading never looks editable. */
.dion-card__body .popup-name:focus {
  outline: none;
}
.dion-card__body .popup-name:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
  border-radius: var(--ui-radius-sm);
}

/* The name line — the heading + its click-to-copy chip side-by-side (24-09). The
   chip aligns to the heading baseline-ish (centred) so it reads as an affordance
   on the name, not a separate row; the heading's right margin is dropped here
   since the chip now occupies that gutter. */
.dion-card__body .place-info-nameline {
  display: flex;
  align-items: center;
  gap: var(--ui-space-xs);
}
.dion-card__body .place-info-nameline .popup-name {
  margin-right: 0;
}

/* ≈N m disclosure / locality — muted Label, the honesty line never accented. */
.dion-card__body .place-card-addr {
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface-muted);
  text-align: left;
}

/* Category sub-line — muted Body. */
.dion-card__body .place-card-cat {
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-body);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface-muted);
  text-align: left;
}

/* The 4-action hub — a row of round 44px icon buttons; info/from/to are neutral
   container chips, the score (all-modes ⊕) is the green --ui-primary pill CTA. */
.dion-card__body .place-card-actions {
  display: flex;
  gap: var(--ui-space-sm);
  align-items: center;
  margin-top: var(--ui-space-md);
  text-align: left;
}

.dion-card__body .place-card-info-btn,
.dion-card__body .place-card-from-btn,
.dion-card__body .place-card-to-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-size: 1.1rem;
  cursor: pointer;
  margin: 0;
}

.dion-card__body .place-card-info-btn[disabled],
.dion-card__body .place-card-from-btn[disabled],
.dion-card__body .place-card-to-btn[disabled],
.dion-card__body .place-card-from-btn[aria-disabled='true'],
.dion-card__body .place-card-to-btn[aria-disabled='true'] {
  opacity: 0.45;
  cursor: not-allowed;
}

.dion-card__body .place-card-score-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin: 0 0 0 auto;
  padding: 0 var(--ui-space-lg);
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-primary);
  color: var(--ui-surface);
  font-family: var(--ui-font-body);
  font-weight: var(--ui-font-weight-label);
  font-size: 1.2rem;
  cursor: pointer;
}

/* The 4-mode FIT-SCORE row. Each mode VALUE is the ONLY place the amber
   score-accent token is spent (THEME-03) — the "no service" token + the em-dash
   render muted via the surrounding text colour, never amber. */
.dion-card__body .place-card-modes {
  margin: var(--ui-space-md) 0 0 0;
  padding: var(--ui-space-md);
  border-radius: var(--ui-radius-lg);
  background: var(--ui-surface-container-high);
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-display);
  font-weight: var(--ui-font-weight-display);
  line-height: var(--ui-line-height-display);
  color: var(--ui-score-accent); /* .place-card-modes score values — amber lives ONLY here */
  text-align: center;
}

/* Area card class heading reuses .popup-name; the AREA-02 honesty note + the
   share-only footer. NO number, NO Score CTA — the share button is the only
   action (resolved decision #2). */
/* The prepended feature-NAME heading on a named area card (lake / park / cemetery). Reuses
   the .popup-name heading style; a small bottom margin separates it from the class label. */
.dion-card__body .area-card-name {
  margin: 0 0 var(--ui-space-xs);
}

.dion-card__body .area-card-footer {
  display: flex;
  margin-top: var(--ui-space-lg);
}

.dion-card__body .area-card-share-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 var(--ui-space-xl);
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-primary);
  color: var(--ui-surface);
  font-family: var(--ui-font-body);
  font-weight: var(--ui-font-weight-label);
  font-size: var(--ui-font-size-body);
  cursor: pointer;
}

/* ── Per-mode panel (UIX-04) ───────────────────────────────────────────────────
   Per-mode show/hide gates off the body.mode-<slug> class that script.js's
   reflectMode() sets from window.dionMode (the GLOBAL seam, never e.detail). The
   CSS does the bulk; panel.js only BUILDS the Leanbh tiles + Castas sliders.

   Default (Gnáth): the chip grid + weight dots show; the Leanbh tile host + the
   Castas slider host + the bike segment are hidden. Leanbh: hide the weight dots
   + the chip groups + the iso block; show the big preset tiles. Castas: show the
   .castas-only surfaces (the slider host + the bike segment). */

/* castas-only surfaces are hidden by default; Castas reveals them. */
.castas-only {
  display: none;
}
body.mode-castas .castas-only {
  display: revert;
}

/* The Leanbh preset-tile host is hidden outside Leanbh. */
.leanbh-presets {
  display: none;
}

body.mode-leanbh .leanbh-presets {
  display: grid;
  gap: var(--ui-space-md);
  margin-top: var(--ui-space-md);
}

/* Leanbh strips the weight controls + the dense chip grid + the iso controls —
   it is the simplest, picture-first mode (max tap targets, no weights). */
body.mode-leanbh .weight-dots,
body.mode-leanbh .weight-level-label,
body.mode-leanbh .chip-groups,
body.mode-leanbh .isochrone-controls {
  display: none;
}

/* Leanbh preset tile — a huge picture-first criterion target (xl radius). */
.leanbh-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--ui-space-sm);
  min-height: 120px;
  padding: var(--ui-space-lg);
  border: 2px solid var(--ui-surface-container-high);
  border-radius: var(--ui-radius-xl);
  background: var(--ui-surface);
  color: var(--ui-on-surface);
  font-family: var(--ui-font-display);
  cursor: pointer;
}

.leanbh-tile.selected {
  border-color: var(--ui-primary);
  background: var(--ui-primary-container);
}

.leanbh-tile-emoji {
  font-size: 40px;
  line-height: 1;
}

.leanbh-tile-label {
  font-size: var(--ui-font-size-heading);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-heading);
}

/* Castas numeric weight slider — a different CONTROL SURFACE over the SAME
   prefs.selected weight. The track is --ui-track, the active fill --ui-primary
   (green) — NEVER the amber score-accent (a weight is not a score). */
.castas-sliders {
  margin-top: var(--ui-space-md);
}

.castas-slider-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ui-space-xs);
  margin-bottom: var(--ui-space-md);
}

.castas-slider-label {
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-body);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
}

.castas-slider {
  width: 100%;
  min-height: 44px;
  accent-color: var(--ui-primary);
  background: var(--ui-track);
}

.castas-slider-word {
  color: var(--ui-on-surface-muted);
}

/* ── Stitch results: headline + breakdown (UIX-04, SPEC §4) ────────────────────
   The honest 0–10 headline is the Display 28 amber score VALUE + a muted /10
   denominator. The per-criterion breakdown row is icon+label (Body) left, a green
   score-BAR (fill --ui-primary, track --ui-track) + the amber numeric VALUE right.
   THEME-03: the amber score-accent lands ONLY on the headline + sub-score
   VALUES — never the bar, the CTA, a chip, a slider, a focus ring (those are green
   --ui-primary or neutral). The score-green inline hexes in stíl.css moved to the
   amber score token where they are score values (.headline-score / .row-subscore). */

.panel-headline {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-xs);
}

/* The headline 0–10 score VALUE — Display 28, amber. */
.headline-score {
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-display);
  font-weight: var(--ui-font-weight-display);
  line-height: var(--ui-line-height-display);
  color: var(--ui-score-accent); /* .headline-score — amber on the score VALUE only (THEME-03) */
}

/* The /10 denominator — muted Label, never accented. */
.headline-outof {
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  color: var(--ui-on-surface-muted);
  margin-left: var(--ui-space-xs);
}

/* Breakdown row — left-aligned card; the sub-score VALUE is the amber accent. */
.breakdown-row {
  display: flex;
  align-items: center;
  gap: var(--ui-space-sm);
  border-radius: var(--ui-radius-md);
}

.breakdown-row .row-name {
  flex: 1 1 auto;
}

/* The per-criterion sub-score VALUE — Display weight, amber. */
.breakdown-row .row-subscore {
  margin-left: auto;
  font-family: var(--ui-font-display);
  font-weight: var(--ui-font-weight-display);
  color: var(--ui-score-accent); /* .row-subscore — amber on the score VALUE only (THEME-03) */
}

/* ── First-run / empty state (UIX-05, SPEC §5) ─────────────────────────────────
   "Brúigh áit ar bith / Tap anywhere to score a place" Heading + a Body sub-line
   + 3 preset chips as affordance ONLY. NO fabricated number/name/route and NO
   amber on screen until a real score exists (T-23-09) — this block spends no
   amber score token at all. */
.panel-empty {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-sm);
  padding: var(--ui-space-md) 0;
}

.panel-empty-title {
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-heading);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-heading);
  color: var(--ui-on-surface);
}

.panel-empty-sub {
  color: var(--ui-on-surface-muted);
}

.panel-empty-presets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ui-space-sm);
  margin-top: var(--ui-space-sm);
}

/* Empty-state preset chip — a neutral container pill (affordance, NOT a score). */
.panel-empty-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ui-space-xs);
  min-height: 44px;
  padding: 0 var(--ui-space-md);
  border: 1px solid var(--ui-surface-container-high);
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  cursor: pointer;
}

.panel-empty-chip.selected {
  border-color: var(--ui-primary);
  background: var(--ui-primary-container);
}

/* Active-criteria context chips (the breakdown's selected set) render as neutral
   --ui-surface-container-high pills — never amber, never the green CTA. */
.chip.selected {
  background: var(--ui-primary-container);
}

/* ── Legend / Eochair — Stitch switch rows (UIX-05, SPEC §6, screen 09) ─────────
   legend.js builds each landcover-class row as a <button> with aria-pressed (the
   on/off semantics) + a decorative .dion-key__switch pill/knob. These rules skin
   the row to the Stitch switch: a green --ui-primary track + a sliding white knob
   when pressed, a muted --ui-track when off. The aria-pressed attribute is the
   single source of truth — CSS only reads it (the existing per-class drivers +
   honest-disable + single-language relabel in legend.js are untouched). Token-only
   (--ui-*) so body.dark-mode re-points the whole Key (Pitfall 4). The toggle rows
   already carry min-height:44px + the hover/disabled rules from stíl.css; this
   block ADDS the switch visual + the muted sub-line, not duplicate layout. */

/* The muted "Saincheap do léarscáil / Customize your map" caption under the Key. */
.dion-key__subline {
  margin: 0 0 var(--ui-space-sm);
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface-muted);
}

/* The switch track sits at the row's right edge (the label flex-grows between the
   swatch and the switch). A pill track with a sliding knob. */
.dion-key__switch {
  flex: 0 0 auto;
  position: relative;
  width: 40px;
  height: 24px;
  margin-left: auto;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-track);
  transition: background-color 120ms ease;
}

.dion-key__switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface);
  box-shadow: var(--ui-elev-sm);
  transition: transform 120ms ease;
}

/* ON (aria-pressed='true'): the track turns green --ui-primary (the SPEC "on"
   colour — NEVER amber, a toggle is not a score) and the knob slides right. */
.dion-key__row--toggle[aria-pressed='true'] .dion-key__switch {
  background: var(--ui-primary);
}

.dion-key__row--toggle[aria-pressed='true'] .dion-key__switch-knob {
  transform: translateX(16px);
}

/* A green --ui-primary focus ring on the row (a11y — never amber, never colour-only:
   the knob position + the track colour also carry the state, WCAG 1.4.1). */
.dion-key__row--toggle:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}

/* ── 27-06: the right-side "Sraitheanna / Layers" rail (layers-rail.js) ───────────────
   Operator UAT: an OSM-style right-edge panel that owns every map TOGGLE (POI/feature
   categories + basemap overlays), grouped — the Key (left) is now a read-only legend.
   A fixed element vertically centred on the right edge; collapsed to a pill, expanding to
   the grouped toggle list. Token-only so body.dark-mode re-points it (no invert). The
   toggle rows reuse the .dion-key__row--toggle aria-pressed switch skin above. */
.dion-rail {
  position: fixed;
  top: 50%;
  right: var(--ui-space-md);
  transform: translateY(-50%);
  z-index: 880; /* above map panes (200), below the card (1200) + banners (1100) */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-family: var(--ui-font-body);
}
.dion-rail__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--ui-space-xs);
  min-height: 44px; /* ≥44px touch target (WCAG 2.5.5) */
  padding: var(--ui-space-sm) var(--ui-space-md);
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  box-shadow: var(--ui-elev-soft);
  cursor: pointer;
  font: inherit;
  font-weight: var(--ui-font-weight-label);
  -webkit-tap-highlight-color: transparent;
}
.dion-rail__toggle-glyph {
  font-size: 1.1rem;
  line-height: 1;
}
/* D-08 (260630-lbo): the X close pinned top-right INSIDE the Key body / the rail
   body — a 44px round neutral-surface target so each control opens-in-place and
   closes from within (the zoom-pill affordance). The bodies are position:absolute
   (Key) / the rail body is the flow root, so float the X to the top-right corner. */
.dion-key__close,
.dion-rail__close {
  position: absolute;
  top: var(--ui-space-sm);
  right: var(--ui-space-sm);
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 50%;
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--ui-elev-soft);
  z-index: 1;
}
/* The rail body is the positioning context for its own X. */
.dion-rail__body {
  position: relative;
}
.dion-rail__toggle:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}
.dion-rail__body {
  margin-top: var(--ui-space-sm);
  padding: var(--ui-space-md);
  width: min(264px, 82vw);
  /* Cap the height (centred on the right edge) so the expanded panel clears the
     bottom-right zoom control; the list scrolls past this. */
  max-height: 68vh;
  overflow-y: auto;
  border-radius: var(--ui-radius-lg);
  background: var(--ui-surface);
  box-shadow: var(--ui-elev-soft);
}
.dion-rail--collapsed .dion-rail__body {
  display: none;
}
.dion-rail__group {
  margin-bottom: var(--ui-space-md);
}
.dion-rail__group:last-child {
  margin-bottom: 0;
}
.dion-rail__heading {
  margin: 0 0 var(--ui-space-xs);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dion-rail__row {
  display: flex;
  align-items: center;
  width: 100%;
  gap: var(--ui-space-sm);
  padding: var(--ui-space-xs);
  border: none;
  background: transparent;
  color: var(--ui-on-surface);
  font: inherit;
  font-size: var(--ui-font-size-body);
  line-height: var(--ui-line-height-body);
  text-align: left;
  cursor: pointer;
  border-radius: var(--ui-radius-sm);
}
.dion-rail__row:hover {
  background: var(--ui-surface-container-high);
}
.dion-rail__label {
  flex: 1 1 auto;
}
.dion-rail__row--disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ── z5 overview chrome hide (operator UAT refine, nations.js body.dion-overview) ──
   At the whole-Celtic-Nations overview (Leaflet zoom ≤ 5) the Layers rail + the Key
   add clutter to the clean silhouette view, so hide both. They reappear at z6+ (once
   a nation is selected and the map zooms in). The zoom +/- pill and the settings gear
   are deliberately NOT hidden — basic navigation stays available. */
body.dion-overview .dion-rail,
body.dion-overview .dion-key {
  display: none;
}

/* ── z5 HYBRID ANTIQUE SEA-CHART overlay (quick 260701, scripts/portolan.js) ──────
   A pointer-events:none atmosphere layer over the map: a paper-grain wash, a soft brown
   vignette and a weathered torn-edge frame. OVERVIEW-ONLY — hidden by default, faded in
   only under body.dion-overview (nations.js toggles it at Leaflet zoom ≤ 5). The DOM
   nodes + their SVG art (grain/frame data-URIs) are built in portolan.js; this owns the
   positioning + the overview-gated visibility. z-index sits ABOVE the GL basemap canvas
   (map panes ~200–400) but BELOW markers/controls (≥600) so it never dims the zoom pill
   or the settings gear. NO amber/orange/yellow (score-reserved). */
.dion-portolan {
  position: absolute;
  inset: 0;
  z-index: 401;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s ease, visibility 0s linear 0.5s;
}
body.dion-overview .dion-portolan {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.6s ease;
}
.dion-portolan > div {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* STAGE 4(a) antique sea texture (quick 260630-lbo) — a tiny repeating tile of faint
   hand-inked wave marks (portolan.js). Kept VERY subtle (low opacity + multiply) so it
   reads as aged sea tooth under the labels/land, never clutter. */
.dion-portolan__sea {
  background-repeat: repeat;
  background-size: 48px 48px;
  opacity: 0.06;
  mix-blend-mode: multiply;
}
body.dark-mode .dion-portolan__sea {
  opacity: 0.08;
}
/* Paper grain — monochrome paper-fibre noise multiplied over the parchment sea. Clearly
   present but tasteful (was a near-invisible 0.09). */
.dion-portolan__grain {
  background-repeat: repeat;
  background-size: 220px 220px;
  opacity: 0.18;
  mix-blend-mode: multiply;
}
/* Weathered vignette — a warm-sepia radial darkening at the paper edges. Softened
   (operator UAT 260630-lbo — BLUE seas) so it reads as a gentle aged paper edge over the
   blue sea rather than a heavy sepia frame. */
.dion-portolan__vignette {
  background: radial-gradient(
    ellipse at center,
    rgba(74, 54, 28, 0) 45%,
    rgba(66, 48, 24, 0.07) 65%,
    rgba(58, 42, 20, 0.17) 84%,
    rgba(42, 28, 12, 0.32) 100%
  );
}
/* Sea-tone EDGE FADE (.dion-portolan__seafade) REMOVED (quick 260630-lbo): it feathered the
   pale background neighbour LAND's straight bbox cuts into the blue sea. The neighbour FILL is
   gone (neighbours are now a thin sepia border line only), so there is no rectangular land
   edge left to dissolve — the radial wash is dropped with it. */
/* Weathered torn-edge frame — the bold hand-drawn deckle (SVG stretched to fit), with a
   soft sepia drop so the torn edge reads with depth against the sea. */
.dion-portolan__frame {
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  opacity: 0.95;
  filter: drop-shadow(0 0 6px rgba(50, 34, 16, 0.28));
}
/* In dark mode the parchment atmosphere stays warm but a touch deeper so the grain +
   vignette still read over the darker sea recolour (portolan.js reads the base lazily). */
body.dark-mode .dion-portolan__grain {
  opacity: 0.22;
}

/* ── STAGE 3 insular typography (quick 260701) — self-hosted OFL fonts ─────────────
   "Uncial Antiqua" (insular majuscule, OFL) for the NATION names + "IM Fell English"
   italic (OFL) for the SEA names. Self-hosted same-origin under assets/fonts (font-src
   'self' already covers them — NO CSP change). font-display: swap so a missing file
   degrades to the fallback stack, never a blank label. */
@font-face {
  font-family: 'Uncial Antiqua';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/uncial-antiqua-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Uncial Antiqua';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/uncial-antiqua-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IM Fell English';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/im-fell-english-italic-latin.woff2') format('woff2');
}

/* ── STAGE 3 "Celtic Nations" banner/cartouche (quick 260630-lbo, portolan.js) ─────
   A tasteful top-centre antique title, overview-only (inside .dion-portolan → inherits the
   visibility gate). Not oversized (clamped for mobile); pinned to the very top edge (the
   northern open sea at the z5 overview) so it never overlaps land or the nation/sea labels.
   pointer-events:none. NO amber/orange/yellow (score-reserved). */
/* selector is `.dion-portolan > .dion-portolan__banner` so it out-specifies the
   `.dion-portolan > div { inset: 0 }` full-bleed rule (which would otherwise force the
   banner full-width and the translateX(-50%) would shove it off the left edge). */
.dion-portolan > .dion-portolan__banner {
  position: absolute;
  inset: auto;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  min-width: 240px;
  max-width: min(62vw, 460px);
  padding: 9px 42px 8px;
  text-align: center;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  filter: drop-shadow(0 1px 3px rgba(50, 34, 16, 0.25));
}
.dion-portolan__banner-native {
  font-family: 'Uncial Antiqua', 'Luminari', 'Papyrus', serif;
  font-size: clamp(16px, 4.4vw, 22px);
  line-height: 1.12;
  letter-spacing: 0.03em;
  color: #4a3820; /* warm sepia ink */
}
.dion-portolan__banner-english {
  font-family: 'IM Fell English', Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-size: clamp(11px, 2.7vw, 14px);
  letter-spacing: 0.08em;
  margin-top: 2px;
  color: #5a4632;
  opacity: 0.9;
}
body.dark-mode .dion-portolan__banner-native,
body.dark-mode .dion-portolan__banner-english {
  color: #2b2016;
}

/* The insular DOM label overlay (built in portolan.js, positioned via
   map.latLngToContainerPoint on every move/zoom). They live inside .dion-portolan, so
   they inherit its overview-only visibility gate. pointer-events:none, sepia ink. */
.dion-portolan__labels {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.dion-portolan-nation,
.dion-portolan-sea {
  position: absolute;
  white-space: nowrap;
  pointer-events: none;
  color: #4a3820; /* warm sepia ink */
  text-shadow: 0 0 3px rgba(233, 220, 192, 0.9), 0 0 6px rgba(233, 220, 192, 0.7);
  will-change: transform, left, top;
}
.dion-portolan-nation {
  font-family: 'Uncial Antiqua', 'Luminari', 'Papyrus', serif;
  font-size: 20px;
  letter-spacing: 0.04em;
}
.dion-portolan-sea {
  font-family: 'IM Fell English', Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-size: 15px;
  letter-spacing: 0.06em;
  color: #5a4632;
  opacity: 0.92;
  /* Sea names are the ONE tappable insular label → a sea info panel (portolan.js
     openSeaPanel, 260630-lbo r3). The shared rule above sets pointer-events:none
     (so nation labels never eat a map-click meant for nation-select); re-enable it
     HERE so a sea-name tap actually reaches its click handler. UAT r7 #3: without
     this the click fell through to the map and no sea panel ever opened — headless
     probes call openSeaPanel() directly so they never caught it. */
  pointer-events: auto;
  touch-action: manipulation;
}
/* Operator UAT refine (quick 260630-lbo): the two-line centred sea-name variant (e.g.
   "An tAigéan" / "Atlantach"). Overrides the default nowrap so the long name breaks over
   two centred lines instead of running across the coast. */
.dion-portolan-sea--stack {
  white-space: normal;
  text-align: center;
  line-height: 1.02;
}
.dion-portolan-sea__line {
  display: block;
}
body.dark-mode .dion-portolan-nation,
body.dark-mode .dion-portolan-sea {
  color: #2b2016;
  text-shadow: 0 0 3px rgba(210, 194, 160, 0.85), 0 0 6px rgba(210, 194, 160, 0.6);
}

/* ── STAGE 4 Celtic decorations (quick 260701, sepia woodcut line-art) ─────────────
   Hand-authored SVG creatures + ships positioned in open water (portolan.js). Each is a
   centre-anchored wrapper: the aged sepia art + a subtle italic caption naming it. Inside
   .dion-portolan → inherits the overview-only visibility gate. pointer-events:none. */
.dion-portolan__decos {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.dion-portolan-deco {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
}
.dion-portolan-deco__art {
  display: block;
  width: 96px;
  height: auto;
  opacity: 0.8;
}
.dion-portolan-deco__cap {
  margin-top: 1px;
  font-family: 'IM Fell English', Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: #5a4632;
  opacity: 0.8;
  text-shadow: 0 0 3px rgba(233, 220, 192, 0.9);
}
body.dark-mode .dion-portolan-deco__art {
  opacity: 0.7;
}
body.dark-mode .dion-portolan-deco__cap {
  color: #2b2016;
  text-shadow: 0 0 3px rgba(210, 194, 160, 0.85);
}

/* ── One responsive system: mobile bottom-sheets + FAB ↔ ≥768px side-panels ─────
   (UIX-06, SPEC "Responsive System" — one DOM, CSS media queries only, NO separate
   builds, NO duplicate markup). The card <dialog> already reflows bottom-sheet ⇄
   side-panel (above). This block reflows the criteria/results panel + adds the
   floating action, and lands the a11y polish (≥44px, green focus rings, non-colour-
   only active states). Token-only so body.dark-mode re-points every surface. */

/* The criteria/results panel: a Stitch bottom-sheet on mobile (rounded top corners +
   elevation, anchored to the bottom edge), a calm side-panel ≥768px (the map stays
   interactive beside it). stíl.css owns the slide transform + the 320px desktop
   width; this only adds the Stitch sheet skin so the panel is not a bare full-bleed
   slab on mobile. */
@media (max-width: 767px) {
  .criteria-panel {
    top: auto;
    max-height: 80vh;
    border-radius: var(--ui-radius-lg) var(--ui-radius-lg) 0 0;
    box-shadow: var(--ui-elev-soft);
    background: var(--ui-surface);
  }

  /* The panel-toggle (🎚️) becomes the floating settings FAB bottom-right (SPEC §5)
     — a 44px round raised control over the full-bleed map. Its aria-controls
     wiring (script.js, untouched) keeps opening the criteria panel. */
  .panel-toggle {
    position: fixed;
    right: var(--ui-space-md);
    bottom: var(--ui-space-md);
    z-index: 960;
    width: 56px;
    height: 56px;
    margin-right: 0;
    border-radius: var(--ui-radius-pill);
    background: var(--ui-surface);
    box-shadow: var(--ui-elev-soft);
  }
}

/* ≥768px: the panel is a calm side-panel (stíl.css 320px width, no scrim) — the map
   stays interactive beside it. The card ::backdrop is already transparent ≥768px
   (above). Nothing to override here beyond the inherited side-panel layout. */

/* ── a11y: visible green focus ring on every card control + the FAB / CTAs ──────
   (UIX-06, WCAG 2.4.7 / 1.4.1). The ring is green --ui-primary — NEVER amber (amber
   is the score signature only, THEME-03). Active/pressed states carry a non-colour-
   only signal (a green fill + the border/elevation change), so meaning never relies
   on colour alone. Targets are ≥44px (the buttons above already set the minimum). */
.dion-card__close:focus-visible,
.dion-card__body .place-card-info-btn:focus-visible,
.dion-card__body .place-card-from-btn:focus-visible,
.dion-card__body .place-card-to-btn:focus-visible,
.dion-card__body .place-card-score-btn:focus-visible,
.dion-card__body .area-card-share-btn:focus-visible,
.panel-toggle:focus-visible,
.leanbh-tile:focus-visible,
.castas-slider:focus-visible,
.panel-empty-chip:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}

/* ══ Stripped Info-First chrome (UIX-08, Plan 24-02, slice-1) ══════════════════
   The slice-1 stripped chrome: a TRANSPARENT header strip (logo + burger only),
   the centered About modal, and the Key (bottom-left) + zoom (bottom-right)
   restyled as persistent floating controls. RESTYLE/RELOCATE ONLY — no map.js /
   legend.js wiring is touched (the Key is already a bottomleft legend.js control,
   zoom already a bottomright map.js control). Token-only (--ui-*) so body.dark-mode
   re-points cleanly; NO amber score-accent token on ANY surface below (UIX-11 —
   amber is the deferred score's signature only). The amber-absence guard scans
   these slice-1-marked regions and stays GREEN. */

/* ── NOTE (24.1): the orphaned slice-1 `header …` layout rules (the transparent
   header strip, the .logo/.menu-toggle text-shadow, the header-scoped
   .lang-toggle/.mode-toggle display:none, and the `order` flip) were REMOVED in
   Plan 24.1-04 — they targeted the Phase-24 <header>/burger chrome that Plan
   24.1-03 deleted from the DOM. The bottom action bar (.dion-actionbar) is the new
   chrome (styled in the 24.1 region below); the floating wordmark is .dion-wordmark
   (24.1); the kept hidden .lang-toggle/.mode-toggle delegation targets are hidden
   globally in stíl.css (24.1). The .dion-about modal + the Key/zoom restyle below
   are REUSED unchanged. 24.1 */

/* ══ Bottom Action Bar — bar shell + 4 controls + 3 upward menus + wordmark ══════
   (UIX-12/16, Plan 24.1-04). The Phase-24 stripped header is gone; this static
   <nav class="dion-actionbar"> is the primary chrome. A SOLID raised --ui-surface
   shelf (NOT transparent — unlike the header it replaces), full-bleed, 64px content
   + safe-area inset, holding the 4 controls in fixed order (Info toggle · Travel ·
   Scoring · Settings). The three menus open UPWARD toward the map, reusing the
   card/dialog vocabulary (--ui-surface panel, --ui-radius-lg, --ui-elev-soft). The
   díon wordmark floats top-left over the map (no opaque bar). Token-only (--ui-*)
   so body.dark-mode re-points (no invert); NO amber score-accent token on ANY
   surface here — the Info-ON state is green --ui-primary (THEME-03; the
   amber-absence guard scans these 24.1 regions). ≥44px touch targets; ≤5 icon-led
   slots on mobile, text labels + more spacing at ≥768px. 24.1 */

/* The ONE layout constant — bar content height (the rest is the 4-pt scale). 24.1 */
:root {
  --dion-actionbar-h: 64px;
}

/* (a) The bar shell — fixed full-bleed solid shelf, safe-area inset, z above the
   map but below banners (.error-banner-stack uses z-index:1000+; this sits below).
   Mobile: 4 equal-width icon-led slots (flex:1). 24.1 */
.dion-actionbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 900;
  display: flex;
  align-items: stretch;
  gap: var(--ui-space-sm);
  height: calc(var(--dion-actionbar-h) + env(safe-area-inset-bottom));
  padding: var(--ui-space-sm) var(--ui-space-md);
  padding-bottom: calc(var(--ui-space-sm) + env(safe-area-inset-bottom));
  background: var(--ui-surface);
  box-shadow: var(--ui-elev-soft);
  font-family: var(--ui-font-body);
}

/* Each of the 4 slots is an equal-width column on mobile (icon-led). The Info
   toggle is a direct child <button>; the three menu slots are .dion-actionbar__slot
   wrappers (they anchor their upward menu). Treat both as flex children. 24.1 */
.dion-actionbar__info,
.dion-actionbar__slot {
  flex: 1 1 0;
  min-width: 0;
  position: relative; /* slot anchors its absolutely-positioned upward menu */
}

/* The bar TRIGGER buttons (Info toggle + the 3 menu triggers) — ≥44px hit area,
   icon-over-label column, token surface, resting chip. 24.1 */
.dion-actionbar__info,
.dion-actionbar__travel,
.dion-actionbar__scoring,
.dion-actionbar__settings {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--ui-space-xs);
  width: 100%;
  min-height: 44px;
  height: 100%;
  padding: var(--ui-space-xs) var(--ui-space-sm);
  border: none;
  border-radius: var(--ui-radius-md);
  background: transparent;
  color: var(--ui-on-surface);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.dion-actionbar__info:hover,
.dion-actionbar__travel:hover,
.dion-actionbar__scoring:hover,
.dion-actionbar__settings:hover {
  background: var(--ui-surface-container-high);
}

.dion-actionbar__info:focus-visible,
.dion-actionbar__travel:focus-visible,
.dion-actionbar__scoring:focus-visible,
.dion-actionbar__settings:focus-visible,
.dion-actionbar__row:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}

.dion-actionbar__glyph {
  font-size: 1.4rem;
  line-height: 1;
}

/* The under-glyph Label — Label role. Mobile (<768px): the bar is ICON-LED, so the
   control labels are HIDDEN (UIX-16 / UI-SPEC §Mobile "icon-led … or icon-only") —
   the emoji glyph carries the control; the @media(min-width:768px) block below reveals
   the labels alongside the glyph. 24.1 */
.dion-actionbar__label {
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  line-height: var(--ui-line-height-label);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 767px) {
  /* Icon-only on mobile: collapse the control labels (the glyph leads). The menu-row
     labels (.dion-actionbar__row-label) are unaffected — menus are full text. */
  .dion-actionbar__label {
    display: none;
  }
}

/* (b) Info ON/OFF toggle (UIX-13). ON = a filled GREEN --ui-primary pill (never
   amber); OFF = resting --ui-surface-container-high chip. Two non-colour-only
   signals: aria-pressed + filled-vs-resting (the markup also carries a glyph). The
   actionbar.js subscriber toggles .is-on alongside aria-pressed. 24.1 */
.dion-actionbar__info {
  background: var(--ui-surface-container-high); /* OFF resting chip */
}

.dion-actionbar__info.is-on,
.dion-actionbar__info[aria-pressed='true'] {
  background: var(--ui-primary);
  color: var(--ui-surface);
}

.dion-actionbar__info.is-on:hover,
.dion-actionbar__info[aria-pressed='true']:hover {
  background: var(--ui-primary);
}

/* The active travel/scoring trigger shows its current-selection glyph (set in the
   markup / reflected by actionbar.js); the trigger reads as resting until hovered. 24.1 */

/* (c) The three UPWARD popover menus — a --ui-surface panel anchored to its slot,
   opening UPWARD (bottom-anchored, growing toward the map). actionbar.js toggles
   BOTH `hidden` (layout in/out) and `.is-open` (the transform/opacity transition).
   --ui-radius-lg top corners, --ui-elev-soft. Exactly one open at a time is JS
   discipline; here we style the open/closed visual. 24.1 */
.dion-actionbar__menu {
  position: absolute;
  bottom: calc(100% + var(--ui-space-sm));
  left: 0;
  right: 0;
  z-index: 1; /* above sibling triggers within the bar's stacking context */
  margin: 0;
  padding: var(--ui-space-sm);
  background: var(--ui-surface);
  border-radius: var(--ui-radius-lg) var(--ui-radius-lg) var(--ui-radius-md) var(--ui-radius-md);
  box-shadow: var(--ui-elev-soft);
  color: var(--ui-on-surface);
  /* Closed-but-not-hidden frame (the [hidden] attr removes it from layout; .is-open
     drives the reveal). When momentarily shown without .is-open it reads collapsed. */
  opacity: 0;
  transform: translateY(var(--ui-space-sm));
  transition: opacity var(--ui-motion-fast, 160ms) ease,
    transform var(--ui-motion-fast, 160ms) ease;
}

.dion-actionbar__menu.is-open {
  opacity: 1;
  transform: translateY(0);
}

/* The menu section caption — Heading role (per the UI-SPEC typography table). 24.1 */
.dion-actionbar__menu-caption {
  margin: 0 0 var(--ui-space-xs);
  padding: 0 var(--ui-space-sm);
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface-muted);
}

/* ── National alerts menu (27-07 Wave A) ─────────────────────────────────────
   The ⚠️ trigger carries a small count badge when warnings are active; the menu
   lists each warning as a level pill + headline, or an honest empty/loading line. */
.dion-actionbar__slot--alerts .dion-actionbar__alerts {
  position: relative;
}
/* 27-10 (operator UAT + Stitch design): the count is a SEPARATE TRAILING CHIP after the
   label — never an absolute badge overlapping the "Alerts" text (the old "A●rts" overlap).
   A normal inline-flex pill at the end of the button row. */
.dion-actionbar__alerts-badge {
  margin-left: var(--ui-space-xs);
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--ui-radius-pill);
  background: #d2691e; /* warning orange — a status-count badge, never the score amber */
  color: #fff;
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
}
.dion-actionbar__alerts-badge[hidden] {
  display: none;
}
/* 27-10 (operator UAT): the alerts badge sits near the TOP of the screen, so its menu must
   open DOWNWARD — the actionbar's upward default (bottom:100%) pushed it off the top edge
   (clipped on the Y-axis). Override JUST the alerts menu to anchor BELOW the trigger and
   give it a proper panel width that stays on-screen. */
.dion-actionbar__menu--alerts {
  top: calc(100% + var(--ui-space-sm));
  bottom: auto;
  right: auto;
  min-width: 248px;
  max-width: 86vw;
  transform: translateY(calc(-1 * var(--ui-space-sm)));
  border-radius: var(--ui-radius-md) var(--ui-radius-md) var(--ui-radius-lg) var(--ui-radius-lg);
}
.dion-actionbar__menu--alerts.is-open {
  transform: translateY(0);
}
.dion-actionbar__alerts-list {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-xs);
  max-height: 50vh;
  overflow-y: auto;
}
.dion-actionbar__alerts-empty {
  margin: 0;
  padding: var(--ui-space-sm) var(--ui-space-md);
  color: var(--ui-on-surface-muted);
  font-style: italic;
  font-size: var(--ui-font-size-label);
}
.dion-actionbar__alert-row {
  display: flex;
  align-items: baseline;
  gap: var(--ui-space-sm);
  margin: 0;
  padding: var(--ui-space-sm) var(--ui-space-md);
  border-radius: var(--ui-radius-sm);
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
}
.dion-actionbar__alert-row + .dion-actionbar__alert-row {
  border-top: 1px solid var(--ui-surface-container-high);
}
.dion-actionbar__alert-level {
  flex: 0 0 auto;
  padding: 1px 7px;
  border-radius: var(--ui-radius-pill);
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #fff;
}
.dion-actionbar__alert-level--yellow { background: #b8860b; }
.dion-actionbar__alert-level--orange { background: #d2691e; }
.dion-actionbar__alert-level--red { background: #b00020; }
.dion-actionbar__alert-text {
  flex: 1 1 auto;
  word-break: break-word;
}

/* A menu row — ≥44px target, leading glyph · Body-role label · trailing ✓. The
   ✓ is hidden until the row is active (.is-active, reflected by actionbar.js from
   dion:*-change); the active row also carries the --ui-surface-container-high tint
   (non-colour-only: tint + check). 24.1 */
.dion-actionbar__row {
  display: flex;
  align-items: center;
  gap: var(--ui-space-sm);
  width: 100%;
  min-height: 44px;
  padding: var(--ui-space-sm) var(--ui-space-md);
  border: none;
  border-radius: var(--ui-radius-sm);
  background: transparent;
  color: var(--ui-on-surface);
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-body);
  font-weight: var(--ui-font-weight-body);
  line-height: var(--ui-line-height-body);
  text-align: left;
  cursor: pointer;
}

.dion-actionbar__row:hover {
  background: var(--ui-surface-container-high);
}

.dion-actionbar__row-label {
  flex: 1 1 auto;
}

/* The trailing check — green --ui-primary (NEVER amber), only on the active row. 24.1 */
.dion-actionbar__check {
  color: var(--ui-primary);
  font-weight: var(--ui-font-weight-label);
  visibility: hidden;
}

.dion-actionbar__row.is-active,
.dion-actionbar__row[aria-checked='true'] {
  background: var(--ui-surface-container-high);
}

.dion-actionbar__row.is-active .dion-actionbar__check,
.dion-actionbar__row[aria-checked='true'] .dion-actionbar__check {
  visibility: visible;
}

/* The disabled transit row — muted, no pointer, the honest "ag teacht / coming
   soon" note in --ui-on-surface-muted; never a fabricated number (honesty). 24.1 */
.dion-actionbar__row--disabled,
.dion-actionbar__row[aria-disabled='true'] {
  cursor: default;
  color: var(--ui-on-surface-muted);
}

.dion-actionbar__row--disabled:hover {
  background: transparent;
}

.dion-actionbar__row-note {
  margin-left: auto;
  font-size: var(--ui-font-size-label);
  color: var(--ui-on-surface-muted);
}

/* ── Unified language dropdown (260630-lbo round-3) ──────────────────────────
   ONE control — a trigger (current flag + native name + chevron) that opens a
   role=listbox popover of all 7 locales — replacing the old GA/EN row + the
   flag-tile grid. Set off from the theme row by a hairline. */
.dion-actionbar__langpick {
  position: relative;
  margin-top: var(--ui-space-xs);
  padding-top: var(--ui-space-xs);
  border-top: 1px solid var(--ui-surface-container-high);
}
.dion-actionbar__menu-subcaption {
  margin: 0 0 var(--ui-space-xs);
  padding: 0 var(--ui-space-sm);
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface-muted);
}
.dion-actionbar__lang-trigger {
  display: flex;
  align-items: center;
  gap: var(--ui-space-sm);
  width: 100%;
  min-height: 48px;
  padding: var(--ui-space-sm) var(--ui-space-md);
  border: 1.5px solid var(--ui-surface-container-high);
  border-radius: var(--ui-radius-md);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-body);
  font-weight: var(--ui-font-weight-label);
  cursor: pointer;
}
.dion-actionbar__lang-trigger:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}
.dion-actionbar__langpick.is-open .dion-actionbar__lang-trigger {
  border-color: var(--ui-primary);
}
.dion-actionbar__lang-trigflag {
  display: inline-flex;
  flex: 0 0 auto;
}
.dion-actionbar__lang-trigname {
  flex: 1 1 auto;
  text-align: left;
}
.dion-actionbar__lang-chevron {
  flex: 0 0 auto;
  color: var(--ui-on-surface-muted);
  transition: transform var(--ui-motion-fast, 160ms) ease;
}
.dion-actionbar__langpick.is-open .dion-actionbar__lang-chevron {
  transform: rotate(180deg);
}
.dion-actionbar__lang-list {
  list-style: none;
  margin: var(--ui-space-xs) 0 0;
  padding: var(--ui-space-xs);
  max-height: 42vh;
  overflow-y: auto;
  background: var(--ui-surface);
  border: 1px solid var(--ui-surface-container-high);
  border-radius: var(--ui-radius-md);
  box-shadow: var(--ui-elev-soft);
}
.dion-actionbar__lang-list[hidden] {
  display: none;
}
.dion-actionbar__lang-option {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--ui-space-sm);
  min-height: 44px;
  padding: var(--ui-space-sm) var(--ui-space-md);
  border-radius: var(--ui-radius-sm);
  color: var(--ui-on-surface);
  font-size: var(--ui-font-size-body);
  cursor: pointer;
}
.dion-actionbar__lang-option:hover {
  background: var(--ui-surface-container-high);
}
.dion-actionbar__lang-option[aria-selected='true'] {
  background: var(--ui-surface-container-high);
}
.dion-actionbar__lang-option[aria-selected='true'] .dion-actionbar__lang-name {
  font-weight: var(--ui-font-weight-heading);
}
.dion-actionbar__lang-flag {
  display: block;
  object-fit: contain;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.18);
}
.dion-actionbar__lang-globe {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 20px;
  font-size: 18px;
  line-height: 1;
}
.dion-actionbar__lang-name {
  flex: 1 1 auto;
  white-space: nowrap;
}
.dion-actionbar__lang-check {
  flex: 0 0 auto;
  color: var(--ui-primary);
  font-weight: var(--ui-font-weight-label);
  visibility: hidden;
}
.dion-actionbar__lang-option[aria-selected='true'] .dion-actionbar__lang-check {
  visibility: visible;
}

/* (d) The floating díon wordmark — top-left over the live map, Heading role, a soft
   text-shadow scrim for legibility over a varied basemap (no opaque fill). Clear of
   the bar (it sits at the top). 24.1 */
.dion-wordmark {
  position: fixed;
  top: var(--ui-space-md);
  left: var(--ui-space-md);
  z-index: 800;
  margin: 0;
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-heading);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-heading);
  color: var(--ui-on-surface);
  text-shadow: 0 1px 3px var(--ui-scrim);
  /* 27-06: the wordmark is now the About affordance (role=button). It must take pointer
     + keyboard events; it is a small fixed mark in the top-left corner clear of the map
     interaction area, so it no longer needs pointer-events:none to protect map clicks. */
  pointer-events: auto;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.dion-wordmark:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 3px;
  border-radius: var(--ui-radius-sm);
}

/* (e) Responsive — ≥768px: more spacing, the bar MAY center its controls, text
   labels gain room (no truncation), menus become compact anchored popovers (not
   full-bleed). The existing @media (min-width:768px) breakpoint convention. 24.1 */
@media (min-width: 768px) {
  .dion-actionbar {
    gap: var(--ui-space-lg);
    justify-content: center;
    padding: var(--ui-space-sm) var(--ui-space-xl);
    padding-bottom: calc(var(--ui-space-sm) + env(safe-area-inset-bottom));
  }

  /* On desktop the slots are content-sized (not stretched full-bleed) and centered. */
  .dion-actionbar__info,
  .dion-actionbar__slot {
    flex: 0 1 auto;
    min-width: 120px;
  }

  .dion-actionbar__label {
    white-space: normal;
  }

  /* Compact anchored popover (not full-width) — a comfortable min, left-anchored. */
  .dion-actionbar__menu {
    right: auto;
    min-width: 240px;
  }

  .dion-wordmark {
    top: var(--ui-space-xl);
    left: var(--ui-space-xl);
  }
}

/* prefers-reduced-motion: drop the menu open/close animation; the open/close
   itself (via [hidden]/.is-open) still works, just without the transition. 24.1 */
@media (prefers-reduced-motion: reduce) {
  .dion-actionbar__menu {
    transition: none;
  }
}

/* (b) About modal — a true CENTERED modal (showModal(), so a real ::backdrop
   scrim). Not a sheet: no grab handle. Generous rhythm, the conviction line in
   the Heading role, the Dún/Close primary button in --ui-primary; ≥44px close
   target; legible in dark via the --ui-* re-point (no invert). slice-1 */
.dion-about {
  max-width: 340px;
  margin: auto; /* centered in the viewport for showModal() */
  border: none;
  border-radius: var(--ui-radius-lg);
  box-shadow: var(--ui-elev-soft);
  background: var(--ui-surface);
  color: var(--ui-on-surface);
  padding: var(--ui-space-2xl) var(--ui-space-xl);
  text-align: center;
}

.dion-about::backdrop {
  background: var(--ui-scrim);
}

.dion-about__logo {
  font-family: var(--ui-font-display);
  font-weight: var(--ui-font-weight-heading);
  font-size: 1.75rem;
  color: var(--ui-primary);
  margin-bottom: var(--ui-space-lg);
}

/* The conviction line — Heading role (the soul of the modal). slice-1 */
.dion-about__conviction {
  font-family: var(--ui-font-display);
  font-size: var(--ui-font-size-heading);
  font-weight: var(--ui-font-weight-heading);
  line-height: var(--ui-line-height-heading);
  color: var(--ui-on-surface);
  margin: 0 0 var(--ui-space-md);
}

/* Body copy — Body role, calm running text. slice-1 */
.dion-about__body {
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-body);
  font-weight: var(--ui-font-weight-body);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
  margin: 0 0 var(--ui-space-lg);
}

/* Version + dion.ie — muted Label, an honest static fact (never accented). slice-1 */
.dion-about__version {
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface-muted);
  margin: 0 0 var(--ui-space-xl);
}

/* Primary Dún/Close button — green --ui-primary, ≥44px target. slice-1 */
.dion-about__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--ui-space-xl);
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-primary);
  color: var(--ui-surface);
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  cursor: pointer;
}

.dion-about__close:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}

/* ══ Stripped home — scores DEFERRED, not removed (UIX-08, Plan 24-06, slice-1) ══
   The Phase-24 deploy-first UAT found the home was NOT stripped: the Phase-23
   scoring/criteria/reachability/mode-tabs panel (#criteria-panel) + its 🎚️ open
   button (.panel-toggle) were still fully visible over the new chrome (the Phase-22
   "frankenstein mix"). This block HIDES both so the home is a full-bleed map +
   transparent header + bottom-left Key + bottom-right zoom, and a plain map-click
   opens the INFO section only. SCORES ARE DEFERRED, NOT REMOVED — the #criteria-panel
   DOM + all of panel.js's render/compute wiring stay intact (re-introduced in a later
   slice); this is display:none only, never a markup/JS deletion. The deferred-score
   amber signature stays absent (no score-accent token here, UIX-11). RESTYLE/
   HIDE ONLY — score.js / aggregate.js / orchestrate.js / map.js / dion-gl-style.js /
   popup.js + dionScoreClick stay byte-untouched. The amber-absence guard scans this
   slice-1-marked region and stays GREEN. slice-1 */

/* The deferred Phase-23 scoring panel — present in the DOM (panel.js still renders
   into it), but NOT displayed this slice. The override beats the stíl.css
   .criteria-panel layout + the @media bottom-sheet skin above (this rule loads later
   and is unconditional). slice-1 */
#criteria-panel,
.criteria-panel {
  display: none !important;
}

/* The 🎚️ "Open criteria" button — the panel it controls is hidden this slice, so the
   button is removed from the visible stripped header (and from the mobile FAB slot the
   restyle gave it above). Hidden in-DOM only; its aria-controls + the script.js wiring
   are untouched (never re-wired) so a later slice can re-show it. slice-1 */
.panel-toggle {
  display: none !important;
}

/* (c) Key (bottom-left) + zoom (bottom-right) as persistent floating controls.
   RESTYLE ONLY — position is unchanged (Key already bottomleft via legend.js,
   zoom already bottomright via map.js). The .dion-key container reads as a raised
   floating chip in the warm-clean system; the Leaflet zoom +/− buttons are
   restyled to match (≥44px targets, Label-role text). Token-only so dark
   re-points (no invert). slice-1 */
.dion-key {
  border-radius: var(--ui-radius-lg);
  box-shadow: var(--ui-elev-soft);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  padding: var(--ui-space-sm);
  font-family: var(--ui-font-body);
}

.dion-key__label {
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface);
}

/* Each Key row meets the ≥44px touch floor. slice-1 */
.dion-key__row {
  min-height: 44px;
}

/* Leaflet zoom control — restyle the +/− buttons as a floating raised control
   (bottom-right; map.js owns the position). ≥44px targets, warm-clean surface.
   slice-1 */
.leaflet-control-zoom {
  border: none;
  border-radius: var(--ui-radius-md);
  box-shadow: var(--ui-elev-soft);
  overflow: hidden;
}

.leaflet-control-zoom a.leaflet-control-zoom-in,
.leaflet-control-zoom a.leaflet-control-zoom-out {
  width: 44px;
  height: 44px;
  line-height: 44px;
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  border: none;
  font-size: 1.2rem;
}

.leaflet-control-zoom a.leaflet-control-zoom-in:hover,
.leaflet-control-zoom a.leaflet-control-zoom-out:hover {
  background: var(--ui-primary-container);
  color: var(--ui-on-surface);
}

/* (f) REFLOW (UIX-15, Plan 24.1-04) — lift the three Leaflet corner controls (the
   bottom-left Key, the bottom-right zoom, the attribution credit) to a RESTING offset
   ABOVE the 64px + safe-area bottom action bar. The offset is set on `bottom` — a
   property the body.card-open `transform` (below) does NOT reset — so the card-open
   translate STACKS additively on top (Pitfall 3: stack, never replace). The constant
   is the single layout token --dion-actionbar-h (declared :root, set by this plan);
   everything else rides the 4-pt scale + env(safe-area-inset-bottom) (Pitfall 4). Pure
   CSS, no JS measurement; token-only so dark re-points. 24.1 */
.leaflet-bottom.leaflet-left,
.leaflet-bottom.leaflet-right,
.leaflet-control-attribution {
  /* 27-06 (operator UAT "the key and zoom controls are too high up"): the +64px
     --dion-actionbar-h lift cleared a BOTTOM action bar, but the info-only build parked
     the only remaining control (the ⚙️ gear) TOP-RIGHT (see the block at ~1499), so there
     is no bottom shelf to clear. Drop the stale bar term and rest the Key + zoom just above
     the bottom edge (safe-area + an 8-pt gap). The card-open transform still stacks on this. */
  bottom: calc(env(safe-area-inset-bottom) + var(--ui-space-sm));
}

/* Portrait overview (operator UAT 2026-07-02): the whole-region z5 view drops Brittany into the
   bottom-RIGHT corner — exactly where the zoom control sits — and the phone viewport is TALLER than
   the map's maxBounds, so the framing cannot shift up to lift Brittany clear. The Key + Layers are
   hidden at the overview, leaving the bottom-LEFT (open Atlantic) free — so move the zoom control
   there, clear of every landmass. Scoped to body.dion-overview (nations.js toggles it at Leaflet
   zoom ≤ 5), so a zoomed-in nation restores the control to the bottom-right. Portrait only; desktop's
   wider overview keeps Brittany clear of the corner already. */
@media (max-width: 767px) {
  body.dion-overview .leaflet-bottom.leaflet-right {
    right: auto;
    left: calc(env(safe-area-inset-left) + var(--ui-space-sm));
  }
}

/* (d) OVERLAP RULE (UIX-11, Plan 24-04) — body.card-open translates the two Leaflet
   corner containers clear of the open INFO section, NO JS measurement (Pattern 2:
   a body state class drives a CSS transform; panel.js only toggles the class on
   open/close). The map stays interactive (the card is non-modal .show() — no
   ::backdrop), so the controls merely SLIDE; they are never occluded.

   The corner containers are the Leaflet defaults: .leaflet-bottom.leaflet-left holds
   the Key (legend.js bottomleft control), .leaflet-bottom.leaflet-right holds the zoom
   (map.js bottomright control). The translate distances are CSS constants keyed to the
   sheet height / panel width tokens (NOT measured) — confirm against the Stitch screens
   at the Wave-4 deploy-first UAT. This block is geometry only, spends no amber. slice-1 */

/* Mobile portrait (< 768px): the section floats up as a ~33% bottom-sheet, so BOTH
   bottom corners ride up above its top edge (translateY up). 34vh clears the ~33%
   sheet with a small margin. */
@media (max-width: 767px) {
  body.card-open .leaflet-bottom.leaflet-left,
  body.card-open .leaflet-bottom.leaflet-right {
    transform: translateY(-47vh);
    transition: transform var(--ui-motion-fast, 160ms) ease;
  }
}

/* Desktop / ≥768px: the section enters as a 1/3-width side-panel anchored to the RIGHT
   (see the responsive reflow below). The Key (bottom-LEFT) is on the opposite side and
   is NOT occluded, so it stays put; only the zoom (bottom-RIGHT) sits under the panel
   column — slide it LEFT clear of the 1/3 panel (translateX = -34vw clears a 33vw
   panel + gutter). */
@media (min-width: 768px) {
  body.card-open .leaflet-bottom.leaflet-right {
    transform: translateX(-34vw);
    transition: transform var(--ui-motion-fast, 160ms) ease;
  }
}

/* prefers-reduced-motion: KEEP the reposition (the controls must still clear the open
   section — it is not decoration), but DROP the animated transition (instant move). */
@media (prefers-reduced-motion: reduce) {
  body.card-open .leaflet-bottom.leaflet-left,
  body.card-open .leaflet-bottom.leaflet-right {
    transition: none;
  }
}

/* (e) INFO-section content (UIX-09/10/11, Plan 24-04) — the place-info.js builder's
   class hooks (.place-info-name heading, .place-info-emoji Leanbh tile, the
   .place-info-row field rows, .copy-chip, .place-info-unavailable muted honest line).
   Token-only (--ui-*) so body.dark-mode re-points every surface (no invert, the
   Phase-22 lesson). Captions are single-language at runtime (relocalized via the
   dion:lang-change path — no bilingual ' / '). NO amber here — facts, never a score.
   slice-1 */

/* The INFO header block (Gnáth/Castas, Stitch B/C) — the place-type TILE beside a
   stacked worded-type chip + name heading + ≈N m disclosure. slice-1 */
.dion-card__body .place-info-header {
  display: flex;
  align-items: flex-start;
  gap: var(--ui-space-md);
  margin: 0 var(--ui-space-2xl) var(--ui-space-md) 0;
}

/* The place-type tile — the emoji in a soft green-tinted rounded square (the primary
   tint, inside the palette — no new token). Matches Stitch B/C. */
.dion-card__body .place-info-tile {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: var(--ui-radius-md);
  background: var(--ui-primary-container);
  font-size: 28px;
  line-height: 1;
}

/* The stacked head text (chip + heading + approx) right of the tile. */
.dion-card__body .place-info-headtext {
  flex: 1 1 auto;
  min-width: 0;
}

/* The worded-type chip — a small uppercase green pill (Stitch B "PÁIRC / PARK").
   Reads single-language at runtime; green tint, never amber (a type label, not a
   score). */
.dion-card__body .place-info-type-chip {
  display: inline-block;
  padding: 2px var(--ui-space-sm);
  border-radius: var(--ui-radius-pill);
  background: var(--ui-primary-container);
  color: var(--ui-primary);
  font-family: var(--ui-font-body);
  font-size: 11px;
  font-weight: var(--ui-font-weight-label);
  line-height: 1.6;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The INFO heading — the BOLD EDITORIAL headline face (27-08). Large, tight, confident
   (Bricolage Grotesque). Sits under the type chip; the block reserves room for the ✕. */
.dion-card__body .place-info-name {
  margin: var(--ui-space-xs) 0 0;
  font-family: var(--ui-font-headline);
  font-weight: 800;
  font-size: 1.9rem;
  line-height: 1.02;
  letter-spacing: -0.02em;
}

/* The ≈N m honesty disclosure — muted Label, never accented (the honest line is
   low-emphasis, never an error red, never amber). */
.dion-card__body .place-info-approx {
  margin: var(--ui-space-xs) 0 0;
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  line-height: var(--ui-line-height-label);
  color: var(--ui-on-surface-muted);
}

/* ── Leanbh (Stitch D) — the big centered picture-first child view ───────────── */
.dion-card__body .place-info-leanbh {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--ui-space-sm);
  padding-top: var(--ui-space-md);
}

/* Leanbh emoji type tile — a BIG picture-first glyph in a soft green container (the
   maximal-simplicity child view; no copy, no rows). Matches Stitch D. */
.dion-card__body .place-info-emoji {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 128px;
  height: 128px;
  border-radius: var(--ui-radius-xl);
  background: var(--ui-primary-container);
  font-size: 72px;
  line-height: 1;
}

/* Leanbh worded type — muted uppercase Label under the tile (Stitch D "PARK"). */
.dion-card__body .place-info-leanbh-type {
  margin: var(--ui-space-sm) 0 0;
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  line-height: var(--ui-line-height-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ui-on-surface-muted);
}

/* In Leanbh the name heading is centered + large (Stitch D), no right inset. */
.dion-card__body .place-info-leanbh .place-info-name {
  margin: 0;
  font-size: var(--ui-font-size-display);
}

/* The metadata rows container (Gnáth/Castas) — divider-separated inline rows
   (Stitch "Place Info — Grid Tiles" metadata section). 27-07 Wave A. */
.dion-card__body .place-info-rows {
  margin-top: var(--ui-space-md);
}

/* A metadata row (Wave A bento layout): an icon, an inline `Label: value`, and a copy
   chip pushed to the right edge; a hairline top divider between rows. ≥44px so the chip
   meets the touch floor. Only PRESENT fields render (empties stripped in place-info.js). */
.dion-card__body .place-info-row {
  display: flex;
  align-items: center;
  gap: var(--ui-space-sm);
  min-height: 44px;
  padding: var(--ui-space-sm) 0;
  border-top: 1px solid var(--ui-surface-container-high);
}

.dion-card__body .place-info-rows .place-info-row:first-child {
  border-top: none;
}

/* The leading row icon — muted, decorative (Stitch location_on / explore glyphs). */
.dion-card__body .place-info-row-icon {
  flex: 0 0 auto;
  font-size: 0.95rem;
  line-height: 1;
  opacity: 0.55;
}

/* The inline `Label: value` middle — takes the remaining width, wraps long values. */
.dion-card__body .place-info-meta {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-body);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
  word-break: break-word;
}

/* The caption (Locality / Coordinates …) — single-language, medium-emphasis inline. */
.dion-card__body .place-info-label {
  font-weight: var(--ui-font-weight-label);
  color: var(--ui-on-surface-muted);
}

/* The field value — full-emphasis on-surface, inline after the label. */
.dion-card__body .place-info-value {
  color: var(--ui-on-surface);
}

/* The honest-unavailable inline span (níl ar fáil / not available) — muted italic,
   never an error treatment: an absent field is an honest degrade, not a failure. */
.dion-card__body .place-info-unavailable {
  color: var(--ui-on-surface-muted);
  font-style: italic;
}

/* The copy chip — a 44px native <button> (the touch floor), neutral container glyph,
   green focus ring (never amber). On a resolved copy it latches .copied (set by
   confirmCopy in place-info.js) → a brief --ui-primary tint so the ✓ reads as success
   without colour-only reliance (the glyph itself changes to ✓). */
.dion-card__body .copy-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.dion-card__body .copy-chip:hover {
  background: var(--ui-primary-container);
}

.dion-card__body .copy-chip:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}

/* The confirmed state — a calm green tint + a transition so the ✓ does not pop. The
   glyph swap (⧉→✓) is the primary non-colour-only success signal. */
.dion-card__body .copy-chip.copied {
  background: var(--ui-primary-container);
  color: var(--ui-primary);
  transition: background-color 160ms ease, color 160ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .dion-card__body .copy-chip.copied {
    transition: none;
  }
}

/* ── (e2) BENTO GRID enrichment tiles (27-07 Wave A, Stitch "Place Info — Grid Tiles")
   The weather/score/biodiversity/lore enrichment renders as a grid of soft rounded
   tiles instead of stacked rows: Weather + Scór as squares side-by-side, Biodiversity +
   Lore full-width. Token-only chrome with soft translucent tints (blue/amber/emerald)
   that read on light AND dark (the no-invert lesson). Only POINT-ACCURATE tiles render
   (place-info.js omits absent sources) — never an empty tile. */
.dion-card__body .place-info-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ui-space-sm);
  margin: var(--ui-space-md) 0 0;
}

/* A tile card — soft rounded container; the square tiles (weather/score) keep a ~1:1
   aspect, the full-width tiles (bio/lore/warn) span both columns. */
.dion-card__body .place-info-tile-card {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-sm);
  padding: var(--ui-space-md);
  border-radius: var(--ui-radius-md);
  background: var(--ui-surface-container-high);
  border: 1px solid transparent;
  min-width: 0;
}
.dion-card__body .place-info-tile-weather,
.dion-card__body .place-info-tile-score {
  aspect-ratio: 1 / 1;
  justify-content: space-between;
}
.dion-card__body .place-info-tile-bio,
.dion-card__body .place-info-tile-lore,
.dion-card__body .place-info-tile-warn {
  grid-column: 1 / -1;
}

/* Soft per-tile tints (translucent → light + dark safe, the stíl.css warn idiom). */
.dion-card__body .place-info-tile-weather {
  background: rgba(56, 125, 196, 0.10);
  border-color: rgba(56, 125, 196, 0.16);
}
.dion-card__body .place-info-tile-score {
  background: rgba(224, 146, 46, 0.10);
  border-color: rgba(224, 146, 46, 0.18);
}
.dion-card__body .place-info-tile-bio {
  background: rgba(46, 160, 96, 0.10);
  border-color: rgba(46, 160, 96, 0.16);
}

/* 27-r7 (operator UAT P3 "lopsided species layout"): the biodiversity block is FULL-WIDTH.
   renderBiodiversity wraps its habitat sections in .place-info-bio so the 2-col bento parent
   (.place-info-grid) can never split a habitat HEADING into one cell and its tiles into the
   other. Inside, each habitat word is a full-width section heading ABOVE a balanced 2-up
   photo-tile grid (matches the Stitch reference). Token colours only → light + dark safe. */
.dion-card__body .place-info-bio {
  grid-column: 1 / -1;
}
.dion-card__body .place-info-habitat-word {
  grid-column: 1 / -1; /* defensive: stays full-width even if ever placed directly in a grid */
  margin: var(--ui-space-md) 0 var(--ui-space-sm);
  font-family: var(--ui-font-headline);
  font-weight: 800;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ui-primary);
}
.dion-card__body .place-info-bio > .place-info-habitat-word:first-child {
  margin-top: 0;
}
.dion-card__body .place-info-habitat-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ui-space-sm);
}
.dion-card__body .place-info-nature-caveat {
  margin: var(--ui-space-md) 0 0;
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface-muted);
}
.dion-card__body .place-info-nature-licence {
  margin: var(--ui-space-xs) 0 0;
  font-size: 10px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ui-on-surface-muted);
}

/* The tile head — a glyph + an uppercase localized key. */
.dion-card__body .place-info-tile-head {
  display: flex;
  align-items: center;
  gap: var(--ui-space-xs);
}
.dion-card__body .place-info-tile-glyph {
  font-size: 1.25rem;
  line-height: 1;
}
.dion-card__body .place-info-tile-key {
  font-family: var(--ui-font-body);
  font-size: 11px;
  font-weight: var(--ui-font-weight-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ui-on-surface-muted);
}
.dion-card__body .place-info-tile-head--bio .place-info-tile-key {
  color: var(--ui-primary);
}

/* Weather tile body — big temperature figure, a summary, muted footnotes. */
.dion-card__body .place-info-tile-figure {
  font-family: var(--ui-font-display, var(--ui-font-body));
  font-size: var(--ui-font-size-display);
  font-weight: 700;
  line-height: 1.05;
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-tile-sub {
  font-size: var(--ui-font-size-label);
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-tile-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ui-space-sm);
  margin-top: var(--ui-space-xs);
  font-size: 10px;
  font-weight: var(--ui-font-weight-label);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ui-on-surface-muted);
}

/* Scór tile — walk/kite/swim rows; the 0–10 figure in the amber score-accent (Stitch
   "accent for scores"). This is the LOCAL recreation suitability, NOT the travel score. */
.dion-card__body .place-info-score-list {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-sm);
  justify-content: center;
  flex: 1 1 auto;
}
.dion-card__body .place-info-score-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ui-space-sm);
}
.dion-card__body .place-info-score-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--ui-font-size-label);
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-score-val {
  font-family: var(--ui-font-display, var(--ui-font-body));
  font-size: 1.15rem;
  font-weight: 800;
  color: var(--ui-score-accent); /* .place-info-score-val — amber on the recreation score VALUE only (THEME-03) */
}

/* Biodiversity tile — the top groups as count columns with hairline dividers. */
.dion-card__body .place-info-bio-cols {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ui-space-sm);
}
.dion-card__body .place-info-bio-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  flex: 1 1 0;
  text-align: center;
}
.dion-card__body .place-info-bio-count {
  font-family: var(--ui-font-display, var(--ui-font-body));
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--ui-primary);
}
.dion-card__body .place-info-bio-name {
  font-size: 10px;
  font-weight: var(--ui-font-weight-label);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--ui-on-surface-muted);
}
.dion-card__body .place-info-bio-div {
  flex: 0 0 auto;
  align-self: stretch;
  width: 1px;
  background: var(--ui-surface-container-high);
}

/* Lore + (local) warning tiles — readable body text. */
.dion-card__body .place-info-lore-text {
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
}

/* ── LIVING SKY weather tile (27-08, approved Stitch design) ──────────────────
   A 2-colspan tile whose BACKGROUND is the time-of-day sky; the sun (day) or moon
   (night) sits on a daylight arc at the current position; the current state sits on top.
   The gradient band + text colour switch by time of day. */
.dion-card__body .place-info-tile-sky {
  grid-column: 1 / -1;
  position: relative;
  overflow: hidden;
  border: none;
  padding: var(--ui-space-md);
  min-height: 168px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  cursor: pointer;
}
.dion-card__body .place-info-sky--dawn { background: linear-gradient(175deg, #ffd9a8 0%, #ffc1a6 34%, #cfe0f0 100%); color: #1b1c1a; }
.dion-card__body .place-info-sky--day { background: linear-gradient(175deg, #a9d2f0 0%, #d6ebfa 55%, #eef5fb 100%); color: #1b1c1a; }
.dion-card__body .place-info-sky--dusk { background: linear-gradient(175deg, #ffae74 0%, #f0937e 42%, #7d72ac 100%); color: #fff; }
.dion-card__body .place-info-sky--night { background: linear-gradient(175deg, #232a64 0%, #171c46 58%, #0f1130 100%); color: #fff; }
.dion-card__body .place-info-sky--dusk .place-info-sky-now,
.dion-card__body .place-info-sky--night .place-info-sky-now { text-shadow: 0 1px 8px rgba(0, 0, 0, 0.35); }

/* The arc — a faint dashed ellipse (only its top shows) across the sky; the celestial
   body is positioned on the implied parabola via the inline left/bottom (trusted numbers). */
.dion-card__body .place-info-sky-arc {
  position: absolute;
  left: 8%;
  right: 8%;
  top: 12px;
  height: 86px;
  pointer-events: none;
}
.dion-card__body .place-info-sky-arc::before {
  content: '';
  position: absolute;
  inset: 0 0 -86px 0;
  border: 1px dashed currentColor;
  border-radius: 50%;
  opacity: 0.28;
}
/* 27-09 (operator UAT #6): the DISTINCT glowing current-time marker on the arc. The
   sun/moon glyph rides the arc at the now-position inside a pulsing glow, tagged "Now" —
   clearly separate from the big weather-condition icon below. */
.dion-card__body .place-info-sky-body {
  position: absolute;
  transform: translate(-50%, 50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  pointer-events: none;
}
.dion-card__body .place-info-sky-nowtag {
  font-size: 8px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 1px 6px;
  border-radius: var(--ui-radius-pill);
  background: rgba(0, 0, 0, 0.3);
  color: #fff;
  white-space: nowrap;
}
.dion-card__body .place-info-sky-nowmark {
  position: relative;
  font-size: 1.55rem;
  line-height: 1;
  filter: drop-shadow(0 0 9px rgba(255, 220, 140, 0.95));
  animation: place-info-now-pulse 2.4s ease-in-out infinite;
}
.dion-card__body .place-info-sky-nowmark::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 34px;
  height: 34px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 238, 170, 0.55) 0%, rgba(255, 238, 170, 0) 70%);
  z-index: -1;
}
.dion-card__body .place-info-sky--night .place-info-sky-nowmark { filter: drop-shadow(0 0 9px rgba(200, 215, 255, 0.9)); }
.dion-card__body .place-info-sky--night .place-info-sky-nowmark::before { background: radial-gradient(circle, rgba(200, 215, 255, 0.5) 0%, rgba(200, 215, 255, 0) 70%); }
@keyframes place-info-now-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.13); }
}
@media (prefers-reduced-motion: reduce) {
  .dion-card__body .place-info-sky-nowmark { animation: none; }
}

/* ════ 27-10 — the blended atmospheric SKY-HEADER (Stitch "Editorial Sheet") ═══════════
   Weather IS the card's full-bleed header background; the identity rests centred ON it; a
   condition-reactive effect layer reflects clear/cloud/rain/fog/snow/storm/windy. Full-bleeds
   to the card edges via negative margins matching the card padding (--ui-space-lg). The band
   gradient comes from the shared .place-info-sky--{dawn,day,dusk,night} rules above. */
.dion-card__body .place-info-skyhead {
  position: relative;
  isolation: isolate;
  margin: calc(-1 * var(--ui-space-lg)) calc(-1 * var(--ui-space-lg)) var(--ui-space-md);
  /* 27-r7 (operator UAT "crowded mess"): a COMPACT weather block, not a full-height band.
     The rows now STACK (identity · readout · arc band · sun ends · precip) with a tidy gap
     and a content-driven height — the old 232px min-height + layered absolutes are gone.
     The top padding keeps clearance for the absolute ℹ️/alert controls; bottom is trimmed. */
  padding: calc(var(--ui-space-lg) + var(--ui-space-xs)) var(--ui-space-md) var(--ui-space-sm);
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ui-space-sm);
  text-align: center;
  overflow: hidden;
  color: #fff;
}
.dion-card__body .place-info-skyhead.place-info-sky--day,
.dion-card__body .place-info-skyhead.place-info-sky--dawn { color: #1b1c1a; }
/* the condition effect layer sits BEHIND the content, over the band gradient */
.dion-card__body .place-info-sky-fx { position: absolute; inset: 0; z-index: -1; pointer-events: none; }

/* corner controls — ℹ️ + alert pill top-LEFT (the dialog ✕ owns top-right) */
.dion-card__body .place-info-skyhead-top {
  position: absolute;
  top: var(--ui-space-sm);
  left: var(--ui-space-sm);
  display: flex;
  align-items: center;
  gap: var(--ui-space-xs);
  z-index: 2;
}
.dion-card__body .place-info-skyhead-top .place-info-osm-btn { background: rgba(0, 0, 0, 0.22); border-radius: 50%; }
.dion-card__body .place-info-skyhead .place-info-sky-alert { position: static; }

/* centred identity ON the sky */
.dion-card__body .place-info-skyhead-id { z-index: 1; max-width: 100%; }
.dion-card__body .place-info-skyhead-id .place-info-type-chip {
  display: inline-block;
  margin-bottom: 6px;
  background: rgba(255, 255, 255, 0.24);
  color: inherit;
  backdrop-filter: blur(2px);
}
.dion-card__body .place-info-skyhead-id .place-info-nameline { display: flex; justify-content: center; align-items: center; gap: var(--ui-space-xs); }
.dion-card__body .place-info-skyhead-id .popup-name {
  text-align: center;
  margin: 0;
  color: inherit;
  font-size: 1.65rem; /* 27-r7: trimmed from 1.95rem for the compact header */
  line-height: 1.05;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.32);
}
.dion-card__body .place-info-skyhead-desc { margin: 5px 0 0; font-size: var(--ui-font-size-label); opacity: 0.92; }

/* GAP-27 round-6 #3 — surfaced OSM identity (description + Wikipedia link) under the heading.
   Renders when an unnamed tile feature carries real identity tags from Overpass. */
.dion-card__body .place-info-osmid {
  margin: var(--ui-space-sm) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-xs);
}
.dion-card__body .place-info-osmid-desc {
  margin: 0;
  font-size: var(--ui-font-size-label);
  line-height: 1.4;
  opacity: 0.92;
}
.dion-card__body .place-info-osmid-wiki {
  align-self: flex-start;
  font-size: var(--ui-font-size-label);
  font-weight: 600;
  color: var(--ui-color-accent, #1a73e8);
  text-decoration: none;
}
.dion-card__body .place-info-osmid-wiki:hover { text-decoration: underline; }

/* the weather readout (tap → detail view). 27-r7: the header gap owns the row rhythm now,
   so the old margin-top is dropped (it double-spaced under the compact stack). */
.dion-card__body .place-info-skyhead-wx {
  margin-top: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--ui-space-sm);
  padding: 0;
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  z-index: 1;
}
.dion-card__body .place-info-skyhead-wx .place-info-sky-icon { font-size: 1.9rem; line-height: 1; }
.dion-card__body .place-info-sky-readout { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.04; }
.dion-card__body .place-info-skyhead-wx .place-info-sky-temp { font-family: var(--ui-font-headline); font-weight: 800; font-size: 1.75rem; letter-spacing: -0.02em; }
.dion-card__body .place-info-skyhead-wx .place-info-sky-cond { font-size: var(--ui-font-size-label); font-weight: var(--ui-font-weight-label); }
.dion-card__body .place-info-skyhead-wx .place-info-sky-moon { font-size: 10px; opacity: 0.85; text-transform: uppercase; letter-spacing: 0.04em; }
.dion-card__body .place-info-skyhead-wx:hover { filter: brightness(1.08); }
.dion-card__body .place-info-skyhead-wx:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; border-radius: var(--ui-radius-sm); }

/* 27-r7 (operator UAT P1 "ANOIS/moon overlap"): the arc is its OWN in-flow band BELOW the
   weather readout — it no longer floats absolutely over the centred name/moon. The now-dot
   rides it HORIZONTALLY (leftPct = day progress) at the band's vertical centre (renderSkyHeader
   passes a fixed 50), so its "Anois/Now" tag can never rise into the moon glyph or the
   moon-phase caption above. identity + readout stay above via z-index. */
.dion-card__body .place-info-skyhead .place-info-sky-arc {
  position: relative;
  left: auto;
  right: auto;
  top: auto;
  bottom: auto;
  width: 82%;
  height: 30px;
  pointer-events: none;
}
/* a gentle dashed swoosh across the band (large radius → only the calm top curve shows);
   the border/dash/opacity come from the shared .place-info-sky-arc::before base rule. */
.dion-card__body .place-info-skyhead .place-info-sky-arc::before {
  inset: 2px 0 auto 0;
  height: 40px;
}
.dion-card__body .place-info-skyhead-id,
.dion-card__body .place-info-skyhead-wx { position: relative; z-index: 2; }
/* the neutral glowing NOW dot (replaces the duplicate sun/moon glyph on the arc) */
.dion-card__body .place-info-sky-nowdot {
  display: block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 10px 3px rgba(255, 255, 255, 0.85);
  animation: place-info-now-pulse 2.4s ease-in-out infinite;
}
.dion-card__body .place-info-sky--night .place-info-sky-nowdot { box-shadow: 0 0 10px 3px rgba(200, 215, 255, 0.9); }
@media (prefers-reduced-motion: reduce) {
  .dion-card__body .place-info-sky-nowdot { animation: none; }
}
/* 27-r7: sunrise/sunset now flow as a thin full-width row under the arc band (was an
   absolute strip at bottom:58px in the old tall header). */
.dion-card__body .place-info-skyhead .place-info-sky-ends {
  position: static;
  width: 100%;
  margin-top: 0;
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  font-weight: 700;
  opacity: 0.9;
  z-index: 1;
}
.dion-card__body .place-info-skyhead .place-info-precip { margin-top: 0; }

/* the next-8-hours precipitation strip at the foot of the header */
.dion-card__body .place-info-precip { width: 100%; margin-top: auto; z-index: 1; }
.dion-card__body .place-info-precip-row { display: flex; align-items: flex-end; gap: 3px; height: 28px; }
.dion-card__body .place-info-precip-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.dion-card__body .place-info-precip-bar { width: 62%; min-height: 2px; border-radius: 2px 2px 0 0; background: rgba(255, 255, 255, 0.4); }
.dion-card__body .place-info-skyhead.place-info-sky--day .place-info-precip-bar,
.dion-card__body .place-info-skyhead.place-info-sky--dawn .place-info-precip-bar { background: rgba(0, 0, 0, 0.18); }
.dion-card__body .place-info-precip-bar.is-wet { background: #5ba8f5; }
.dion-card__body .place-info-precip-hr { font-size: 8px; opacity: 0.78; margin-top: 2px; }

/* ── condition-reactive effect layers (subtle, GPU-light, prefers-reduced-motion safe) ── */
.dion-card__body .place-info-wx--cloudy .place-info-sky-fx,
.dion-card__body .place-info-wx--windy .place-info-sky-fx {
  background:
    radial-gradient(60% 40% at 22% 30%, rgba(255, 255, 255, 0.28), transparent 70%),
    radial-gradient(50% 36% at 75% 22%, rgba(255, 255, 255, 0.22), transparent 70%);
  animation: place-info-drift 26s linear infinite;
}
.dion-card__body .place-info-wx--fog .place-info-sky-fx {
  background: linear-gradient(180deg, rgba(220, 224, 228, 0.45), rgba(220, 224, 228, 0.12) 60%, transparent);
  backdrop-filter: blur(1px);
}
.dion-card__body .place-info-wx--rain .place-info-sky-fx,
.dion-card__body .place-info-wx--storm .place-info-sky-fx {
  background-image: repeating-linear-gradient(74deg, rgba(255, 255, 255, 0.22) 0 1px, transparent 1px 7px);
  background-size: 100% 200px;
  animation: place-info-rain 0.7s linear infinite;
}
.dion-card__body .place-info-wx--storm .place-info-sky-fx { box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.35); }
.dion-card__body .place-info-wx--snow .place-info-sky-fx {
  background-image:
    radial-gradient(2px 2px at 20% 10%, #fff, transparent),
    radial-gradient(2px 2px at 60% 30%, #fff, transparent),
    radial-gradient(2px 2px at 80% 60%, rgba(255,255,255,0.8), transparent),
    radial-gradient(2px 2px at 35% 75%, rgba(255,255,255,0.8), transparent);
  background-size: 100% 220px;
  animation: place-info-snow 6s linear infinite;
}
@keyframes place-info-drift { from { background-position: 0 0, 0 0; } to { background-position: 120px 0, -120px 0; } }
@keyframes place-info-rain { from { background-position: 0 0; } to { background-position: 0 200px; } }
@keyframes place-info-snow { from { background-position: 0 0; } to { background-position: 0 220px; } }
@media (prefers-reduced-motion: reduce) {
  .dion-card__body .place-info-sky-fx { animation: none !important; }
}

/* Current state — centered big matched icon + huge temp + condition. */
.dion-card__body .place-info-sky-now {
  position: relative;
  text-align: center;
  z-index: 1;
}
.dion-card__body .place-info-sky-icon { font-size: 2.4rem; line-height: 1; }
.dion-card__body .place-info-sky-temp {
  font-family: var(--ui-font-headline);
  font-weight: 800;
  font-size: 2.6rem;
  line-height: 1.02;
  letter-spacing: -0.02em;
}
.dion-card__body .place-info-sky-cond { font-size: var(--ui-font-size-label); font-weight: var(--ui-font-weight-label); }
.dion-card__body .place-info-sky-moon { font-size: 11px; opacity: 0.85; margin-top: 2px; text-transform: uppercase; letter-spacing: 0.04em; }

/* Compact weather-alert pill (the operator's "alert indicator"), top-right. */
.dion-card__body .place-info-sky-alert {
  position: absolute;
  top: var(--ui-space-sm);
  right: var(--ui-space-sm);
  z-index: 2;
  padding: 2px 9px;
  border-radius: var(--ui-radius-pill);
  background: rgba(0, 0, 0, 0.16);
  color: #fff;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dion-card__body .place-info-sky-alert-yellow { background: #b8860b; }
.dion-card__body .place-info-sky-alert-orange { background: #d2691e; }
.dion-card__body .place-info-sky-alert-red { background: #b00020; }

/* Sunrise / sunset times at the foot corners. */
.dion-card__body .place-info-sky-ends {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: space-between;
  margin-top: var(--ui-space-sm);
  font-size: 11px;
  font-weight: var(--ui-font-weight-label);
  opacity: 0.9;
}

/* ── Weather detail sub-view (27-08 I4) — breadcrumb, charts, kite gauge ──────── */
.dion-card__body .place-info-crumb {
  display: flex;
  align-items: center;
  gap: var(--ui-space-sm);
  margin-bottom: var(--ui-space-md);
}
.dion-card__body .place-info-back {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ui-surface-container-high);
  border-radius: var(--ui-radius-pill);
  background: transparent;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-back:hover { background: var(--ui-surface-container-high); }
.dion-card__body .place-info-back:focus-visible { outline: 2px solid var(--ui-primary); outline-offset: 2px; }
.dion-card__body .place-info-crumb-label {
  font-family: var(--ui-font-headline);
  font-weight: 800;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
}
.dion-card__body .place-info-wx-alert {
  margin: var(--ui-space-sm) 0;
  padding: var(--ui-space-sm) var(--ui-space-md);
  border-radius: var(--ui-radius-md);
  background: rgba(210, 105, 30, 0.13);
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-wx-alert.place-info-sky-alert-orange { background: rgba(210, 105, 30, 0.18); }
.dion-card__body .place-info-wx-alert.place-info-sky-alert-red { background: rgba(176, 0, 32, 0.16); }
.dion-card__body .place-info-wx-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ui-space-sm);
  margin-top: var(--ui-space-md);
}
.dion-card__body .place-info-wx-block {
  padding: var(--ui-space-md);
  border-radius: var(--ui-radius-md);
  background: var(--ui-surface-container-high);
}
.dion-card__body .place-info-wx-figure {
  font-family: var(--ui-font-headline);
  font-weight: 700;
  font-size: 1.05rem;
  margin: var(--ui-space-xs) 0;
}
/* mini bar charts (wind / precip over the next hours) */
.dion-card__body .place-info-chart { margin-top: var(--ui-space-sm); }
.dion-card__body .place-info-chart-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 48px;
}
.dion-card__body .place-info-bar {
  flex: 1 1 0;
  min-width: 0;
  border-radius: 3px 3px 0 0;
  background: var(--ui-primary);
  opacity: 0.85;
}
.dion-card__body .place-info-chart-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  font-size: 9px;
  color: var(--ui-on-surface-muted);
}
/* KITE 5-band gauge — coloured bars, current band raised + labelled */
.dion-card__body .place-info-kite { grid-column: 1 / -1; }
.dion-card__body .place-info-kite-bars {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 56px;
  margin: var(--ui-space-sm) 0;
}
.dion-card__body .place-info-kite-bar {
  flex: 1 1 0;
  border-radius: 4px 4px 0 0;
  opacity: 0.45;
  transition: opacity 120ms ease;
}
.dion-card__body .place-info-kite-bar.is-current { opacity: 1; box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.12) inset; }
.dion-card__body .place-info-kite-k0 { background: #b8bdb5; }
.dion-card__body .place-info-kite-k1 { background: #ccdf9e; }
.dion-card__body .place-info-kite-k2 { background: #a8d06a; }
.dion-card__body .place-info-kite-k3 { background: var(--ui-primary); }
.dion-card__body .place-info-kite-k4 { background: #e08a2e; }
.dion-card__body .place-info-kite-k5 { background: #c0392b; }
.dion-card__body .place-info-kite-now { font-weight: 800; font-size: var(--ui-font-size-label); }
.dion-card__body .place-info-kite-k0-text { color: #6b7065; }
.dion-card__body .place-info-kite-k1-text,
.dion-card__body .place-info-kite-k2-text { color: #5a7d2e; }
.dion-card__body .place-info-kite-k3-text { color: var(--ui-primary); }
.dion-card__body .place-info-kite-k4-text { color: #b06a14; }
.dion-card__body .place-info-kite-k5-text { color: #b00020; }
/* 28-04: dark re-points for the band-word -text hexes (k3 rides --ui-primary; the
   hard-coded k0/k1/k2/k4/k5 text hexes would strand illegible on dark surfaces —
   the dark-mode invert-removal trap). Brightened variants of the SAME band hues. */
body.dark-mode .dion-card__body .place-info-kite-k0-text { color: #9aa095; }
body.dark-mode .dion-card__body .place-info-kite-k1-text,
body.dark-mode .dion-card__body .place-info-kite-k2-text { color: #a8d06a; }
body.dark-mode .dion-card__body .place-info-kite-k4-text { color: #f0a45a; }
body.dark-mode .dion-card__body .place-info-kite-k5-text { color: #cf5c6b; }

/* ── Phase-28 (28-04) card blocks — marine tile · activity rows · danger · foot ──
   Token-scale only (--ui-space-*, --ui-radius-*, --ui-font-size-*); translucent
   tints are light+dark safe (the shipped tile-tint idiom at 2090); every
   hard-coded hex gets a body.dark-mode re-point. Nothing here is tappable (the
   gauge bars are decorative), so no new touch-target surface is introduced. */

/* Marine conditions tile — full-width (the locked tide-line shape needs the row). */
.dion-card__body .place-info-tile-marine {
  grid-column: 1 / -1;
  background: rgba(56, 125, 196, 0.10);
  border-color: rgba(56, 125, 196, 0.16);
}
.dion-card__body .place-info-marine-rows {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-sm);
}
.dion-card__body .place-info-marine-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ui-space-sm);
}
.dion-card__body .place-info-marine-label {
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  color: var(--ui-on-surface-muted);
}
.dion-card__body .place-info-marine-val {
  font-size: var(--ui-font-size-body);
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-tide {
  font-size: var(--ui-font-size-body);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
}
/* The D-28-C model-cell disclosure — in-tile footer, muted, never below Label size. */
.dion-card__body .place-info-marine-disc {
  margin-top: var(--ui-space-xs);
  font-size: var(--ui-font-size-label);
  color: var(--ui-on-surface-muted);
}

/* Activity-suitability tile — full-width rows; the amber accent stays reserved
   for .place-info-score-val (THEME-03, pinned above at 2172). */
.dion-card__body .place-info-tile-act {
  grid-column: 1 / -1;
  background: rgba(224, 146, 46, 0.10);
  border-color: rgba(224, 146, 46, 0.18);
}
.dion-card__body .place-info-act-list {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-md);
}
/* The per-activity gauge reuses the shipped kite band classes at a row-scale height. */
.dion-card__body .place-info-act .place-info-act-bars {
  height: 26px;
  gap: 4px;
  margin: var(--ui-space-xs) 0 2px;
}
.dion-card__body .place-info-act-word {
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  color: var(--ui-on-surface-muted);
}
/* Golf label-primary bucket: the band word is the read; the number is muted. */
.dion-card__body .place-info-act-bucket {
  display: inline-flex;
  align-items: baseline;
  gap: var(--ui-space-sm);
}
.dion-card__body .place-info-act-bucket-word {
  font-family: var(--ui-font-display, var(--ui-font-body));
  font-weight: 800;
  font-size: 1.05rem;
}
.dion-card__body .place-info-act-bucket-num {
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  color: var(--ui-on-surface-muted);
}

/* Danger signal — the warn-tile language, destructive-tinted; NO green/safe state
   exists (T-28-10) and the amber accent NEVER appears here. */
.dion-card__body .place-info-danger {
  background: rgba(176, 0, 32, 0.07);
  border-color: rgba(176, 0, 32, 0.14);
}
.dion-card__body .place-info-danger-lines {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-sm);
  font-size: var(--ui-font-size-body);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-danger-line {
  padding-left: var(--ui-space-sm);
  border-left: 3px solid var(--ui-on-surface-muted);
}
.dion-card__body .place-info-danger-line--danger {
  border-left-color: var(--ui-destructive);
  font-weight: 700;
}
.dion-card__body .place-info-danger-line--caution { border-left-color: #d2691e; }
body.dark-mode .dion-card__body .place-info-danger-line--caution { border-left-color: #e0904a; }
.dion-card__body .place-info-danger-line--note { color: var(--ui-on-surface-muted); }
/* The mandatory authority deferral — always last, muted body text. */
.dion-card__body .place-info-danger-defer {
  margin: var(--ui-space-sm) 0 0;
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface-muted);
}

/* Caption + timestamp foot — muted Label-size disclosure lines under the grid. */
.dion-card__body .place-info-foot {
  display: flex;
  flex-direction: column;
  gap: var(--ui-space-xs);
  margin-top: var(--ui-space-sm);
}
.dion-card__body .place-info-caption,
.dion-card__body .place-info-timestamp {
  margin: 0;
  font-size: var(--ui-font-size-label);
  line-height: var(--ui-line-height-body);
  color: var(--ui-on-surface-muted);
}

/* Motion: the reused gauge bars carry the shipped opacity transition — freeze it
   under reduced motion (extends the existing @media reduce discipline). */
@media (prefers-reduced-motion: reduce) {
  .dion-card__body .place-info-kite-bar { transition: none !important; }
}

/* ── Nature drill-down (27-09) — full-bleed species PHOTO tiles (Stitch 705ec7b3) +
   the species PHOTO CAROUSEL (Stitch c8e37832). Photos arrive async; each state (photo /
   loading skeleton / emoji fallback) is honest. ── */
.dion-card__body .place-info-nature-head { margin: var(--ui-space-sm) 0 var(--ui-space-md); }
.dion-card__body .place-info-nature-sub {
  margin: 2px 0 0;
  font-size: var(--ui-font-size-label);
  color: var(--ui-on-surface-muted);
}
.dion-card__body .place-info-nature-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ui-space-sm);
}
/* 27-r7 "Tuilleadh / Show more" reveal — a calm full-width pill (neutral token chrome,
   NEVER the amber score accent). Light + dark via --ui-* tokens. */
.dion-card__body .place-info-nature-more {
  display: block;
  width: 100%;
  margin-top: var(--ui-space-sm);
  padding: var(--ui-space-sm) var(--ui-space-md);
  border: 1px solid var(--ui-surface-container-high);
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-family: var(--ui-font-body);
  font-size: var(--ui-font-size-label);
  font-weight: var(--ui-font-weight-label);
  cursor: pointer;
}
.dion-card__body .place-info-nature-more:hover { filter: brightness(1.05); }
.dion-card__body .place-info-nature-more:focus-visible { outline: 2px solid var(--ui-primary); outline-offset: 2px; }
.dion-card__body .place-info-tile-naturetile {
  position: relative;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: flex-end;
  padding: 0;
  border: none;
  border-radius: var(--ui-radius-md);
  overflow: hidden;
  background: var(--ui-surface-container-high);
  text-align: left;
  cursor: pointer;
  font-family: var(--ui-font-body);
}
.dion-card__body .place-info-naturetile-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* a foot gradient so the name reads white over any photo (Stitch overlay). */
.dion-card__body .place-info-tile-naturetile.has-photo::after {
  content: '';
  position: absolute;
  inset: 38% 0 0 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.74) 100%);
}
.dion-card__body .place-info-naturetile-emoji {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.4rem;
}
.dion-card__body .place-info-naturetile-name {
  position: relative;
  z-index: 1;
  padding: var(--ui-space-md);
  font-family: var(--ui-font-headline);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.08;
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-tile-naturetile.has-photo .place-info-naturetile-name {
  color: #fff;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
}
/* the 'N recorded' count under the group name on the photo tile (27-10) */
.dion-card__body .place-info-naturetile-count {
  display: block;
  margin-top: 1px;
  font-family: var(--ui-font-body);
  font-weight: 500;
  font-size: 11px;
  opacity: 0.9;
}
.dion-card__body .place-info-tile-naturetile:hover { filter: brightness(1.05); }
.dion-card__body .place-info-tile-naturetile:focus-visible { outline: 2px solid var(--ui-primary); outline-offset: 2px; }
.dion-card__body .place-info-naturetile-skel { position: absolute; inset: 0; }
.dion-card__body .place-info-crumb-sep { margin: 0 6px; color: var(--ui-on-surface-muted); }

/* Species detail — the NAME above a real photo carousel, then a blurb + GBIF provenance. */
.dion-card__body .place-info-species { margin-top: var(--ui-space-sm); }
.dion-card__body .place-info-species-name { margin: 0 0 var(--ui-space-md); }
.dion-card__body .place-info-carousel { position: relative; }
.dion-card__body .place-info-carousel-track {
  display: flex;
  gap: var(--ui-space-sm);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  border-radius: var(--ui-radius-md);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.dion-card__body .place-info-carousel-track::-webkit-scrollbar { display: none; }
.dion-card__body .place-info-carousel-slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  aspect-ratio: 4 / 3;
  border-radius: var(--ui-radius-md);
  overflow: hidden;
  background: var(--ui-surface-container-high);
}
.dion-card__body .place-info-carousel-slide img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dion-card__body .place-info-carousel-dots { display: flex; justify-content: center; gap: 6px; margin-top: var(--ui-space-sm); }
.dion-card__body .place-info-carousel-dots span { width: 6px; height: 6px; border-radius: 50%; background: var(--ui-on-surface-muted); opacity: 0.5; }
.dion-card__body .place-info-species-photo {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  border-radius: var(--ui-radius-md);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface-muted);
}
.dion-card__body .place-info-species-emoji { font-size: 3.2rem; line-height: 1; }
.dion-card__body .place-info-species-extract {
  margin: var(--ui-space-md) 0 0;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ui-on-surface);
}
.dion-card__body .place-info-species-note {
  margin: var(--ui-space-sm) 0 0;
  font-size: var(--ui-font-size-label);
  color: var(--ui-on-surface-muted);
}

/* Shared shimmer skeletons — reserved-space tiles on first card open (operator UAT #5)
   + per-photo loading squares. A neutral translucent band so it works light + dark. */
.dion-card__body .place-info-skel,
.dion-card__body .place-info-naturetile-skel,
.dion-card__body .place-info-species-skel {
  background-image: linear-gradient(
    100deg,
    rgba(127, 127, 127, 0) 30%,
    rgba(127, 127, 127, 0.22) 50%,
    rgba(127, 127, 127, 0) 70%
  );
  background-color: var(--ui-surface-container-high);
  background-size: 220% 100%;
  background-repeat: no-repeat;
  animation: place-info-shimmer 1.3s linear infinite;
}
.dion-card__body .place-info-grid--loading { display: grid; grid-template-columns: 1fr 1fr; gap: var(--ui-space-sm); }
.dion-card__body .place-info-skel-sky { grid-column: 1 / -1; aspect-ratio: 2 / 1; border-radius: var(--ui-radius-md); }
.dion-card__body .place-info-skel-bio { grid-column: 1 / -1; height: 92px; border-radius: var(--ui-radius-md); }
@keyframes place-info-shimmer {
  0% { background-position: 220% 0; }
  100% { background-position: -220% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .dion-card__body .place-info-skel,
  .dion-card__body .place-info-naturetile-skel,
  .dion-card__body .place-info-species-skel { animation: none; }
}

/* The pinned LOCAL severe-warning band (above identity, D-05). */
.dion-card__body .place-info-alert-band {
  margin-bottom: var(--ui-space-md);
}

/* The header ℹ️ raw-OSM toggle — a small neutral round button at the header's right
   (clear of the dialog ✕). Green focus ring, never amber. */
.dion-card__body .place-info-osm-btn {
  flex: 0 0 auto;
  align-self: flex-start;
  margin-right: var(--ui-space-lg);
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ui-surface-container-high);
  border-radius: var(--ui-radius-pill);
  background: transparent;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
}
.dion-card__body .place-info-osm-btn:hover {
  background: var(--ui-surface-container-high);
}
.dion-card__body .place-info-osm-btn:focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
}

/* The revealed raw-OSM JSON block — a monospace, scrollable, copyable pre. */
.dion-card__body .place-info-osm {
  margin-top: var(--ui-space-md);
  padding-top: var(--ui-space-sm);
  border-top: 1px solid var(--ui-surface-container-high);
}
.dion-card__body .place-info-osm-hdr {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ui-space-sm);
  margin-bottom: var(--ui-space-xs);
}
.dion-card__body .place-info-osm-hdr-label {
  font-size: 11px;
  font-weight: var(--ui-font-weight-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ui-on-surface-muted);
}
.dion-card__body .place-info-osm-json {
  margin: 0;
  max-height: 220px;
  overflow: auto;
  padding: var(--ui-space-sm);
  border-radius: var(--ui-radius-md);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.5;
  white-space: pre;
}

/* (f) Responsive reflow (UIX-11) — the INFO section is a ~33% bottom-sheet on mobile
   portrait (the shell rules above already anchor .dion-card to the bottom edge; this
   caps its height to ~33vh so it floats up a third, not a full slab) and a 1/3-width
   side-panel anchored RIGHT at ≥768px (extending the Phase-23 360px top-right card
   toward the UI-SPEC 1/3 side-panel). The float-up / side-in is a transform/opacity
   transition honoring prefers-reduced-motion. slice-1 */
@media (max-width: 767px) {
  /* Designed bottom-sheet (Stitch B) — a ~⅓–45% sheet floating up from the bottom edge
     with rounded top corners + soft shadow + the grab handle (shell rules above). It can
     grow for a long Castas body but starts at ~45vh. The overlap rule rides the Leaflet
     corners above it. */
  .dion-card {
    /* Full-bleed mobile sheet (24-09 UAT tweak): a native <dialog> defaults to
       `width: fit-content`, so the place/area card sat at ~349px regardless of
       viewport. Force it to span the viewport edge-to-edge (x=0, width=100vw);
       box-sizing is border-box so the padding stays inside the bleed. The ≥768px
       ⅓ side-panel width above is unchanged. NOT full-height (45vh sheet). */
    width: 100%;
    max-width: 100%;
    max-height: 45vh;
    min-height: 45vh;
    overflow-y: auto;
  }
}

@media (min-width: 768px) {
  /* FULL-HEIGHT right ⅓ side-panel (Stitch C) — a calm column flush to the right screen
     edge spanning the full viewport height, NOT a small floating top-right card. CSS only,
     no new token. The bottom-right zoom is slid clear of this column by the overlap rule. */
  .dion-card {
    inset: 0 0 0 auto;
    width: 33vw;
    max-width: 33vw;
    min-width: 360px;
    height: 100vh;
    max-height: 100vh;
    overflow-y: auto;
    padding: var(--ui-space-xl) var(--ui-space-lg);
    border-radius: 0;
    box-shadow: var(--ui-elev-soft);
  }
}

/* Dark-mode map container background (UIX-11 polish) — Leaflet defaults the
   .leaflet-container background to #ddd, so any moment the dark GL basemap is
   still painting/repainting (e.g. the dionSetBasemapFlavor swap) flashes a light
   grey slab over the dark UI. Re-point it to the dark page token so repaints flash
   dark, not light. CSS only — map.js / the GL engine stay byte-untouched. */
body.dark-mode #map,
body.dark-mode .leaflet-container {
  background-color: var(--ui-bg);
}

/* ── Info-only chrome: Settings gear parked TOP-RIGHT, transparent (operator UAT) ───────
   The info-only build leaves only the ⚙️ Settings control; move it off the bottom shelf
   to a transparent top-right gear over the map, mirroring the top-left díon wordmark. This
   MUST live in components.css (loads AFTER stíl.css) to override the base .dion-actionbar
   shelf rule above. The travel/scoring slot-hide stays in stíl.css (it uses !important). */
.dion-actionbar {
  position: fixed;
  inset: var(--ui-space-md) var(--ui-space-md) auto auto; /* top-right */
  left: auto;
  bottom: auto;
  width: auto;
  height: auto;
  padding: 0;
  background: transparent;
  box-shadow: none;
}
.dion-actionbar__slot--settings {
  flex: 0 0 auto;
  min-width: 0;
}
/* The gear trigger: transparent, just the ⚙️ glyph, with a soft scrim drop-shadow so it
   reads over a varied basemap. The under-glyph label is dropped (the icon is self-evident;
   the accessible name stays in the markup). */
.dion-actionbar__settings {
  width: auto;
  min-height: 44px; /* ≥44px touch target (WCAG 2.5.5) — was a tight icon-only hit area */
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--ui-space-xs);
  background: transparent;
  filter: drop-shadow(0 1px 2px var(--ui-scrim, rgba(0, 0, 0, 0.45)));
}
.dion-actionbar__settings:hover {
  background: transparent;
}
.dion-actionbar__settings .dion-actionbar__glyph {
  font-size: 1.8rem; /* a touch larger as a standalone floating control */
}
.dion-actionbar__settings .dion-actionbar__label {
  display: none;
}

/* Scoring-unavailable banner dismiss (✕) — quick 260630-lbo. The app-wide red
   .data-error-banner (styled in stíl.css) gains a close button at its right edge. The
   glyph inherits the banner's white text on the destructive red (no new hue), sits over a
   ≥24px hit area, and is keyboard-focusable. The banner is made position:relative so the
   button can be pinned to the right without shifting the centred message. */
.scoring-error-banner {
  position: relative;
}
.scoring-error-banner__close {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  padding: 0;
  border: none;
  border-radius: var(--ui-radius-sm, 4px);
  background: transparent;
  color: inherit; /* white glyph on the red bar */
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.scoring-error-banner__close:hover {
  background: rgba(255, 255, 255, 0.2);
}
.scoring-error-banner__close:focus-visible {
  outline: 2px solid var(--ui-surface, #fff);
  outline-offset: 1px;
}
/* The settings popover opens DOWNWARD now (the gear is at the top), right-anchored with a
   usable width (rows like "Teanga: Gaeilge · English" need room). */
.dion-actionbar__menu--settings {
  top: calc(100% + var(--ui-space-sm));
  bottom: auto;
  left: auto;
  right: 0;
  min-width: min(280px, 88vw);
}

/* ── z5 Celtic Nations overview nation panel (260630-lbo, D-06/D-07) ──────────
   Orientation-driven on ALL devices (REPLACES the 768px width rule for this NEW
   element — the existing .dion-card / .criteria-panel width queries are left
   alone): portrait → bottom 1/3, landscape → right 1/3; the selected nation fits
   the remaining 2/3 (nations.js fitBounds padding). Token-only colours so dark
   re-points automatically. */
.dion-nation-panel {
  position: fixed;
  z-index: 1200;
  background: var(--ui-surface);
  color: var(--ui-on-surface);
  box-shadow: var(--ui-elev-soft);
  overflow: auto;
  padding: var(--ui-space-lg, 1rem);
  box-sizing: border-box;
}
.dion-nation-panel[hidden] {
  display: none;
}

@media (orientation: portrait) {
  .dion-nation-panel {
    inset: auto 0 0 0;
    height: 33vh;
    border-radius: var(--ui-radius-lg) var(--ui-radius-lg) 0 0;
  }
}
@media (orientation: landscape) {
  .dion-nation-panel {
    inset: 0 0 0 auto;
    width: 33vw;
    border-radius: var(--ui-radius-lg) 0 0 var(--ui-radius-lg);
  }
}

/* X close: a 44px round target pinned top-right INSIDE the panel. */
.dion-nation__close {
  position: absolute;
  top: var(--ui-space-sm, 0.5rem);
  right: var(--ui-space-sm, 0.5rem);
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 50%;
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--ui-elev-soft);
}

.dion-nation__header {
  display: flex;
  align-items: center;
  gap: var(--ui-space-sm, 0.5rem);
  margin: 0 2.75rem 0.75rem 0; /* clear the X */
}
.dion-nation__flag {
  font-size: 2rem;
  line-height: 1;
}
.dion-nation__flag-chip {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  font-size: 0.8rem;
}
.dion-nation__name {
  margin: 0;
  font-size: 1.5rem;
}
.dion-nation__english {
  margin: 0;
  opacity: 0.75;
  font-size: 0.95rem;
}

.dion-nation__facts {
  margin: 0 0 0.75rem 0;
}
.dion-nation__fact {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.2rem 0;
}
.dion-nation__fact-label {
  opacity: 0.7;
  font-size: 0.85rem;
}
.dion-nation__fact-value {
  text-align: right;
  font-size: 0.9rem;
}
.dion-nation__notes {
  margin: 0 0 0.75rem 0;
  padding-left: 1.1rem;
  font-size: 0.9rem;
}
.dion-nation__notes li {
  margin: 0.2rem 0;
}

.dion-nation__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ui-space-sm, 0.5rem);
}
.dion-nation__action {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 0.4rem 0.75rem;
  border: none;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface-container-high);
  color: var(--ui-on-surface);
  font-size: 0.9rem;
  cursor: pointer;
  box-shadow: var(--ui-elev-soft);
}

/* Sea info panel (260630-lbo round-3) — reuses .dion-nation-panel positioning; the note
   is a short curated honest descriptor under the sea name. */
.dion-sea-panel__note {
  margin: var(--ui-space-sm, 0.5rem) 0 0;
  color: var(--ui-on-surface);
  font-size: 0.95rem;
  line-height: var(--ui-line-height-body, 1.5);
}

/* z5 Celtic Nations overview hover tooltip (quick task 260630-lbo, TOUCH 3) — a small
   pill that follows the cursor over a nation at the overview, showing its native name +
   "→" as a "View" affordance. Positioned by scripts/nations.js (absolute, inside the map
   container); non-interactive so it never eats a click. Token colours only (no amber —
   reserved for the score); dark mode rides the --ui-* token overrides automatically. */
.dion-nation-tip {
  position: absolute;
  z-index: 600;
  display: none;
  pointer-events: none;
  padding: 0.2rem 0.55rem;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-surface);
  color: var(--ui-on-surface);
  font-size: 0.82rem;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: var(--ui-elev-soft);
}
