/* Transparent Flock — themed surveillance-tracker UI */

:root, [data-theme="dark"] {
    /* Topbar height — shared by .topbar (min-height) and the sticky
       top-offset of .table th, so a scrolled table header sits flush
       against the topbar's bottom edge instead of guessing a magic
       number (was a hardcoded 60px ~13px below the real ~46px bar). */
    --topbar-h: 48px;
    --bg: #0a0c10;
    --bg-elev: #11151c;
    --bg-elev-2: #161b24;
    --border: #1f2733;
    --border-bright: #2c3645;
    --text: #e6edf3;
    --text-dim: #8b949e;
    --text-mute: #6e7681;
    --accent: #00ffd1;
    --accent-dim: #008f75;
    --added: #3fb950;
    --added-glow: rgba(63, 185, 80, 0.18);
    --deleted: #ff4d6d;
    --deleted-glow: rgba(255, 77, 109, 0.18);
    --modified: #ffb86b;
    --modified-glow: rgba(255, 184, 107, 0.18);
    --warn: #ffd166;
    --shadow: 0 1px 0 rgba(255,255,255,0.02);
    --tile-url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png';
    --map-bg: #0c1116;
}

[data-theme="light"] {
    --bg: #f6f8fb;
    --bg-elev: #ffffff;
    --bg-elev-2: #eef2f7;
    --border: #d8dee6;
    --border-bright: #b8c1cc;
    --text: #1a2332;
    --text-dim: #5a6677;
    --text-mute: #8a94a3;
    --accent: #0a7d6a;
    --accent-dim: #0a7d6a;
    --added: #1a7f37;
    --added-glow: rgba(26, 127, 55, 0.12);
    --deleted: #cf222e;
    --deleted-glow: rgba(207, 34, 46, 0.10);
    --modified: #bf6500;
    --modified-glow: rgba(191, 101, 0, 0.10);
    --warn: #9a6b00;
    --shadow: 0 1px 2px rgba(20, 30, 50, 0.04);
    --map-bg: #e8edf3;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--text);
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
    font-size: 14px;
    line-height: 1.5;
    transition: background 0.15s, color 0.15s;
}

/* Sticky-footer scaffolding: body is a vertical flex column tall enough to
   fill the viewport; the main container claims all leftover space; the
   footer naturally lands at the bottom on short pages and right after
   content on long ones. */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}
.container {
    flex: 1 0 auto;
}
.site-footer {
    flex-shrink: 0;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code, .mono { font-family: var(--mono, 'JetBrains Mono', 'Fira Code', 'SF Mono', Menlo, Consolas, monospace); font-size: 0.92em; }

/* ---------- top bar ---------- */
.topbar {
    display: flex;
    align-items: center;
    min-height: var(--topbar-h);
    gap: 16px;
    padding: 8px clamp(16px, 3vw, 24px);
    background: var(--bg-elev);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 1000;
    backdrop-filter: blur(8px);
    flex-wrap: wrap;
}

.brand {
    font-family: var(--mono, monospace);
    font-weight: 700;
    font-size: 16px;
    letter-spacing: 0.5px;
    color: var(--text);
    display: flex;
    align-items: center;
    gap: 8px;
}
.brand .brand-logo {
    /* Single black-fill SVG, recolored per theme via CSS mask-image:
       background-color drives the visible color, the SVG just defines
       the shape. Aspect ~1.47:1 after viewBox tightened to bird bbox. */
    height: 30px;
    width: 44px;
    flex: 0 0 auto;
    display: inline-block;
    background-color: currentColor;
    color: #000;  /* light theme: black mark */
    -webkit-mask: url(/static/logo.svg) no-repeat center / contain;
            mask: url(/static/logo.svg) no-repeat center / contain;
}
[data-theme="dark"] .brand .brand-logo { color: var(--accent); }

.nav { display: flex; gap: 4px; flex: 1; }
/* Push Sign in to the far right of the nav — common 'account' UX placement. */
.nav a.nav-signin { margin-left: auto; }
.nav a {
    padding: 6px 12px;
    border-radius: 6px;
    color: var(--text-dim);
    font-size: 13px;
    transition: all 0.15s;
}
.nav a:hover { background: var(--bg-elev-2); color: var(--text); text-decoration: none; }
.nav a.active { background: var(--bg-elev-2); color: var(--accent); }

/* Hamburger button — shown only on narrow screens (see @media below).
   On wide, .nav is the visible primary nav and .nav-toggle is hidden. */
.nav-toggle {
    display: none;
    background: var(--bg-elev-2);
    border: 1px solid var(--border-bright);
    color: var(--text-dim);
    padding: 6px 10px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
}
.nav-toggle:hover { color: var(--text); border-color: var(--accent); }
.nav-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.topbar-search {
    display: flex;
    align-items: stretch;
    gap: 0;
    margin-left: auto;
    margin-right: 8px;
    min-width: 280px;
}
.topbar-search input[type="search"] {
    flex: 1 1 auto;
    background: var(--bg-elev-2);
    border: 1px solid var(--border-bright);
    color: var(--text);
    padding: 6px 12px;
    border-radius: 6px 0 0 6px;
    font-family: var(--mono, monospace);
    font-size: 12px;
    min-width: 0;
    outline: none;
    transition: border-color 0.12s;
}
.topbar-search input[type="search"]:focus { border-color: var(--accent); }
.topbar-search button {
    background: var(--bg-elev-2);
    border: 1px solid var(--border-bright);
    border-left: none;
    color: var(--text-dim);
    padding: 0 12px;
    border-radius: 0 6px 6px 0;
    cursor: pointer;
    font-size: 14px;
}
.topbar-search button:hover { color: var(--accent); border-color: var(--accent-dim); }
@media (max-width: 900px) {
    .topbar-search { display: none; }   /* mobile: search via the /search page nav link */
}

.snapshot-picker {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-dim);
}
.snapshot-picker select {
    background: var(--bg-elev-2);
    border: 1px solid var(--border-bright);
    color: var(--text);
    padding: 6px 10px;
    border-radius: 6px;
    font-family: var(--mono, monospace);
    font-size: 12px;
    cursor: pointer;
}

.osm-user {
    font-family: var(--mono, monospace);
    font-size: 12px;
    color: var(--text);
    border-bottom: 1px dotted var(--border-bright);
}
.osm-user:hover { color: var(--accent); text-decoration: none; }
.osm-user.suspect-context { color: var(--warn); }

/* ---------- /search layout ---------- */
.search-summary { margin-bottom: 16px; }
.search-h1 {
    margin: 4px 0;
    font-family: var(--mono, monospace);
    font-size: 16px;
    color: var(--text);
    font-weight: 600;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}
.search-h1 .search-q {
    background: var(--bg-elev-2);
    border: 1px solid var(--border-bright);
    color: var(--accent);
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 13px;
}

.grid.search-layout {
    grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
    align-items: start;
}
@media (max-width: 1100px) {
    .grid.search-layout { grid-template-columns: 1fr; }
}

.search-rail { display: flex; flex-direction: column; gap: 16px; }
.search-results {
    min-width: 0;   /* allow the grid cell to shrink */
    /* The table now lives inside a .table-scroll wrapper (same pattern
       as .changes-feed-body) — that wrapper IS the sticky-scope and
       handles horizontal pan at narrow widths.  See .table-scroll
       block elsewhere in this stylesheet. */
}

.search-table {
    table-layout: fixed;
    width: 100%;
    min-width: 880px;  /* matches .changes-feed-body — table-scroll wrapper pans horizontally below this */
}
.search-table td, .search-table th {
    /* Break at word boundaries only. `overflow-wrap: anywhere` was the cause
       of the "one character per line" pile-up on Tags when the viewport was
       narrow — let horizontal scroll handle width instead of per-character
       breaking. */
    overflow-wrap: break-word;
    word-break: normal;
}
.search-table th:nth-child(1), .search-table td:nth-child(1) { width: 130px; }   /* OSM ID — 13 digits + chrome */
.search-table th:nth-child(2), .search-table td:nth-child(2) { width: 17%; }     /* Operator */
.search-table th:nth-child(3), .search-table td:nth-child(3) { width: 12%; }     /* Manufacturer — usually short ("Flock", "Ubicquia") */
.search-table th:nth-child(4), .search-table td:nth-child(4) { width: 20%; }     /* Last change — badge + user link + verdict + italic comment */
.search-table th:nth-child(5), .search-table td:nth-child(5) { width: 170px; padding-right: 18px; }  /* Coords — extra right gutter so it doesn't crash into Tags */
.search-table th:nth-child(6), .search-table td:nth-child(6) { width: auto; }    /* Tags */
.search-table .badge { white-space: nowrap; }

.facet-list {
    list-style: none;
    margin: 0; padding: 0;
}
.facet-list li {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
    padding: 4px 0;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
}
.facet-list li:last-child { border-bottom: none; }
.facet-list a { color: var(--text); text-decoration: none; }
.facet-list a:hover { color: var(--accent); }

.dsl-card .dsl-blurb {
    margin-top: -4px;
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.55;
}
.dsl-card .dsl-blurb code {
    background: var(--bg-elev-2);
    border: 1px solid var(--border);
    padding: 0 4px;
    border-radius: 3px;
    font-size: 11px;
    color: var(--text);
}
.dsl-help {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 12px 14px;
    margin: 8px 0 0;
    font-size: 12px;
}
.dsl-help dt {
    color: var(--text-mute);
    text-transform: uppercase;
    letter-spacing: 1px;
    font-size: 10px;
    padding-top: 4px;
    white-space: nowrap;
}
.dsl-help dd {
    margin: 0;
    color: var(--text-dim);
    line-height: 1.6;
    /* Critical: lets code children wrap inside the grid track instead of
       blowing past the right edge of the card on long examples. */
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
}
.dsl-help code {
    background: var(--bg-elev-2);
    padding: 1px 6px;
    border-radius: 3px;
    border: 1px solid var(--border);
    font-size: 11px;
    color: var(--text);
    /* No nowrap — long compound examples need to soft-wrap */
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* ---------- community templates ---------- */
.templates-card { padding: 18px 20px; }
.template-group { margin-top: 18px; }
.template-group:first-of-type { margin-top: 8px; }
.template-group-h {
    margin: 0 0 8px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: var(--text-dim);
}
.template-list { list-style: none; margin: 0; padding: 0; }
.template-list li {
    border-bottom: 1px solid var(--border);
    padding: 10px 0;
}
.template-list li:last-child { border-bottom: none; }
.template-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 10px 12px;
    color: var(--text);
    text-decoration: none;
    padding: 6px 8px;
    border-radius: 6px;
    transition: background 0.12s;
}
.template-row:hover {
    background: var(--bg-elev-2);
    text-decoration: none;
}
.template-label { font-size: 13px; }
.template-q {
    flex: 1 1 100%;
    font-family: var(--mono, monospace);
    font-size: 11px;
    color: var(--text-dim);
    background: var(--bg-elev-2);
    border: 1px solid var(--border);
    padding: 3px 8px;
    border-radius: 4px;
    word-break: break-all;
}
.template-hint {
    margin: 4px 8px 0 8px;
    font-size: 11px;
    color: var(--text-mute);
    line-height: 1.5;
}
.fill-pill {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 3px;
    font-family: var(--mono, monospace);
    font-size: 9px;
    letter-spacing: 1px;
    color: var(--warn);
    background: rgba(255, 209, 102, 0.10);
    border: 1px solid var(--warn);
    text-transform: uppercase;
}

.pager {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 14px;
    padding: 10px 0;
}

.theme-toggle {
    background: var(--bg-elev-2);
    border: 1px solid var(--border-bright);
    color: var(--text-dim);
    padding: 6px 10px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    transition: all 0.15s;
}
.theme-toggle:hover { color: var(--text); border-color: var(--accent); }
[data-theme="dark"] .theme-toggle .moon { display: inline; }
[data-theme="dark"] .theme-toggle .sun  { display: none; }
[data-theme="light"] .theme-toggle .moon { display: none; }
[data-theme="light"] .theme-toggle .sun  { display: inline; }

/* ---------- layout ---------- */
.container {
    /* Fluid width — use the whole screen up to a generous cap on ultra-wide
       monitors, with comfortable side padding instead of a hard center. */
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 24px clamp(20px, 3vw, 56px);
}
@media (min-width: 2400px) {
    /* On ultra-wide / portrait-rotated setups, keep readable rather than
       letting individual rows stretch a meter wide. */
    .container { max-width: 2200px; margin: 0 auto; }
}
.grid { display: grid; gap: 16px; }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.split { grid-template-columns: 2fr 1fr; }
/* map-and-changes: map dominant left, changes feed side-by-side right.
   The right rail is now wide enough to hold all 5 columns without forcing
   horizontal scroll. */
.grid.map-and-changes {
    grid-template-columns: minmax(0, 1.2fr) minmax(520px, 1.05fr);
    align-items: stretch;
}
.rail { display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 1100px) {
    .grid.cols-4, .grid.cols-3 { grid-template-columns: repeat(2, 1fr); }
    .grid.split, .grid.map-and-changes { grid-template-columns: 1fr; }
}

/* Changes feed alongside the map. Heights mirror .map-card.tall exactly so
   the table can never push the map below the fold — the body becomes its
   own scroll viewport when the row count exceeds the available space. */
.changes-feed {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 0;
    /* Match .map-card.tall: min-height calc(100vh - 220px). Also constrain
       max-height so a 101K-row baseline can't blow out the layout. */
    min-height: 560px;
    max-height: calc(100vh - 220px);
    height: calc(100vh - 220px);
}
.changes-feed .card-head { flex: 0 0 auto; }
.changes-feed-body {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: auto;     /* horizontal pan when the table is wider than its container */
    min-height: 0;
    /* iOS / trackpad momentum scroll inside the inner viewport */
    -webkit-overflow-scrolling: touch;
}
.changes-feed-body .table {
    font-size: 12px;
    /* Fixed layout = predictable column widths from the rules below.
       min-width keeps the table at a readable size; when the viewport is
       narrower the WHOLE table horizontally scrolls (not a single cell). */
    table-layout: fixed;
    width: 100%;
    min-width: 880px;
}
.changes-feed-body .table th,
.changes-feed-body .table td {
    /* Break at word boundaries only. The previous `overflow-wrap: anywhere`
       was the cause of the "one character per line" pile-up on the detail
       column when the viewport was narrow — let horizontal scroll handle
       width instead of per-character breaking. */
    overflow-wrap: break-word;
    word-break: normal;
}
/* Column-width hints. Sums to ~880px so the min-width is honest.
   Six columns: Action / OSM ID / Edited by / UID / Changeset / Detail. */
.changes-feed-body .table th:nth-child(1), .changes-feed-body .table td:nth-child(1) { width: 100px; }  /* Action — fits "MODIFIED" */
.changes-feed-body .table th:nth-child(2), .changes-feed-body .table td:nth-child(2) { width: 130px; }  /* OSM ID — 13 digits */
.changes-feed-body .table th:nth-child(3), .changes-feed-body .table td:nth-child(3) { width: 180px; }  /* Edited by */
.changes-feed-body .table th:nth-child(4), .changes-feed-body .table td:nth-child(4) { width: 90px; }   /* UID */
.changes-feed-body .table th:nth-child(5), .changes-feed-body .table td:nth-child(5) { width: 130px; }  /* Changeset */
.changes-feed-body .table th:nth-child(6), .changes-feed-body .table td:nth-child(6) { width: 250px; }  /* Detail */
/* Badges are short labels — never wrap, regardless of cell width. */
.changes-feed-body .badge { white-space: nowrap; }
/* Sticky header inside scroll container — overrides the default sticky-to-topbar offset. */
.changes-feed-body .table thead th {
    position: sticky;
    top: 0;
    z-index: 5;
}
/* Mobile: same 6 columns, same widths, same min-width. The wrapper
   horizontally scrolls so the user pans through the table instead of
   columns disappearing or letters stacking on top of each other.
   Only thing that changes is font + padding for thumb-tap readability. */
@media (max-width: 480px) {
    .changes-feed-body .table { font-size: 13px; }
    .changes-feed-body .table th,
    .changes-feed-body .table td { padding: 8px 6px; }
}
/* Below 1100px the grid collapses to one column (map on top, feed below).
   Cap the feed to a comfortable viewport-relative height with its own
   internal scroll so the page itself stays short. */
@media (max-width: 1100px) {
    .changes-feed {
        height: 60vh;
        min-height: 320px;
        max-height: 70vh;
    }
}

/* ---- .table-scroll: the homepage's .changes-feed-body pattern,
   generalised for every standalone table (/changes, /search,
   /operators, /osm-changeset).  Same trick the dashboard feed
   already uses successfully:
     - wrapper has overflow-y:auto + a viewport-relative max-height,
       so vertical overflow scrolls INSIDE the wrapper (and the
       sticky thead inside scopes to that wrapper);
     - wrapper has overflow-x:auto + the table has table-layout:fixed
       + a sensible min-width, so on narrow screens the whole table
       pans horizontally — rows always look like rows, no per-cell
       letter pileup, no card-stack restructuring.
   On wide screens the table fits and the wrapper is a no-op. */
.table-scroll {
    overflow-x: auto;
    overflow-y: auto;
    max-height: calc(100vh - 240px);
    -webkit-overflow-scrolling: touch;
}
.table-scroll > .table {
    table-layout: fixed;
    width: 100%;
    margin: 0;
}
.table-scroll > .table th,
.table-scroll > .table td {
    overflow-wrap: break-word;
    word-break: normal;
}
/* Sticky thead inside the wrapper — overrides .table th's default
   top: var(--topbar-h) (which was the right answer when the table
   sticky was viewport-relative; here the wrapper IS the scroll
   container). */
.table-scroll > .table thead th {
    top: 0;
    z-index: 5;
}
/* Badges never wrap regardless of cell width. */
.table-scroll .badge { white-space: nowrap; }

/* Per-table column widths.  Each .table on the standalone pages
   carries one of these classes; sum drives the table's effective
   min-width so the wrapper triggers horizontal scroll at the
   right point.  Columns numbered left-to-right matching each
   template's <thead> order. */

/* /changes — 9 columns, min-width ~1320 */
.changes-table { min-width: 1320px; }
.changes-table th:nth-child(1), .changes-table td:nth-child(1) { width: 100px; }   /* Action */
.changes-table th:nth-child(2), .changes-table td:nth-child(2) { width: 130px; }   /* OSM ID */
.changes-table th:nth-child(3), .changes-table td:nth-child(3) { width: 160px; }   /* Operator */
.changes-table th:nth-child(4), .changes-table td:nth-child(4) { width: 140px; }   /* Manufacturer */
.changes-table th:nth-child(5), .changes-table td:nth-child(5) { width: 180px; }   /* Edited by */
.changes-table th:nth-child(6), .changes-table td:nth-child(6) { width: 90px;  }   /* UID */
.changes-table th:nth-child(7), .changes-table td:nth-child(7) { width: 130px; }   /* Changeset */
.changes-table th:nth-child(8), .changes-table td:nth-child(8) { width: 140px; }   /* Coords */
.changes-table th:nth-child(9), .changes-table td:nth-child(9) { width: 250px; }   /* Detail */

/* /osm-changeset — 5 columns, min-width ~720 */
.changeset-table { min-width: 720px; }
.changeset-table th:nth-child(1), .changeset-table td:nth-child(1) { width: 110px; } /* Action */
.changeset-table th:nth-child(2), .changeset-table td:nth-child(2) { width: 140px; } /* OSM ID */
.changeset-table th:nth-child(3), .changeset-table td:nth-child(3) { width: 200px; } /* Operator */
.changeset-table th:nth-child(4), .changeset-table td:nth-child(4) { width: 140px; } /* Manufacturer */
.changeset-table th:nth-child(5), .changeset-table td:nth-child(5) { width: 130px; } /* When */

/* /operators — 5 columns, min-width ~720 */
.operators-table { min-width: 720px; }
.operators-table th:nth-child(1), .operators-table td:nth-child(1) { width: 280px; } /* Operator */
.operators-table th:nth-child(2), .operators-table td:nth-child(2) { width: 200px; } /* Manufacturer */
.operators-table th:nth-child(3), .operators-table td:nth-child(3) { width: 80px;  } /* Active */
.operators-table th:nth-child(4), .operators-table td:nth-child(4) { width: 80px;  } /* Removed */
.operators-table th:nth-child(5), .operators-table td:nth-child(5) { width: 80px;  } /* Total */

/* Thick, labeled snapshot dividers between groups in the changes feed. */
.snap-divider td {
    padding: 14px 12px 8px;
    border-top: 3px solid var(--border-bright);
    border-bottom: 1px solid var(--border);
    background: var(--bg-elev-2);
    text-transform: uppercase;
    letter-spacing: 1px;
}
.snap-divider:first-child td { border-top: none; }
.snap-pill {
    display: inline-block;
    padding: 2px 8px;
    background: var(--bg);
    border: 1px solid var(--accent-dim);
    color: var(--accent);
    border-radius: 4px;
    font-family: var(--mono, monospace);
    font-size: 10px;
    font-weight: 700;
    margin-right: 10px;
}
.snap-when {
    font-family: var(--mono, monospace);
    font-size: 10px;
    color: var(--text-mute);
    text-transform: none;
    letter-spacing: 0;
}
.snap-count {
    margin-left: 10px;
    font-family: var(--mono, monospace);
    font-size: 10px;
    color: var(--text-dim);
    text-transform: none;
    letter-spacing: 0;
}
.snap-divider.empty td {
    background: transparent;
    border-top-style: dashed;
    border-top-color: var(--border);
}
.snap-divider.empty .snap-pill {
    color: var(--text-mute);
    border-color: var(--border-bright);
}
.snap-empty-note {
    margin-left: 10px;
    font-family: var(--mono, monospace);
    font-size: 10px;
    color: var(--text-mute);
    font-style: italic;
    text-transform: none;
    letter-spacing: 0;
}

/* Tiny inline changeset chip next to the editor name. */
.cs-link {
    display: inline-block;
    margin-left: 6px;
    padding: 0 5px;
    border-radius: 3px;
    background: var(--bg);
    border: 1px solid var(--border-bright);
    font-family: var(--mono, monospace);
    font-size: 10px;
    color: var(--text-mute);
}
.cs-link:hover { color: var(--accent); border-color: var(--accent-dim); text-decoration: none; }

/* OSM UID chip — stable numeric identity for the editor account.
   Visually subordinate to the username (the human-readable handle) but
   strictly the source of truth for "is this the same OSM account?" */
.osm-uid {
    font-family: var(--mono, monospace);
    font-size: 11px;
    color: var(--text-dim);
    border-bottom: 1px dotted var(--border-bright);
}
.osm-uid:hover { color: var(--accent); text-decoration: none; }

/* Previous-snapshot legend dot. */
.legend .dot.prev {
    background: var(--text-mute);
    box-shadow: none;
    opacity: 0.7;
}

.card {
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 18px;
    transition: border-color 0.15s, background 0.15s;
    box-shadow: var(--shadow);
}
.card.flush { padding: 0; }
.card:hover { border-color: var(--border-bright); }
.card h2, .card h3 {
    margin: 0 0 12px;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-dim);
}
.card-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
}
.card-head h2 { margin: 0; }

/* ---------- stat tiles ---------- */
.stat {
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 18px;
    position: relative;
    overflow: hidden;
    box-shadow: var(--shadow);
}
.stat::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, transparent 0%, var(--glow, transparent) 100%);
    pointer-events: none;
    opacity: 0.6;
}
.stat .label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: var(--text-mute);
    margin-bottom: 6px;
}
.stat .value {
    font-family: var(--mono, monospace);
    font-size: 32px;
    font-weight: 700;
    line-height: 1;
}
.stat.added   { --glow: var(--added-glow); }
.stat.added   .value { color: var(--added); }
.stat.deleted { --glow: var(--deleted-glow); }
.stat.deleted .value { color: var(--deleted); }
.stat.modified { --glow: var(--modified-glow); }
.stat.modified .value { color: var(--modified); }
.stat.total   .value { color: var(--accent); }

/* ---------- map ---------- */
/* The map should flex to fill its card. Pages opt in by giving the
   parent .card class="flush map-card" and the map div either an
   inline height OR by relying on the .full-map wrapper below. */
#map, .map {
    height: 100%;
    width: 100%;
    min-height: 360px;
    border-radius: 0 0 10px 10px;
    background: var(--map-bg);
    overflow: hidden;
}
.map-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 560px;
}
.map-card .map-body {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
}
.map-card .map-body #map { position: absolute; inset: 0; height: 100%; width: 100%; min-height: 0; border-radius: 0; }
.map-card.tall { min-height: calc(100vh - 220px); }
.map-card.short { min-height: 420px; }

.leaflet-container {
    background: var(--map-bg) !important;
    font-family: -apple-system, sans-serif !important;
}
.leaflet-popup-content-wrapper {
    background: var(--bg-elev) !important;
    color: var(--text) !important;
    border: 1px solid var(--border-bright);
    border-radius: 8px !important;
    font-size: 12px;
}
.leaflet-popup-tip { background: var(--bg-elev) !important; border: 1px solid var(--border-bright); }
.leaflet-popup-content { margin: 10px 14px !important; line-height: 1.5; }
.leaflet-popup-content a { color: var(--accent); }
.leaflet-control-attribution {
    background: rgba(0, 0, 0, 0.55) !important;
    color: #cfd6df !important;
    font-size: 10px !important;
}
[data-theme="light"] .leaflet-control-attribution {
    background: rgba(255, 255, 255, 0.85) !important;
    color: var(--text-dim) !important;
}
.leaflet-control-attribution a { color: inherit !important; }
.leaflet-control-zoom a {
    background: var(--bg-elev) !important;
    border-color: var(--border-bright) !important;
    color: var(--text) !important;
}

/* ---------- /subscribe ---------- */
.subscribe-wrap { display: block; }
.subscribe-grid {
    grid-template-columns: minmax(0, 1.5fr) minmax(360px, 1fr);
    align-items: start;     /* don't stretch the form card to match the map's height */
    gap: 16px;
}
@media (max-width: 1100px) { .grid.subscribe-grid { grid-template-columns: 1fr; } }
/* Subscribe-page map gets a moderate height — not the dashboard's giant tall variant. */
.map-card.subscribe-map-card { min-height: 480px; }
.map-card.subscribe-map-card .map-body { min-height: 0; }
.subscribe-card { display: flex; flex-direction: column; }

/* Nominatim search-as-you-type input + dropdown on /subscribe. */
.geo-search { position: relative; }
.geo-suggest {
    position: absolute;
    top: 100%; left: 0; right: 0;
    z-index: 50;
    margin-top: 4px;
    background: var(--bg-elev);
    border: 1px solid var(--border-bright);
    border-radius: 6px;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.30);
    max-height: 280px;
    overflow-y: auto;
}
.geo-suggest-item {
    padding: 8px 12px;
    cursor: pointer;
    border-bottom: 1px solid var(--border);
    transition: background 0.10s;
}
.geo-suggest-item:last-child { border-bottom: none; }
.geo-suggest-item:hover { background: var(--bg-elev-2); }
.geo-suggest-item .geo-name { font-size: 12px; color: var(--text); line-height: 1.4; }
.geo-suggest-item .geo-meta {
    font-family: var(--mono, monospace);
    font-size: 10px;
    color: var(--text-mute);
    margin-top: 2px;
}
.geo-suggest-empty, .geo-suggest-loading {
    padding: 10px 12px;
    font-size: 11px;
    color: var(--text-mute);
    font-style: italic;
}
.form-label {
    display: block;
    margin: 12px 0 4px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-mute);
}
.form-label input, .form-label select {
    width: 100%;
    margin-top: 4px;
    background: var(--bg-elev-2);
    border: 1px solid var(--border-bright);
    color: var(--text);
    padding: 8px 10px;
    border-radius: 6px;
    font-family: -apple-system, sans-serif;
    font-size: 13px;
    box-sizing: border-box;
}
.form-label input:focus, .form-label select:focus {
    border-color: var(--accent);
    outline: none;
}
.form-bbox {
    margin: 16px 0;
    padding: 10px 12px;
    background: var(--bg-elev-2);
    border-radius: 6px;
    border: 1px dashed var(--border-bright);
    font-size: 11px;
    color: var(--text-dim);
}
.form-bbox code {
    display: block;
    margin-top: 4px;
    color: var(--text);
    font-size: 11px;
    word-break: break-all;
}
.sub-result {
    margin-top: 14px;
    padding: 10px 14px;
    border-radius: 6px;
    font-size: 13px;
}
.sub-result.ok {
    background: var(--added-glow);
    border: 1px solid var(--added);
    color: var(--text);
}
.sub-result.ok b { color: var(--added); }
.sub-result.error {
    background: var(--deleted-glow);
    border: 1px solid var(--deleted);
    color: var(--text);
}

/* Visually-hidden but available to screen readers + search engines. */
.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0);
    white-space: nowrap; border: 0;
}

/* Subscribe hero — first-class call-to-action. */
.hero-cta {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 14px 18px;
    margin-bottom: 16px;
    border-radius: 10px;
    border: 1px solid var(--accent);
    /* default = light mode. Mirrors the dark-mode alpha-gradient pattern
       (subtle tint fading out to the right) but uses a slightly higher
       alpha so the darker light-mode teal reads against the bright bg.
       Replaces the previous solid var(--accent-dim) which was opaque
       in light mode (--accent-dim = --accent there), producing a
       blocky teal-on-white slab and washing out on the leftmost edge. */
    background: linear-gradient(90deg, rgba(10, 125, 106, 0.16), rgba(10, 125, 106, 0.03) 80%);
    color: var(--text);
    text-decoration: none;
    flex-wrap: wrap;
    transition: filter 0.12s, transform 0.12s;
}
[data-theme="dark"] .hero-cta {
    background: linear-gradient(90deg, rgba(0,255,209,0.10), rgba(0,255,209,0.02) 80%);
}
.hero-cta:hover { text-decoration: none; filter: brightness(1.06); transform: translateY(-1px); }
.hero-cta-tag {
    font-family: var(--mono, monospace);
    font-size: 10px;
    letter-spacing: 1.5px;
    color: var(--accent);
    border: 1px solid var(--accent);
    padding: 3px 8px;
    border-radius: 4px;
    white-space: nowrap;
}
.hero-cta-msg { flex: 1 1 280px; font-size: 13px; line-height: 1.5; }
.hero-cta-msg b { color: var(--text); }
.hero-cta-go {
    color: var(--accent);
    font-family: var(--mono, monospace);
    font-size: 12px;
    white-space: nowrap;
    font-weight: 700;
}
@media (max-width: 720px) {
    .hero-cta { padding: 12px 14px; gap: 10px; }
    .hero-cta-msg { font-size: 12px; flex: 1 1 100%; order: 2; }
    .hero-cta-go { order: 3; }
}

/* Bulk-removal alerts on the dashboard — surfaces single-changeset deletion sprees. */
.bulk-alerts {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}
.bulk-alert {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 16px;
    border: 1px solid var(--deleted);
    background: linear-gradient(90deg, rgba(255, 77, 109, 0.10), rgba(255, 209, 102, 0.05));
    border-radius: 10px;
    color: var(--text);
    text-decoration: none;
    transition: filter 0.12s;
    flex-wrap: wrap;
}
.bulk-alert:hover { text-decoration: none; filter: brightness(1.10); }
.bulk-alert-tag {
    font-family: var(--mono, monospace);
    font-size: 10px;
    letter-spacing: 1.5px;
    color: var(--deleted);
    border: 1px solid var(--deleted);
    background: var(--deleted-glow);
    padding: 3px 8px;
    border-radius: 4px;
    white-space: nowrap;
}
.bulk-alert-msg { flex: 1; font-size: 13px; }
.bulk-alert-msg b { color: var(--text); }
.bulk-alert-go {
    color: var(--accent);
    font-size: 12px;
    white-space: nowrap;
    font-family: var(--mono, monospace);
}

/* Custom marker-cluster icon: pie-chart wedges of action mix on the current
   snapshot, monochrome gray for the previous-snapshot baseline overlay. */
.tf-cluster {
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--bg);
    font-family: var(--mono, monospace);
    font-weight: 700;
    font-size: 11px;
    border: 2px solid var(--bg);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 4px 10px rgba(0, 0, 0, 0.35);
    cursor: pointer;
}
.tf-cluster .tf-cluster-num {
    background: var(--bg-elev);
    color: var(--text);
    border-radius: 50%;
    width: 60%;
    height: 60%;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border-bright);
}
.tf-cluster-prev {
    background: var(--text-mute);
    opacity: 0.7;
    color: var(--bg);
}
[data-theme="light"] .tf-cluster-prev { background: #b8c1cc; color: #1a2332; }

/* legend */
.legend {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    font-size: 12px;
    color: var(--text-dim);
}
.legend .item { display: flex; align-items: center; gap: 6px; }
.legend .dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 2px solid var(--bg);
}
.legend .dot.added    { background: var(--added);    box-shadow: 0 0 8px var(--added-glow); }
.legend .dot.deleted  { background: var(--deleted);  box-shadow: 0 0 8px var(--deleted-glow); }
.legend .dot.modified { background: var(--modified); box-shadow: 0 0 8px var(--modified-glow); }

/* ---------- tables ---------- */
.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
.table th, .table td {
    padding: 10px 12px;
    text-align: left;
    border-bottom: 1px solid var(--border);
}
.table th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-mute);
    background: var(--bg-elev-2);
    position: sticky;
    top: var(--topbar-h);
    z-index: 10;
}
.table tbody tr:hover { background: var(--bg-elev-2); }
.table .num { font-family: var(--mono, monospace); }
.table td.tight { white-space: nowrap; }

/* action badges */
.badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 4px;
    font-family: var(--mono, monospace);
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    white-space: nowrap;
}
.badge.added     { background: var(--added-glow);    color: var(--added);    border: 1px solid var(--added); }
.badge.deleted   { background: var(--deleted-glow);  color: var(--deleted);  border: 1px solid var(--deleted); }
.badge.modified  { background: var(--modified-glow); color: var(--modified); border: 1px solid var(--modified); }
/* untracked = tag strip / reclassification. NOT a teardown — use a neutral
   muted-warn tone instead of the red `deleted` glow so subscribers and
   readers don't mis-read it as a takedown signal. */
.badge.untracked { background: rgba(139, 148, 158, 0.18); color: var(--text-dim); border: 1px dashed var(--border-bright); }

/* OSM editor risk-tier indicator (malicious-deletion detection). Sits
   inline next to the username in changelog rows. Tooltip carries the
   computed reason; size + uppercase make it scannable. */
.risk-badge {
    display: inline-block;
    margin-left: 4px;
    padding: 0 4px;
    border-radius: 3px;
    font-family: var(--mono, monospace);
    font-size: 9.5px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    line-height: 14px;
    vertical-align: middle;
    cursor: help;
}
.risk-low    { background: rgba(255,200,30,0.12);  color: #ffc81e; border: 1px solid rgba(255,200,30,0.55); }
.risk-medium { background: rgba(255,140,30,0.18);  color: #ffae3a; border: 1px solid rgba(255,140,30,0.65); }
.risk-high   { background: rgba(255,70,90,0.22);   color: #ff7080; border: 1px solid rgba(255,70,90,0.75); font-weight: 700; }
/* OSM moderator-block status. Neutral slate palette — 'case closed by
   the community' rather than 'this user is currently dangerous to you'. */
.blocked-badge { background: rgba(120,140,170,0.18); color: #9aa8c0; border: 1px solid rgba(120,140,170,0.55); }
[data-theme="light"] .risk-low    { color: #b27500; background: rgba(255,200,30,0.16); }
[data-theme="light"] .risk-medium { color: #b85200; background: rgba(255,140,30,0.18); }
[data-theme="light"] .risk-high   { color: #b00020; background: rgba(255,70,90,0.20); }

/* ---------- filter bar ---------- */
.filters {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
    margin-bottom: 16px;
}
.filters label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-mute);
    margin-right: 4px;
}
.filters select, .filters input {
    background: var(--bg-elev);
    border: 1px solid var(--border-bright);
    color: var(--text);
    padding: 6px 10px;
    border-radius: 6px;
    font-family: var(--mono, monospace);
    font-size: 12px;
}
.btn {
    background: var(--bg-elev);
    border: 1px solid var(--border-bright);
    color: var(--text-dim);
    padding: 6px 12px;
    border-radius: 6px;
    font-size: 12px;
    cursor: pointer;
    text-decoration: none;
    display: inline-block;
}
.btn:hover { color: var(--text); border-color: var(--accent-dim); text-decoration: none; }
.btn.primary { background: var(--accent-dim); color: var(--bg); border-color: var(--accent); }
.btn.primary:hover { background: var(--accent); color: var(--bg); }

/* ---------- camera detail page ---------- */
.cam-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 24px;
    margin-bottom: 24px;
    flex-wrap: wrap;
}
.cam-header h1 {
    font-family: var(--mono, monospace);
    font-size: 20px;
    margin: 0 0 4px;
    color: var(--text);
}
.cam-header .meta {
    color: var(--text-dim);
    font-size: 13px;
}
.cam-header .meta b { color: var(--text); font-weight: 500; }
.cam-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 6px;
    font-family: var(--mono, monospace);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
}
.cam-status.alive     { background: var(--added-glow);            color: var(--added); }
.cam-status.removed   { background: var(--deleted-glow);          color: var(--deleted); }
.cam-status.untracked { background: rgba(139, 148, 158, 0.18);    color: var(--text-dim); border: 1px dashed var(--border-bright); }

.tag-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 4px 16px;
    font-family: var(--mono, monospace);
    font-size: 12px;
}
.tag-grid dt { color: var(--text-dim); }
.tag-grid dd { color: var(--text); margin: 0; word-break: break-word; }

/* timeline */
.timeline { position: relative; padding-left: 20px; }
.timeline::before {
    content: '';
    position: absolute;
    left: 4px;
    top: 4px;
    bottom: 4px;
    width: 2px;
    background: var(--border);
}
.tl-event {
    position: relative;
    margin-bottom: 16px;
    padding-bottom: 16px;
    border-bottom: 1px dashed var(--border);
}
.tl-event:last-child { border-bottom: none; }
.tl-event::before {
    content: '';
    position: absolute;
    left: -20px;
    top: 6px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 3px var(--bg);
}
.tl-event.added::before    { background: var(--added); }
.tl-event.deleted::before  { background: var(--deleted); }
.tl-event.modified::before { background: var(--modified); }
.tl-event .when {
    font-family: var(--mono, monospace);
    font-size: 11px;
    color: var(--text-mute);
}
.tl-event .what { margin-top: 2px; }
.tl-event .detail {
    font-family: var(--mono, monospace);
    font-size: 11px;
    color: var(--text-dim);
    margin-top: 4px;
    background: var(--bg-elev-2);
    padding: 6px 10px;
    border-radius: 4px;
    border-left: 2px solid var(--border-bright);
    white-space: pre-wrap;
    word-break: break-word;
}

/* OSM history block */
.osm-author {
    background: var(--bg-elev-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px 14px;
    margin-bottom: 8px;
}
.osm-author.removed { border-left: 3px solid var(--deleted); }
.osm-author.added   { border-left: 3px solid var(--added); }
.osm-author .row1 {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    flex-wrap: wrap;
}
.osm-author .who {
    font-family: var(--mono, monospace);
    font-weight: 600;
    color: var(--text);
}
.osm-author .who.suspicious { color: var(--warn); }
.osm-author .when {
    font-family: var(--mono, monospace);
    font-size: 11px;
    color: var(--text-mute);
}
.osm-author .ver {
    display: inline-block;
    padding: 1px 6px;
    background: var(--bg);
    border: 1px solid var(--border-bright);
    border-radius: 4px;
    font-family: var(--mono, monospace);
    font-size: 10px;
    color: var(--text-dim);
    margin-right: 6px;
}
.osm-author .changeset-info {
    margin-top: 6px;
    font-size: 11px;
    color: var(--text-dim);
    font-family: var(--mono, monospace);
}
.osm-author .changeset-info .label { color: var(--text-mute); }
.osm-author .changeset-comment {
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-dim);
    font-style: italic;
}

.flag-banner {
    background: linear-gradient(135deg, rgba(255, 209, 102, 0.12), rgba(255, 77, 109, 0.08));
    border: 1px solid var(--warn);
    border-radius: 8px;
    padding: 12px 16px;
    margin-bottom: 16px;
    color: var(--warn);
    font-size: 13px;
}
.flag-banner b { color: var(--text); }

.empty-state {
    text-align: center;
    padding: 80px 24px;
    color: var(--text-dim);
}
.empty-state h1 { color: var(--text); font-family: var(--mono, monospace); }

.spinner {
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 2px solid var(--border-bright);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.site-footer {
    margin-top: 40px;
    padding: 18px clamp(20px, 3vw, 56px);
    border-top: 1px solid var(--border);
    background: var(--bg-elev);
    color: var(--text-mute);
    font-size: 12px;
}
.footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 12px;
    line-height: 1.6;
}
.footer-brand {
    font-family: var(--mono, monospace);
    font-weight: 700;
    letter-spacing: 1px;
    color: var(--text-dim);
}
.footer-sep { color: var(--border-bright); }
.footer-contact a { font-family: var(--mono, monospace); }
.footer-meta a { color: var(--text-dim); }
.footer-meta a:hover { color: var(--accent); }
.footer-disclaimer {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
    font-size: 10.5px;
    line-height: 1.55;
    color: var(--text-mute);
    font-style: italic;
}
.footer-disclaimer a { color: var(--text-dim); text-decoration: underline; text-decoration-color: var(--border-bright); }
.footer-disclaimer a:hover { color: var(--accent); text-decoration-color: var(--accent); }
@media (max-width: 700px) {
    .footer-inner { flex-direction: column; align-items: flex-start; }
    .footer-sep { display: none; }
}

/* ============================================================
   MOBILE — narrow-viewport overrides.
   Breakpoints chosen to match the layout-collapse points the
   rest of the stylesheet already uses.
   ============================================================ */

/* Below 900px: search input drops out of the topbar (Search nav link
   still works, /search has its own input). Already in the .topbar-search
   rule above. The brand label gets smaller. */
@media (max-width: 900px) {
    .brand { font-size: 13px; }
    .nav { gap: 2px; }
    .nav a { padding: 5px 8px; font-size: 12px; }
    .snapshot-picker select { font-size: 11px; padding: 5px 8px; }
}

/* 720–900px: tables stay tabular, topbar squeezes. Stat tiles 2-up. */
@media (max-width: 900px) {
    .grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
    .stat .value { font-size: 24px; }
}

/* Below 720px: tables become card-stacks, the changes feed map/table
   stack vertically with an internal-scroll cap. */
@media (max-width: 720px) {
    .container { padding: 16px clamp(12px, 4vw, 20px); }
    .topbar {
        padding: 8px 12px;
        gap: 8px;
    }
    /* Hamburger swap: nav collapses behind the toggle button.  The
       brand and theme toggle stay in the visible top row; the search
       form is already hidden at <=900px.  .topbar-extra (e.g. the
       snapshot picker on /changes) keeps its place too. */
    .nav-toggle { display: inline-flex; align-items: center; justify-content: center; }
    .nav { display: none; }
    .topbar.nav-open .nav {
        display: flex;
        flex-direction: column;
        width: 100%;
        order: 99;
        gap: 2px;
        margin-top: 8px;
        padding-top: 8px;
        border-top: 1px solid var(--border);
    }
    .topbar.nav-open .nav a {
        padding: 10px 12px;
        font-size: 14px;
        border-radius: 4px;
    }
    .topbar.nav-open .nav a.nav-signin { margin-left: 0; }
    .brand .brand-logo { height: 26px; width: 38px; }
    .theme-toggle { padding: 4px 8px; font-size: 12px; }

    /* Stat tiles 2-up with smaller values */
    .grid.cols-3, .grid.cols-2 { grid-template-columns: 1fr; }
    .stat { padding: 12px 14px; }
    .stat .label { font-size: 10px; }
    .stat .value { font-size: 22px; }

    /* Card padding shrinks */
    .card { padding: 14px; }
    .card-head { padding: 10px 14px; }
    .card-head h2 { font-size: 12px; }

    /* The big map/changes split is already 1fr at 1100px; here we
       cap each piece to a sensible vh so neither dominates. */
    .map-card.tall { min-height: 50vh; }
    .changes-feed { height: 60vh; min-height: 320px; max-height: 70vh; }

    /* Tables: density shrink for narrow screens.  The "header covers
       the first entry / hovers oddly" symptom was the old
       .card > .table { display: block; overflow-x: auto } rule
       scoping `position: sticky` to the table itself.  Standalone
       tables are now wrapped in .table-scroll (see the rule near
       .changes-feed-body — same pattern as the homepage feed:
       table-layout: fixed + min-width + sticky thead inside an
       overflow-auto wrapper).  No mobile-only restructuring needed. */
    .table { font-size: 12px; }
    .table th, .table td { padding: 8px 10px; }

    /* Search results table — pack denser. */
    .search-table th, .search-table td { padding: 6px 8px; font-size: 11px; }

    /* Cam header / facets — full-width stacks */
    .cam-header { gap: 12px; }
    .cam-header h1 { font-size: 16px; }
    .grid.split, .grid.search-layout, .grid.map-and-changes { gap: 12px; }
    .filters { gap: 6px; }
    .filters .btn { padding: 5px 10px; font-size: 11px; }

    /* Bulk-alert callout — vertical stack */
    .bulk-alert { padding: 10px 12px; }
    .bulk-alert-msg { font-size: 12px; flex: 1 1 100%; }

    /* DSL help block — fewer columns */
    .dsl-help { grid-template-columns: 1fr; gap: 4px 0; }
    .dsl-help dt { padding-top: 8px; }

    /* Subscribe form — tighter spacing */
    .subscribe-card { padding: 14px; }
    .form-bbox code { font-size: 10px; }

    /* Footer — already collapses to vertical at 700px (rule above) */
}

/* Below 480px (iPhone SE class): the truly tiny screens. */
@media (max-width: 480px) {
    .topbar { gap: 6px; padding: 6px 10px; }
    .topbar .brand { flex: 1 1 auto; }
    .snapshot-picker { font-size: 11px; }
    .snapshot-picker select { max-width: 200px; }

    /* Map and feed get even more aggressive height caps */
    .map-card.tall { min-height: 42vh; }
    .changes-feed { height: 55vh; }

    /* Stats: single column, smaller text */
    .grid.cols-4 { grid-template-columns: 1fr 1fr; gap: 8px; }
    .stat { padding: 10px 12px; }
    .stat .value { font-size: 18px; }

    /* Search summary heading wraps */
    .search-h1 { font-size: 13px; }
    .search-h1 .search-q { font-size: 11px; }
}

.muted { color: var(--text-mute); }
.dim { color: var(--text-dim); }
.right { text-align: right; }
hr.div { border: none; border-top: 1px solid var(--border); margin: 24px 0; }

/* OSM changeset attribution — shown on camera timeline events,
   search result rows, and the changes feed. The comment surfaces the
   reason for the change (so a 262-camera "lowercase OSM keys" sweep
   reads as cleanup rather than mass tampering); the verdict dot is
   the LLM classifier's verdict on the changeset. */
.cs-comment {
    font-size: 11px;
    color: var(--text-dim);
    font-style: italic;
    margin-top: 4px;
    line-height: 1.4;
    word-break: break-word;
}
.cs-editor {
    font-size: 11px;
    margin-left: 6px;
}
.cs-verdict {
    display: inline-block;
    margin-left: 6px;
    font-size: 14px;
    vertical-align: middle;
    line-height: 1;
    cursor: help;
}
.cs-verdict-consistent  { color: var(--added); }
.cs-verdict-suspicious  { color: var(--warn); }
.cs-verdict-inconsistent{ color: var(--deleted); }

/* External-link warning modal — see static/external-link-warning.js.
   Styled deliberately serious (warn-colored header bar, monospace,
   no rounded fluff) so the moment-of-consent reads like a security
   notice, not a marketing modal. */
.elw-modal {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: none;
    align-items: center;
    justify-content: center;
}
.elw-modal.elw-open { display: flex; }
.elw-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(4px);
}
.elw-window {
    position: relative;
    z-index: 1;
    max-width: 560px;
    width: calc(100% - 32px);
    background: var(--bg-elev);
    border: 1px solid var(--warn);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
.elw-header {
    background: var(--warn);
    color: #1a1a1a;
    padding: 10px 14px;
    font-weight: 700;
    font-size: 13px;
    letter-spacing: 1px;
    text-transform: uppercase;
}
.elw-icon { margin-right: 8px; }
.elw-body {
    padding: 16px;
    color: var(--text);
    font-size: 13px;
    line-height: 1.5;
}
.elw-lede { margin: 0 0 12px; font-weight: 600; }
.elw-destination {
    padding: 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    margin-bottom: 14px;
}
.elw-destination-label {
    display: block;
    color: var(--text-mute);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-bottom: 4px;
}
.elw-url {
    font-weight: 600;
    color: var(--accent);
    font-size: 12px;
    word-break: break-all;
    user-select: all;
}
.elw-body ul { margin: 8px 0 14px; padding-left: 18px; }
.elw-body li { margin-bottom: 6px; }
.elw-fine {
    color: var(--text-dim);
    font-size: 11px;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.elw-footer {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    padding: 12px 16px;
    background: var(--bg);
    border-top: 1px solid var(--border);
}
.elw-cancel, .elw-continue {
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: 8px 14px;
    border: 1px solid var(--border-bright);
    background: var(--bg-elev-2);
    color: var(--text);
    cursor: pointer;
}
.elw-cancel:hover, .elw-cancel:focus { border-color: var(--accent); outline: none; }
.elw-continue {
    background: var(--warn);
    color: #1a1a1a;
    border-color: var(--warn);
}
.elw-continue:hover, .elw-continue:focus { filter: brightness(1.1); outline: none; }
@media (max-width: 480px) {
    .elw-window { width: calc(100% - 16px); }
    .elw-footer { flex-direction: column-reverse; }
    .elw-cancel, .elw-continue { width: 100%; }
}
