/* Eclipse Recon: tool-specific layout. The table system, type, plates and both
   colour modes come from PREPRINT; this file lays out a map page: a full-bleed
   relief map pinned under the masthead, with cards floating on it. */

/* An explicit display (flex on tables) must never beat the hidden attribute. */
[hidden] { display: none !important; }

html, body { height: 100%; }
body {
  display: flex; flex-direction: column;
  overflow: hidden;
  /* The dynamic viewport where it exists. A phone's URL bar grows and shrinks,
     and 100% of the initial containing block leaves either a gap or an
     overflow under the browser chrome. */
  height: 100dvh;
  /* A map page owns its edges: no pull-to-refresh firing under a pan. */
  overscroll-behavior: none;
}

/* Touch behaviour for the whole page. No grey flash under a tap; the drags
   this page owns (sheet heads, the scrub, the map) never start a text
   selection; and anything with a tooltip drops the long-press callout. */
button, .sheet-head, #scrub-wrap { -webkit-tap-highlight-color: transparent; }
button { touch-action: manipulation; }
.sheet-head { user-select: none; -webkit-user-select: none; }
[data-tip] { -webkit-touch-callout: none; }

/* A switch states itself by filling with ink, the same signal the system's
   Segmented uses. Every aria-pressed control on this page uses it:
   suitability, the duration switch, follow. */
.btn-ghost[aria-pressed='true'] {
  background: var(--pp-edge); border-color: var(--pp-edge);
  color: var(--pp-paper);
}

/* compact masthead: the page below needs the room. The pad is the header's
   rather than the wrap's, because the bench lamp hangs off the top edge of the
   header and is clipped by it; on the wrap it would start the fixture in
   mid-air. Every rem of it is taken from the map, which is why this one is a
   third of what a tool page gets. */
.wrap.tool-head { flex: 0 0 auto; width: 100%; }
.wrap.tool-head > header {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding-top: 1.1rem; padding-bottom: 0.8rem;
  /* A strip of chrome rather than a masthead, so the fixture comes down to a
     third of its full size — enough to read as the lamp it is everywhere else
     on the site, small enough that the throw lands on the rule and not on the
     map. */
  --w-lamp-unit: 0.34px;
}
/* The lamp hangs off the content column's right edge and this row is the
   rightmost thing under it, so the pad keeps the add button clear of the
   fixture. The cone passes behind it, which is what a cone drawn under the page
   is for. */
.which-eclipse { display: flex; align-items: center; gap: 0.5rem;
  padding-right: 58px; }
/* the drawn select is width:100% of its picker; the picker needs a width
   of its own or the two collapse to zero together */
.which-eclipse .picker, .which-eclipse select { width: 13.5em; }
.which-eclipse .we-label {
  font-size: 0.72rem; letter-spacing: var(--w-track-label, 0.06em);
  text-transform: uppercase; color: var(--pp-faint);
}

/* ---- the stage ------------------------------------------------------ */

#stage {
  position: relative; flex: 1 1 auto; min-height: 0;
  border-top: 1px solid var(--pp-line);
}
#map {
  position: absolute; inset: 0;
  background: color-mix(in srgb, var(--pp-ink) 8%, var(--pp-paper));
  cursor: var(--pp-cursor-cross, crosshair);
}
/* The relief arrives already in its final greys — app.js paints the tiles,
   no filter corrects them. Dark mode dims the sheet, and the SAME filter
   goes on the key's swatches, because a key that skips the filter is a key
   to a different map. The reading rule holds in both modes: palest = water,
   darker = higher. */
[data-mode="dark"] #map .pane-relief,
[data-mode="dark"] #hypso-key i {
  filter: brightness(0.66) contrast(1.05);
}
#map .pane-labels { filter: grayscale(1); opacity: 0.85; }
[data-mode="dark"] #map .pane-labels { filter: grayscale(1) invert(1); }

/* the zoom keys stand above the clock strip, not buried behind it —
   the console is a sheet (z 1100) and leaflet's corner is not */
.leaflet-bottom.leaflet-right { margin-bottom: 66px; }
.leaflet-control-zoom { border: 1px solid var(--pp-line) !important;
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent) !important; }
.leaflet-control-zoom a {
  background: var(--pp-paper) !important; color: var(--pp-ink) !important;
  border-bottom: 1px solid var(--pp-hair) !important;
}
.leaflet-control-zoom a:hover { background: var(--w-wash-0) !important; }

/* the locate key: the same paper-and-ink bar the zoom keys wear, one
   step below them. app.js adds it only where the browser can geolocate. */
.locate-ctl { border: 1px solid var(--pp-line) !important;
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent) !important; }
.locate-ctl a {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  background: var(--pp-paper) !important; color: var(--pp-ink) !important;
}
.locate-ctl a:hover { background: var(--w-wash-0) !important; }
.locate-ctl a.locating svg { animation: locate-breathe 1.1s ease-in-out infinite; }
@keyframes locate-breathe { 50% { opacity: 0.25; } }
.locate-ctl a.failed { color: var(--pp-state-danger) !important; }
@media (prefers-reduced-motion: reduce) {
  .locate-ctl a.locating svg { animation: none; opacity: 0.4; }
}

/* target mark: a surveyor's cross, not a gunsight */
.reticle { pointer-events: none; color: var(--pp-ink); }
.reticle .r-ring {
  position: absolute; inset: 6px; border: 1.6px solid currentColor;
  border-radius: 50%;
}
.reticle .r-cross::before, .reticle .r-cross::after {
  content: ''; position: absolute; background: currentColor;
}
.reticle .r-cross::before { left: 50%; top: -2px; bottom: -2px; width: 1.6px;
  margin-left: -0.8px; }
.reticle .r-cross::after  { top: 50%; left: -2px; right: -2px; height: 1.6px;
  margin-top: -0.8px; }

.ge-label, .ct-label {
  font-family: var(--pp-font-mono); font-size: 9px; line-height: 1.25;
  letter-spacing: 0.04em; white-space: nowrap; color: var(--pp-ink);
  text-shadow: 0 0 3px var(--pp-paper), 0 0 3px var(--pp-paper),
               0 0 3px var(--pp-paper);
}
.ct-label { color: var(--pp-faint); }

.sweep-dot-tip {
  background: var(--pp-paper); border: 1px solid var(--pp-line);
  border-radius: 0; color: var(--pp-ink);
  font: 11px/1.4 var(--pp-font-mono); padding: 4px 7px;
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent);
}
.leaflet-tooltip.sweep-dot-tip::before { display: none; }

/* ---- sheets (the paper cards) --------------------------------------- */

.sheet {
  position: absolute; z-index: 1100;
  background: var(--pp-paper);
  border: 1px solid var(--pp-line);
  box-shadow: 5px 5px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent);
  display: flex; flex-direction: column;
}
.sheet-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--pp-hair);
  flex: 0 0 auto;
  /* the whole bar folds its sheet (app.js): a strip is a target, a
     12px glyph is not */
  cursor: var(--pp-cursor-point, pointer);
}
.sheet-head h2 {
  margin: 0; font-family: var(--pp-font-display);
  font-size: 0.95rem; font-weight: 700; line-height: 1.1;
}
.sheet-fold {
  border: none; background: none; cursor: var(--pp-cursor-point, pointer);
  color: var(--pp-faint); font-size: 0.75rem; padding: 0 0.2rem;
  display: inline-flex; align-items: center; justify-content: center;
}
.head-keys { display: flex; align-items: center; gap: 0.2rem; }
.sheet-fold:hover { color: var(--pp-ink); }
/* the chevron points where the body will go: up while open (it rolls up
   into the head), down once folded (it comes back out) */
.sheet-fold[data-fold] svg {
  transition: transform var(--pp-dur-state) var(--pp-ease-out);
}
.sheet.folded .sheet-fold[data-fold] svg { transform: rotate(180deg); }
.sheet-body { padding: 0.75rem; overflow-y: auto; scrollbar-width: thin;
  /* a sheet's scroll ends at the sheet: it never chains into a page
     bounce behind the map */
  overscroll-behavior: contain; }
.sheet.folded .sheet-body { display: none; }
/* the drawer's peek line exists only on a phone, and only at the lowest
   snap — the media block below turns it on */
.head-peek { display: none; }

.sheet h3 {
  margin: 0 0 0.4rem; font-family: var(--pp-font-body);
  font-size: 0.72rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--w-track-label, 0.06em); color: var(--pp-faint);
}
.sheet h3 small { text-transform: none; font-weight: 400; }
.sheet code { font-family: var(--pp-font-mono); font-size: 0.85em; }

/* one step under the other sheets: the phone's search bar (z 1100,
   earlier in the DOM) must drop its results OVER this chip, while the
   dossier — same 1100, later in the DOM — still rises over the bar */
#intel { top: 12px; left: 12px; width: 300px; max-height: calc(100% - 88px);
  z-index: 1090; }
#dossier { top: 12px; right: 12px; width: 384px; max-height: calc(100% - 88px); }

.stats { display: grid; grid-template-columns: auto 1fr; gap: 2px 12px;
  margin: 0 0 0.8rem; }
.stats dt { font-size: 0.68rem; text-transform: uppercase;
  letter-spacing: var(--w-track-label, 0.06em); color: var(--pp-faint);
  align-self: baseline; }
.stats dd { margin: 0; text-align: right; font-family: var(--pp-font-mono);
  font-size: 0.82rem; font-variant-numeric: tabular-nums; }

.tl-global { margin-bottom: 0.8rem; }
.tl-row { display: flex; align-items: baseline; gap: 0.5rem; padding: 1px 0;
  font-size: 0.8rem; }
.tl-row b { font-family: var(--pp-font-mono); font-weight: 400;
  color: var(--pp-faint); width: 2.2em; font-size: 0.72rem; }
.tl-row code { font-variant-numeric: tabular-nums; }
.tl-row span { color: var(--pp-faint); font-size: 0.72rem; flex: 1;
  line-height: 1.25; }
.tl-row .btn-ghost { font-size: 0.62rem; padding: 0.05em 0.45em;
  letter-spacing: 0.05em; min-height: 0; line-height: 1.5; }

.search { margin-bottom: 0.8rem; position: relative; }
.search .field { width: 100%; }
#search-results {
  list-style: none; margin: 4px 0 0; padding: 0;
  border: 1px solid var(--pp-hair); background: var(--pp-paper);
  max-height: 180px; overflow-y: auto;
}
#search-results li { padding: 0.3rem 0.55rem; font-size: 0.82rem;
  border-bottom: 1px solid var(--pp-hair);
  cursor: var(--pp-cursor-point, pointer); }
#search-results li:hover { background: var(--w-wash-0); }
#search-results li small { color: var(--pp-faint); }

#sweep-btn { width: 100%; margin-bottom: 0.6rem; }

#sweep-progress { position: relative; height: 14px;
  border: 1px solid var(--pp-hair); margin-bottom: 0.6rem; }
#sweep-progress i { position: absolute; inset: 1px auto 1px 1px; width: 0;
  background: color-mix(in srgb, var(--pp-ink) 22%, transparent);
  transition: width 0.2s; }
#sweep-progress span { position: absolute; inset: 0; text-align: center;
  font-size: 0.6rem; letter-spacing: 0.12em; line-height: 13px;
  text-transform: uppercase; color: var(--pp-faint); }

#sweep-list { list-style: none; margin: 0.5rem 0 0; padding: 0; }
#sweep-list li { display: grid;
  grid-template-columns: 1.6em 1fr auto;
  gap: 1px 0.5rem; padding: 0.35rem 0.15rem; align-items: baseline;
  border-bottom: 1px solid var(--pp-hair);
  cursor: var(--pp-cursor-point, pointer); }
#sweep-list li:hover { background: var(--w-wash-0); }
#sweep-list .rk { color: var(--pp-faint);
  font-family: var(--pp-font-mono); font-size: 0.7rem; }
#sweep-list .nm { font-size: 0.82rem; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
#sweep-list .sc { font-family: var(--pp-font-mono);
  font-weight: 700; font-variant-numeric: tabular-nums; }
#sweep-list .meta { grid-column: 2 / 4;
  color: var(--pp-faint); font-family: var(--pp-font-mono);
  font-size: 0.66rem; }


/* ---- site report ---------------------------------------------------- */

.tgt-id { margin-bottom: 0.7rem; }
#tgt-name { display: block; font-family: var(--pp-font-display);
  font-weight: 700; font-size: 0.95rem; }
.tgt-id code { display: inline-block; margin: 2px 0.8rem 0 0;
  color: var(--pp-faint); font-size: 0.72rem; }

#dossier section { margin-bottom: 0.8rem; border-top: 1px solid var(--pp-hair);
  padding-top: 0.5rem; }
.h-status { float: right; font-family: var(--pp-font-mono);
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--pp-faint); }
.h-status.ok { color: var(--pp-state-ok); }
.h-status.err { color: var(--pp-state-danger); }

.circ-type { font-size: 0.82rem; margin-bottom: 0.4rem; }
.circ-type b.total { color: var(--pp-plate-3-text); }
.circ-type b.partial { color: var(--pp-ink); }
table.circ { width: 100%; border-collapse: collapse;
  font-family: var(--pp-font-mono); font-size: 0.8rem; }
table.circ th { text-align: left; color: var(--pp-faint); font-weight: 400;
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  padding: 2px 0.35rem 2px 0; }
table.circ td { padding: 2px 0.35rem 2px 0;
  font-variant-numeric: tabular-nums; }
table.circ td.dim { color: var(--pp-faint); }
table.circ tr.hl td { color: var(--pp-plate-3-text); }
.circ-facts { display: grid; grid-template-columns: 1fr 1fr;
  gap: 3px 0.8rem; margin-top: 0.55rem; font-size: 0.8rem; }
.circ-facts div { display: flex; justify-content: space-between;
  gap: 0.4rem; }
.circ-facts span { color: var(--pp-faint); font-size: 0.64rem;
  text-transform: uppercase; letter-spacing: 0.05em; align-self: baseline; }
.circ-facts code { font-variant-numeric: tabular-nums; }

.hz-wrap { position: relative;
  /* a horizontal drag reads the profile, a vertical one still scrolls
     the sheet */
  touch-action: pan-y; }
.hz-wrap svg { display: block; width: 100%; height: auto; }
.hz-tip { position: absolute; pointer-events: none; display: none;
  background: var(--pp-paper); border: 1px solid var(--pp-line);
  font-family: var(--pp-font-mono); font-size: 0.66rem;
  padding: 2px 6px; white-space: nowrap; z-index: 5; }
.terrain-note { font-size: 0.68rem; color: var(--pp-faint);
  margin: 0.35rem 0 0; line-height: 1.45; }
.terrain-verdict { font-size: 0.78rem; margin-top: 0.4rem; }
.terrain-verdict b.ok { color: var(--pp-state-ok); }
.terrain-verdict b.bad { color: var(--pp-state-danger); }
.terrain-verdict b.part { background: linear-gradient(transparent 15%,
  var(--w-mark-fill) 15% 88%, transparent 88%); padding: 0 0.15em; }

.wx-mode { font-size: 0.64rem; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--pp-faint); margin-bottom: 0.4rem; }
.wx-mode b { color: var(--pp-ink); }
.wx-hero { display: flex; align-items: center; gap: 0.9rem;
  margin-bottom: 0.5rem; }
.wx-score { font-family: var(--pp-font-mono); font-size: 2rem;
  font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.wx-hero .lbl { font-size: 0.6rem; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--pp-faint); }
.wx-hero .word { font-size: 0.85rem; font-weight: 700;
  letter-spacing: 0.04em; }
.wx-corridor { margin: 0.45rem 0; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--pp-faint); }
.wx-corridor b { font-family: var(--pp-font-mono); font-size: 0.88rem; }
.wx-corridor code { display: block; text-transform: none;
  letter-spacing: 0; font-size: 0.72rem; color: var(--pp-muted);
  margin-top: 1px; }
.cloudbars { margin: 0.5rem 0; }
.cbar { display: grid; grid-template-columns: 3.2em 1fr 2.6em; gap: 0.5rem;
  align-items: center; margin: 4px 0;
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; }
.cbar .t { color: var(--pp-faint); }
.cbar .rail { height: 8px; background: var(--w-wash-0); position: relative; }
.cbar .rail i { position: absolute; inset: 0 auto 0 0;
  background: color-mix(in srgb, var(--pp-ink) 55%, transparent); }
.cbar .v { text-align: right; font-family: var(--pp-font-mono);
  font-size: 0.78rem; font-variant-numeric: tabular-nums;
  text-transform: none; }
.wx-extra { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.4rem;
  font-size: 0.66rem; margin-top: 0.4rem; }
.wx-extra div { border: 1px solid var(--pp-hair); padding: 0.25rem 0.4rem; }
.wx-extra span { display: block; color: var(--pp-faint); font-size: 0.58rem;
  text-transform: uppercase; letter-spacing: 0.06em; }
.wx-extra code { font-size: 0.78rem; }
.wx-strip { margin-top: 0.5rem; }
.wx-strip svg { display: block; width: 100%; height: auto; }

/* the verdict: the score itself, in the ramp's own ink, with the factor
   arithmetic laid out underneath — a number you can audit, not a stamp */
.v-hero { display: flex; align-items: baseline; gap: 0.35rem;
  margin-bottom: 0.55rem; }
.v-num { font-family: var(--pp-font-mono); font-size: 2.3rem;
  font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.v-num.pending { color: var(--pp-faint); }
.v-unit { color: var(--pp-faint); font-family: var(--pp-font-mono);
  font-size: 0.8rem; }
.v-gate { margin-left: 0.6rem; font-family: var(--pp-font-mono);
  font-size: 0.72rem; letter-spacing: 0.05em; color: var(--pp-faint); }
.v-facts { display: grid; grid-template-columns: 1fr;
  gap: 3px 0.8rem; font-size: 0.8rem; }
.v-facts div { display: flex; align-items: baseline; gap: 0.5rem; }
/* wide enough for CENTRELINE, the longest label the panel prints */
.v-facts span { color: var(--pp-faint); font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.05em; width: 6.8em;
  flex: none; }
.v-facts code { font-variant-numeric: tabular-nums; }
.v-facts code:first-of-type { flex: 1; }
.v-facts .fx { color: var(--pp-faint); text-align: right; min-width: 3.4em; }

/* the big-field print: cells are data, so they scale as squares, not blur */
.suit-img { image-rendering: pixelated; }

#suit-grade, #suit-dur { font-size: 0.62rem; padding: 0.3em 0.7em; }

/* the crawler's progress line in the event panel */
#survey-row { display: flex; align-items: baseline; gap: 0.5rem;
  margin-top: 0.55rem; font-size: 0.8rem; }
#survey-row span { color: var(--pp-faint); font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.05em; flex: none; }
#survey-row b { font-family: var(--pp-font-mono); font-weight: 700; }
#survey-row code { color: var(--pp-faint); font-size: 0.68rem;
  margin-left: auto; }

.score-how { margin-top: 0.6rem; font-size: 0.74rem; }
.score-how summary { color: var(--pp-faint); font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.05em;
  cursor: var(--pp-cursor-point, pointer); }
.score-how code { display: block; margin: 0.45rem 0;
  font-size: 0.74rem; }
.score-how p { margin: 0; line-height: 1.5; color: var(--pp-muted); }

/* ---- the clock strip ------------------------------------------------ */

#console {
  left: 12px; right: 12px; bottom: 12px; top: auto;
  flex-direction: row; align-items: center; gap: 0.6rem;
  padding: 0.45rem 0.75rem;
}
#play { min-width: 2.2em; }
/* the system floors a select face at 12rem for menus of words; ×300 is
   not twelve rems of words, and every pixel the face hoards here comes
   straight out of the scrub */
#console .picker { flex: 0 0 auto; width: 5.4em; }
#console .picker .select-trigger { min-width: 0; width: 100%; }
#scrub-wrap { position: relative; flex: 1; height: 30px; }
#scrub { -webkit-appearance: none; appearance: none;
  position: absolute; inset: 0; width: 100%; margin: 0;
  background: transparent; border: none; z-index: 3; }
#scrub::-webkit-slider-runnable-track { height: 2px; margin-top: 14px;
  background: var(--pp-line); }
#scrub::-webkit-slider-thumb { -webkit-appearance: none; width: 3px;
  height: 18px; margin-top: -8px; background: var(--pp-ink);
  cursor: var(--pp-cursor-resize-col, ew-resize); }
#scrub::-moz-range-track { height: 2px; background: var(--pp-line); }
#scrub::-moz-range-thumb { width: 3px; height: 18px; border: none;
  border-radius: 0; background: var(--pp-ink);
  cursor: var(--pp-cursor-resize-col, ew-resize); }
#scrub-band { position: absolute; top: 13px; height: 4px;
  background: var(--pp-plate-3); z-index: 2; pointer-events: none; }
#scrub-marks { position: absolute; inset: 0; z-index: 1;
  pointer-events: none; }
#scrub-marks i { position: absolute; top: 10px; width: 1px; height: 10px;
  background: var(--pp-line); }
#scrub-marks label { position: absolute; top: -1px;
  transform: translateX(-50%); font-family: var(--pp-font-mono);
  font-size: 0.55rem; letter-spacing: 0.05em; color: var(--pp-faint); }
.readouts { display: flex; align-items: baseline; gap: 0.9rem;
  white-space: nowrap; font-size: 0.74rem; }
.readouts code { font-variant-numeric: tabular-nums; }
.readouts #clock-ut { font-size: 0.95rem; font-weight: 700; }
.readouts .ro-unit { font-size: 0.6rem; color: var(--pp-faint);
  margin-left: -0.55rem; }

/* The corner annotations: the altitude key standing directly on the credits
   it belongs with, one pinned stack so neither can wander into the other
   however many lines the credits wrap to. */
#map-annot {
  position: absolute; right: 14px; bottom: 66px; z-index: 900;
  display: flex; flex-direction: column; align-items: flex-end; gap: 5px;
}
#credits {
  max-width: 340px; text-align: right;
  font-family: var(--pp-font-mono); font-size: 0.56rem; line-height: 1.5;
  color: var(--pp-faint);
  text-shadow: 0 0 3px var(--pp-paper), 0 0 3px var(--pp-paper);
}

/* The altitude key: the hypsometric bar every printed atlas carries, at the
   micro scale of the credits it stands on. Swatch, then the metre line
   where that band begins; app.js fills it from the array that paints the
   tiles. The swatches keep a hairline so the water swatch does not dissolve
   into the paper it nearly matches. */
#hypso-key {
  display: flex; align-items: center;
  font-family: var(--pp-font-mono); font-size: 0.56rem; line-height: 1;
  color: var(--pp-faint);
  text-shadow: 0 0 3px var(--pp-paper), 0 0 3px var(--pp-paper);
}
#hypso-key i {
  width: 13px; height: 8px; flex: none;
  border: 1px solid color-mix(in srgb, var(--pp-ink) 25%, transparent);
}
#hypso-key b {
  font-weight: 400; margin: 0 5px 0 3px;
}
#hypso-key b:last-child { margin-left: 2px; }

/* ---- the suitability switch ----------------------------------------- */

#suit-ctl {
  position: absolute; left: 12px; bottom: 66px; z-index: 900;
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
}
#suit-btn { background: var(--pp-paper); font-size: 0.72rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  font-family: var(--pp-font-mono); padding: 0.35em 0.7em;
  display: inline-flex; align-items: center; gap: 0.5em;
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent); }
/* the ID above pins a paper face, so ON is restated at the same weight:
   filled with ink, the one statement every switch here makes */
#suit-btn[aria-pressed="true"] {
  background: var(--pp-edge); border-color: var(--pp-edge);
  color: var(--pp-paper);
}
/* the little ramp says "this key paints the map" before any word does;
   the same viridis the field itself wears, so the key IS its legend */
.ramp-chip { width: 22px; height: 8px; flex: none;
  border: 1px solid color-mix(in srgb, var(--pp-ink) 25%, transparent);
  background: linear-gradient(90deg,
    #440154, #482878, #3e4989, #31688e, #26828e,
    #1f9e89, #35b779, #6ece58, #b5de2b, #fde725); }
.score-legend { display: flex; align-items: center; gap: 4px;
  font-family: var(--pp-font-mono); font-size: 0.56rem;
  color: var(--pp-faint);
  text-shadow: 0 0 3px var(--pp-paper), 0 0 3px var(--pp-paper); }
/* the score scale: viridis, worst to best — the same stops rampColor()
   interpolates in app.js. Data, not chrome: one scale in both modes. */
.suit-ramp { width: 74px; height: 9px; flex: none;
  border: 1px solid color-mix(in srgb, var(--pp-ink) 25%, transparent);
  background: linear-gradient(90deg,
    #440154, #482878, #3e4989, #31688e, #26828e,
    #1f9e89, #35b779, #6ece58, #b5de2b, #fde725); }
.score-legend b { font-weight: 400; }
#suit-legend b:first-of-type { order: -1; }
#suit-mode { margin-left: 4px; }
#suit-prog { width: 120px; height: 5px; border: 1px solid var(--pp-hair);
  background: var(--pp-paper); }
#suit-prog i { display: block; height: 100%; width: 0;
  background: var(--pp-plate-1); transition: width 0.25s; }

/* ---- the search bar on the map (phones) ------------------------------
   Below 900px app.js lifts the event panel's search block out of the
   fold and stands it here, full width at the top of the map — the one
   place a first visit cannot miss it. The drawn magnifier says what the
   field is before the placeholder loads a word. */

#map-search {
  position: absolute; z-index: 1100;
  top: 8px;
  left: calc(8px + env(safe-area-inset-left, 0px));
  right: calc(8px + env(safe-area-inset-right, 0px));
}
#map-search::before {
  content: ''; position: absolute; left: 13px; top: 14px;
  width: 16px; height: 16px; z-index: 6; pointer-events: none;
  background: var(--pp-faint);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6'%3E%3Ccircle cx='6.7' cy='6.7' r='4.4'/%3E%3Cpath d='M10.2 10.2 14.3 14.3'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6'%3E%3Ccircle cx='6.7' cy='6.7' r='4.4'/%3E%3Cpath d='M10.2 10.2 14.3 14.3'/%3E%3C/svg%3E") center / contain no-repeat;
}
#map-search .search { margin: 0; }
#map-search h3 { display: none; }
#map-search .field { padding-left: 2.2rem;
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent); }
#map-search #search-results { position: absolute; left: 0; right: 0;
  top: 100%; margin-top: 4px; z-index: 7; max-height: 240px;
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent); }

/* the one-time hand: a slip of paper above the console, gone at the
   first target or the first ✕, remembered by the browser */
#tap-hint {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(70px + env(safe-area-inset-bottom, 0px)); z-index: 1000;
  display: flex; align-items: center; gap: 0.2rem;
  max-width: calc(100% - 24px);
  background: var(--pp-paper); border: 1px solid var(--pp-line);
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent);
  padding: 0.5rem 0.2rem 0.5rem 0.75rem;
  font-family: var(--pp-font-mono); font-size: 0.7rem; line-height: 1.45;
  color: var(--pp-muted);
}
#tap-hint button { border: none; background: none; color: var(--pp-faint);
  min-width: 40px; min-height: 34px; font-size: 0.8rem;
  cursor: var(--pp-cursor-point, pointer); }

/* ---- the elements dialog -------------------------------------------- */

#add-dlg { border: 1px solid var(--pp-line); background: var(--pp-paper);
  color: var(--pp-ink); padding: 1rem;
  width: min(560px, calc(100vw - 2rem));
  /* a phone with its keyboard up leaves little height; the dialog
     scrolls rather than losing its buttons below the fold */
  max-height: calc(100dvh - 2rem); overflow-y: auto;
  box-shadow: 5px 5px 0 color-mix(in srgb, var(--pp-ink) 14%, transparent); }
#add-dlg::backdrop { background: rgba(0, 0, 0, 0.35); }
#add-dlg h3 { margin: 0 0 0.6rem; font-family: var(--pp-font-body);
  font-size: 0.72rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--w-track-label, 0.06em); color: var(--pp-faint); }
#add-text { width: 100%; resize: vertical;
  font-family: var(--pp-font-mono); font-size: 0.7rem; line-height: 1.5;
  background: var(--pp-surface); color: var(--pp-ink);
  border: 1px solid var(--pp-line); padding: 0.5rem; }
.add-err { min-height: 1em; margin: 0.4rem 0; color: var(--pp-state-danger);
  font-family: var(--pp-font-mono); font-size: 0.68rem; }
.add-row { display: flex; justify-content: flex-end; gap: 0.5rem; }

.which-eclipse #ecl-add { min-width: 2em; padding: 0.2em 0.5em;
  font-size: 0.9rem; line-height: 1; }

/* Small screens. The event panel folds to a chip at top left. The report panel
   becomes a bottom drawer above the console, with a grabber in its head
   dragged between three positions: peek, half and full. app.js does the
   dragging; this block styles it. Everything pressable meets the 44px target,
   and everything pinned to an edge respects the device insets. */

@media (max-width: 900px) {
  .wrap.tool-head > header { padding-top: 0.9rem; padding-bottom: 0.6rem; }
  /* the console owns the bottom edge; the zoom keys step above it, and
     everything pinned to the bottom respects the phone's home bar */
  .leaflet-bottom.leaflet-right {
    margin-bottom: calc(58px + env(safe-area-inset-bottom, 0px)); }
  /* with the drawer up, the keys step over its peek bar too — at the
     higher snaps the drawer simply covers them, which is what a sheet
     over a map does */
  #stage:has(#dossier:not([hidden])) .leaflet-bottom.leaflet-right {
    margin-bottom: calc(118px + env(safe-area-inset-bottom, 0px)); }
  /* the chips clear the search bar standing above them */
  #intel { left: calc(8px + env(safe-area-inset-left, 0px)); top: 58px;
    width: min(300px, 78vw); max-height: calc(46vh - 50px); }
  #dossier {
    left: calc(8px + env(safe-area-inset-left, 0px));
    right: calc(8px + env(safe-area-inset-right, 0px)); top: auto;
    bottom: calc(62px + env(safe-area-inset-bottom, 0px)); width: auto;
    /* the drawer's height belongs to the hand: app.js sets it per snap,
       and this is only where it starts before the first touch */
    height: 44dvh; max-height: none; overflow: hidden;
    transition: height calc(var(--pp-dur-state) * 2) var(--pp-ease-out);
  }
  /* while a finger holds it, the drawer follows the finger, not a curve */
  #dossier.dragging { transition: none; }
  /* rising from the bottom is where the drawer lives; appearing from
     nowhere is what it must not do */
  #dossier:not([hidden]) {
    animation: drawer-rise calc(var(--pp-dur-state) * 2) var(--pp-ease-out);
  }
  #dossier .sheet-head { position: relative; padding-top: 0.85rem;
    /* the head is the drag surface: the browser gets no say in what a
       vertical swipe on it means */
    touch-action: none; }
  /* the grabber: the one mark that says "this edge moves" */
  #dossier .sheet-head::before {
    content: ''; position: absolute; top: 4px; left: 50%;
    width: 36px; height: 4px; transform: translateX(-50%);
    background: color-mix(in srgb, var(--pp-ink) 28%, transparent);
    border-radius: var(--pp-radius-sm);
  }
  /* the fold key made sense when folding was a click; a drawer is
     dragged or tapped, so only ✕ keeps a key */
  #dossier .sheet-fold[data-fold] { display: none; }
  /* at peek the head bar is the whole drawer: the title gives its seat
     to the score and the place it belongs to */
  #dossier.at-peek .sheet-head h2 { display: none; }
  #dossier.at-peek .head-peek { display: block; }
  .head-peek { flex: 1; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap;
    font-size: 0.85rem; line-height: 1.2; }
  .head-peek b { font-family: var(--pp-font-mono); font-weight: 700;
    font-variant-numeric: tabular-nums; margin-right: 0.35rem; }
  #console { left: calc(8px + env(safe-area-inset-left, 0px));
    right: calc(8px + env(safe-area-inset-right, 0px));
    bottom: calc(8px + env(safe-area-inset-bottom, 0px)); gap: 0.4rem;
    padding: 0.35rem 0.5rem; }
  /* the clock stays — a simulation without its clock is unreadable —
     and the umbra telemetry goes, because the scrub needs the room */
  .readouts { gap: 0.45rem; }
  .ro-unit, #ro-shadow, #ro-vel, #ro-dur { display: none; }
  #map-annot { display: none; }
  /* the suitability switch moves to the top corner, where no bottom sheet
     can ever bury it — under the search bar like the event chip */
  #suit-ctl { top: 58px; right: calc(8px + env(safe-area-inset-right, 0px));
    left: auto; bottom: auto; align-items: flex-end; }
}
@keyframes drawer-rise {
  from { transform: translateY(24px); opacity: 0; }
}
@media (max-width: 520px) {
  /* one row of chrome — the arrow, the name, the two keys — then the
     eclipse row, then the map. Every rem here is taken from the map. */
  .wrap.tool-head > header { padding-top: 0.5rem; padding-bottom: 0.45rem; gap: 0.45rem; }
  .wrap.tool-head header > div:first-child {
    display: flex; align-items: center; gap: 0.55rem; min-width: 0; }
  /* the arrow alone: font-size 0 hides the words while a screen reader
     keeps the link's full name */
  .back-link { font-size: 0; gap: 0; }
  .back-link svg { width: 15px; height: 15px; }
  .title-row { margin: 0; gap: 0.5rem; min-width: 0; }
  .tool-title { font-size: 1.3rem; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
  .which-eclipse { width: 100%; padding-right: 30px; }
  .which-eclipse .we-label { display: none; }
  .which-eclipse .picker, .which-eclipse select { flex: 1; width: auto; }
  /* the share and pin keys drop their words and ride beside the title,
     so the masthead spends two rows, not four — the map is the page */
  .share-btn [data-share-label], .share-btn [data-pin-label] { display: none; }
  .share-btn { padding: 0 0.5rem; }
  /* follow keeps its seat as a bare reticle — chasing the umbra by
     thumb-panning is the one map gesture a phone is genuinely bad at */
  #track { min-width: 44px; padding: 0 0.55rem; }
  #track .track-word { display: none; }
  /* the event panel opens as a full-width drawer rather than a 300px
     card — search results and the timeline need the measure — and folds
     back to a chip that leaves the map alone */
  #intel { left: 8px; right: 8px; width: auto; max-height: 52vh; }
  #intel.folded { width: auto; right: auto; }
  /* the scrub is the simulation's main control, and squeezed beside the
     clock it was 120px of a four-hour eclipse. It takes its own row;
     everything above the console steps up to clear the taller bar. */
  #console { flex-wrap: wrap; row-gap: 0.25rem; }
  #scrub-wrap { flex: 1 1 100%; order: 5; }
  .readouts { margin-left: auto; }
  #dossier { bottom: calc(102px + env(safe-area-inset-bottom, 0px)); }
  #tap-hint { bottom: calc(112px + env(safe-area-inset-bottom, 0px)); }
  .leaflet-bottom.leaflet-right {
    margin-bottom: calc(100px + env(safe-area-inset-bottom, 0px)); }
  #stage:has(#dossier:not([hidden])) .leaflet-bottom.leaflet-right {
    margin-bottom: calc(158px + env(safe-area-inset-bottom, 0px)); }
}

/* A phone held sideways: the masthead shrinks to a strip — the arrow
   folds into the title row the way the narrow layout folds it — so the
   map keeps real height, and the top drawers cannot swallow what is
   left of it. */
@media (max-width: 900px) and (max-height: 520px) {
  .wrap.tool-head > header { padding-top: 0.35rem; padding-bottom: 0.3rem; }
  .wrap.tool-head header > div:first-child {
    display: flex; align-items: center; gap: 0.55rem; min-width: 0; }
  .back-link { font-size: 0; gap: 0; }
  .back-link svg { width: 15px; height: 15px; }
  .title-row { margin: 0; }
  .tool-title { font-size: 1.1rem; }
  #intel { max-height: calc(100% - 170px); }
}

/* Touch: every control meets the target size, and the hover tips that
   cannot fire do not hold information hostage — a long press on anything
   tipped speaks the same words (app.js), and the essentials are in the
   panels regardless. */
@media (pointer: coarse) {
  .sheet-fold { min-width: 44px; min-height: 34px; }
  .sheet-head { padding-top: 0.6rem; padding-bottom: 0.6rem; }
  /* a 3px scrub thumb is a mouse's control; a thumb needs a post */
  #scrub::-webkit-slider-thumb { width: 16px; height: 24px;
    margin-top: -11px; }
  #scrub::-moz-range-thumb { width: 16px; height: 24px; }
  #play { min-width: 44px; }
  .tl-row .btn-ghost { min-height: 30px; padding: 0.05em 0.8em; }
  #search-results li { padding: 0.55rem 0.6rem; }
  #sweep-list li { padding: 0.5rem 0.15rem; }
  #suit-btn { padding: 0.55em 0.9em; }
  #suit-grade, #suit-dur { padding: 0.5em 0.8em; }
  .which-eclipse #ecl-add { min-width: 2.4em; min-height: 36px; }
  /* under 16px, iOS zooms the page into a focused field and leaves it
     there; at 16px it trusts the layout */
  #search, #add-text { font-size: 1rem; }
  /* the zoom and locate keys grow into finger keys */
  .leaflet-control-zoom a { width: 40px !important; height: 40px !important;
    line-height: 40px !important; font-size: 19px; }
  .locate-ctl a { width: 40px; height: 40px; }
}
