/* ============================================================================
   PetMap — app styles.
   Sits on top of shared/theme.css + shared/components.css, which are a
   drop-in style pack from Tigrriis/style-tests (currently "Soft Product").
   Everything here is expressed in that pack's custom properties, so swapping
   those two files re-skins the app without touching this one.

   Rule of thumb when editing: no hex codes, no hard-coded radii, no
   text-transform. A pack owns those. Anything hard-coded here is a bug that
   only surfaces the next time the pack changes.
   ============================================================================ */

/* --------------------------------------------------- STATUS PALETTE ------ */
/* Report status is the one piece of semantics a style pack cannot own: a pack
   knows "danger", it does not know "missing". These alias the pack's signal
   tokens so the meaning is declared in exactly one place — the legend dots and
   pins below read them, and so does PetMapUtil.COLOURS in app.js, which used
   to keep its own copy of the same mapping.

   --status-missing is deliberately named rather than using --danger directly.
   --danger is also the Remove button, the field-error red and the map error
   pill; a missing pet is not an error, and the day that hue should stop being
   the destructive red, this is the only line that has to change. */
:root {
  --status-missing:  var(--danger);
  --status-found:    var(--accent);
  --status-sighting: var(--warn);
  --status-reunited: var(--ok);
  --status-coverage: var(--ok);
}

/* ------------------------------------------------ FILLED CONTROLS -------- */
/* The pack hard-codes #fff on every filled control, and its dark palette
   *lightens* --accent and --danger. That lightening is right for those tokens
   as text — a tag, a link, an error message on a dark page — and wrong beneath
   a white label: white lands at 3.49:1 on a dark-mode primary button and
   2.87:1 on a danger one.

   Fixed by pinning the *fill*, not the text. Filled controls use their own
   token, dark enough for white in either theme, so a button looks the same in
   both — white on indigo, the way a primary button is expected to look.

   The first attempt did the opposite: it kept the pack's lightened fill and
   flipped the label dark. That passes contrast (5.29:1) and still reads as
   broken, because dark text on a pale button looks unstyled rather than
   deliberate. Passing a ratio is not the same as looking right. */
:root {
  --accent-fill: var(--accent);      /* #5b5bd6 — white on it is 5.37:1 */
  --danger-fill: var(--danger);      /* #c63a4f — white on it is 5.10:1 */
}
[data-theme="dark"] {
  /* The light values, deliberately: dark mode lightens these tokens for text,
     which is exactly what makes them fail as a fill. */
  --accent-fill: #5b5bd6;
  --danger-fill: #c63a4f;
}

.btn--primary { --_bg: var(--accent-fill); --_fg: #fff; }
.btn--danger  { --_bg: var(--danger-fill); --_fg: #fff; }
@media (hover: hover) {
  .btn--primary:hover { --_bg: var(--accent-hi); color: #fff; }
  .btn--danger:hover  { background: var(--danger-fill); color: #fff; }
}

/* Light --danger (#d2455a) is fractionally too light both for white text
   (4.43:1) and as text on its own --danger-soft fill (3.94:1, the "Missing"
   tag). One step darker clears both at 5.10 and 4.53.

   Scoped with :not([data-theme="dark"]) deliberately: a bare :root here has
   the same specificity as the pack's [data-theme="dark"] block and loads
   after it, so it would silently override the dark-mode red as well. */
:root:not([data-theme="dark"]) { --danger: #c63a4f; }

/* ------------------------------------------------------------ SHELL ------ */

body { background: var(--paper); color: var(--ink-800); }

.app-nav { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* Deliberately no colour override on the header nav. Arete HUD's header was a
   dark console bar so it needed --console-fg; Soft Product's is a light panel,
   where that same rule made the links near-invisible. Let the pack's own
   .btn--ghost decide, and let a pack with a dark header handle it in the pack. */

/* The contract has no .accent primitive, so the brand highlight lives here.
   It was silently doing nothing before — no pack ever defined it. */
.app-header__logo .accent { color: var(--accent); }

.page {
  max-width: 62rem;
  margin: 0 auto;
  padding: var(--sp-5) var(--sp-4) var(--sp-8);
}
.page--narrow { max-width: 34rem; }

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-5);
}
.page-head h1 {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-5);
  letter-spacing: -0.01em;
}
/* The pack's lighter ink steps do not clear AA on its own light surfaces:
   --ink-500 measures about 3.98:1 and --ink-400 about 2.83:1, against a 4.5:1
   floor for body text. So every piece of secondary and tertiary text in this
   app sits on --ink-600, and hierarchy is carried by size and weight instead
   of by lightness. Do not "restore" a lighter tone for subtlety — it fails.

   This is a pack bug rather than an app one, and worth fixing upstream in
   Tigrriis/style-tests so the other apps inherit it; these overrides can go
   the day it is. Dark mode clears AA on its own and needs no equivalent. */
.muted,
.field-hint,
.app-header__sub { color: var(--ink-600); }
.req { color: var(--danger); }

.site-footer {
  display: flex;
  gap: var(--sp-4);
  flex-wrap: wrap;
  align-items: center;
  padding: var(--sp-4);
  border-top: 1px solid var(--line);
  font-size: var(--fs-1);
  color: var(--ink-600);
}
.site-footer a { color: var(--accent-ink); }

/* Persistent until the address is confirmed — not a dismissible flash, since
   an unverified account is quietly missing every message sent to it. */
.verify-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  background: var(--warn-soft);
  border-bottom: 1px solid var(--warn);
  font-size: var(--fs-2);
}
.verify-bar form { margin: 0; }

/* ----------------------------------------------------------- FLASHES ----- */

.flashes {
  position: fixed;
  top: 64px;
  right: var(--sp-4);
  z-index: 1200;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  max-width: min(26rem, calc(100vw - 2rem));
  pointer-events: none;
}
.flash {
  pointer-events: auto;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line-strong);
  border-left-width: 3px;
  background: var(--panel);
  box-shadow: var(--shadow-2);
  font-size: var(--fs-2);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.flash--error   { border-left-color: var(--danger); background: var(--danger-soft); }
.flash--success { border-left-color: var(--ok);     background: var(--ok-soft); }
.flash--info    { border-left-color: var(--accent); background: var(--accent-soft); }
.flash.is-fading { opacity: 0; transform: translateX(8px); }

.banner {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line-strong);
  border-left-width: 3px;
  margin-bottom: var(--sp-4);
  font-size: var(--fs-2);
}
.banner--danger { border-left-color: var(--danger); background: var(--danger-soft); }
.banner--ok     { border-left-color: var(--ok);     background: var(--ok-soft); }

/* --------------------------------------------------------- MAP PAGE ------ */

.body--map { overflow: hidden; }

.map-shell { display: block; }

.map-layout {
  display: grid;
  /* Filters | map | results. The map takes whatever is left. */
  grid-template-columns: 17rem minmax(0, 1fr) 19rem;
  gap: 0;
  /* --header-h is measured by app.js, because header height belongs to the
     style pack. The fallback only applies for the instant before scripts run. */
  height: calc(100vh - var(--header-h, 64px));
}
.map-filters, .map-results {
  border-radius: 0;
  border-top: none;
  border-bottom: none;
  overflow-y: auto;
  background: var(--panel);
}
.map-filters { border-left: none; }
.map-results { border-right: none; }

.map-main { position: relative; min-width: 0; }
#map { position: absolute; inset: 0; background: var(--panel-2); }

.map-status {
  position: absolute;
  left: 50%;
  bottom: var(--sp-4);
  transform: translateX(-50%);
  z-index: 500;
  padding: var(--sp-2) var(--sp-4);
  background: var(--panel);
  color: var(--ink-700);
  font-size: var(--fs-1);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  max-width: 90%;
  text-align: center;
}
/* This used to sit on --console-*, which the contract keeps dark in both
   modes — a piece of instrument hardware floating over a soft light UI, and a
   holdover from the HUD pack. It reads as a card now. The error state can use
   the danger pair directly because both --danger and --danger-soft flip with
   the theme, so the contrast holds in light and dark alike; that was the one
   thing the console surface was buying, and it is no longer needed. */
.map-status.is-error {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
}

/* Filter chips */
.chipset { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.chip {
  font: 500 var(--fs-1)/1 var(--font-ui);
  letter-spacing: var(--tracking-cap);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line-strong);
  background: var(--panel-2);
  color: var(--ink-600);
  cursor: pointer;
  border-radius: var(--radius);
  transition: all var(--dur) var(--ease);
}
.chip:hover { border-color: var(--accent); color: var(--accent-ink); }
.chip.is-on {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: #fff;
}
.chip--missing.is-on { background: var(--danger-fill); border-color: var(--danger-fill); }
.chip--found.is-on   { background: var(--accent-fill); border-color: var(--accent-fill); }

.map-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-size: var(--fs-1);
  color: var(--ink-600);
}
.dot {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: middle;
}
/* Shape as well as colour, matching .pin-icon--* below — the legend is only
   useful if its swatch looks like the thing on the map. */
.dot--missing  { background: var(--status-missing); }
.dot--found    { background: var(--status-found); border-radius: 3px; }
.dot--reunited { background: var(--status-reunited); position: relative; }
.dot--reunited::after {
  content: "";
  position: absolute;
  inset: 2px;
  border-radius: 50%;
  background: var(--panel);
}
.dot--searched { background: var(--status-coverage); opacity: 0.45; border-radius: 2px; }

/* ------------------------------------------------- SEARCH COVERAGE ------- */

.track-list { display: grid; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.track-row {
  display: grid;
  grid-template-columns: 12px 1fr;
  gap: var(--sp-3);
  align-items: start;
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--line);
}
.track-row .dot { margin-top: 5px; }
.track-row small { color: var(--ink-600); font-size: var(--fs-0); }
.track-row__note { color: var(--ink-600); font-size: var(--fs-1); }

.search-controls {
  padding: var(--sp-4);
  border: 1px solid var(--line-strong);
  background: var(--panel-2);
  margin-bottom: var(--sp-4);
}

/* Shown every time, above the start button. The trim is only ~50 m now, so
   this advice is the actual protection rather than a footnote to it. */
.safety-warning {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4);
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  border-left-width: 4px;
  font-size: var(--fs-2);
  line-height: 1.5;
}
.safety-warning__icon {
  font-size: var(--fs-4);
  line-height: 1.2;
  color: var(--warn);
  flex: none;
}
.safety-warning strong { display: block; margin-bottom: 2px; }

.live-readout {
  display: flex;
  gap: var(--sp-6);
  flex-wrap: wrap;
  padding-bottom: var(--sp-3);
  margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
}
.live-status {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  color: var(--ink-600);
  margin: 0 0 var(--sp-3);
}
.live-status.is-error { color: var(--danger); }

/* Leaflet draws each covered square as its own rectangle; keeping them
   borderless means overlapping cells read as one continuous wash rather than
   as a grid of outlines. */
.leaflet-interactive.coverage-cell { stroke: none; }

/* Results list */
.result-item {
  display: grid;
  grid-template-columns: 44px 1fr 12px;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--line);
  color: inherit;
  text-decoration: none;
}
.result-item:hover { background: var(--panel-2); }
.result-item img,
.result-item__blank {
  width: 44px; height: 44px;
  object-fit: cover;
  background: var(--panel-3);
  border: 1px solid var(--line);
}
.result-item small { color: var(--ink-600); font-size: var(--fs-0); }
.result-item .dot { margin: 0; }

/* Leaflet pins */
/* The ring separates a pin from the basemap, which swaps light/dark with the
   theme — so it tracks --panel rather than a fixed white.

   Shape carries the report type alongside colour. Missing vs found is the
   distinction the entire map turns on, and it was previously red vs indigo and
   nothing else — red against green (reunited) being exactly the pair the
   common colour vision deficiencies collapse. The dot's fill is set inline by
   pinIcon(), so the shapes here work with whatever colour it is given:
   circle = missing, rounded square = found, diamond = sighting, ring =
   reunited. Keep these in step with .dot--* in the legend above. */
.pin-icon__dot {
  display: block;
  width: 16px; height: 16px;
  margin: 3px;
  border-radius: 50%;
  border: 2px solid var(--panel);
  box-shadow: var(--shadow-2);
}
.pin-icon--found .pin-icon__dot { border-radius: 4px; }

/* Rotated, so it is sized down to keep the 22.6px diagonal of a 16px square
   inside the 22px icon box Leaflet was told to expect. */
.pin-icon--sighting .pin-icon__dot {
  width: 14px; height: 14px;
  margin: 4px;
  border-radius: 2px;
  transform: rotate(45deg);
}

/* A ring, punched out with an overlay rather than a transparent background:
   the fill colour arrives inline on this element and cannot be overridden from
   here. An inset shadow was the obvious move and is the wrong one — it paints
   inward from the edge, so what survives is a shrunken disc, not a ring. */
.pin-icon--reunited .pin-icon__dot { position: relative; }
.pin-icon--reunited .pin-icon__dot::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--panel);
}

.pin-icon--drag .pin-icon__dot { width: 20px; height: 20px; margin: 1px; }

.pet-popup { display: flex; gap: var(--sp-3); }
.pet-popup__img {
  width: 72px; height: 72px; object-fit: cover;
  border: 1px solid var(--line); flex: none;
}
.pet-popup__title { font-weight: 700; font-size: var(--fs-3); }
.pet-popup__meta  { font-size: var(--fs-1); color: var(--ink-600); }
.pet-popup__note {
  font-size: var(--fs-0);
  color: var(--warn);
  letter-spacing: var(--tracking-cap);
  margin-top: 2px;
}
.pet-popup .btn { margin-top: var(--sp-2); }

/* Leaflet ships `.leaflet-container a { color: #0078A8 }`. That selector is
   (0,1,1) and `.btn { color: var(--_fg) }` is only (0,1,0), so every button
   rendered inside a map popup came out link-blue on its own fill — #0078A8 on
   the indigo, which is barely legible. Restated here at (0,2,1) so the button's
   own foreground wins wherever it appears.

   Worth knowing if you add any other control inside a map: Leaflet's anchor
   styling outranks a single class, so anchors need the extra specificity. */
.leaflet-container a.btn,
.leaflet-container a.btn:hover { color: var(--_fg); }

/* Leaflet's own chrome, re-skinned to the token set. */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background: var(--panel);
  color: var(--ink-800);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}
.leaflet-popup-content { margin: var(--sp-3); font-family: var(--font-ui); }
.leaflet-container { font-family: var(--font-ui); background: var(--panel-2); }
.leaflet-bar a {
  background: var(--panel);
  color: var(--ink-700);
  border-bottom-color: var(--line);
}
.leaflet-bar a:hover { background: var(--panel-2); }
.marker-cluster div {
  background: var(--accent-fill);
  color: #fff;                 /* --accent-fill stays dark enough for white in both themes */
  font-family: var(--font-ui);
  font-weight: 700;
}
/* Halo derived from the accent rather than a fixed colour, so it follows the
   pack. The flat fallback covers browsers without color-mix. */
.marker-cluster { background: var(--accent-soft); }
@supports (background: color-mix(in srgb, red 50%, transparent)) {
  .marker-cluster { background: color-mix(in srgb, var(--accent) 28%, transparent); }
}

/* ---------------------------------------------------------- FORMS -------- */

.form-page { max-width: 52rem; margin: 0 auto; padding: var(--sp-5) var(--sp-4) var(--sp-8); }
.form-page .panel { margin-bottom: var(--sp-4); }

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--sp-4);
}
.field--wide { grid-column: 1 / -1; }

.input-row { display: flex; gap: var(--sp-2); }
.input-row .input { flex: 1; }
.input--sm { padding: 4px 6px; font-size: var(--fs-1); width: auto; }

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

.radio-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--sp-3); }
.radio-card {
  display: block;
  padding: var(--sp-4);
  border: 1px solid var(--line-strong);
  background: var(--panel-2);
  cursor: pointer;
  transition: all var(--dur) var(--ease);
}
.radio-card:hover { border-color: var(--accent); }
.radio-card:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.radio-card input { margin-right: var(--sp-2); }
.radio-card__title { font-weight: 700; font-size: var(--fs-3); }
.radio-card__sub { display: block; margin-top: 2px; font-size: var(--fs-1); color: var(--ink-600); }

.pin-map-wrap { margin: var(--sp-4) 0; }
.pin-map {
  height: 22rem;
  border: 1px solid var(--line-strong);
  background: var(--panel-2);
}
.pin-readout {
  margin: var(--sp-2) 0 0;
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  color: var(--ink-600);
}
.pin-readout.is-error { color: var(--danger); }

.check { display: flex; gap: var(--sp-3); align-items: flex-start; margin-top: var(--sp-4); }
.check input { margin-top: 3px; flex: none; }
.check small { display: block; margin-top: 2px; }

.form-actions { display: flex; gap: var(--sp-3); align-items: center; margin-top: var(--sp-5); }

.photo-strip { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.photo-strip__item { margin: 0; text-align: center; }
.photo-strip__item img,
.photo-strip__thumb img {
  display: block;
  width: 96px; height: 96px;
  object-fit: cover;
  border: 1px solid var(--line);
  margin-bottom: var(--sp-1);
}
.photo-strip__thumb { padding: 0; border: none; background: none; cursor: pointer; }
.photo-strip__thumb:hover img { border-color: var(--accent); }

/* --------------------------------------------------------- DETAIL -------- */

.detail { max-width: 60rem; margin: 0 auto; padding: var(--sp-5) var(--sp-4) var(--sp-8); }
.detail .panel { margin-bottom: var(--sp-4); }

.detail__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.detail__head h1 { margin: var(--sp-2) 0 var(--sp-1); font-size: var(--fs-6); line-height: 1.1; }
.detail__sub { margin: 0; color: var(--ink-600); font-size: var(--fs-2); }
.detail__owner-actions { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.detail__owner-actions form { margin: 0; }

.badges { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

.detail__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}
.detail__hero {
  width: 100%;
  max-height: 26rem;
  object-fit: cover;
  border: 1px solid var(--line-strong);
  background: var(--panel-2);
  display: block;
  margin-bottom: var(--sp-3);
}
.detail__nophoto {
  display: grid;
  place-items: center;
  height: 12rem;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  color: var(--ink-600);
  font-size: var(--fs-1);
  letter-spacing: var(--tracking-cap);
}
.detail__facts { margin: 0; }
.detail__desc { white-space: pre-wrap; margin-top: var(--sp-4); }
.detail__phone { margin-top: var(--sp-4); font-size: var(--fs-4); }
.detail__footnote { text-align: center; color: var(--ink-600); font-size: var(--fs-1); }

.facts { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2) var(--sp-4); margin: 0; }
.facts dt {
  font-size: var(--fs-0);
  letter-spacing: var(--tracking-cap);
  color: var(--ink-600);
}
.facts dd { margin: 0; }

.detail-map { height: 20rem; border: 1px solid var(--line-strong); }

.sightings { list-style: none; margin: 0 0 var(--sp-4); padding: 0; }
.sighting {
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--line);
}
.sighting__when {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  font-weight: 600;
  font-size: var(--fs-2);
}
/* Quieter than the status tags — it qualifies an entry rather than labelling
   an outcome, and should not compete with "Missing" or "Reunited" for attention. */
.tag--linked {
  background: var(--panel-3);
  color: var(--ink-600);
  border-color: var(--line-strong);
  font-weight: 500;
}
.sighting__note { margin: var(--sp-1) 0; white-space: pre-wrap; }
.sighting__photo { max-width: 14rem; border: 1px solid var(--line); display: block; margin: var(--sp-2) 0; }
.sighting__meta {
  display: flex; gap: var(--sp-2); align-items: center;
  font-size: var(--fs-0); color: var(--ink-600);
}
.sighting__meta form { margin: 0; }

.btn-link {
  background: none; border: none; padding: 0;
  color: var(--danger); cursor: pointer;
  font: inherit; font-size: var(--fs-0); text-decoration: underline;
}

.disclosure { border-top: 1px solid var(--line); padding-top: var(--sp-3); }
.disclosure summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--accent-ink);
  margin-bottom: var(--sp-3);
}

/* ---------------------------------------------------- LISTS & AUTH ------- */

.report-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.report-card {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) auto;
  gap: var(--sp-4);
  align-items: center;
  padding: var(--sp-3);
  border: 1px solid var(--line);
  background: var(--panel);
}
.report-card__photo img,
.report-card__nophoto {
  display: grid; place-items: center;
  width: 72px; height: 72px;
  object-fit: cover;
  border: 1px solid var(--line);
  background: var(--panel-3);
  font-size: var(--fs-0);
  color: var(--ink-600);
}
.report-card__title {
  display: block;
  margin-top: var(--sp-1);
  font-weight: 700;
  font-size: var(--fs-3);
  color: inherit;
  text-decoration: none;
}
.report-card__title:hover { color: var(--accent-ink); }
.report-card__meta { margin: 2px 0 0; font-size: var(--fs-1); color: var(--ink-600); }
.report-card__actions { display: flex; gap: var(--sp-2); align-items: center; }
.report-card__actions form { margin: 0; }

.empty {
  text-align: center;
  padding: var(--sp-8) var(--sp-4);
  color: var(--ink-600);
}
.error-code {
  font-family: var(--font-mono);
  font-size: var(--fs-7);
  color: var(--ink-300);
  margin: 0;
}

.auth-card { margin-top: var(--sp-6); }
.auth-alt { margin-top: var(--sp-4); font-size: var(--fs-2); text-align: center; }
.btn--block { width: 100%; justify-content: center; }

.prose ul { padding-left: 1.2rem; }
.prose li { margin-bottom: var(--sp-2); }

.inline-form { display: inline-flex; gap: 4px; align-items: center; margin: 0; }
.row-actions { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.cell-clip { max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
tr.is-removed { opacity: 0.55; }
.tab.is-active { border-bottom-color: var(--accent); color: var(--accent-ink); }

/* --------------------------------------------------------- MOBILE -------- */

@media (max-width: 60rem) {
  .map-layout {
    grid-template-columns: 1fr;
    grid-template-rows: auto minmax(20rem, 55vh) auto;
    height: auto;
  }
  .body--map { overflow: auto; }
  .map-main { height: 55vh; }
  .map-filters, .map-results { max-height: none; border: 1px solid var(--line); }
  .detail__grid { grid-template-columns: 1fr; }
  .detail__head h1 { font-size: var(--fs-5); }
  .report-card { grid-template-columns: 56px minmax(0, 1fr); }
  .report-card__actions { grid-column: 1 / -1; }
  .app-header { flex-wrap: wrap; height: auto; padding: var(--sp-2); }
  .flashes { top: auto; bottom: var(--sp-4); left: var(--sp-4); right: var(--sp-4); max-width: none; }
}
