/* ============================================================================
   LittleAtlas.app — styles.css

   Structure
     1. Tokens (light + dark)
     2. Reset and the full-screen shell
     3. The map itself
     4. Title bar
     5. Names panel (label toggle matrix)
     6. Depth tracker
     7. Zoom out
     8. Toast
     9. Install modal
    10. Responsive rules
    11. Motion and focus

   Every colour in here is a variable. Switching themes swaps one attribute on
   <html> and nothing recalculates except paint.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   1. TOKENS
   ------------------------------------------------------------------------ */
:root {
  /* Type */
  /*
     Fallbacks matter more here than in most apps. This is a world atlas, so the
     type has to carry Vietnamese tone marks, Turkish dotless i, Polish ogoneks
     and the rest of Latin Extended — and it has to do it offline, when Google
     Fonts may not be in the cache. Inter and Plus Jakarta Sans both cover
     Vietnamese; behind them sit Noto Sans and the platform UI faces, which do
     too. Arial is last because it does not, and by then something has gone
     properly wrong.

     If a place name ever renders as "ThÃ¼ringen" or "Ä?iá»‡n BiÃªn", that is
     not a missing glyph — every character in those strings drew fine. It is
     double-encoded text arriving from the database; see api.php?action=health.
  */
  --font-display: 'Plus Jakarta Sans', 'Inter', 'Noto Sans', system-ui,
                  -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-body: 'Inter', 'Noto Sans', system-ui, -apple-system, 'Segoe UI',
               Roboto, 'Helvetica Neue', Arial, sans-serif;

  /* Spacing and shape */
  --pad: 14px;
  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 22px;
  --tap: 48px;               /* nothing interactive is smaller than this */

  /* Safe areas on notched phones */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);

  --ease: cubic-bezier(0.32, 0.72, 0.24, 1);
}

/* --- Light: pale chart paper over shallow water ------------------------- */
[data-theme='light'] {
  --paper: #f4f7fb;
  --surface: #ffffff;
  --surface-sunk: #eef3f9;
  --text: #14283c;
  --text-dim: #5c7186;
  --line: #d5e0ec;
  --shadow: 0 10px 30px rgba(20, 40, 60, 0.13);
  --shadow-soft: 0 4px 14px rgba(20, 40, 60, 0.09);

  --sea: #c9e0f1;
  --sea-line: rgba(20, 60, 95, 0.13);
  --stroke: #14283c;          /* the heavy outline every shape shares */
  --land-quiet: #e7edf4;      /* layers you have already zoomed past */
  --stroke-quiet: #a9bccf;

  --ink-continent: #6c5ce7;
  --ink-country: #0d9b8a;
  --ink-state: #d98008;
  --accent: #ee5842;
  --ink-city: #d3348a;
  --ink-ocean: #3f7ba3;

  --fill-continent: #ded9fb;
  --fill-country: #cbeee8;
  --fill-state: #fbe6c4;
  --fill-city: #fad3e8;

  --focus: #0b64d6;
  --scrim: rgba(14, 30, 46, 0.45);
}

/* --- Dark: deep water, luminous ink ------------------------------------- */
[data-theme='dark'] {
  --paper: #0d1622;
  --surface: #17202e;
  --surface-sunk: #101a26;
  --text: #e8f0f9;
  --text-dim: #93a6ba;
  --line: #27364a;
  --shadow: 0 12px 34px rgba(0, 0, 0, 0.55);
  --shadow-soft: 0 4px 16px rgba(0, 0, 0, 0.4);

  --sea: #0f1e2e;
  --sea-line: rgba(150, 195, 235, 0.11);
  --stroke: #b9cee2;
  --land-quiet: #1b2735;
  --stroke-quiet: #3c4c60;

  --ink-continent: #a99bff;
  --ink-country: #4fd6c0;
  --ink-state: #ffc25c;
  --accent: #ff8f75;
  --ink-city: #ff8ac4;
  --ink-ocean: #7db4d8;

  --fill-continent: #2c2a52;
  --fill-country: #123b3c;
  --fill-state: #3d2f16;
  --fill-city: #3a1c31;

  --focus: #6fb2ff;
  --scrim: rgba(0, 0, 0, 0.6);
}

/* ---------------------------------------------------------------------------
   2. RESET AND SHELL
   ------------------------------------------------------------------------ */
*,
*::before,
*::after { box-sizing: border-box; }

/* Several controls set display explicitly, which would otherwise beat the
   UA rule for [hidden]. This keeps the attribute authoritative. */
[hidden] { display: none !important; }

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;             /* no page scrolling, ever */
  overscroll-behavior: none;    /* no rubber-band bounce on iOS or Android */
}

body {
  font-family: var(--font-body);
  background: var(--paper);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;  /* long-press must not offer "Save image" */
  user-select: none;
  transition: background-color 220ms var(--ease), color 220ms var(--ease);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   3. THE MAP
   ------------------------------------------------------------------------ */
#stage {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;               /* survives the mobile URL bar collapsing */
}

#map {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;           /* d3.zoom owns every gesture inside here */
  cursor: grab;
}
#map.is-panning { cursor: grabbing; }

#sea {
  fill: var(--sea);
  transition: fill 220ms var(--ease);
}

#graticule {
  fill: none;
  stroke: var(--sea-line);
  stroke-width: var(--sw-quiet, 1px);
  pointer-events: none;
}

/*
   Everything that can be given up for the length of a gesture, is.

   optimizeSpeed turns off anti-aliasing on the outlines, which on a tablet is
   the difference between a smooth pinch and a stuttering one. Labels are hidden
   rather than repositioned sixty times a second, and the graticule stops being
   drawn at all. All three come back the moment the finger lifts, and the map is
   moving too fast during a gesture for any of it to be missed.
*/
#map.is-interacting .region,
#map.is-interacting #water path,
#map.is-interacting #graticule { shape-rendering: optimizeSpeed; }
#map.is-interacting .region { transition: none; }
#map.is-interacting #labels { visibility: hidden; }
#map.is-interacting #graticule { display: none; }

/* Every boundary. vector-effect keeps outlines exactly as heavy at zoom 3000
   as at zoom 1, which is what makes the flat vector look hold up. */
.region {
  stroke: var(--stroke);
  /* Counter-scaled by app.js, which writes --sw-* on #viewport once per frame.
     The previous approach, vector-effect: non-scaling-stroke, asks the renderer
     to redo stroke geometry for every path on every frame of a pinch; on a
     tablet with a hundred provinces on screen that was the single biggest
     source of dropped frames. The fallback width keeps outlines visible if the
     script has not run yet. */
  stroke-width: 1.4px;
  stroke-width: var(--sw-base, 1.4px);
  stroke-linejoin: round;
  cursor: pointer;
  transition: fill 200ms var(--ease), fill-opacity 200ms var(--ease),
              stroke 200ms var(--ease);
}

.region[data-level='continent'] { fill: var(--fill-continent); }
.region[data-level='country']   { fill: var(--fill-country); }
.region[data-level='state']     { fill: var(--fill-state); }

/* The live layer: the one thing on screen the child is meant to tap. */
.region.is-live { stroke-width: var(--sw-live, 2.2px); }
.region.is-live[data-level='continent'] { stroke: var(--ink-continent); }
.region.is-live[data-level='country']   { stroke: var(--ink-country); }
.region.is-live[data-level='state']     { stroke: var(--ink-state); }

/* An ancestor you already chose: covered by its own children, so it only shows
   at the edges and never needs to respond. */
.region.is-past {
  fill: var(--land-quiet);
  stroke: var(--stroke-quiet);
  stroke-width: var(--sw-quiet, 1px);
  pointer-events: none;
}

/* A shape on a level you have passed that you did not choose — Europe while
   you are inside North America. Same quiet fill as an ancestor, but still
   selectable: tapping it switches to that place instead of making you climb
   all the way back out first. */
.region.is-sibling {
  fill: var(--land-quiet);
  stroke: var(--stroke-quiet);
  stroke-width: var(--sw-quiet, 1px);
  cursor: pointer;
}
@media (hover: hover) {
  .region.is-sibling:hover {
    fill: var(--surface);
    stroke: var(--stroke);
    stroke-width: var(--sw-live, 2px);
  }
}

/* The shape you are standing inside. A heavier outline rather than a drop
   shadow: an SVG filter on a shape this complex is re-rasterised on every frame
   of a pinch, and it was costing more than it was worth. */
.region.is-focus {
  fill: var(--surface);
  stroke: var(--stroke);
  stroke-width: var(--sw-focus, 3.2px);
  pointer-events: none;
}

@media (hover: hover) {
  .region.is-live:hover { fill-opacity: 0.72; stroke-width: var(--sw-focus, 3.2px); }
}
.region.is-live:active { fill-opacity: 0.55; }

/* ---------------------------------------------------------------------------
   Lakes and rivers
   ------------------------------------------------------------------------ */
#water {
  pointer-events: none;   /* a tap on the Amazon still selects Brazil */
}

.lake {
  fill: var(--sea);
  stroke: var(--ink-ocean);
  stroke-width: var(--sw-quiet, 1px);
  stroke-linejoin: round;
}

.river {
  fill: none;
  stroke: var(--ink-ocean);
  stroke-width: var(--sw-quiet, 1px);
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.72;
}
/* The Nile, the Amazon, the Yangtze: drawn like the landmarks they are. */
.river--major {
  stroke-width: var(--sw-base, 1.4px);
  opacity: 0.85;
}

/* City dots. Radius is counter-scaled in app.js so a dot is always thumb-sized. */
/* Dots live inside a counter-scaled group, so their stroke is already constant
   on screen and needs no special handling. */
.city-dot {
  fill: var(--ink-city);
  stroke: var(--paper);
  stroke-width: 1.6;
  cursor: pointer;
}
.city-dot:hover { stroke: var(--ink-city); }

/* Labels. paint-order puts the halo behind the glyphs, which is what keeps
   small type readable over a busy coastline without a background box. */
.map-label {
  font-family: var(--font-body);
  font-weight: 600;
  fill: var(--text);
  stroke: var(--paper);
  stroke-width: 3.4px;
  paint-order: stroke fill;
  text-anchor: middle;
  dominant-baseline: middle;
  pointer-events: none;
  letter-spacing: -0.01em;
}
.map-label[data-level='continent'] {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  fill: var(--ink-continent);
}
.map-label[data-level='country'] { font-weight: 700; }
.map-label[data-level='city'] {
  font-weight: 600;
  fill: var(--ink-city);
  text-anchor: start;
}
.map-label.is-hidden { display: none; }

/* Names on the layers you have passed: readable, but clearly not the thing in
   front of you. */
.map-label.is-quiet { opacity: 0.6; }

/* Water, set the way an atlas sets it: italic, letter-spaced, and haloed
   against the sea rather than the paper. */
.map-label[data-level='ocean'],
.map-label[data-level='lake'],
.map-label[data-level='river'] {
  font-style: italic;
  font-weight: 500;
  fill: var(--ink-ocean);
  stroke: var(--sea);
  letter-spacing: 0.16em;
  text-anchor: middle;
}

/* ---------------------------------------------------------------------------
   4. TITLE BAR
   ------------------------------------------------------------------------ */
#titlebar {
  position: fixed;
  top: 0; left: 0; right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: calc(var(--safe-top) + 10px) calc(var(--safe-right) + var(--pad))
           10px calc(var(--safe-left) + var(--pad));
  pointer-events: none;         /* the map stays draggable behind the bar */
  z-index: 30;
}
#titlebar > * { pointer-events: auto; }

/* The wordmark grows as you read it, which is the whole idea of the app. The
   parts butt straight up against each other — "LittleAtlas" is one word, and
   the size and colour changes do the separating that a space would. */
.wordmark {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1rem, 3.6vw, 1.3rem);
  line-height: 1;
  letter-spacing: -0.03em;
  display: flex;
  align-items: baseline;
  gap: 0;
  color: var(--text);
  text-shadow: 0 1px 0 var(--paper), 0 0 12px var(--paper);
}
.wordmark__little { font-size: 0.66em; color: var(--text-dim); }
.wordmark__big    { font-size: 1.3em; color: var(--ink-country); }
.wordmark__suffix { font-size: 0.6em; color: var(--text-dim); letter-spacing: 0; }

.titlebar__tools { display: flex; gap: 8px; }

.tool {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: var(--tap);
  padding: 0 14px;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font: 600 0.86rem/1 var(--font-body);
  box-shadow: var(--shadow-soft);
  cursor: pointer;
  transition: transform 140ms var(--ease), border-color 140ms var(--ease),
              background-color 200ms var(--ease);
}
.tool:hover { border-color: var(--ink-country); }
.tool:active { transform: scale(0.96); }
.tool[aria-expanded='true'] {
  border-color: var(--ink-country);
  background: var(--surface-sunk);
}

.tool__icon {
  width: 20px; height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}
.tool--install .tool__icon { stroke: var(--ink-country); }

/* One button, two glyphs: show the theme you would switch *to*. */
[data-theme='light'] .tool__icon--sun  { display: none; }
[data-theme='dark']  .tool__icon--moon { display: none; }

/* ---------------------------------------------------------------------------
   5. NAMES PANEL
   ------------------------------------------------------------------------ */
.panel {
  position: fixed;
  top: calc(var(--safe-top) + 68px);
  right: calc(var(--safe-right) + var(--pad));
  width: min(300px, calc(100vw - 2 * var(--pad)));
  padding: 16px;
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  z-index: 40;
  animation: pop 180ms var(--ease);
}
@keyframes pop {
  from { opacity: 0; transform: translateY(-8px) scale(0.98); }
}

.panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.panel__head h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}
.panel__close {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: none; border-radius: 50%;
  background: var(--surface-sunk);
  color: var(--text);
  cursor: pointer;
}
.panel__close svg {
  width: 16px; height: 16px;
  fill: none; stroke: currentColor;
  stroke-width: 2.2; stroke-linecap: round;
}
.panel__rule { height: 1.5px; background: var(--line); margin: 12px 0; }

.switch-list { list-style: none; margin: 0; padding: 0; }

.switch {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--tap);
  padding: 0 4px;
  cursor: pointer;
  font-size: 0.92rem;
  border-radius: var(--radius-sm);
}
.switch:hover { background: var(--surface-sunk); }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch__track {
  position: relative;
  flex: none;
  width: 44px; height: 26px;
  border-radius: 999px;
  background: var(--surface-sunk);
  border: 1.5px solid var(--line);
  transition: background-color 160ms var(--ease), border-color 160ms var(--ease);
}
.switch__knob {
  position: absolute;
  top: 2px; left: 2px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--stroke-quiet);
  transition: transform 180ms var(--ease), background-color 160ms var(--ease);
}
.switch input:checked + .switch__track {
  /* Solid fallback first for older WebKit, then the tinted version. */
  background: var(--surface-sunk);
  background: color-mix(in srgb, var(--ink-country) 22%, var(--surface));
  border-color: var(--ink-country);
}
.switch input:checked + .switch__track .switch__knob {
  transform: translateX(18px);
  background: var(--ink-country);
}
.switch input:focus-visible + .switch__track {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
.switch__text { font-weight: 500; }
.switch--master .switch__text { font-weight: 700; }

/* A colour chip that ties each row to the ink used on the map. */
.switch__swatch {
  width: 12px; height: 12px;
  border-radius: 3px;
  flex: none;
  border: 1.5px solid var(--stroke);
}
.switch[data-level='continent'] .switch__swatch { background: var(--ink-continent); }
.switch[data-level='country']   .switch__swatch { background: var(--ink-country); }
.switch[data-level='state']     .switch__swatch { background: var(--ink-state); }
.switch[data-level='city']      .switch__swatch { background: var(--ink-city); border-radius: 50%; }
.switch[data-level='ocean']     .switch__swatch { background: var(--sea); border-color: var(--ink-ocean); }

/* Lakes are haloed against the land they sit in, not the sea. Rivers keep a
   tighter letterfit so a long name still follows a bend. */
.map-label[data-level='lake'] {
  stroke: var(--land-quiet);
  letter-spacing: 0.06em;
}
.map-label[data-level='river'] {
  letter-spacing: 0.08em;
  stroke-width: 2.6px;
}

/* ---------------------------------------------------------------------------
   6. DEPTH TRACKER — the signature element
   ------------------------------------------------------------------------ */
#tracker {
  position: fixed;
  left: calc(var(--safe-left) + var(--pad));
  bottom: calc(var(--safe-bottom) + var(--pad));
  width: 234px;
  padding: 12px;
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  z-index: 25;
}

.tracker__radar {
  position: relative;
  margin-bottom: 10px;
}
#radar {
  display: block;
  width: 100%;
  height: auto;
  background: var(--sea);
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--line);
}
#radarLand path { fill: var(--land-quiet); stroke: var(--stroke-quiet); stroke-width: 0.5; }
#radarMark rect {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  rx: 1.5;
}
#radarMark circle { fill: var(--accent); }
#radarMark circle.pulse {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.2;
  animation: pulse 2.4s var(--ease) infinite;
}
@keyframes pulse {
  0%   { r: 2; opacity: 0.9; }
  100% { r: 12; opacity: 0; }
}
.tracker__radar-caption {
  position: absolute;
  left: 7px; bottom: 5px;
  margin: 0;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.tracker__rungs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rung {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 4px 8px;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-dim);
  text-align: left;
  font-family: var(--font-body);
  cursor: default;
  transition: background-color 160ms var(--ease), color 160ms var(--ease);
}
.rung__glyph { width: 22px; height: 22px; flex: none; }
.rung__glyph rect {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-dasharray: 2.4 2.4;   /* dashed until you have actually been there */
}
.rung__glyph rect:last-child { fill: none; }

.rung__text { min-width: 0; }
.rung__level {
  display: block;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  opacity: 0.75;
}
.rung__value {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: -0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Visited: solid glyph, real name, tappable to climb back. */
.rung.is-visited {
  color: var(--text);
  cursor: pointer;
}
.rung.is-visited .rung__glyph rect { stroke-dasharray: none; }
.rung.is-visited:hover { background: var(--surface-sunk); }

/* Current: the whole rung takes the level's colour. */
.rung.is-current {
  background: var(--surface-sunk);
  cursor: default;
}
.rung.is-current[data-level='continent'] { color: var(--ink-continent); }
.rung.is-current[data-level='country']   { color: var(--ink-country); }
.rung.is-current[data-level='state']     { color: var(--ink-state); }
.rung.is-current[data-level='city']      { color: var(--ink-city); }
.rung.is-current .rung__glyph rect:last-of-type { fill: currentColor; }

/* Not reached yet, and not present in the data for this branch. */
.rung.is-absent { opacity: 0.42; }

/* ---------------------------------------------------------------------------
   7. ZOOM OUT
   Sits in the upper right, first in the tool row. Heavier than its neighbours
   because it is the one control a lost child needs to find immediately.
   ------------------------------------------------------------------------ */
#zoomOut.tool--back {
  border: 2px solid var(--stroke);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.92rem;
  letter-spacing: -0.02em;
  padding: 0 16px 0 11px;
  max-width: min(300px, 42vw);
}
#zoomOut.tool--back .tool__icon { stroke-width: 2.6; }
#zoomOut.tool--back .tool__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#zoomOut.tool--back:hover { border-color: var(--stroke); transform: translateX(-2px); }

/* One label for roomy screens, one for phones. */
.tool__label--short { display: none; }
@media (max-width: 900px) {
  .tool__label--long { display: none; }
  .tool__label--short { display: inline; }
}

/* ---------------------------------------------------------------------------
   8. TOAST
   ------------------------------------------------------------------------ */
#toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--safe-bottom) + 22px);
  transform: translateX(-50%);
  max-width: min(420px, calc(100vw - 32px));
  padding: 12px 18px;
  background: var(--stroke);
  color: var(--paper);
  border-radius: 999px;
  font-size: 0.88rem;
  font-weight: 600;
  text-align: center;
  box-shadow: var(--shadow);
  z-index: 45;
  animation: rise 220ms var(--ease);
}
@keyframes rise {
  from { opacity: 0; transform: translate(-50%, 10px); }
}

/* ---------------------------------------------------------------------------
   9. INSTALL MODAL
   ------------------------------------------------------------------------ */
#installBackdrop {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  z-index: 50;
  animation: fade 200ms var(--ease);
}
@keyframes fade { from { opacity: 0; } }

#installModal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(420px, calc(100vw - 28px));
  max-height: min(88vh, 660px);
  overflow-y: auto;
  padding: 26px 24px 20px;
  background: var(--surface);
  border: 2px solid var(--stroke);
  border-radius: 26px;
  box-shadow: var(--shadow);
  z-index: 51;
  animation: modal-in 260ms var(--ease);
}
@keyframes modal-in {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 16px)) scale(0.96); }
}

.modal__close {
  position: absolute;
  top: 12px; right: 12px;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border: none; border-radius: 50%;
  background: var(--surface-sunk);
  color: var(--text);
  cursor: pointer;
}
.modal__close svg {
  width: 17px; height: 17px;
  fill: none; stroke: currentColor;
  stroke-width: 2.2; stroke-linecap: round;
}

.modal__art { display: grid; place-items: center; margin-bottom: 10px; }
.modal__art svg { width: 96px; height: 96px; }
.modal__phone {
  fill: var(--surface-sunk);
  stroke: var(--stroke);
  stroke-width: 2.4;
}
.modal__globe { fill: var(--fill-country); stroke: var(--ink-country); stroke-width: 2.4; }
.modal__meridian { fill: none; stroke: var(--ink-country); stroke-width: 1.4; }
.modal__arrow {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

#installModal h2 {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.3rem;
  letter-spacing: -0.03em;
  text-align: center;
}
#installIntro {
  margin: 0 0 18px;
  text-align: center;
  color: var(--text-dim);
  font-size: 0.92rem;
  line-height: 1.45;
}

.modal__steps {
  margin: 0 0 20px;
  padding: 0;
  list-style: none;
  counter-reset: step;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.modal__steps li {
  counter-increment: step;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  background: var(--surface-sunk);
  border-radius: var(--radius);
  font-size: 0.92rem;
  line-height: 1.45;
}
.modal__steps li::before {
  content: counter(step);
  flex: none;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ink-country);
  color: var(--surface);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.82rem;
}
/* The inline glyphs used for the iOS Share box and the Android overflow menu. */
.step-glyph {
  display: inline-grid;
  place-items: center;
  width: 22px; height: 22px;
  vertical-align: -6px;
  margin: 0 2px;
  border-radius: 6px;
  background: var(--surface);
  border: 1.5px solid var(--line);
}
.step-glyph svg {
  width: 14px; height: 14px;
  fill: none; stroke: var(--ink-country);
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

.modal__actions { display: flex; gap: 10px; }
.btn {
  flex: 1;
  min-height: 52px;
  padding: 0 18px;
  border: 2px solid var(--stroke);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.98rem;
  cursor: pointer;
  transition: transform 140ms var(--ease);
}
.btn:active { transform: scale(0.97); }
.btn--primary { background: var(--ink-country); color: #ffffff; border-color: var(--stroke); }
[data-theme='dark'] .btn--primary { color: #05221f; }

/* ---------------------------------------------------------------------------
   10. RESPONSIVE
   Below 860px the tracker becomes a bottom strip: the radar shrinks to a
   square at the head of the row and the rungs scroll horizontally.
   ------------------------------------------------------------------------ */
@media (max-width: 860px) {
  #tracker {
    top: auto;
    left: calc(var(--safe-left) + 8px);
    right: calc(var(--safe-right) + 8px);
    bottom: calc(var(--safe-bottom) + 8px);
    width: auto;
    display: flex;
    align-items: stretch;
    gap: 10px;
    padding: 8px;
    border-radius: var(--radius);
  }

  .tracker__radar { margin: 0; flex: none; width: 84px; }
  #radar { align-self: center; }
  .tracker__radar-caption { display: none; }

  .tracker__rungs {
    flex-direction: row;
    gap: 6px;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tracker__rungs::-webkit-scrollbar { display: none; }

  .rung {
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 2px;
    min-width: 108px;
    padding: 6px 10px;
    scroll-snap-align: start;
    background: var(--surface-sunk);
  }
  .rung__glyph { width: 18px; height: 18px; }
  .rung__value { font-size: 0.82rem; max-width: 92px; }
  .rung.is-current { outline: 2px solid currentColor; }

  #zoomOut.tool--back {
    padding: 0 13px 0 9px;
    max-width: 130px;
    font-size: 0.86rem;
  }

  #toast { bottom: calc(var(--safe-bottom) + 116px); }

  /* The names panel becomes a bottom sheet — easier to reach one-handed. */
  .panel {
    top: auto;
    left: 8px; right: 8px;
    bottom: calc(var(--safe-bottom) + 8px);
    width: auto;
    border-radius: var(--radius-lg);
    animation: sheet-in 220ms var(--ease);
  }
  @keyframes sheet-in { from { opacity: 0; transform: translateY(18px); } }

  /* Icons only for the secondary tools, to keep the bar short. Zoom out keeps
     its word, because an unlabelled chevron is not something a six-year-old
     should have to decode. */
  .tool:not(.tool--back) .tool__label { display: none; }
  .tool:not(.tool--back) { padding: 0; width: var(--tap); justify-content: center; }
}

@media (max-width: 420px) {
  .rung { min-width: 96px; }
  #installModal { padding: 22px 18px 16px; }
}

/* Very short landscape windows: drop the radar so the rungs still fit. */
@media (max-height: 460px) and (orientation: landscape) {
  .tracker__radar { display: none; }
}

/* ---------------------------------------------------------------------------
   11. MOTION AND FOCUS
   ------------------------------------------------------------------------ */
:where(button, input, [tabindex]):focus-visible,
.region:focus-visible,
.city-dot:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
