﻿/* ============================================================
   P4 — trust stack: unverified markers, share row, timeline
   ============================================================ */

/* Round-95k: the Dark basemap is OSM inverted in CSS — no keyed provider required. */
.tiles-darkened{ filter:invert(1) hue-rotate(180deg) brightness(.91) contrast(.9) saturate(.7); }
/* Round-95m: the Street base, calmed toward the old Voyager feel — desaturated, a touch
   brighter, contrast eased so the service pins carry the colour hierarchy again. */
.tiles-muted{ filter:saturate(.55) brightness(1.04) contrast(.94); }
/* Round-96l: street is real CARTO Voyager end-to-end — no filters. (.tiles-muted above
   stays for any future raw-OSM layer.) */

/* unverified community reports: hollow marker in the service color */
.inc-marker.unv{
  background:rgba(243,244,241,.9) !important;
  border:2.5px dashed currentColor;
}

/* resolved incidents: dimmed grace-period rendering */
.inc-marker.dim{ opacity:.45; filter:grayscale(.5); }

/* Round-130: the rounds-40/70 per-network share rows (.share-row/.share-inline) were retired —
   every share button now opens the OS sheet via aquaShareLink; their CSS went with them. */

/* public incident timeline */
.tl-row{
  border-left:3px solid var(--track); padding:6px 0 6px 12px; margin-left:4px;
  font-size:.85rem; color:var(--ink-soft);
}
.tl-row b{ color:var(--deep); }
.tl-row small{ color:var(--ink-soft); }

/* Phase 3: the response strip on an agency's queue card — Dispatched / Arrived / Cleared */
.resp-strip{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:12px; padding-top:12px; border-top:1px dashed var(--track); }
.resp-k{ font-size:.85rem; color:var(--deep); margin-right:4px; }
.resp-unit{ width:auto; max-width:220px; }
.resp-ago{ display:flex; align-items:center; gap:6px; font-size:.82rem; color:var(--ink-soft); }

/* Phase 2b item 5: area replay (/eoc/replay) — map + running log, slider bar under them */
.rpl-field{ display:flex; flex-direction:column; gap:4px; font-size:.85rem; font-weight:600; }
.rpl-days{ display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; }
.rpl-wrap{ display:grid; grid-template-columns:minmax(0,1fr) 320px; gap:14px; }
.rpl-map{ height:62vh; min-height:360px; border-radius:14px; overflow:hidden; }
.rpl-side{ max-height:62vh; overflow:auto; padding:14px; }
.rpl-counts{ font-weight:700; margin:0; }
.rpl-log{ list-style:none; margin:0; padding:0; font-size:.82rem; }
.rpl-log li{ border-left:3px solid var(--track); padding:4px 0 4px 10px; margin-bottom:4px; }
.rpl-log li time{ color:var(--ink-soft); font-variant-numeric:tabular-nums; }
.rpl-log li.rpl-em{ border-left-color:#C0392B; }
.rpl-log li.rpl-ok{ border-left-color:#4F8577; }
.rpl-log li.rpl-al{ border-left-color:#D9822B; }
.rpl-bar{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-top:12px; padding:12px 14px; }
.rpl-bar input[type=range]{ flex:1; min-width:180px; }
.rpl-clock{ font-weight:700; font-variant-numeric:tabular-nums; min-width:150px; }
.rpl-speed{ display:flex; align-items:center; gap:6px; font-size:.85rem; }
.rpl-speed select{ width:auto; }
@keyframes rplIn{ from{ stroke-width:16; stroke-opacity:.25; } }
.rpl-new{ animation:rplIn .9s ease-out; }
@media (prefers-reduced-motion: reduce){ .rpl-new{ animation:none; } }
@media (max-width:860px){
  .rpl-wrap{ grid-template-columns:1fr; }
  .rpl-map{ height:50vh; }
  .rpl-side{ max-height:40vh; }
}

/* Phase 2b item 1: the incident's impact zone — one row per ring, counts only */
.imp-ring{ border-left:4px solid var(--imp-c, var(--track)); padding:8px 0 8px 12px; margin:0 0 6px 2px; }
.imp-head{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; flex-wrap:wrap; }
.imp-head b{ color:var(--deep); font-size:.92rem; }
.imp-ppl{ font-weight:700; font-size:.92rem; }
.imp-ppl.none{ font-weight:400; color:var(--ink-soft); font-size:.82rem; }
.imp-fac{ display:flex; flex-wrap:wrap; gap:4px 12px; margin-top:4px; font-size:.82rem; color:var(--ink-soft); }
.imp-fac span b{ color:var(--deep); font-weight:700; }
.imp-note{ margin:10px 0 0; font-size:.78rem; color:var(--ink-soft); line-height:1.4; }

/* legal pages */
.legal{ max-width:820px; margin:0 auto; }
.legal h2{ color:var(--ink); font-size:1.3rem; margin:28px 0 10px; }
.legal p, .legal li{ color:var(--ink-soft); font-size:.98rem; line-height:1.65; }
.legal ul{ margin:8px 0 8px 22px; }
.legal .flag{
  background:#F3E9D8; border:1px solid #DECBA4; color:#7A5A22;
  padding:12px 16px; border-radius:10px; font-size:.9rem; margin-bottom:22px;
}
/* ---------------- omnibox search (P5) ---------------- */
.omni{ position:relative; margin-left:22px; flex:0 1 320px; }
.omni input{
  width:100%; padding:9px 14px; border-radius:999px; border:1.5px solid var(--track);
  background:#FBFBF8; font-family:var(--font); font-size:.92rem; color:var(--ink);
}
.omni input:focus{ outline:none; border-color:var(--accent); }
.omni-results{
  position:absolute; top:110%; left:0; right:0; z-index:1300;
  background:var(--paper); border:1px solid var(--track); border-radius:14px;
  box-shadow:0 14px 40px rgba(44,59,54,.25); overflow:hidden; max-height:calc(var(--app-h, 100vh) * 0.7); overflow-y:auto;
}
.omni-group{
  padding:8px 14px 4px; font-size:.7rem; font-weight:800; letter-spacing:1.5px;
  text-transform:uppercase; color:var(--sage2);
}
.omni-hit{ display:block; padding:8px 14px; text-decoration:none; min-width:0; }
/* Round-91p: a wide dropdown with the groups as columns.
   Stacked vertically, four categories meant scrolling a narrow strip past three of them to see
   the fourth — on a screen with room to spare. The panel now widens beyond the input and lays the
   groups out side by side, anchored to the input's RIGHT edge so it grows leftwards into the page
   rather than off it. Below 900px nothing applies: .omni-col is a plain block and the groups
   stack exactly as before, which is what a phone wants anyway. */
@media (min-width: 900px){
  .omni-results{
    /* FIXED and centred, not anchored to the input. Right-anchoring a 1040px panel to an input
       that sits well left of centre put three of its four columns off the left edge of the screen
       — measured at x=-622. A centred band under the header cannot overflow either side whatever
       the header layout does, and reads as the full-width panel this is meant to be. */
    /* Stays ABSOLUTE, anchored to the search box as it always was — the vertical placement was
       never the problem. Only the width changes: right:0 made the panel match the input, and
       right-anchoring a wide one instead pushed three of its four columns off the left edge
       (measured at x=-622). Anchoring left and capping the width against the viewport keeps it
       on screen without needing to know where in the header the box sits.

       position:fixed was tried and is the wrong tool here: percentage offsets on a fixed element
       resolve against the VIEWPORT, so top:100% put the panel at the bottom of the window. */
    left:0; right:auto;
    width:min(1040px, calc(100vw - 28px));
    display:grid; grid-template-columns:repeat(auto-fit, minmax(230px, 1fr));
    align-items:start; gap:0 8px; padding:6px 4px;
  }
  .omni-col{ min-width:0; }
  /* Long headlines and OSM descriptions WRAP rather than run on.
     They were nowrap+ellipsis, which is the wrong trade in a column this narrow: a report headline
     and an address like "La, Accra, La-Dade-Kotopon Municipal District, Greater Accra Region" both
     lose the part that identifies them, and the last column lost its text past the panel edge.
     Two lines, wrapped, says which result this is; one clipped line does not.
     min-width:0 on the ANCHOR too — a grid item's automatic minimum is its content, so without it
     the longest address widens the whole column and pushes its neighbours out of the panel. */
  .omni-hit{ min-width:0; }
  .omni-hit b, .omni-hit small{
    white-space:normal; overflow-wrap:anywhere;
    display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden;
  }
}
.omni-hit:hover{ background:var(--mintbg); text-decoration:none; }
.omni-hit b{ display:block; color:var(--ink); font-size:.92rem; }
.omni-hit small{ color:var(--ink-soft); font-size:.78rem; }
.omni-empty{ padding:14px; color:var(--ink-soft); font-size:.9rem; }
/* The sheet-only controls never show on desktop. `.nav-icon.omni-open` rather than a bare
   `.omni-open`: the trigger also carries .nav-icon, whose display:inline-flex is declared later
   in this file at the same specificity and would otherwise win the cascade and leak the phone
   trigger onto desktop. */
.omni-close, .omni-clear, .nav-icon.omni-open{ display:none; }

/* ============ round-84: phone search sheet ============================================
   Two problems, one fix.

   1. iOS Safari auto-zooms the page whenever a focused input has font-size < 16px. The nav
      inputs were 15.2px, so tapping search zoomed the viewport and pushed the right-hand
      icon cluster off-screen — the grid button sat at x=365 of a 375px viewport, so it took
      almost nothing to lose it. That is the "nav extends and the grid menu goes slightly
      beyond screen view" report. 16px is the whole fix; it is a threshold, not a preference.

   2. Below 900px .omni was simply display:none, so on a phone there was NO site search on any
      page except the live map. The map's own box stayed, but shrunk to ~229px shared with the
      icons — enough to type into, not enough to read results in.

   So on a phone the box stops being a box: focusing it promotes the SAME element to a
   full-screen sheet, with the input at the top and results filling the rest. One input, one
   results container, same ids — the delegated search JS is untouched.
   ====================================================================================== */
/* Round-145: 1024px, not 900 — signed in, the row is six icons + the Report pill, and with the
   320px search box beside them the grid menu fell off the right edge between 901 and ~1000px. */
@media (max-width: 1024px){
  /* the inline pill disappears; the sheet and the nav trigger replace it */
  .omni:not(.omni-sheet){ display:none; }
  .nav-icon.omni-open{ display:inline-flex; }

  /* THE iOS ZOOM FIX. Targeted by id on purpose: the global form-control rule in style.css is
     `input:not([type="checkbox"]):not([type="radio"])...` — eight attribute selectors, so
     specificity 0,8,1. A sane `.omni-sheet input` (0,1,1) loses to it and the input silently
     stays at 15.2px, which is exactly the bug. One id (1,0,0) settles it for both the inline
     map box and the sheet. Do not "tidy" this into a class selector. */
  #omniInput{ font-size:16px; }
}

/* Round-186 (branch nav-06, owner's call): the search trigger wears the live map's mint disc.
   On the map the magnifier already sits on a round #E4ECE6 pad inside the search pill; on every
   other page it was a bare glyph among bare glyphs, so the one thing in that row people go
   looking for was the hardest to find.

   Colour only — no size. The header already sizes its own row (42 / 38 / 36px as the screen
   narrows) and the disc should be whatever the icons beside it are; naming a width here would
   fight three later rules of equal specificity and lose the argument at 420px anyway.
   Scoped to the widths where this trigger exists at all — above 1024px the inline .omni pill
   takes over and there is no button to paint. */
@media (max-width: 1024px){
  .site-header .nav-icon.omni-open{ border-radius:50%; background:#E4ECE6; }
  .site-header .nav-icon.omni-open:hover{ background:#DAE4DC; }
}

@media (max-width: 1024px){
  html.search-open, html.search-open body{ overflow:hidden; }   /* no scrolling behind the sheet */

  /* Two separate things the header does to its own children, both of which broke the sheet.

     1. CONTAINING BLOCK. .site-header carries backdrop-filter:blur(8px), and a filtered element
        becomes the containing block for every position:fixed descendant. The sheet lives inside
        the header, so `inset:0` anchored to the header rather than the viewport and the sheet
        opened 93px down the page.

     2. STACKING CONTEXT. The header is position:fixed with z-index:1100, which makes it a
        stacking context — so the sheet's z-index:2000 only ranks it against its siblings INSIDE
        the header. Against the signed-out "be the first to know" bar (#signupBar, z-index:1200,
        a sibling of the header, not a child) the entire header lost, and the bar sat on top of
        the search UI. Raising the header itself is the fix; raising the child never could be.

     Both are scoped to while the sheet is open, and cost nothing visually because the sheet
     covers the header completely. */
  html.search-open .site-header, html.search-open .map-header{
    backdrop-filter:none;
    z-index:2100;          /* must exceed #signupBar's 1200 */
  }

  /* GRID, not flex-wrap. The first version used `display:flex; flex-wrap:wrap` with the results
     pane at `flex:1 1 100%`, which sizes a wrapped item to its CONTENT height rather than to the
     space left over. The pane grew to 1132px inside an 812px viewport, clientHeight equalled
     scrollHeight, so overflow-y:auto had nothing to do and the results simply ran off the bottom
     of the screen — unreachable, because the body is scroll-locked while the sheet is up.

     A grid row can be told to stop. `minmax(0, 1fr)` is the load-bearing part: a plain `1fr` has
     an automatic minimum of min-content and would grow to fit the list all over again. The 0
     minimum lets the row be capped at the leftover height, which is what finally gives the pane
     something to scroll. */
  .omni.omni-sheet, .msearch.omni-sheet{
    position:fixed; inset:0; top:var(--vvt, 0px); z-index:2000;   /* round-97h: iOS pan */
    display:grid;
    grid-template-columns:auto minmax(0,1fr) auto;   /* back | input | clear */
    grid-template-rows:auto minmax(0,1fr);           /* bar  | scrollable body */
    align-items:center; gap:8px;
    width:auto; margin:0;
    /* Round-97: --safe-top first. env() alone is 0 on Android (no cutout), which is why the
       back arrow and the input sat under the clock and battery on the G6. */
    padding:calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 10px) 12px 0;
    background:var(--paper); border:0; border-radius:0; box-shadow:none;
    /* Round-97: sized from --vvh, not dvh. dvh is a parse error pre-108 (the G6's WebView is
       Chrome 101), so that line was simply dropped and the sheet stayed a full 100vh tall with
       its results list running on behind the keyboard. --vvh is maintained by site.js from the
       visual viewport, or from the native keyboard bridge in-app, so it is correct on every
       engine we ship to. vh stays first as the pre-var fallback. */
    /* Round-180 (owner): NOT sized from --vvh any more. --vvh is updated by script after the
       keyboard has gone, so the sheet grew back a beat late and the page behind it showed
       below. The sheet now always reaches the bottom (inset:0); the keyboard's share is
       reserved INSIDE the results list instead, where a late update is invisible. */
  }
  .omni-sheet .omni-results{ padding-bottom:max(0px, calc(var(--app-h, 100vh) - var(--vvh, var(--app-h, 100vh)))); }
  /* Placed explicitly so DOM order does not matter — the map header's box also contains a logo
     and a Go button, both hidden in sheet mode, and hidden items leave no grid track behind. */
  .omni-sheet .omni-close{ grid-column:1; grid-row:1; }
  .omni-sheet input      { grid-column:2; grid-row:1; }
  .omni-sheet .omni-clear{ grid-column:3; grid-row:1; }
  .omni-sheet .omni-results,
  .omni-sheet .omni-hint { grid-column:1 / -1; grid-row:2; align-self:stretch; min-height:0; }
  /* the map box's logo is redundant once the sheet fills the screen — the back arrow is clearer */
  .omni-sheet .msearch-logo, .omni-sheet .msearch-btn{ display:none; }

  .omni-sheet .omni-close{
    display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
    width:40px; height:40px; padding:0; border:0; border-radius:50%;
    background:none; color:var(--ink); cursor:pointer;
  }
  .omni-sheet .omni-close:active{ background:var(--mintbg); }
  .omni-sheet .omni-close svg{ width:24px; height:24px; }

  .omni-sheet input{
    min-width:0; height:44px; margin:0; padding:0 12px;
    border:1.5px solid var(--track); border-radius:999px; background:#FBFBF8;
    font-size:16px;            /* THE iOS zoom threshold — do not lower this */
    color:var(--ink);
  }
  .omni-sheet input:focus{ outline:none; border-color:var(--accent); }
  /* the platform's own clear affordance would sit under ours */
  .omni-sheet input::-webkit-search-cancel-button{ -webkit-appearance:none; appearance:none; }

  .omni-sheet .omni-clear{
    display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
    width:var(--close-size, 44px); height:var(--close-size, 44px); padding:0; border:0; border-radius:50%;
    background:none; color:var(--ink-soft); font-size:1.1rem; line-height:1; cursor:pointer;
  }
  .omni-sheet .omni-clear[hidden]{ display:none; }

  /* results stop being a floating dropdown and become the scrollable body of the sheet */
  .omni-sheet .omni-results{
    position:static; width:100%; max-height:none;
    margin:0; border:0; border-radius:0; box-shadow:none; background:none;
    overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch;
    padding-bottom:calc(var(--safe-bottom, env(safe-area-inset-bottom, 0px)) + 16px);
  }
  .omni-sheet .omni-hit{
    padding:13px 8px;                       /* >=44px tall: a thumb target, not a mouse target */
    border-bottom:1px solid var(--track);
  }
  .omni-sheet .omni-hit b{ font-size:1rem; }
  .omni-sheet .omni-hit small{ font-size:.85rem; }
  .omni-sheet .omni-hit:active{ background:var(--mintbg); }
  .omni-sheet .omni-group{ padding:14px 8px 6px; font-size:.72rem; }
  .omni-sheet .omni-empty{ padding:20px 8px; }

  /* shown before anything is typed, so the sheet is never a blank white screen.
     display:block is load-bearing — the base `.omni-hint{display:none}` sits later in this file
     and would otherwise win by cascade order regardless of this rule's higher specificity. */
  .omni-sheet .omni-hint{
    display:block; flex:1 1 100%; padding:18px 8px; color:var(--ink-soft); font-size:.9rem;
  }
  .omni-sheet .omni-results:not([style*="none"]) ~ .omni-hint{ display:none; }
}
.omni-hint{ display:none; }

.foot-legal{ display:flex; flex-wrap:wrap; gap:4px 16px; margin-top:10px; }
.foot-legal a{ font-size:.82rem !important; color:#9fb8ab !important; display:inline !important; padding:0 !important; }
.foot-disclaimer{ font-size:.8rem; color:#9fb8ab; margin-top:12px; max-width:640px; }

/* ---- P7: locked service pills + partner-with-us panel ---- */
.pill-svc.locked{ opacity:.55; border-style:dashed; }
.pill-svc.locked:hover{ opacity:.85; border-color:var(--track); }
.partner-panel{
  position:fixed; top:calc(var(--header-h) + 60px); left:50%; transform:translateX(-50%);
  z-index:970; width:min(380px, calc(100vw - 28px)); padding:18px 20px;
}
.partner-panel h3{ margin-right:28px; }
.partner-panel p{ font-size:.9rem; margin-top:8px; }

/* ---- P18: "More" nav dropdown ---- */
.nav-more{ position:relative; display:inline-block; }
.nav-more-btn{
  background:none; border:none; cursor:pointer; font-family:var(--font);
  font-weight:600; font-size:inherit; color:inherit; padding:6px 2px;
}
.nav-more-menu{
  display:none; position:absolute; top:100%; right:0; min-width:180px; z-index:1300;
  background:var(--paper, #F1EBE4); border:1.5px solid var(--track); border-radius:12px;
  box-shadow:0 10px 28px rgba(44,59,54,.18); padding:8px 0;
}
.nav-more.open .nav-more-menu{ display:block; }
.nav-more-menu a{ display:block; padding:9px 18px; }
.nav-more-menu a:hover{ background:rgba(97,141,124,.1); }
@media (max-width: 900px){
  .nav-more{ display:block; }
  .nav-more-menu{ position:static; display:block; border:none; box-shadow:none; padding:0; }
  .nav-more-btn{ display:none; }
}

/* ---- P18: aggregation bubbles (zoom-banded map) ---- */
.agg-bubble{
  width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  color:#fff; font-weight:800; font-size:.85rem; font-family:var(--font);
  border:2.5px solid rgba(255,255,255,.9); box-shadow:0 3px 10px rgba(44,59,54,.35);
}

/* ---- Brand refresh: theme-colored icons + themed scrollbars ---- */

/* Emoji rendered as single-color silhouettes (theme colors only):
   color:transparent + zero-blur text-shadow paints the glyph in one color. */
.inc-marker{ color:transparent; text-shadow:0 0 0 var(--ico, #F3F4F1); }
.ico-t{ color:transparent; text-shadow:0 0 0 var(--ico, var(--accent, #618D7C)); }
.chip.sel .ico-t, .chip.sel-amber .ico-t, .chip.sel-red .ico-t{ text-shadow:0 0 0 #F3F4F1; }
/* neutralize native emoji colors on admin/nav card headings */
.grid a.card > h3{ filter:grayscale(1); }

/* Themed scrollbars (Firefox + WebKit) */
html{ scrollbar-color:#618D7C transparent; scrollbar-width:thin; }

/* Round-17b: native checkboxes/radios/switches wear the brand accent everywhere
   (settings toggles, map pin filters, story filters, report form, admin pages) */
input[type="checkbox"], input[type="radio"], input[type="range"], progress{
  accent-color:var(--accent, #618D7C);
}

/* ---- Round-23: site-wide native control theme ----
   `.field input/select/textarea` (style.css) already themed form-card controls, but every
   control OUTSIDE a .field (admin tables, inline filters, popovers) fell back to browser
   chrome — which is why several pages hand-rolled the same inline select styling. These are
   deliberately LOW specificity (bare element selectors), so every existing rule that targets
   a control via a class (.field input, .msearch input, .compose textarea, .base-switch …)
   still wins, and inline styles still win over everything. */

/* Drives the native bits CSS can't reach: the select popup list, date/time picker panels,
   spinners and autofill. The whole site is the light "paper" theme. */
:root{ color-scheme:light; }

input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]),
select,
textarea{
  font-family:var(--font); font-size:.95rem; color:var(--ink, #2C3B36);
  background:#FBFBF8; border:1.5px solid var(--track, #ADC3B9); border-radius:10px;
  padding:9px 12px; transition:border-color .15s, box-shadow .15s;
}
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):focus,
select:focus, textarea:focus{
  outline:none; border-color:var(--accent, #618D7C); box-shadow:0 0 0 3px rgba(97,141,124,.18);
}
input:disabled, select:disabled, textarea:disabled{
  background:#F1F0EA; color:var(--ink-soft, #55635C); cursor:not-allowed; opacity:.75;
}
input::placeholder, textarea::placeholder{ color:var(--ink-soft, #55635C); opacity:.8; }

/* select: swap the OS arrow for a brand chevron (appearance:none kills the native one).
   `.field select` (style.css) MUST be listed explicitly: it is more specific (0,1,1) than a bare
   `select` (0,0,1), and its `background:` SHORTHAND resets background-image — so without this the
   native arrow would be suppressed while the chevron never painted, leaving no indicator at all.
   Same specificity + p4.css loads later ⇒ these win. */
select,
.field select{
  appearance:none; -webkit-appearance:none; -moz-appearance:none; cursor:pointer;
  padding-right:34px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234F8577' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 11px center; background-size:14px;
}
select:disabled{ cursor:not-allowed; }
html[dir="rtl"] select,
html[dir="rtl"] .field select{ padding-right:12px; padding-left:34px; background-position:left 11px center; }
/* the popup list itself (honoured by Firefox/Chromium on some platforms) */
select option{ background:#FBFBF8; color:var(--ink, #2C3B36); }

/* date/time pickers: tint the OS indicator toward the brand instead of flat black */
input::-webkit-calendar-picker-indicator{ cursor:pointer; opacity:.65; }
input::-webkit-calendar-picker-indicator:hover{ opacity:1; }
/* file inputs (evidence upload, SafeNet photo): theme the button half */
input[type="file"]{ font-family:var(--font); font-size:.92rem; color:var(--ink, #2C3B36); }
input[type="file"]::file-selector-button{
  font-family:var(--font); font-weight:800; font-size:.85rem; cursor:pointer;
  margin-right:10px; padding:8px 14px; border-radius:999px; border:0;
  background:var(--accent, #618D7C); color:#fff;
}
input[type="file"]::file-selector-button:hover{ background:var(--deep, #4F8577); }
::-webkit-scrollbar{ width:11px; height:11px; }
::-webkit-scrollbar-track{ background:transparent; }
::-webkit-scrollbar-thumb{
  background:#618D7C; border-radius:6px; border:2.5px solid #E2DCD3;
}
::-webkit-scrollbar-thumb:hover{ background:#4F8577; }

/* ---- Simplified header: right cluster, app-launcher grid menu, avatar-only user ---- */
.nav-right{ margin-left:auto; display:flex; align-items:center; gap:12px; }
.nav-right > *{ flex:0 0 auto; } /* round-11: icons must never squash into ovals */
@media (max-width: 640px){
  /* round-11: small screens drop the brand text + slogan, keep the droplet */
  .site-header .brand > span{ display:none; }
  .nav-right{ gap:8px; }
}
/* Round-95l: at 360dp (LG G6 class — much of the Ghana install base) the standard header's
   row overflowed and the grid button was clipped off the right edge. Squeeze: tighter
   padding and gaps, slightly smaller circles, and the map shortcut goes — the brand logo
   already links to the live map. */
@media (max-width: 420px){
  .site-header{ padding:0 10px; }
  .nav-right{ gap:4px; }
  /* Round-96e/g: map + stories icons live on phones AND the brand pin stays (user asked
     for the app icon on every nav) — only the wordmark yields. Measured on the G6: the
     row has ~100px slack at 360dp with 36px icons, so the ~44px pin fits. */
  .site-header .brand span{ display:none; }
  .nav-icon{ width:36px; height:36px; }
  /* Round-96h: on every user page EXCEPT stories (fewer icons there — it reads fine
     packed right) the icon row spreads across the phone width instead of bunching
     right with dead space after the brand pin. The map header is separate markup. */
  .site-header .nav-right{ flex:1 1 auto; justify-content:space-between; gap:0; margin-left:6px; }
  .nav-icon svg{ width:20px; height:20px; }
  .nav-right .bell, .nav-right .msg-btn{ padding:4px; }
  .nav-report{ transform:scale(.92); }
}

.bell{ position:relative; font-size:1.15rem; padding:6px; border-radius:8px; color:transparent; text-shadow:0 0 0 var(--deep, #4F8577); display:inline-flex; align-items:center; justify-content:center; }
.msg-btn{ color:var(--deep, #4F8577); text-shadow:none; }
/* Round-94c: .bell was styled for an EMOJI (transparent text + shadow silhouette). These two
   carry real SVGs now, so undo that and size them like the envelope always was. */
.nav-svg{ color:var(--deep, #4F8577); text-shadow:none; }
.nav-svg svg{ width:20px; height:20px; }
.nav-svg.on{ color:var(--accent, #618D7C); }
.msg-btn svg{ width:20px; height:20px; }
.bell:hover{ background:rgba(97,141,124,.12); text-decoration:none; }
/* Round-188b: a count badge is a DOT with a number in it, not a pill. `padding: 1px 5px` sized
   the box to the glyph, so a single digit came out wider than it was tall — an ovoid on Alerts,
   Communities and Stories. A fixed 17px box with the digit centred is round for 1-9 and grows
   only when the content genuinely needs it ("9+"), where a slight stadium is correct rather
   than a squashed circle. box-sizing so the 2px ring the bar's badges wear does not add to it. */
.bell-badge{
  position:absolute; top:0; right:0; background:#C0392B; color:#fff;
  font-size:.6rem; font-weight:800; border-radius:999px; line-height:1;
  box-sizing:border-box; min-width:17px; height:17px; padding:0 4px;
  display:inline-flex; align-items:center; justify-content:center;
  text-shadow:none;
}
.bell-badge[hidden]{ display:none; }

.avatar{ display:inline-flex; }
.avatar img, .avatar-fallback{
  width:34px; height:34px; border-radius:50%; object-fit:cover;
  border:2px solid var(--accent, #618D7C);
}
.avatar-fallback{
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--accent, #618D7C); color:#F3F4F1; font-weight:800; font-size:1rem;
}
.avatar:hover img, .avatar:hover .avatar-fallback{ border-color:var(--deep, #4F8577); }

.nav-grid{ position:relative; }
.grid-btn{
  background:none; border:0; cursor:pointer; color:var(--deep, #4F8577);
  width:42px; height:42px; border-radius:10px;
  display:inline-flex; align-items:center; justify-content:center;
  transition:background .15s;
}
.grid-btn:hover, .nav-grid.open .grid-btn{ background:rgba(97,141,124,.14); }

.grid-menu{
  display:none; position:absolute; top:calc(100% + 8px); inset-inline-end:0; z-index:1200;
  width:min(340px, calc(100vw - 20px));
  background:#F3F4F1; border:1.5px solid var(--track, #ADC3B9); border-radius:16px;
  box-shadow:0 14px 36px rgba(44,59,54,.22); padding:12px;
  grid-template-columns:repeat(3, 1fr); gap:4px;
  /* Round-84: the menu grew (get-the-app tile, language rows) past a short phone's screen —
     cap it to the viewport and scroll inside, without the page scrolling behind it. */
  max-height:min(calc(var(--app-h, 100vh) * 0.74), calc(var(--app-h, 100vh) - 110px)); overflow-y:auto; overscroll-behavior:contain;
}
.nav-grid.open .grid-menu{ display:grid; }
/* Round-94: the grid menu lives inside the header's stacking context (z 1100), and the mobile
   bottom-sheet incident panel sits at 1250 — the open menu rendered UNDER the panel. Lift the
   whole header only while the menu is open; the sheet's close button stays on top otherwise. */
/* Round-94c: 1300 cleared the mobile incident sheet (1250) but NOT Ada's FAB (2500), so the
   menu still opened behind Ada on ordinary pages. Above everything the menu can meet. */
/* The ladder this has to clear, top rung last: Ada's FAB 2500 < the phone messenger shell
   2600 < the open grid menu. Equal z-index loses to whatever comes later in the document,
   which is how the menu ended up behind the messages pane. */
html.grid-open .site-header, html.grid-open .map-header{ z-index:2700; }   /* :has twin (pre-105) */
html:has(.nav-grid.open) .site-header, html:has(.nav-grid.open) .map-header{ z-index:2700; }
.grid-menu a{
  display:flex; flex-direction:column; align-items:center; gap:6px; text-align:center;
  padding:12px 6px; border-radius:12px; font-size:.78rem; font-weight:600;
  color:var(--ink, #2C3B36); line-height:1.2;
}
.grid-menu a:hover{ background:rgba(97,141,124,.12); text-decoration:none; }
/* outline tiles: accent stroke, transparent fill */
.grid-menu a svg{ width:24px; height:24px; color:var(--accent, #618D7C); flex:0 0 auto; }

/* ---- Live-map nav: transparent bar over the map (Google-Maps style) ---- */
:root{ --msearch-w:min(400px, 46vw); }
.map-header{
  position:fixed; top:var(--signup-h, 0px); left:0; right:0; z-index:1100;
  height:64px;   /* literal: --header-h is header + sign-up bar when the bar shows */
  display:flex; align-items:center; gap:12px; padding:0 14px;
  background:transparent; pointer-events:none; /* the map stays draggable between the islands */
}
.map-header .msearch, .map-header .nav-right{ pointer-events:auto; }
/* round-15f/16b: the icon cluster must never shrink below its content (the grid button
   was sliding off-screen on narrow phones when signed out) — the search box may SHRINK
   instead, but never GROW (flex-grow was stretching it over the service pills on desktop) */
.map-header .nav-right{ flex:0 0 auto; }
.map-header .msearch{ flex:0 1 auto; min-width:0; }

.msearch{
  position:relative; display:flex; align-items:center; gap:6px;
  width:var(--msearch-w); padding:5px 8px 5px 10px;
  background:rgba(243,244,241,.96); border:1px solid var(--track); border-radius:12px;
  box-shadow:0 4px 18px rgba(44,59,54,.22);
}
.msearch-logo{ display:inline-flex; flex:0 0 auto; }
.msearch-logo svg{ width:26px; height:26px; }
.msearch input{
  flex:1; min-width:0; border:0; background:none; padding:7px 4px;
  font-family:var(--font); font-size:.95rem; color:var(--ink);
}
.msearch input:focus{ outline:none; }
.msearch-btn{
  flex:0 0 auto; background:none; border:0; cursor:pointer; padding:6px;
  border-radius:8px; color:var(--accent); display:inline-flex;
}
.msearch-btn svg{ width:20px; height:20px; }
.msearch-btn:hover{ background:rgba(97,141,124,.12); }
/* Round-195d: the phone's copy of the map magnifier, which is a LINK to /search. Hidden on
   desktop, where the pill it sits in is a working search box with inline results.

   Round-199n: this has to sit AFTER .msearch-btn's display, not before it. The link carries BOTH
   classes, the two selectors have the same specificity, and the later one wins — so while this
   rule lived further up the file the link was displayed on desktop anyway and the header showed
   TWO magnifiers side by side. Reported by the owner; the media query at the bottom of this file
   hid #omniGo on phones, which is why only large screens showed it. */
.msearch-page{ display:none; }
.msearch .omni-results{ top:calc(100% + 8px); }

/* right-cluster buttons get a paper backing so they read over the map */
.map-header .grid-btn, .map-header .bell{
  background:rgba(243,244,241,.96); border:1px solid var(--track);
  border-radius:50%; box-shadow:0 4px 14px rgba(44,59,54,.18);
}
.map-header .bell{ width:42px; height:42px; display:inline-flex; align-items:center; justify-content:center; padding:0; }
.map-header .grid-btn:hover, .map-header .nav-grid.open .grid-btn{ background:#E9EFE9; }

@media (max-width: 720px){
  /* chips drop below the bar (see .svc-wrap); min-width:0 lets the box shrink past
     the input's intrinsic size so the right cluster never overflows */
  .map-header .msearch{ flex:1 1 auto; width:auto; min-width:0; }
  .map-header{ gap:8px; padding:0 10px; }
  /* Round-94: below ~560px the inline input is a sliver — icon-only reads better. The 🔍
     still opens the full-screen sheet (tap → input focus → round-84 sheet), where the real
     input lives. :not(.omni-sheet) keeps the sheet's own input visible. */
}
@media (max-width: 560px){
  .map-header .msearch:not(.omni-sheet){ flex:0 0 auto; gap:2px; padding:5px 6px; }
  .map-header .msearch:not(.omni-sheet) input,
  .map-header .msearch:not(.omni-sheet) .omni-clear{ display:none; }
}

/* ---- Nav quick actions: live map icon + report button (both navs) ---- */
.nav-icon{
  width:42px; height:42px; border-radius:50%; color:var(--deep, #4F8577);
  display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
  position:relative;   /* round-96e: the stories icon carries a .bell-badge */
  /* Round-96d: the search trigger is a <button> (the other nav icons are <a>), and
     Android's WebView paints the UA button border/background on it — iOS doesn't,
     which is why only Android showed a dark ring. Neutralize explicitly. */
  border:0; background:none; padding:0; cursor:pointer;
}
.nav-icon svg{ width:22px; height:22px; }
.nav-icon:hover{ background:rgba(97,141,124,.12); }
.nav-report{
  display:inline-flex; align-items:center; gap:8px; flex:0 0 auto;
  background:var(--st-impact, #C0392B); color:#fff !important; border-radius:999px;
  padding:9px 16px; font-weight:700; font-size:.9rem;
}
.nav-report svg{ width:18px; height:18px; }
.nav-report:hover{ background:#d64536; text-decoration:none; }
@media (max-width: 900px){
  .nav-report .lbl{ display:none; }
  .nav-report{ width:42px; height:42px; padding:0; justify-content:center; border-radius:50%; }
}

/* ---- Time row (bottom) + mobile report shortcut ---- */
.time-row{
  position:fixed; bottom:18px; left:50%; transform:translateX(-50%); z-index:940;
  display:flex; align-items:center; gap:10px;
}
.time-row .base-switch{ position:static; transform:none; }
.map-report-sm{
  display:none; width:40px; height:40px; border-radius:50%; flex:0 0 auto;
  background:var(--st-impact, #C0392B); color:#fff;
  align-items:center; justify-content:center; box-shadow:0 6px 20px rgba(44,59,54,.3);
}
.map-report-sm svg{ width:19px; height:19px; }
.map-report-sm:hover{ background:#d64536; }
@media (max-width: 720px){
  .map-header .nav-report{ display:none; } /* report moves next to the timeline */
  /* Round-82: stories does the same, or the page carries two report buttons at once — the
     header pill and the one now sitting beside its time bands. */
  body:has(.stories-page) .site-header .nav-report{ display:none; }
  .map-actions{ display:none; }            /* join/deploy + zoom are desktop-only */
  .map-report-sm{ display:inline-flex; }
  .time-row{ left:10px; right:10px; bottom:14px; transform:none; }
  /* Round-88: was `flex:1 1 auto`, which STRETCHED the pill to the full width of the row.
     The pill carries the background, border and radius, so on any screen where the bands do
     not fill the row it painted a long empty strip to the right of the settings button —
     clearly visible on a tablet (~610 CSS px, bands end around 367px). Phones never showed it
     because at 360px the bands overflow and the pill is full anyway.

     `0 1 auto` lets it shrink and scroll exactly as before when the bands are too wide, but
     hug its content when they are not; the auto margins then centre it under the map the same
     way the desktop rule does with translateX(-50%). */
  .time-row .base-switch{ flex:0 1 auto; max-width:100%; margin-inline:auto;
                          overflow-x:auto; scrollbar-width:none; }
}

/* Round-88: centring the pill on the VIEWPORT (rather than in the space the report button
   leaves over) was tried and REVERTED — do not retry it this way. `position:absolute` on
   .base-switch takes it out of the flex row, and it then loses its content height: measured
   2px tall with 16px buttons inside, i.e. the bands vanish into a hairline. Anchoring with
   top/translateY fixed the vertical drift but not the collapse.

   The in-flow rule above is what ships: the pill hugs its content (gap gone, which was the
   actual bug) and sits on its original baseline. It rides ~48px right of the true centre at
   610px wide, because the report button is a flex sibling consuming space on one side only.
   Fixing that properly means giving the row a symmetric left-hand counterweight, or moving
   the report button out of the row — a layout change, not a tweak. */

/* Round-88: camera-capture buttons, sharing the .up-rec row with the audio recorder.
   The inputs are visually hidden inside their <label>s, so the label IS the button; keeping
   them in the DOM rather than display:none matters, because a hidden-but-present input is
   still clickable through its label and still opens the WebView file chooser.

   Two things here are load-bearing, both found by measuring the live page:

   1. display:contents on the wrapper. As a real flex ITEM it took a shrunken width and wrapped
      its two buttons into a column beside the audio button. contents dissolves the box so both
      labels become direct children of .up-rec and all three share one row, gap and wrap rule.
      The .up-rec in the selector is needed to out-specify `.in-app .app-only{display:block}`
      in style.css, which ties on specificity and loads later.

   2. The label reset. These sit inside .field, whose `label{display:block}` and larger form
      font were winning: the capture labels measured 42px tall at 15.2px text against the audio
      BUTTON's 37px at 14.08px — same padding, different type. Restating display and font here
      is what actually makes a <label> and a <button> the same control. */
.in-app .up-rec .up-capture{ display:contents; }
.up-rec .up-capture label{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  width:auto; margin:0; cursor:pointer;
  font-size:.88rem; line-height:normal; font-weight:700;   /* == .btn-sm, measured */
}
.up-capture input.up-cap{ position:absolute; width:1px; height:1px; opacity:0;
                          overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }

/* ── Round-88b: the phone composer ────────────────────────────────────────────────────────
   Describe-and-attach as ONE box, the way every messaging app the reporter already uses does it.
   Shown in the app and on any narrow viewport; the desktop keeps its drag-and-drop zone, because
   dragging is a desktop idiom and there is nothing to drag on a phone. */
/* Round-88h: the COMPOSER carries the frame now, not the textarea.
   Thumbnails sit inside that frame directly under the text, so the box holds the whole message —
   words and evidence together — instead of a bordered textarea with attachments floating below
   it. The textarea therefore goes borderless and transparent, and the composer reserves a strip
   at the bottom for the three buttons (which are absolutely positioned to it). */
.composer{
  position:relative;
  border:1px solid var(--track, #ADC3B9); border-radius:14px;
  background:var(--panel, #FBFBF8);
  padding-bottom:46px;                            /* the button strip */
}
.composer:focus-within{ border-color:var(--accent, #618D7C); }
/* Three classes to out-specify .field textarea in style.css, which loads after this file. */
.field .composer textarea{
  border:0; background:transparent; box-shadow:none; outline:none;
  width:100%; display:block; margin:0; border-radius:14px 14px 0 0;
  padding:12px 12px 4px; resize:vertical;
}
.composer-acts{
  /* Round-88f: shown on EVERY viewport. The drop zone it used to replace on mobile is gone —
     these three buttons are now the only way to attach a file, on phone, in-app and desktop. */
  display:flex; position:absolute; right:9px; bottom:9px;
  gap:6px; align-items:center;
  /* Explicit z-index: without it the buttons sat in the same stacking level as the textarea and
     taps fell through to it — the giveaway was the page scrolling (the textarea taking focus)
     instead of the menu opening.

     1250, not 3: this rule creates a STACKING CONTEXT, and the sheet inside it cannot escape its
     parent's level however high its own z-index is. At 3 the sheet opened but the location map
     (Leaflet panes 400–600) and the sticky send bar (1200) painted straight over it, hiding the
     third item. It has to sit above both for the sheet to be usable. */
  z-index:6;   /* just above .submit-bar (5); both far below the nav at 1100 */
}

/* Mobile gets the composer INSTEAD of the drop zone and the button row, not as well as.
   The evidence LABEL goes too — hiding only its contents left a heading with nothing beneath it.
   #upTiles stays: attached files must still be visible and removable. */

/* The drop zone is hidden VISUALLY, not with display:none — #upInput lives inside it, and the
   composer's buttons click that input to open the picker. A display:none file input cannot open
   a dialog (the same trap .up-file's own comment warns about), so hiding the zone that way made
   every composer upload button silently dead. sr-only keeps the input alive and clickable while
   taking no space. */

/* Two classes on the selector, not one. `.field label{display:block}` in style.css is (0,1,1)
   and loads AFTER this file, so a bare `.cbtn` lost the display battle on the camera button —
   which is a <label> — and it rendered as a squashed block instead of a centred circle. */
.composer-acts .cbtn{
  width:34px; height:34px; border-radius:50%; flex:0 0 34px;
  display:inline-flex; align-items:center; justify-content:center;
  border:1px solid var(--track, #ADC3B9); background:var(--panel, #FBFBF8);
  color:var(--accent, #618D7C); cursor:pointer; padding:0; margin:0;
  list-style:none;                         /* <summary> renders a marker without this */
  font-size:0;                             /* kills the inline-text baseline gap around the svg */
}
.composer-acts .cbtn::-webkit-details-marker{ display:none; }
.composer-acts .cbtn::marker{ content:""; }
.composer-acts .cbtn svg{ width:18px; height:18px; display:block; }
/* Round-89: no camera on this device. Left visible rather than hidden so the reporter can see
   the capability exists and why it is unavailable (the title carries the explanation) — a button
   that silently disappears reads as a bug, and they can still attach an existing file. */
.composer-acts .cbtn-off{ opacity:.42; cursor:not-allowed; }
/* position:relative so the .up-file input inside pins to THIS label, exactly as .up-zone does.
   Without it the input anchored to .composer-acts instead — outside its own label's box — and
   the file dialog never opened. The rule that used to override .up-file here is gone: the drop
   zone's sr-only pattern is the one proven to open a picker in this WebView, so the composer
   uses it unchanged rather than a second, subtly different copy. */
.composer-acts .cbtn{ position:relative; }
.composer-acts details[open] > .cbtn{ background:var(--accent, #618D7C); color:#FBFBF8; border-color:var(--accent, #618D7C); }
/* Held-to-talk reads as live: the button itself is the recording indicator. */
.composer-acts .cbtn.mic.rec{ background:#C0392B; border-color:#C0392B; color:#FBFBF8; transform:scale(1.08); }
@media (prefers-reduced-motion:reduce){ .composer-acts .cbtn.mic.rec{ transform:none; } }

.composer-acts .plus-wrap{ position:relative; display:inline-flex; }
/* Anchored to the button again (round-88c), now that it is two items rather than three: the
   full-width sheet was heavier than the choice deserved. The earlier off-screen problem is gone
   because .composer-acts sits at z-index 1250, so nothing paints over it, and a two-item menu is
   short enough to clear the top of the viewport from any realistic composer position. */
.composer-acts .plus-menu{
  position:absolute; bottom:42px; right:0; z-index:1300;   /* above the sticky send bar */
  min-width:210px; padding:8px; border-radius:16px;
  /* Clean sheet: plain white, no visible frame. Depth comes from a soft shadow and a hairline
     the same colour as the page, so the menu reads as a card floating over the form rather than
     a boxed-in panel. The bordered version fought every surface it opened over. */
  background:#FFFFFF; border:1px solid rgba(44,59,54,.06);
  box-shadow:0 12px 30px -10px rgba(44,59,54,.28), 0 2px 6px rgba(44,59,54,.06);
  display:flex; flex-direction:column; gap:2px;
}
/* Buttons now, not labels — but the .field label rules still need out-specifying, and these must
   shed the default button chrome (border, grey background, centred text) that would otherwise
   turn a clean menu row into a boxed control. */
.composer-acts .plus-item{
  display:flex; align-items:center; gap:10px; cursor:pointer;
  padding:10px 11px; border-radius:10px; font-size:.9rem; font-weight:600;
  color:var(--ink, #2C3B36); margin:0;
  width:100%; text-align:left; background:transparent; border:0; font-family:inherit;
}
.composer-acts .plus-item:hover{ background:rgba(97,141,124,.12); }
.composer-acts .plus-item svg{ width:18px; height:18px; color:var(--accent, #618D7C); flex:0 0 18px; }
/* Same fix as .cbtn above: anchor the .up-file input inside its own label instead of overriding
   the sr-only pattern with an unanchored copy. */
.composer-acts .plus-item{ position:relative; }

.composer-foot{ margin-top:6px; min-height:1.2em; }
.composer-limits{ font-size:.8rem; color:var(--ink-faint, #8A968F); }
.rec-live{ display:inline-flex; align-items:center; gap:7px; font-size:.85rem; font-weight:700; color:#C0392B; }
/* Round-142l: the clock in the composer row, next to the (now square) stop button */
.rec-inline{ font-variant-numeric:tabular-nums; padding:0 6px; }
.rec-inline .rec-dot{ width:9px; height:9px; border-radius:50%; background:#C0392B; --pulse-min:.3; animation:aq-pulse 1s infinite; }
/* Round-142l: audio tiles — outline glyphs, a play/pause button and a clock */
.up-tile .up-typ svg{ width:26px; height:26px; color:#F3F4F1; }
.up-tile .up-aplay{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); width:38px; height:38px;
  border-radius:50%; border:0; background:rgba(255,255,255,.92); color:var(--deep, #4F8577);
  display:flex; align-items:center; justify-content:center; cursor:pointer; z-index:3; padding:0;
}
.up-tile .up-aplay svg{ width:20px; height:20px; }
.up-tile .up-aplay.on{ background:var(--deep, #4F8577); color:#fff; }
.up-tile .up-aclock{ position:absolute; top:4px; left:6px; font-size:.64rem; font-weight:700; color:#F3F4F1; z-index:2; font-variant-numeric:tabular-nums; }
.up-tile .up-aplay ~ .up-typ, .up-tile:has(.up-aplay) .up-typ{ opacity:.35; }
/* Round-142l: the SafeNet timeline avatar wears the real profile */
.sn2-day .ev .who{ overflow:hidden; }
.sn2-day .ev .who img{ width:100%; height:100%; object-fit:cover; }
.sn2-day .ev .who .ini{ font-size:.8rem; font-weight:700; color:var(--deep, #4F8577); }
.sn2-day .ev .who .oic{ display:flex; color:var(--deep, #4F8577); }
.sn2-day .ev .who .oic svg{ width:20px; height:20px; }

/* Round-88: the routing card on the report confirmation screen. Reads as a follow-up, not as a
   second form — no red, no required marks, and it is skippable simply by walking away, because
   the report is already filed by the time it appears. */
.routing-card{
  margin-top:16px; padding:14px 16px; border-radius:12px;
  border:1px solid var(--track, #ADC3B9); background:var(--panel, #FBFBF8);
}
.routing-row{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.routing-row select{ flex:1 1 180px; min-width:0; }

/* Round-88: "Add more details" — the folded optional fields on the report form.
   A native <details> rather than a Blazor toggle: it opens before interactivity has loaded,
   keeps the accessible disclosure semantics for free, and survives an enhanced navigation. */
.more-details{ border:1px solid var(--track, #ADC3B9); border-radius:12px;
               background:var(--panel, #FBFBF8); margin-bottom:16px; }
.more-details > summary{
  cursor:pointer; list-style:none; padding:13px 15px;
  font-weight:700; font-size:.94rem; color:var(--accent, #618D7C);
  display:flex; align-items:center; justify-content:space-between; gap:10px;
}
.more-details > summary::-webkit-details-marker{ display:none; }   /* Safari's default triangle */
.more-details > summary::after{ content:"+"; font-size:1.2rem; line-height:1; font-weight:400; }
.more-details[open] > summary::after{ content:"–"; }
.more-details > summary:focus-visible{ outline:2px solid var(--accent, #618D7C); outline-offset:2px; border-radius:12px; }
.more-details-body{ padding:0 15px 4px; }

/* Round-88: the always-visible send bar. Sticks to the bottom of the viewport so the exit is
   never a scroll away, and carries the line that names what is still missing. The fade lets the
   form scroll under it instead of ending in a hard band. */
/* Solid, not a gradient. The first version faded to transparent at the top, and content passing
   underneath ghosted through it — including the red "call 112 first" box, which is the single
   worst thing on this page to render as a half-legible smear. A hard edge reads as a bar; a
   translucent one reads as a bug. */
/* z-index 1200, not 5: Leaflet stacks its own panes at 400–600 and its controls at 800–1000, so
   the location map painted straight over the sticky bar — the submit button and the "still
   needed" line vanished behind it. Anything sticky on a page that embeds a map has to clear
   Leaflet's ceiling, not the page's. */
.submit-bar{
  position:sticky; bottom:0; z-index:5; margin-top:10px; padding:12px 0 10px;
  background:var(--panel, #FBFBF8);
  border-top:1px solid var(--track, #ADC3B9);
  box-shadow:0 -8px 18px -14px rgba(44,59,54,.5);
}
.submit-need, .submit-ready{ margin:0 0 9px; font-size:.86rem; font-weight:700; text-align:center; }
.submit-need{ color:#96661C; }          /* amber ink — a blocker, not an error */
.submit-ready{ color:#1B8F68; }

/* Round-88: the location preview under the report form's geo card. Fixed height because Leaflet
   measures its container on init — an auto-height box is 0px tall at that moment and the map
   renders as a grey sliver. Non-interactive by construction (see aquaGeoPreview), so it takes
   no pointer events and never steals a scroll on a touch screen. */
/* Round-89b: position+z-index CONTAINS Leaflet. Its panes (400-600) and controls (800-1000)
   otherwise compete in the page's stacking context, which is what forced the sticky bar up to
   1200 — above the nav at 1100, so the submit button rode over the header when scrolling. With
   the map boxed into its own context, everything around it can stay in normal page range. */
.geo-preview{ position:relative; z-index:0;
              height:200px; margin-top:10px; border-radius:12px; overflow:hidden;
              border:1px solid var(--track, #ADC3B9); background:#EDE7E0; }
.geo-preview .leaflet-container{ height:100%; width:100%; background:#EDE7E0; }
/* No touch-action override here on purpose. Leaflet sets its own (`pan-x pan-y` with dragging
   disabled), which is already what we want: one finger scrolls the form, and the two-finger
   pinch is handled by Leaflet's own touch handlers rather than the browser. An earlier attempt
   to set `pan-y pinch-zoom` here simply lost to leaflet.css and did nothing. */
/* The zoom buttons need a visible EDGE. An earlier `border:none` here left white controls on a
   near-white basemap: they were rendered and clickable but effectively invisible on the device,
   which is worse than not having them — the affordance is the whole point. */
.geo-preview .leaflet-control-zoom{ border:1px solid var(--track, #ADC3B9); border-radius:8px;
                                    overflow:hidden; box-shadow:0 1px 5px rgba(44,59,54,.28); }
.geo-preview .leaflet-control-zoom a{ width:30px; height:30px; line-height:30px;
                                      background:#FBFBF8; color:var(--ink, #2C3B36);
                                      font-size:18px; font-weight:700; }
.geo-preview .leaflet-control-zoom a + a{ border-top:1px solid var(--track, #ADC3B9); }

/* Round-88: sizes for the outline icons that replaced the emoji on the report and official-alert
   forms. OutlineIcons.Svg emits a bare <svg viewBox="0 0 24 24"> with no width/height, so without
   these it lays out at the SVG default of 300×150 — the icons must be sized wherever they appear.
   Colour needs no rule: the paths use stroke="currentColor", which is the whole point of the
   change — an emoji renders in the font's own palette (and differently per OEM), so it could
   never follow the theme. */
.geo-card .geo-ico svg{ width:20px; height:20px; display:block; }
.up-zone .up-ico svg{ width:26px; height:26px; display:block; margin:0 auto; }
.up-rec .btn svg,
.up-rec .up-capture label svg{ width:16px; height:16px; flex:0 0 auto; }
.up-tiles .up-typ svg{ width:18px; height:18px; }
/* inline with text (mode chips, and the location line on alert cards) */
.chip svg{ width:15px; height:15px; vertical-align:-2px; }
.oalert-loc svg{ width:14px; height:14px; vertical-align:-2px; }

/* ---- Service-chip overflow scroll arrows ---- */
.has-pill-arrows{ position:relative; }
/* Round-173 (owner's report: the live map scrolls sideways). The map's chip strip is
   position:fixed (style.css .svc-wrap); the generic arrows rule above turned it RELATIVE, so
   at every width under 1600px it became a full-width block in normal flow sitting 8px to the
   right of the viewport edge — eight pixels of horizontal page scroll on any pointer device.
   A fixed box is a containing block for the absolute arrows just the same. */
.svc-wrap.has-pill-arrows{ position:fixed; }
.pill-arrow{
  /* Round-94: same height as the service pills (8px padding + text + 2px borders ≈ 38px), so
     the arrow sits flush with the row instead of floating above its centre line.
     Round-142t: absolute — an overlay on the row's edge, never a flex sibling that reflows it. */
  position:absolute; top:50%; transform:translateY(-50%); z-index:2;
  flex:0 0 auto; width:38px; height:38px; border-radius:50%; cursor:pointer;
  border:1px solid var(--track); background:rgba(243,244,241,.96); color:var(--deep, #4F8577);
  display:none; align-items:center; justify-content:center;
  box-shadow:0 3px 10px rgba(44,59,54,.2);
}
.pill-arrow svg{ width:16px; height:16px; }
.pill-arrow.right svg{ transform:rotate(180deg); }
.pill-arrow.show{ display:inline-flex; }
.pill-arrow.left{ left:-4px; }
.pill-arrow.right{ right:-4px; }

/* ---- Collapsible map sidebar cards (uniform width, minimize to icon) ---- */
.map-card.collapsible{ width:250px; }
.card-head{ display:flex; align-items:center; gap:8px; cursor:pointer; user-select:none; }
.card-head h2, .card-head h3{ margin:0 !important; flex:1; }
.card-ico{ display:inline-flex; flex:0 0 auto; }
.card-ico svg{ width:18px; height:18px; color:var(--tan-light, #E3C9A5); }
.card-caret{ display:inline-flex; flex:0 0 auto; margin-left:auto; }
.card-caret svg{ width:15px; height:15px; color:#DCE5DD; transition:transform .2s; }
.card-body{ margin-top:10px; }
/* keep the bottom-anchored Layers card from riding over the Sensor card */
.legend .card-body{ max-height:calc(var(--app-h, 100vh) - 400px); max-height:calc(var(--app-h, 100dvh) - 400px); overflow-y:auto; }
/* bottom-right stack: SafeNet watch card + weather card flow without overlapping */
/* round-15: right-hand column — assistant ✨ pinned top-right, the card chips
   vertically CENTERED on the right edge (recenters as cards expand/collapse) */
.right-stack{
  position:fixed; right:14px; top:50%; transform:translateY(-50%); bottom:auto; z-index:900;
  display:flex; flex-direction:column; gap:10px; align-items:flex-end;
  max-height:calc(var(--app-h, 100vh) - var(--header-h) - 120px); overflow-y:auto;
  scrollbar-width:none; padding-left:6px;
}
.right-stack::-webkit-scrollbar{ display:none; }
/* round-73b: when the map cannot render, its controls do nothing — hide the sensor-network
   and layers cards rather than offer buttons that open empty panels. Stories and Ada stay:
   both work without a map. (#weatherCard / #safeNetCard also carry .net-stats and are
   JS-revealed, so they stay hidden here too, which is what we want.) */
html.legacy-browser .right-stack .net-stats,
html.legacy-browser .right-stack .legend,
html.legacy-browser .svc-wrap{ display:none !important; }
/* on the live map the assistant button heads the centered chip column.
   round-15d: the CSS default IS the final position (3 minimized chips below), so it
   never jumps on load; the script only fine-tunes (SafeNet chip, expanded cards) and
   the transition keeps that nudge soft. */
html.pg-map .asst-fab{ top:calc(50% - 140px); right:16px; transform:none; z-index:900;
  font-size:.9rem;   /* round-145f (owner's call): .9rem on the live map */
  box-shadow:none;   /* round-145f (owner's call): no shadow behind Ada on the live map */
  transition:top .3s ease; }
body:has(#map) .asst-fab{
  top:calc(50% - 140px); right:16px; transform:none; box-shadow:none; font-size:.9rem;
  transition:top .3s ease;
  /* z-index:900 — the detail panel (#stationPanel, z-index:1000) must pass in front. The FAB
     sits mid-right, exactly where that panel opens, so at its default 2500 it floated over the
     report content and its ✕. 900 is the .right-stack value: the FAB heads that chip column and
     those chips already sit behind the panel, so the whole column now passes under it together.
     Scoped to the map — off-map the FAB keeps 2500, where it has no panel to yield to.
     The drawer stays at 2400 and still opens over the panel, which is what you want when the
     user deliberately summons Ada. */
  z-index:900;
}
html.pg-map .asst-fab:hover{ transform:scale(1.07); box-shadow:none; }
body:has(#map) .asst-fab:hover{ transform:scale(1.07); box-shadow:none; }
@media (max-width: 720px){
  .right-stack{ max-height:calc(var(--app-h, 100vh) - var(--header-h) - 170px); }
  body:has(#map) .asst-fab{ width:44px; height:44px; font-size:1.15rem; top:calc(50% - 132px); }
}

/* ---- Round-17: simplified report panel — feed-card layout, folds, aliceblue pills ---- */
.inc-topline{ display:flex; align-items:center; gap:10px; justify-content:space-between; margin-bottom:10px; }
.inc-when{ color:var(--ink-soft, #55635C); font-size:.8rem; white-space:nowrap; }
.sp-fold{
  margin-top:12px; border:1px solid var(--track, #ADC3B9); border-radius:12px;
  background:rgba(255,255,255,.55); overflow:hidden;
}
.sp-fold summary{
  cursor:pointer; user-select:none; list-style:none; display:flex; align-items:center; gap:8px;
  font-weight:800; font-size:.82rem; letter-spacing:.6px; text-transform:uppercase;
  color:var(--deep, #4F8577); padding:10px 14px;
}
.sp-fold summary::-webkit-details-marker{ display:none; }
.sp-fold summary::before{ content:"▸"; font-size:.8rem; transition:transform .2s; }
.sp-fold[open] summary::before{ transform:rotate(90deg); }
.sp-fold .sp-fold-body{ padding:2px 14px 12px; }
/* round-19b: pills in the panel body AND the panel header (service + severity) wear white */
#spBody .pill, #spPill .pill{ background:#fff; }

/* round-17c: owner controls on the reporter's own report */
.own-box{
  margin-top:12px; border:1.5px dashed var(--accent, #618D7C); border-radius:12px;
  padding:10px 14px 12px; background:rgba(97,141,124,.06);
}
.own-row{ display:flex; align-items:center; gap:8px; }
.own-row b{ flex:1; font-size:.85rem; color:var(--deep, #4F8577); text-transform:uppercase; letter-spacing:.5px; }
.own-form{ margin-top:10px; }
.own-form .field{ margin-bottom:8px; }
.own-form input, .own-form select, .own-form textarea{
  width:100%; padding:8px 10px; border:1.5px solid var(--track, #ADC3B9); border-radius:10px;
  font-family:var(--font); font-size:.9rem;
}

/* ---- Round-17: user chip — avatar + name linking to the profile ---- */
.uchip{
  display:inline-flex; align-items:center; gap:8px; text-decoration:none; color:inherit;
  max-width:100%;
}
a.uchip:hover .uchip-name{ text-decoration:underline; }
.uchip img, .uchip-fb{
  width:26px; height:26px; border-radius:50%; object-fit:cover; flex:0 0 26px;
}
.uchip-fb{
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--accent, #618D7C); color:#fff; font-weight:800; font-size:.75rem;
}
.uchip-fb.uchip-anon{ background:var(--sage2, #8FA99B); }
.uchip-name{ font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ---- Round-17: settings as icon cards (grid on desktop, tap-to-expand bills on mobile) ---- */
.settings-grid{
  display:grid; grid-template-columns:repeat(2, 1fr); gap:16px; align-items:start;
  max-width:980px; margin:0 auto;
}
.scard{
  background:var(--paper, #FBFBF8); border:1.5px solid var(--track, #ADC3B9);
  border-radius:16px; overflow:hidden;
}
.scard-head{
  display:flex; align-items:center; gap:12px; padding:14px 18px;
  border-bottom:1px solid rgba(173,195,185,.35);
}
.scard-head h3{ margin:0; font-size:1rem; flex:1; }
.scard-ico{ display:inline-flex; flex:0 0 auto; }
.scard-ico svg{ width:20px; height:20px; color:var(--accent, #618D7C); }
.scard-caret{ display:none; }
.scard-body{ padding:16px 18px 18px; }
.scard-toggle{
  display:flex; align-items:flex-start; gap:10px; font-weight:600; font-size:.92rem;
  margin-bottom:10px; cursor:pointer;
}
.scard-toggle input{ width:auto !important; margin-top:3px; flex:0 0 auto; }
@media (max-width: 820px){
  .settings-grid{ grid-template-columns:1fr; gap:10px; }
  /* mobile: each card collapses to a tappable bill; only the open one shows its body */
  .scard-head{ cursor:pointer; user-select:none; border-bottom:0; }
  .scard-caret{ display:inline-flex; margin-left:auto; }
  .scard-caret svg{ width:15px; height:15px; color:var(--sage2, #8FA99B); transform:rotate(-90deg); transition:transform .2s; }
  .scard.open .scard-caret svg{ transform:rotate(90deg); }
  .scard:not(.open) .scard-body{ display:none; }
  .scard.open .scard-head{ border-bottom:1px solid rgba(173,195,185,.35); }
}

/* ---- Round-16: stories chip on the map + the stories feed page ---- */
.right-stack .stories-chip{
  position:relative; width:44px; height:44px; border-radius:50%; flex:0 0 auto;
  display:flex; align-items:center; justify-content:center; text-decoration:none;
}
.right-stack .stories-chip:hover{ text-decoration:none; filter:brightness(1.1); }
.right-stack .stories-chip .card-ico svg{ width:20px; height:20px; }
.story-badge{
  position:absolute; top:-4px; right:-4px; background:#C0392B; color:#fff;
  font-size:.62rem; font-weight:800; border-radius:999px; padding:1px 5px; line-height:1.4;
  border:2px solid #fff;
}

.stories-page{ background:var(--cream, #F1EBE4); min-height:var(--app-h, 100vh); }   /* round-142f: cream, like every page */
.stories-filters{
  position:sticky; top:var(--header-h); z-index:500; background:#fff;   /* round-158 (owner's call): white, not the page cream */
  border-bottom:1px solid #ECEAE4; padding:10px 14px 12px;
  display:flex; flex-direction:column; gap:10px; align-items:center;
}
.stories-svc-wrap{ display:flex; align-items:center; max-width:100%; min-width:0; }
.stories-svc{ display:flex; gap:8px; overflow-x:auto; max-width:100%; min-width:0; padding-bottom:2px; scrollbar-width:none; scroll-behavior:smooth; }
/* Round-94: the feed's pills are the MAP's pills (same classes, same on/off skin); soften the
   map's floating shadow — these sit on a white bar, not over tiles. */
.stories-svc .pill-svc{ box-shadow:none; backdrop-filter:none; background:#fff; }
.stories-svc .pill-svc.on{ background:var(--accent, #618D7C); }
.stories-svc::-webkit-scrollbar{ display:none; }
/* round-16b: scroll arrows appear when the chips overflow (like the map's service pills) */
.story-arrow{
  flex:0 0 auto; width:30px; height:30px; border-radius:50%; cursor:pointer;
  border:1.5px solid var(--track, #ADC3B9); background:#fff; color:var(--deep, #4F8577);
  display:none; align-items:center; justify-content:center;
}
.story-arrow svg{ width:16px; height:16px; }
.story-arrow.right svg{ transform:rotate(180deg); }
.story-arrow.show{ display:inline-flex; }
.story-arrow.left{ margin-right:6px; }
.story-arrow.right{ margin-left:6px; }
.story-arrow:hover{ border-color:var(--accent, #618D7C); background:var(--mintbg, #EAF1EC); }
.story-chip{
  display:inline-flex; align-items:center; gap:7px; white-space:nowrap; cursor:pointer;
  border:1.5px solid var(--track, #ADC3B9); border-radius:999px; background:#fff;
  font-family:var(--font); font-size:.85rem; font-weight:700; color:var(--ink, #2C3B36);
  padding:7px 14px; transition:all .15s;
}
.story-chip .dot{ width:10px; height:10px; border-radius:50%; }
.story-chip:hover{ border-color:var(--accent, #618D7C); }
.story-chip.on{ background:var(--accent, #618D7C); border-color:var(--accent, #618D7C); color:#fff; }
/* Round-94 (design 10's segment, on design 02's feed): a frosted pill with the active scope
   filled green — the same control the app shows under "Near you". */
.stories-scope{
  display:inline-flex; gap:2px; padding:3px; border-radius:999px;
  background:rgba(255,255,255,.55); border:1px solid rgba(255,255,255,.8);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  box-shadow:0 6px 18px rgba(44,59,54,.08);
}
.stories-scope button{
  border:0; background:transparent; cursor:pointer; font-family:var(--font); font-size:.82rem;
  font-weight:700; color:var(--ink-soft, #55635C); padding:6px 16px; border-radius:999px;
  transition:background .15s, color .15s;
}
.stories-scope button.on{ background:var(--accent, #618D7C); color:#fff; }
.stories-filters{ background:#fff; }   /* round-158: solid white — the owner asked for white, not a glass tint */
/* Round-94: on desktop the scope segment and the service pills share ONE row — scope left,
   services beside it — instead of stacking. Below 900 px they stack again (the pills scroll). */
@media (min-width:901px){
  .stories-filters{ flex-direction:row; justify-content:center; align-items:center; gap:14px; }
  .stories-scope{ flex:0 0 auto; }
  .stories-svc-wrap{ flex:0 1 auto; min-width:0; }
  .scope-toggle{ display:none; }
}
/* ≤900: the segment folds into one button on the pill row — two rows of chrome, not three */
.scope-toggle{
  display:none; align-items:center; gap:6px; flex:0 0 auto; margin-right:8px;
  border:1.5px solid var(--track, #ADC3B9); border-radius:999px; background:#fff; cursor:pointer;
  font-family:var(--font); font-size:.85rem; font-weight:700; color:var(--ink, #2C3B36); padding:7px 12px;
}
.scope-toggle svg{ width:15px; height:15px; }
@media (max-width:900px){
  .stories-filters{ position:relative; }
  .scope-toggle{ display:inline-flex; }   /* small screens only — desktop shows the full segment */
  .stories-scope{ display:none; }
  .stories-filters.scope-open .stories-scope{
    display:inline-flex; position:absolute; top:calc(100% - 4px); left:12px; z-index:650;
    background:#fff; box-shadow:0 14px 36px rgba(44,59,54,.22); border:1px solid var(--track, #ADC3B9);
  }
}

.stories-wrap{ max-width:1100px; margin:0 auto; padding:18px 14px 96px; } /* clears the fixed time bands */
/* Round-82: the strip no longer positions itself — .time-row does, exactly as on the map, and
   `.time-row .base-switch{ position:static; transform:none }` makes this a flow child of it.
   The old fixed/centred declarations here were left inert by that change and are gone rather
   than kept as dead rules that contradict where the element actually sits. */
/* mobile: stay content-sized and centred (no empty trailing space), but cap to the viewport and
   scroll horizontally if the pills don't fit — matches the live map's scroll behaviour */
@media (max-width: 720px){
  .stories-time{ max-width:calc(100vw - 20px); overflow-x:auto; scrollbar-width:none; }
  .stories-time::-webkit-scrollbar{ display:none; }
}
/* Round-94: design 02 — masonry columns, the photo leads, text-only reports are solid tiles in
   their service colour. Columns collapse to one on phones; the community page shares the list
   and inherits the layout. */
.story-list{ columns:3 300px; column-gap:14px; }
.story-card{
  break-inside:avoid; margin:0 0 14px; overflow:hidden;
  background:#fff; border:1px solid #E7E4DC; border-radius:18px; padding:0;
  box-shadow:0 1px 3px rgba(44,59,54,.06);
}
.story-card > .media-gallery{ margin:0 !important; border-radius:18px 18px 0 0; overflow:hidden; gap:2px; }
.story-card > .media-gallery *{ border-radius:0 !important; }
.story-card .story-body{ padding:12px 14px 10px; }
.story-loc{ font-size:.82rem; color:var(--ink-soft, #55635C); margin:2px 0 6px; }
.story-official{
  display:inline-flex; align-items:center; font-size:.68rem; font-weight:800; letter-spacing:.5px;
  text-transform:uppercase; border-radius:999px; padding:2px 9px;
  background:var(--mintbg, #EAF1EC); color:var(--deep, #2C3B36);
}
@media (max-width:720px){ .story-list{ columns:1; } }
.story-head{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:8px; }
.story-svc{
  display:inline-flex; align-items:center; gap:6px; font-size:.78rem; font-weight:800;
  color:var(--svc, #618D7C); text-transform:uppercase; letter-spacing:.5px;
}
.story-svc .dot{ width:10px; height:10px; border-radius:50%; background:var(--svc, #618D7C); }
.story-sev{
  font-size:.68rem; font-weight:800; text-transform:uppercase; letter-spacing:.5px;
  border-radius:999px; padding:2px 9px;
}
   versions read as tags; these read as the WARNING/EMERGENCY they are. Same palette as the
   mockup: emergency red, warning amber, advisory the brand green. */
.sev-advisory{ background:#618D7C; color:#fff; }
.sev-warning{ background:#D9922B; color:#fff; }
.sev-emergency{ background:#B3261E; color:#fff; }
.sev-resolved{ background:#1FA97A; color:#fff; }
/* Round-94c: a community report nobody has confirmed yet. Hollow and dashed, the same
   language the map’s unverified pins speak — a solid chip would look like a verdict. */
.sev-unverified{ background:transparent; color:var(--ink-soft, #55635C); border:1.5px dashed currentColor; padding:1px 8px; }
.story-meta{ font-size:.78rem; color:var(--ink-soft, #55635C); margin-left:auto; }
.story-title{ font-size:1.06rem; color:var(--ink, #2C3B36); margin:0 0 6px; }
.story-desc{ font-size:.95rem; line-height:1.55; color:var(--ink-soft, #55635C); white-space:pre-line; }
.story-desc.clamp{ display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden; }

/* Round-25: YouTube-style "Show more/less" on long comment bodies (map panel, lightbox, stories) */
.js-cmt-body.cmt-clamp{ display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden; }
.cmt-showmore{ border:0; background:none; padding:2px 0; margin-top:2px; color:var(--deep, #4F8577);
  font-family:var(--font); font-weight:600; font-size:.82rem; cursor:pointer; }
.cmt-showmore:hover{ text-decoration:underline; }
.story-more{
  border:0; background:none; color:var(--deep, #4F8577); font-family:var(--font);
  font-weight:700; font-size:.85rem; cursor:pointer; padding:4px 0;
}
.story-card .story-body .media-gallery{ margin-top:12px; }
.story-actions{
  display:flex; align-items:center; gap:10px; margin-top:14px; padding-top:12px;
  border-top:1px solid #EFEDE7;
}
.story-act{
  border:1.5px solid var(--track, #ADC3B9); border-radius:999px; background:#fff; cursor:pointer;
  font-family:var(--font); font-size:.84rem; font-weight:700; color:var(--ink, #2C3B36);
  padding:6px 13px; transition:all .15s;
  /* round-71f: icon, label and count sit on ONE line — counts were wrapping underneath
     the icon on narrow phones */
  display:inline-flex; align-items:center; gap:5px; white-space:nowrap; flex:0 0 auto;
}
.story-act:hover{ border-color:var(--accent, #618D7C); background:var(--mintbg, #EAF1EC); }
.story-act.on{ background:var(--accent, #618D7C); border-color:var(--accent, #618D7C); color:#fff; }
/* round-71f: mobile = icons only (labels return on desktop); tighter padding keeps the
   row inside 375 px screens */
@media (max-width: 720px){
  .story-actions .act-lbl{ display:none; }
  .story-act{ padding:5px 10px; }
}
/* round-55: dislike uses the SAME theme colour as heart/love (round-53's separate tan removed) —
   the 💔 glyph already distinguishes it, so the selected state stays visually consistent. */
.story-map{ margin-left:auto; display:inline-flex; align-items:center; gap:5px; font-size:.84rem; font-weight:700; color:var(--deep, #4F8577); }
.story-map svg{ width:16px; height:16px; }
/* Round-94 (design 02): the action row is text — icon + count in the muted ink, no pills, the
   way the mockup reads; the selected state is the accent colour, and the map link keeps the
   far right. Works on white cards and on the solid service tiles alike. */
.story-list .story-actions, .inc-react.story-actions{ flex-wrap:wrap; gap:10px; margin-top:10px; padding-top:10px; border-top:1px solid #EFEDE7; }
.inc-react.story-actions{ border-top:0; padding-top:0; }
.story-list .story-actions .act-lbl{ display:none; }
.story-list .story-actions .act-lbl.always{ display:inline; }   /* the mockup writes "Share" out */
.story-list .story-act .ico-t svg{ width:15px; height:15px; display:block; }
.story-list .story-act{ padding:0; border:0; background:transparent; font-size:.85rem; font-weight:700; color:var(--ink-soft, #65756D); gap:5px; }
/* the global .ico-t is the emoji-silhouette trick (color:transparent + text-shadow); these are
   currentColor SVGs now, so undo it or the strokes inherit transparent and vanish */
.story-list .story-act .ico-t{ font-size:1rem; line-height:1; color:inherit; text-shadow:none; }
.story-list .story-act:hover{ background:transparent; color:var(--accent, #618D7C); }
.story-list .story-act.on{ background:transparent; border:0; color:var(--accent, #618D7C); }
.story-list .story-act svg{ width:16px; height:16px; }
.story-list .story-map{ flex:0 0 auto; }
/* Round-130: announcement cards (and their comment threads) borrow the report panel's action
   grammar — bare outline icon + number in the muted ink, no pill borders. Same rules as
   .inc-react above, scoped to the cpage2 post card. */
.ann-actions{ flex-wrap:wrap; gap:10px; margin-top:10px; padding-top:10px; }
.ann-actions .story-act, .ann-thread .story-act{
  padding:0; border:0; background:transparent; font-size:.85rem; font-weight:700;
  color:var(--ink-soft, #65756D); gap:5px;
}
.ann-actions .story-act .ico-t, .ann-thread .story-act .ico-t{ font-size:1rem; line-height:1; color:inherit; text-shadow:none; }
.ann-actions .story-act .ico-t svg, .ann-thread .story-act .ico-t svg{ width:15px; height:15px; display:block; }
.ann-actions .story-act:hover, .ann-thread .story-act:hover,
.ann-actions .story-act.on, .ann-thread .story-act.on{ background:transparent; border:0; color:var(--accent, #618D7C); }
.ann-actions .story-act:disabled{ opacity:.5; cursor:default; }
.ann-actions .act-lbl.always{ display:inline; }
.story-err{ color:#8e2a1f; font-size:.82rem; margin-top:8px; }
.story-comments{ margin-top:12px; border-top:1px solid #EFEDE7; padding-top:12px; }
.story-cmt{ display:flex; gap:10px; margin-bottom:10px; }
.story-cmt .cmt-av{ width:30px; height:30px; border-radius:50%; object-fit:cover; flex:0 0 30px; }
.story-cmt .cmt-av-fallback{
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--accent, #618D7C); color:#fff; font-weight:800; font-size:.8rem;
}
.story-cmt b a{ color:var(--ink, #2C3B36); }
.story-cmt p{ font-size:.9rem; color:var(--ink-soft, #55635C); margin:2px 0; }
.story-cmt small{ color:var(--sage2, #8FA99B); font-size:.74rem; }
.story-cmt-empty{ color:var(--ink-soft, #55635C); font-size:.88rem; }
.story-compose{ display:flex; gap:8px; align-items:flex-end; margin-top:8px; }
.story-compose textarea{
  flex:1; min-height:44px; padding:9px 12px; border:1.5px solid var(--track, #ADC3B9);
  border-radius:12px; font-family:var(--font); font-size:.9rem; resize:vertical;
}
.story-compose textarea:focus{ outline:none; border-color:var(--accent, #618D7C); }
.story-sentinel{ text-align:center; padding:22px 0; }
.story-spinner{ color:var(--ink-soft, #55635C); font-size:.9rem; }

/* Round-142 (motion P2): the feed's placeholder cards. Eight of them, the shape of a real card
   (photo block, three lines, an action row), inside the same multicol flow so the columns are
   already the right height when the real cards replace them; painted only while #storyList is
   aria-busy, like every other skeleton. And the entrance: first-page cards arrive one after
   another (35 ms apart, eight at most — ~250 ms in all), later pages simply fade. JS-built
   nodes, so a keyframe is safe; `both` holds the start frame until the delay elapses. */
/* Round-205s: the placeholder must be as TALL as what replaces it.

   Measured against the live feed on a phone: eight placeholders held about 324px each where the
   cards that arrived needed about 520px, so the list grew by more than half its own height as
   they swapped and every bar anchored to the document moved. On iOS that is visible on every
   resume, because WKWebView discards a backgrounded page and reloads it — the reader returns to
   the app and watches the feed jump.

   min-height rather than height: a placeholder may be taller than the guess (the text variant
   in a narrow column) and must not be clipped, but it may never be shorter. Applied only while
   the region is marked, so a real card is never given a floor it does not want. */
#storyList.skel-sized{ --skel-card-h:520px; }
#storyList.skel-sized .story-card.skel-card{ min-height:var(--skel-card-h); }
/* The text-only variant carries no photo block, so it needs less — but still far more than the
   three lines alone would give it. Round-142t made a third of them text-only; this gives that
   third an honest height instead of collapsing the column around it. */
#storyList.skel-sized .story-card.skel-card:not(:has(.skel-block)){ min-height:calc(var(--skel-card-h) * 0.62); }
.story-card.skel-card{ padding:0; border-color:#EEEBE4; box-shadow:none; }
.story-card.skel-card .skel-block{ border-radius:18px 18px 0 0; }
.story-card.skel-card .sk-body{ padding:12px 14px 12px; }
.story-card.skel-card .sk-acts{ display:flex; gap:14px; margin-top:12px; }
.story-card.skel-card .sk-acts .skel{ width:36px; height:12px; margin:0; }
/* Round-142: the other sketched shapes — a directory card (hero block + three lines), a chat
   bubble (fixed widths, since a sketch has no text to size it), and the notifications timeline
   (its rows sit left of the rail, so the rail's indent is undone). */
.cdir-card.skel-card{ box-shadow:none; background:#fff; }
.cdir-card.skel-card .skel-block{ border-radius:0; }
.cdir-card.skel-card .cdir-body{ padding:12px 15px 16px; }
.msg-bubble.skel-bubble{ width:62%; padding:10px 12px; }
.msg-bubble.skel-bubble.mine{ width:48%; }
.msg-bubble.skel-bubble .skel-line{ margin:.3em 0; }
.ntf-tl[aria-busy="true"]{ padding-left:0; }
.ntf-tl[aria-busy="true"] .skel-row{ padding:14px 0; }
.story-card.card-in{ animation:cardIn var(--dur-3, 260ms) var(--ease-out, ease-out) both; animation-delay:calc(var(--i, 0) * 35ms); }
/* Round-207 (effect 5): cards arrive one after another and NOTHING MOVES. The 6px rise that was
   here is a layout-looking motion on a page whose whole complaint was things shifting on load;
   the stagger alone says "these arrived". */
@keyframes cardIn{ from{ opacity:0 } }
/* Long single-column feeds render only what is near the viewport; multicol is excluded on
   purpose — inside it the intrinsic-size estimate feeds column balancing and makes columns jump. */
@media (max-width:720px){ .story-card{ content-visibility:auto; contain-intrinsic-size:auto 360px; } }
.story-end{ text-align:center; color:var(--sage2, #8FA99B); font-weight:700; padding:18px 0 0; }
/* Round-96i: empty Local feed offers the way out — a link-shaped button onto the All chip */
.story-scope-all{
  border:0; background:none; padding:0; cursor:pointer; font:inherit;
  color:var(--deep, #4F8577); text-decoration:underline;
}
@media (max-width: 720px){
  .stories-filters{ padding:8px 10px 10px; }
  .story-meta{ margin-left:0; width:100%; }
}

/* ---- Evidence gallery + lightbox ---- */
/* round-19: Facebook-style media grid — spans the panel width, arranges by count.
   Default (no n-class, e.g. the agency queue) = responsive thumbnail grid; the FB
   .n* layouts override it on the map panel + stories. */
.media-gallery{
  display:grid; gap:3px; margin-top:12px; width:100%; border-radius:12px; overflow:hidden;
  grid-template-columns:repeat(auto-fill, minmax(84px, 1fr));
}
.media-gallery .gal-audio{ grid-column:1 / -1; } /* audio rows span all columns */
.media-gallery.n1{ grid-template-columns:1fr; }
.media-gallery.n2{ grid-template-columns:1fr 1fr; }
.media-gallery.n3{ grid-template-columns:1fr 1fr; }
.media-gallery.n3 .gal-item:first-child{ grid-column:1 / -1; }
.media-gallery.n4{ grid-template-columns:1fr 1fr; }
.media-gallery.nmany{ grid-template-columns:1fr 1fr 1fr; }
.gal-item{
  position:relative; width:100%; aspect-ratio:1 / 1; border-radius:0; overflow:hidden; cursor:pointer;
  border:0; background:var(--mintbg, #EAF1EC); padding:0;
  display:flex; align-items:center; justify-content:center;
}
/* a lone image shows in full (16:9), not cropped square */
.media-gallery.n1 .gal-item{ aspect-ratio:16 / 10; }
.gal-item img{ width:100%; height:100%; object-fit:cover; display:block; }
/* Round-142 (motion P2): the tile is the photo's own dominant colour until the photo lands
   (--ph, server-computed at upload; mint for older rows), and the photo fades in over it.
   is-loaded is JS-owned (site.js marks it on load), so a transition is safe here. */
.gal-item[style*="--ph"]{ background:var(--ph, var(--mintbg, #EAF1EC)); }
img.img-fade{ opacity:0; transition:opacity var(--dur-3, 260ms) var(--ease-out, ease-out); }
img.img-fade.is-loaded{ opacity:1; }
.gal-item.video{ font-size:1.5rem; background:var(--panel-grad, #2C3B36); color:#F3F4F1; }
.gal-item:hover{ filter:brightness(.94); }
/* audio players stack below the visual grid */
.media-audio{ display:flex; flex-direction:column; gap:6px; margin-top:8px; }
/* "+N more" overlay on the last visible tile */
.gal-item .gal-more{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  background:rgba(24,32,29,.55); color:#fff; font-size:1.4rem; font-weight:800;
}
/* round-18: video with a generated poster frame — image + centered play badge */
.gal-item.video.poster{ position:relative; background:#000; }
.gal-item .gal-play{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-size:1.4rem; color:#fff; text-shadow:0 1px 6px rgba(0,0,0,.6);
  background:rgba(0,0,0,.18);
}

.lightbox{ position:fixed; inset:0; top:var(--vvt, 0px); z-index:3000; display:none; background:rgba(24,32,29,.9); }   /* round-97h: iOS pan */
.lightbox.open{ display:flex; }
.lb-media{ flex:1; position:relative; display:flex; align-items:center; justify-content:center; min-width:0; }
.lb-stage{ max-width:calc(100% - 130px); max-height:calc(var(--app-h, 100vh) * 0.92); display:flex; align-items:center; justify-content:center; }
.lb-stage img, .lb-stage video{ max-width:100%; max-height:calc(var(--app-h, 100vh) * 0.92); border-radius:10px; }
.lb-close, .lb-nav{
  position:absolute; z-index:1; border:0; cursor:pointer; color:#F3F4F1;
  background:rgba(243,244,241,.14); width:44px; height:44px; border-radius:50%;
  font-size:1.3rem; display:inline-flex; align-items:center; justify-content:center;
}
.lb-close:hover, .lb-nav:hover{ background:rgba(243,244,241,.28); }
/* Round-97: the lightbox is full-bleed, so this sat under the clock and battery on a notched
   iPhone. .lb-acts already pays the bottom inset the same way (line 1288); this is its top half.
   --safe-top first because Android's WebView leaves env() at 0 — the native bridge fills the
   variable instead. */
.lb-close{ top:calc(14px + var(--safe-top, env(safe-area-inset-top, 0px))); right:14px; }
/* ...and while the lightbox is up, the detail panel's own close button sits behind it, showing
   through the scrim as a second, offset ✕. One close affordance at a time. */
html.lb-open .station-panel .sp-close{ opacity:0; pointer-events:none; }
.lb-nav{ top:50%; transform:translateY(-50%); font-size:2rem; }
.lb-prev{ left:14px; }
.lb-next{ right:14px; }
.lb-count{
  position:absolute; bottom:12px; left:50%; transform:translateX(-50%);
  color:#F3F4F1; font-size:.85rem; background:rgba(24,32,29,.55); padding:3px 12px; border-radius:999px;
}
/* Round-95e: design "10-airy-editorial" — paper rail, serif heading, white shadowed
   avatars, and a floating white composer card with a borderless textarea. Scoped to the
   lightbox so the map panel's comment styling is untouched. */
.lb-side{
  width:min(360px, 90vw); background:var(--paper, #F3F4F1);
  display:flex; flex-direction:column; gap:8px; padding:18px 0 14px;
}
/* Round-95c: comments-only mode — the comment icon reuses THIS modal. Desktop (a story with
   no media, or none passed): the rail alone as a centred card. Phones: the rail alone, full
   screen. The media pane is hidden entirely in this mode. */
.lightbox.comments-only .lb-media{ display:none; }
/* Round-95j: media-only full-screen viewer (phones) — no comments rail.
   Round-139d was design 03 (a dark scrim, a vertical action rail). Round-142e is design
   media-viewer/07 REVISED (owner's call), on the PHONE and, by the same call, on the DESKTOP
   modal too: paper background; the media as a rounded card over a BLURRED copy of itself on
   white (so a landscape and a portrait both fit their frame); a top bar with the app's own
   back arrow — the search modal's 40 px button and 24 px caret — beside story SEGMENTS in
   place of the "1 / n" counter; ‹ › at the vertical centre of the media's edges; the story's
   title and place as a caption; and on the desktop the comments rail as before, now a white
   column. The ✕ and the counter stay in the DOM and are hidden everywhere but comments-only. */
.lb-top, .lb-cap{ display:none; }
.lightbox:not(.comments-only){ background:var(--paper, #F3F4F1); }
.lightbox:not(.comments-only) .lb-media{
  flex-direction:column; align-items:stretch; justify-content:flex-start;
  padding-top:var(--safe-top, env(safe-area-inset-top, 0px));
}
.lightbox:not(.comments-only) .lb-close, .lightbox:not(.comments-only) .lb-count{ display:none; }
.lightbox:not(.comments-only) .lb-top{
  display:grid; grid-template-columns:40px 1fr 40px; align-items:center; gap:8px;
  padding:6px 12px 6px; flex:none;
}
.lb-back{
  display:inline-flex; align-items:center; justify-content:center; width:40px; height:40px;
  padding:0; border:0; border-radius:50%; background:none; color:var(--ink, #2C3B36); cursor:pointer;
}
.lb-back:hover, .lb-back:active{ background:var(--mintbg, #E7EDE6); }
.lb-back svg{ width:24px; height:24px; }
[dir="rtl"] .lb-back svg{ transform:scaleX(-1); }
.lb-seg{ display:flex; gap:5px; }
.lb-seg i{ flex:1; height:3px; border-radius:2px; background:var(--track, #ADC3B9); }
.lb-seg i.on{ background:var(--deep, #4F8577); }
.lightbox:not(.comments-only) .lb-stage{
  position:relative; flex:1; min-height:0; max-width:none; max-height:none; margin:2px 12px 0;
  border-radius:16px; overflow:hidden; background:#fff; box-shadow:0 2px 10px rgba(44,59,54,.10);
  display:flex; align-items:center; justify-content:center;
}
.lb-blur{
  position:absolute; inset:-32px; background:center / cover no-repeat;
  filter:blur(26px) saturate(1.05); opacity:.9; pointer-events:none;
}
.lightbox:not(.comments-only) .lb-stage img, .lightbox:not(.comments-only) .lb-stage video{
  position:relative; width:100%; height:100%; max-height:none; object-fit:contain; border-radius:0;
}
.lightbox:not(.comments-only) .lb-cap{ display:block; flex:none; padding:12px 16px 2px; color:var(--ink, #2C3B36); }
.lightbox:not(.comments-only) .lb-cap b{ font-size:.92rem; display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.lightbox:not(.comments-only) .lb-cap small{ color:var(--ink-soft, #5C6E66); font-size:.76rem; }
.lightbox:not(.comments-only) .lb-cap[hidden]{ display:none; }
/* ‹ › ride the media's edges at its vertical centre, quiet on the paper */
.lightbox:not(.comments-only) .lb-nav{
  top:50%; width:38px; height:38px; font-size:1.6rem; color:var(--ink, #2C3B36);
  background:rgba(255,255,255,.82); box-shadow:0 1px 4px rgba(44,59,54,.18);
}
.lightbox:not(.comments-only) .lb-nav:hover{ background:#fff; }
.lightbox:not(.comments-only) .lb-prev{ left:22px; }
.lightbox:not(.comments-only) .lb-next{ right:22px; }
/* the desktop rail: a white column beside the paper media pane */
.lightbox:not(.comments-only):not(.media-only) .lb-side{ background:#fff; border-left:1px solid var(--track, #ADC3B9); }
.lightbox:not(.comments-only):not(.media-only) .lb-stage{ margin:4px 20px 0; }
.lightbox:not(.comments-only):not(.media-only) .lb-cap{ padding-bottom:16px; }
/* the phone: no rail, no arrows — a swipe on the media moves between items (site.js) — and
   the actions as a horizontal bar on the paper */
.lightbox.media-only .lb-side{ display:none; }
.lightbox.media-only .lb-nav{ display:none; }
.lightbox.media-only .lb-stage{ touch-action:none; user-select:none; -webkit-user-select:none; will-change:transform; }   /* the sheet does not scroll; the swipe owns the surface */
.lightbox.media-only .lb-stage img{ -webkit-user-drag:none; user-drag:none; }
.lightbox.media-only .lb-acts{
  position:static; flex:none; flex-direction:row; justify-content:space-around; align-items:center;
  gap:0; background:none;
  padding:10px 8px calc(16px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
}
.lightbox.media-only .lb-act{ flex-direction:row; gap:6px; padding:6px 10px; font-size:.9rem; color:var(--ink-soft, #5C6E66); }
.lightbox.media-only .lb-act .ico-t{ width:auto; height:auto; border-radius:0; background:none; display:flex; align-items:center; }
.lightbox.media-only .lb-act .ico-t svg{ width:22px; height:22px; }
.lightbox.media-only .lb-act.on{ color:var(--deep, #4F8577); font-weight:700; }
.lb-acts{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  display:flex; justify-content:space-around; align-items:center;
  padding:10px 8px calc(12px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  background:linear-gradient(transparent, rgba(10,14,12,.78) 40%);
}
.lb-acts[hidden]{ display:none; }
.lb-act{
  border:0; background:none; color:#E7EDE8; cursor:pointer; font-family:var(--font);
  display:flex; align-items:center; gap:6px; font-size:.9rem; padding:6px 10px;
}
/* Round-97: the global .ico-t is the emoji-silhouette trick — color:transparent plus a
   text-shadow standing in for the glyph. window.aquaActIcons are currentColor SVGs, not emoji,
   so that transparent color was resolving their stroke to nothing: on the G6 the bar rendered
   as bare counters, "0 0 0 0", with the share button (icon and no counter) fully invisible.
   Undo the trick here and let the stroke take .lb-act's own colour, exactly as .story-act
   already does at line 1174. */
.lb-act .ico-t{ font-size:1.15rem; color:inherit; text-shadow:none; }
.lb-act .ico-t svg{ width:19px; height:19px; display:block; }
.lb-act.on{ color:#9FD3BC; font-weight:700; }
.lightbox.comments-only{ align-items:center; justify-content:center; }
.lightbox.comments-only .lb-side{
  width:min(560px, 94vw); max-height:calc(var(--app-h, 100vh) * 0.82); height:min(calc(var(--app-h, 100vh) * 0.82), 720px);
  border-radius:16px;
}
@media (max-width:900px){
  /* Round-97: sizing the SHEET from --vvh was only half of it — the container is still the whole
     screen and comments-only CENTRES its child, so once the keyboard shrank the sheet it floated
     in the middle of the screen with its lower half (the composer) behind the keys: "the comment
     modal's textarea doesn't display". Size the container to the same visible height and stretch
     the sheet into it, so the composer lands exactly on the keyboard's top edge. */
  .lightbox.comments-only{
    align-items:stretch;
    height:var(--app-h, 100vh); height:var(--vvh, var(--app-h, 100dvh));
  }
  .lightbox.comments-only .lb-side{
    width:100%; max-height:none; border-radius:0;
    /* round-97: --vvh, so the comment field is never left behind the keyboard */
    height:var(--app-h, 100vh); height:var(--vvh, var(--app-h, 100dvh));
    /* Round-97k: 8/6 rather than 18/14. The inset already supplies the clearance the notch
       needs; these numbers are only the breathing room on top of it, and full-screen the old
       values read as a gap rather than as spacing. */
    padding-top:calc(8px + var(--safe-top, env(safe-area-inset-top, 0px)));
    padding-bottom:calc(6px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  }
  .lightbox.comments-only .lb-side h3{ font-size:1.05rem; }
  .lightbox.comments-only .lb-side-head{ position:relative; padding-top:14px; }
  /* Round-97k: same 51px as .station-panel.ed's ✕ — full screen, this is the only way out, and
     the 32px desktop button is a mouse target, not a thumb one. */
  .lightbox.comments-only .lb-side-close{ display:flex; align-items:center; justify-content:center; }
}
.lb-side-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:12px; padding:0 20px; }
.lb-side h3{
  font-family:Georgia, "Times New Roman", serif; font-weight:700;
  font-size:1.02rem; line-height:1.3; margin:0; color:var(--ink, #2C3B36);
}
/* the media pane's floating ✕ disappears with the pane in comments-only mode; the rail's own
   close is the one that must always exist. Hidden when the media pane (and its ✕) shows. */
.lb-side-close{
  width:32px; height:32px; border-radius:50%; border:0;
  background:#fff; color:var(--ink, #2C3B36); cursor:pointer; flex:0 0 auto;
  box-shadow:0 2px 8px rgba(0,0,0,.08);
}
.lightbox:not(.comments-only) .lb-side-close{ display:none; }
.lb-comments{ flex:1; overflow-y:auto; display:flex; flex-direction:column; gap:16px; min-height:0; padding:2px 20px 8px; }
.lightbox .cmt .cmt-av, .lightbox .cmt .cmt-av-fallback{
  width:36px; height:36px; flex:0 0 36px; border-radius:50%;
  box-shadow:0 2px 8px rgba(0,0,0,.08);
}
.lightbox .cmt .cmt-av-fallback{ background:#fff; color:var(--deep, #4F8577); font-weight:700; font-size:.8rem;
  display:flex; align-items:center; justify-content:center; }
.lightbox .cmt .cmt-author b, .lightbox .cmt .cmt-head b{ font-size:.88rem; }
.lightbox .cmt .js-cmt-body{ font-size:.92rem; line-height:1.55; color:#3E4A44; margin:3px 0; }
.lightbox .cmt .cmt-actions small{ color:var(--soft, #5C6E66); font-size:.75rem; }
/* the floating composer card — borderless textarea, tools row with emoji · counter · Post */
.lb-form{
  display:block; margin:0 14px; background:#fff; border-radius:16px;
  box-shadow:0 8px 26px rgba(44,59,54,.14); padding:12px 14px;
}
/* Round-97f: the signed-out prompt is injected by site.js as a bare <p class="hint lb-signin">
   and had NO rule of its own. .lb-side carries no horizontal padding — every other child pads
   itself (.lb-side-head and .lb-comments 20px, .lb-form its own margin) — so the prompt sat flush
   against the left edge and hard on the bottom of the screen. It stands in for the composer when
   signed out, so it gets the composer's slot: same inset, same card, centred. */
.lb-signin{
  margin:0 14px; padding:14px; text-align:center;
  background:#fff; border-radius:16px; box-shadow:0 8px 26px rgba(44,59,54,.14);
}
.lb-signin a{ font-weight:700; }
.lb-form textarea{
  width:100%; min-height:56px; border:0; padding:4px 2px; background:none;
  font-family:var(--font); resize:none;
}
.lb-form textarea:focus{ outline:none; }
/* three children + space-between = emoji left, counter CENTRED, Post far right */
.lb-tools{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:4px; }
.lb-tools .emoji-btn{ border:0; background:none; font-size:1.25rem; cursor:pointer; padding:0; }
/* Round-97: this is the comment character counter and it is NOT the media counter, which
   owns .lb-count and is absolutely positioned as a pill at the bottom-centre of the
   lightbox. Both markup nodes carried .lb-count, so the character count inherited that
   absolute pill and hung off the bottom edge of the composer card — and
   querySelector(".lb-count") returned the MEDIA node (first in DOM order), so the live
   count updated an invisible element and never moved. */
.lb-chars{ color:var(--soft, #5C6E66); font-size:.74rem; }
.lb-send{
  border:0; border-radius:999px; padding:9px 22px; background:var(--accent, #618D7C);
  color:#fff; font-weight:700; font-family:var(--font); cursor:pointer;
}
.lb-send:disabled{ opacity:.45; cursor:default; }
.lb-reply{ color:var(--accent, #618D7C); font-weight:700; cursor:pointer; }

.lightbox .cmt.lb-child{ margin-left:44px; }
.lb-reply-chip{
  display:flex; align-items:center; gap:8px; margin:0 20px 6px; padding:6px 12px;
  background:var(--mint, #EAF1EC); border-radius:10px; font-size:.85rem; color:var(--soft, #5C6E66);
}
.lb-reply-chip[hidden]{ display:none; }
.lb-chip-x{ margin-left:auto; border:0; background:none; cursor:pointer; color:inherit; }
.lb-stage audio{ width:min(80vw, 460px); }
@media (max-width: 900px){
  .lightbox.open{ flex-direction:column; }
  .lb-stage{ max-width:100%; max-height:calc(var(--app-h, 100vh) * 0.55); }
  .lb-stage img, .lb-stage video{ max-height:calc(var(--app-h, 100vh) * 0.55); }
  .lb-side{ width:auto; max-height:calc(var(--app-h, 100vh) * 0.42); }
}

/* ---- Round-19: audio player tile in the media grid ---- */
.gal-audio{ display:flex; align-items:center; gap:10px; width:100%; padding:10px 12px; }
.gal-audio .gal-audio-ico{ font-size:1.4rem; flex:0 0 auto; display:inline-flex; align-items:center; }
/* round-152 (owner): the mic is the outline icon in the service family, in the theme colour —
   an emoji rendered as a different picture on every platform and carried its own colour. */
.gal-audio .gal-audio-ico svg, .gal-audio .gal-audio-ico .gal-mic{ width:20px; height:20px; color:var(--deep, #4F8577); stroke:currentColor; }
.gal-audio audio{ flex:1; min-width:0; height:38px; }

/* ---- Round-19: composers — full-width textarea with the Post button inside bottom-right ---- */
.compose{ position:relative; margin-top:12px; }
.compose textarea{
  display:block; width:100%; box-sizing:border-box; min-height:84px; resize:vertical;
  padding:11px 13px 52px; border:1.5px solid var(--track, #ADC3B9); border-radius:14px;
  font-family:var(--font); font-size:.92rem; background:#fff; color:var(--ink, #2C3B36);
}
.compose textarea:focus{ outline:none; border-color:var(--accent, #618D7C); }
/* round-19b: keep the send button clear of the textarea border on all sides */
.compose-tools{
  position:absolute; right:11px; bottom:11px; display:flex; align-items:center; gap:6px;
}
.compose-emoji{
  border:0; background:none; cursor:pointer; font-size:1.15rem; line-height:1;
  padding:4px; border-radius:8px; color:var(--ink-soft, #55635C);
}
.compose-emoji:hover{ background:rgba(97,141,124,.12); }
.compose-send{
  border:0; cursor:pointer; border-radius:999px; padding:7px 16px;
  background:var(--accent, #618D7C); color:#fff; font-family:var(--font); font-weight:800; font-size:.85rem;
}
.compose-send:hover{ background:var(--deep, #4F8577); }
.compose-send:disabled{ opacity:.5; cursor:default; }

/* ---- Round-19b: custom tag input with suggestions ---- */
.tag-input{ display:flex; gap:8px; align-items:center; }
.tag-input input{
  flex:1; min-width:0; padding:9px 12px; border:1.5px solid var(--track, #ADC3B9); border-radius:10px;
  font-family:var(--font); font-size:.9rem; background:#fff; color:var(--ink, #2C3B36);
}
.tag-input input:focus{ outline:none; border-color:var(--accent, #618D7C); }
.tag-input .btn{ flex:0 0 auto; }

/* ---- Round-19: modern file uploader (report form) ---- */
/* visually-hidden but present + clickable via the label — display:none breaks the file
   dialog on many mobile browsers, so use the sr-only pattern instead */
.up-file{
  position:absolute; left:0; top:0; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
  /* left/top pin it inside .up-zone — unanchored, some engines kept the input's intrinsic
     ~270px width, poking past the right edge and giving the page a horizontal scrollbar */
}
.up-zone{
  position:relative; border:2px dashed var(--track, #ADC3B9); border-radius:14px; padding:20px; text-align:center;
  cursor:pointer; transition:border-color .15s, background .15s; background:var(--mintbg, #EAF1EC);
}
.up-zone:hover, .up-zone.drag{ border-color:var(--accent, #618D7C); background:#fff; }
.up-zone .up-ico{ font-size:1.7rem; }
.up-zone .up-hint{ font-size:.85rem; color:var(--ink-soft, #55635C); margin-top:4px; }
/* Round-88g: the attachment strip that sits under the composer. Smaller than the standalone
   grid and light-framed, so a row of thumbnails reads as part of the message box rather than a
   second gallery. Scrolls sideways instead of growing the form when several files are attached. */
.up-grid-compose{ grid-template-columns:repeat(auto-fill, minmax(72px, 1fr)); gap:8px;
                  margin:2px 12px 6px; }   /* inset to the frame, clear of the button strip */
.up-grid-compose .up-tile{ border:1px solid rgba(44,59,54,.10); background:#FFFFFF; }
.up-grid-compose .up-name{ font-size:.62rem; }
/* Round-97b: #upTiles moved out of the composer frame, so it spaces itself against the box
   instead of being inset into it. */
.rpt-full #upTiles{ margin:10px 0 0; }
.up-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(92px, 1fr)); gap:10px; margin-top:12px; }
.up-tile{
  position:relative; aspect-ratio:1 / 1; border-radius:12px; overflow:hidden;
  border:1.5px solid var(--track, #ADC3B9); background:var(--panel-grad, #2C3B36);
  display:flex; align-items:center; justify-content:center; color:#F3F4F1; font-size:1.5rem;
}
.up-tile img, .up-tile video{ width:100%; height:100%; object-fit:cover; }
.up-tile .up-typ{ font-size:1.7rem; }
.up-tile .up-play{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-size:1.3rem; color:#fff; text-shadow:0 1px 6px rgba(0,0,0,.6); background:rgba(0,0,0,.18);
  pointer-events:none;
}
.up-tile .up-name{
  position:absolute; left:0; right:0; bottom:0; font-size:.66rem; color:#fff; padding:3px 5px;
  background:rgba(24,32,29,.6); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; z-index:2;
}
.up-tile .up-del{
  position:absolute; top:4px; right:4px; width:22px; height:22px; border-radius:50%; border:0;
  background:rgba(24,32,29,.65); color:#fff; cursor:pointer; font-size:.85rem; line-height:1;
  display:flex; align-items:center; justify-content:center; z-index:3;
}
.up-tile .up-del:hover{ background:#C0392B; }
/* per-file progress + status while uploading */
.up-tile .up-bar{ position:absolute; left:0; bottom:0; height:4px; background:var(--accent, #618D7C); transition:width .2s; z-index:2; }
.up-tile .up-pct{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  background:rgba(24,32,29,.5); color:#fff; font-weight:800; font-size:.82rem; z-index:1;
}
.up-tile .up-badge{
  position:absolute; top:4px; left:4px; width:22px; height:22px; border-radius:50%;
  display:flex; align-items:center; justify-content:center; font-size:.8rem; font-weight:800; z-index:2;
}
.up-tile .up-badge.ok{ background:#1FA97A; color:#fff; }
.up-tile .up-badge.err{ background:#C0392B; color:#fff; }
.up-tile.error{ border-color:#C0392B; }
.up-tile.uploading{ opacity:.95; }
.up-toast{ margin-top:8px; font-size:.85rem; color:#8e2a1f; }
/* round-55: Facebook-style threaded comment replies */
.cmt-thread{ margin-bottom:2px; }
.cmt-replies{ margin-left:38px; border-left:2px solid var(--track,#E2DCD3); padding-left:10px; margin-top:2px; }
.cmt-reply-to{ color:var(--accent,#618D7C); font-weight:600; margin-right:3px; }
.cmt-more-replies{ background:none; border:none; color:var(--accent,#618D7C); cursor:pointer; font-size:.82rem; font-weight:600; padding:4px 0 6px; }
.cmt-more-replies:hover{ text-decoration:underline; }
.cmt-replying{ display:flex; align-items:center; justify-content:space-between; gap:8px; background:rgba(97,141,124,.12); border-radius:8px; padding:5px 10px; margin-bottom:6px; font-size:.82rem; color:var(--ink-soft,#6b7a72); }
.cmt-reply-x{ background:none; border:none; cursor:pointer; color:var(--ink-soft,#6b7a72); font-size:.95rem; line-height:1; padding:0 2px; }
.cmt-reply-x:hover{ color:var(--accent,#618D7C); }
@media (max-width:640px){ .cmt-replies{ margin-left:22px; padding-left:8px; } }
/* round-51: live-map point context menu (right-click / long-press) */
.mapctx{
  position:fixed; z-index:1300; min-width:230px; max-width:min(88vw,300px);
  background:var(--panel,#fff); border:1px solid var(--line,#E2DCD3); border-radius:14px;
  box-shadow:0 14px 40px rgba(44,59,54,.28); overflow:hidden; padding:5px;
  animation:mapctxIn .12s ease-out;
}
@keyframes mapctxIn{ from{ opacity:0; transform:scale(.96) } to{ opacity:1; transform:scale(1) } }
.mapctx-head{
  display:flex; align-items:center; gap:7px; padding:8px 10px 9px; margin:0 2px 4px;
  border-bottom:1px solid var(--line,#E2DCD3); font-size:.82rem; color:var(--ink-soft,#6b7a72);
}
.mapctx-label{ font-weight:700; color:var(--ink,#2C3B36); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mapctx-item{
  display:flex; align-items:center; gap:11px; width:100%; min-height:44px; padding:10px 12px;
  border:none; background:transparent; border-radius:9px; cursor:pointer; text-align:left;
  font-size:.92rem; color:var(--ink,#2C3B36); transition:background .1s;
}
.mapctx-item:hover, .mapctx-item:focus-visible{ background:rgba(97,141,124,.13); outline:none; }
.mapctx-item:active{ background:rgba(97,141,124,.22); }
.mapctx-ic{ width:22px; text-align:center; font-size:1.05rem; flex:0 0 22px; }
@media (max-width:640px){
  .mapctx{ min-width:0; width:min(92vw,320px); border-radius:16px; }
  .mapctx-item{ min-height:50px; font-size:1rem; }
}
/* round-52: enlarge Ada media (images, charts, tables) into a modal */
.ada-media{ position:relative; max-width:100%; }
.ada-zoom{
  position:absolute; top:6px; right:6px; z-index:3; width:28px; height:28px; padding:0;
  border:none; border-radius:7px; background:rgba(44,59,54,.6); color:#fff; cursor:pointer;
  font-size:1rem; line-height:1; display:flex; align-items:center; justify-content:center;
  opacity:0; transition:opacity .12s;
}
.ada-media:hover .ada-zoom, .ada-zoom:focus-visible{ opacity:1; }
@media (hover:none){ .ada-zoom{ opacity:.9; } } /* touch: always visible, no hover */
.ada-modal{
  position:fixed; inset:0; z-index:4000; background:rgba(20,26,24,.82);
  display:flex; align-items:center; justify-content:center; padding:24px; animation:mapctxIn .15s ease-out;
}
/* Round 222 → 224 (owner's pick, design A): the iOS app's download sheet (aquaAppFiles). It rises
   from the bottom over a dimmed page; the backdrop covers the native bars' slots, so the app hides
   them while it is up. Sized from --app-h and padded by --safe-bottom (pinned dims). */
.file-sheet{
  position:fixed; inset:0; z-index:4100; background:rgba(20,26,24,.48);
  display:flex; align-items:flex-end; justify-content:center;
}
.file-sheet-card{
  background:#fff; width:100%; max-width:520px; box-sizing:border-box;
  border-radius:26px 26px 0 0; padding:10px 20px calc(22px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  display:flex; flex-direction:column; gap:16px; font-family:var(--font); color:var(--ink, #2C3B36);
  box-shadow:0 -12px 40px rgba(20,26,24,.22); max-height:calc(var(--app-h, 100dvh) - 40px); overflow:auto;
  animation:fileSheetIn .24s cubic-bezier(.2,.8,.25,1) both;
}
@keyframes fileSheetIn{ from{ transform:translateY(100%); } to{ transform:none; } }
@media (prefers-reduced-motion: reduce){ .file-sheet-card{ animation:none; } }
.file-sheet-grab{ width:38px; height:5px; border-radius:3px; background:#D5DBD7; align-self:center; flex:none; }
.file-sheet-top{ display:flex; align-items:center; gap:14px; }
.file-sheet-tile{ position:relative; flex:none; width:54px; height:62px; border-radius:10px; background:#F4EBDD;
  display:flex; align-items:flex-end; justify-content:center; padding-bottom:9px; box-sizing:border-box; }
.file-sheet-tile i{ position:absolute; top:0; right:0; width:16px; height:16px; background:#E8D7BD; border-radius:0 10px 0 6px; }
.file-sheet-tile b{ font-size:.75rem; letter-spacing:.6px; color:#8A5E2C; }
.file-sheet-id{ display:flex; flex-direction:column; gap:3px; min-width:0; flex:1; }
.file-sheet-title{ font-size:.8rem; font-weight:800; color:#3F6F62; letter-spacing:.4px; text-transform:uppercase; }
.file-sheet-name{ font-size:1.06rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.file-sheet-meta{ font-size:.875rem; color:var(--ink-soft, #55635C); }
.file-sheet-msg{ margin:0; font-size:.92rem; color:var(--ink-soft, #55635C); }
.file-sheet-peek{ border:1px solid #E7E2DA; border-radius:12px; overflow:hidden; font-size:.82rem; }
.file-sheet-peek .fs-row{ display:grid; grid-template-columns:repeat(var(--cols, 3), minmax(0, 1fr)); gap:8px; padding:8px 12px; border-top:1px solid #EFEBE4; }
.file-sheet-peek .fs-row span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.file-sheet-peek .fs-head{ border-top:0; background:#EEF3EF; font-weight:700; color:#3F6F62; }
.file-sheet-peek .fs-more{ display:block; color:var(--ink-soft, #55635C); }
.file-sheet-primary, .file-sheet-second{ border:0; font:inherit; font-weight:700; display:flex; align-items:center; justify-content:center; gap:9px; cursor:pointer; }
.file-sheet-primary{ height:54px; border-radius:16px; background:#3F6F62; color:#fff; font-size:1.06rem; }
.file-sheet-second{ height:48px; border-radius:14px; background:#EEF3EF; color:var(--ink, #2C3B36); font-size:1rem; flex:1; }
.file-sheet-primary svg, .file-sheet-second svg{ width:19px; height:19px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.file-sheet-second svg{ stroke:#3F6F62; }
.file-sheet-acts{ display:flex; gap:10px; }
.file-sheet-hint{ margin:-4px 0 0; font-size:.82rem; color:var(--ink-soft, #55635C); text-align:center; }
.ada-modal-inner{
  background:var(--panel,#fff); border-radius:14px; padding:16px; position:relative;
  max-width:min(96vw,1100px); max-height:calc(var(--app-h, 100vh) * 0.92); overflow:auto; box-shadow:0 20px 60px rgba(0,0,0,.5);
}
.ada-modal-inner img, .ada-modal-inner svg{ max-width:100%; height:auto; display:block; }
/* round-53: the cloned table lands outside .msg-bubble, so restate its styling here (bigger
   than in-bubble since it's the enlarged view) */
.ada-modal-inner .tbl-scroll{ overflow-x:auto; max-width:100%; }
.ada-modal-inner table{ border-collapse:collapse; width:auto; min-width:100%; font-size:.95rem; }
.ada-modal-inner th, .ada-modal-inner td{ border:1px solid var(--track, #ADC3B9); padding:9px 14px; text-align:left; white-space:nowrap; }
.ada-modal-inner th{ background:var(--deep, #4F8577); color:#F3F4F1; font-weight:700; position:sticky; top:0; }
.ada-modal-inner tbody tr:nth-child(even){ background:rgba(97,141,124,.07); }
.ada-modal-close{
  position:sticky; top:0; float:right; width:34px; height:34px; border:none; border-radius:8px;
  background:var(--accent,#618D7C); color:#fff; cursor:pointer; font-size:1.05rem; z-index:5;
}
@media (max-width:640px){ .ada-modal{ padding:10px; } .ada-modal-inner{ padding:10px; } }
/* round-50: copy button on Ada assistant replies — subtle, appears on bubble hover */
.msg-bubble{ position:relative; }
/* round-58: sit the copy button BELOW the message content (not overlapping the last line) */
.asst-copy{
  display:block; margin-top:5px; width:26px; height:24px; padding:0;
  border:none; border-radius:7px; background:transparent; color:var(--sage2,#8FA99B);
  cursor:pointer; font-size:.95rem; line-height:1; opacity:0; transition:opacity .12s, background .12s;
}
.msg-bubble.mine .asst-copy{ margin-left:auto; }  /* right-aligned user bubble → keep it under the text */
/* round-60: quota usage meter (account Plan & Credits) */
.quota-bar{ height:8px; border-radius:6px; background:rgba(97,141,124,.18); overflow:hidden; }
.quota-bar span{ display:block; height:100%; background:var(--accent,#618D7C); border-radius:6px; transition:width .3s; }
/* round-56: Ada facility pins — a type emoji in a service-coloured circle */
.fac-pin{ width:26px; height:26px; border-radius:50%; display:flex; align-items:center;
  justify-content:center; font-size:14px; box-shadow:0 2px 5px rgba(44,59,54,.4); border:2px solid #F3F4F1; }
.msg-bubble:hover .asst-copy, .asst-copy:focus-visible{ opacity:1; }
.asst-copy:hover{ background:rgba(97,141,124,.14); color:var(--accent,#618D7C); }
.up-rec{ display:flex; align-items:center; gap:10px; margin-top:10px; flex-wrap:wrap; }
.up-rec .rec-dot{ width:10px; height:10px; border-radius:50%; background:#C0392B; --pulse-min:.3; animation:aq-pulse 1s infinite; }

/* ---- Round-21: GPS location card + submit loader ---- */
.geo-card{
  display:flex; align-items:center; gap:12px;
  padding:14px 16px; border-radius:12px;
  border:1.5px solid var(--track); background:#f7fafc;
}
.geo-card.located{ border-color:#1E824C; background:#f2fbf5; }
.geo-card.error{ border-color:#C0392B; background:#fdf3f2; }
.geo-card > div{ flex:1 1 auto; min-width:0; }
.geo-card .hint{ margin-top:2px; }
.geo-ico{ font-size:1.4rem; line-height:1; }
.geo-spin, .btn-spin{
  display:inline-block; border-radius:50%;
  border:2.5px solid rgba(0,0,0,.15); border-top-color:currentColor;
  animation:geospin .7s linear infinite; flex:none;
}
.geo-spin{ width:20px; height:20px; color:#1E5AA8; border-width:3px; }
.btn-spin{ width:15px; height:15px; color:#fff; vertical-align:-2px; margin-right:8px; }
@keyframes geospin{ to{ transform:rotate(360deg) } }
.submit-status{
  margin:10px 2px 0; text-align:center; font-size:.9rem; font-weight:600;
  color:var(--ink-soft); --pulse-min:.55; animation:aq-pulse 1.4s ease-in-out infinite;
}
/* Round-142 (motion P0): THE opacity pulse. recpulse, statuspulse, admPulse and opsPulse were
   this same keyframe written four times under four names with four dips; the dip is now the
   call site's --pulse-min. */
@keyframes aq-pulse{ 0%,100%{ opacity:1 } 50%{ opacity:var(--pulse-min, .5) } }

/* ---- Round-74: admin console shell (sidebar + main) ----
   Admin pages render through AdminLayout, not MainLayout, so there is no marketing footer
   and the page shell is a two-column grid. 1000px is an existing breakpoint in this file
   (see the map chips rules below) — no new breakpoint is introduced. */
.adm-shell{
  display:grid; grid-template-columns:248px 1fr; align-items:start;
  padding-top:var(--header-h); min-height:var(--app-h, 100vh);
}
.adm-main{ min-width:0; }               /* lets wide tables scroll instead of blowing the grid */
.adm-main .page{ padding-top:0; }       /* the shell already offsets for the fixed header */
.adm-main .hero-inner{ padding:28px 0 24px; }
.adm-main .section{ padding:28px 0 48px; }
.adm-main .admin-crumbs{ padding:12px 20px 0; margin-top:0; }

.adm-sidebar{
  position:sticky; top:var(--header-h); align-self:start;
  /* round-80: a full viewport height (not max-height) so the right-hand divider runs to the
     bottom of the window instead of stopping under the last nav item, which left the rail
     looking like a floating box on short pages. */
  height:calc(var(--app-h, 100vh) - var(--header-h)); overflow-y:auto;
  padding:14px 10px 24px; border-right:1px solid var(--track);
  background:var(--paper); scrollbar-width:thin;
}
/* groups reuse .scard so site.js's existing .scard-head handler gives tap-to-expand */
.adm-sidebar .adm-group{ border:0; border-radius:0; background:transparent; margin-bottom:2px; }
.adm-sidebar .scard-head{ padding:8px 10px; border-bottom:0; }
.adm-sidebar .scard-head h3{
  font-size:.72rem; text-transform:uppercase; letter-spacing:.06em;
  color:var(--ink-soft); font-weight:800;
}
.adm-sidebar .scard-body{ padding:0 0 6px; }
.adm-link{
  display:flex; align-items:center; gap:9px; padding:7px 12px; border-radius:9px;
  color:var(--ink); text-decoration:none; font-size:.88rem; font-weight:600;
}
.adm-link:hover{ background:var(--mintbg); text-decoration:none; }
.adm-link.on{ background:var(--accent); color:#fff; }
.adm-ico{ width:18px; text-align:center; flex:0 0 auto; }
.adm-menu-btn{ display:none; background:none; border:0; font-size:1.25rem; cursor:pointer; color:var(--ink); }
.adm-header .brand .tag{ background:#fff; color:var(--sage2); }

/* Round-83: the admin header is ONE non-wrapping flex row — brand, menu button, country
   scope picker, map link, profile, app grid — and nothing in it was allowed to shrink
   (.nav-right > * is flex:0 0 auto, .cpick-input is width:100%, .adm-scope had no rules at
   all). On a phone the row overflowed and the right-hand cluster was pushed past the edge:
   profile image, messages and the grid launcher were all in the DOM and unreachable.
   Everything below just gives the row somewhere to give. */
@media (max-width: 1000px){
  .adm-shell{ grid-template-columns:1fr; }
  .adm-menu-btn{ display:inline-block; order:-1; margin-right:6px; }
  /* Keep the mark, drop the wordmark — it is the widest thing in the row and the least useful
     on a screen where the page title is two lines below it. */
  .adm-header .brand span{ display:none; }
  .adm-header{ padding:0 12px; gap:6px; }
  /* The scope picker is the other wide element. It stays (country scope matters on every admin
     page) but is capped and allowed to shrink instead of setting the row's width. */
  .adm-header .adm-scope{ flex:1 1 auto; min-width:0; max-width:190px; }
  .adm-header .adm-scope .cpick-input{ min-width:0; }
  /* And the right-hand cluster is pinned: it is the last thing that should ever be squeezed out. */
  .adm-header .nav-right{ flex:0 0 auto; gap:10px; margin-left:auto; }
}

/* Phones proper. 190px of country picker still pushed the app-grid launcher 30px past the edge
   on a 375px screen, so the cap tightens again here — measured, not guessed. "Ghana" and every
   other country name still reads at 120px; the full list opens on tap regardless. */
@media (max-width: 560px){
  .adm-header{ padding:0 10px; gap:4px; }
  /* Measured on the real page at 375px, not a mock: .nav-right holds SIX items — scope 120,
     map 42, bell 37, messages 32, avatar 34, app-grid 42 — plus gaps, totalling 347 beside a
     78px brand. 425 > 375, so the app-grid launcher fell off the end. The map shortcut is the
     only duplicate in that row (it is also the first tile inside the grid menu it was pushing
     off-screen), so on a phone it is the one that goes. */
  .adm-header .nav-right > .nav-icon{ display:none; }
  .adm-header .adm-scope{ max-width:110px; }
  .adm-header .nav-right{ gap:6px; }
  /* The INPUT has to be narrow to fit the row; the open LIST does not — it is absolutely
     positioned, so it costs the header nothing.
     But it must be anchored to the HEADER, not to the input. .cpick is position:relative, so
     right:0 pinned the list's right edge to the 110px input's right edge (x=196) and a 320px
     list then ran to x=-124 — off the left of the screen. Dropping .cpick out of the positioning
     chain on mobile makes the fixed, full-width header the anchor instead, so left/right insets
     are effectively viewport insets and the list cannot leave the screen at any width. */
  .adm-header .adm-scope .cpick{ position:static; }
  .adm-header .adm-scope .cpick-list{
    left:10px; right:10px; width:auto; top:calc(100% + 4px);
  }
  .adm-sidebar{
    position:fixed; top:var(--header-h); left:0; bottom:0; width:264px; z-index:1150;
    transform:translateX(-100%); transition:transform .2s ease;
    box-shadow:6px 0 24px rgba(44,59,54,.18); max-height:none;
  }
  .adm-shell.nav-open .adm-sidebar{ transform:none; }
  /* the .scard mobile rules collapse groups by default; open ones stay expanded */
  .adm-sidebar .scard-head{ cursor:pointer; }
}

/* ---- Round-21: admin breadcrumb ---- */
.admin-crumbs{ margin-top:12px; font-size:.86rem; color:var(--ink-soft);
  display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.admin-crumbs a{ color:var(--navy, #1E5AA8); text-decoration:none; font-weight:600; }
.admin-crumbs a:hover{ text-decoration:underline; }
.admin-crumbs .sep{ opacity:.5; }
.admin-crumbs .cur{ color:var(--ink); font-weight:700; }

/* ---- Round-21: platform role popover (admin users) ---- */
.role-pop-wrap{ position:relative; display:inline-block; }
.role-pop{ position:absolute; top:calc(100% + 6px); left:0; z-index:60; width:270px;
  background:var(--paper, #fff); border:1.5px solid var(--track); border-radius:12px;
  box-shadow:0 14px 40px rgba(44,59,54,.22); padding:8px; text-align:left; }
.role-pop h4{ margin:4px 8px 6px; font-size:.7rem; text-transform:uppercase; letter-spacing:.04em; color:var(--ink-soft); }
.role-pop label{ display:flex; align-items:flex-start; gap:9px; padding:7px 8px; border-radius:8px;
  cursor:pointer; font-size:.86rem; font-weight:600; line-height:1.25; }
.role-pop label:hover{ background:#EEF3EE; }
.role-pop label.disabled{ opacity:.55; cursor:not-allowed; }
.role-pop label input{ width:auto; margin-top:2px; flex:none; }
.role-pop .rp-desc{ display:block; font-size:.74rem; color:var(--ink-soft); font-weight:400; }
.role-pop .rp-div{ height:1px; background:var(--track); margin:6px 4px; }
.role-tags{ display:flex; flex-wrap:wrap; gap:5px; align-items:center; }
.role-tag{ font-size:.72rem; padding:3px 9px; border-radius:999px; background:#E7EFE7;
  color:var(--deep, #1E824C); font-weight:700; }
.role-tag.admin{ background:#FBE3E0; color:#B23B2E; }
.role-none{ color:var(--ink-soft); font-size:.82rem; }

/* ---- Round-22: area autocomplete ---- */
.ac-wrap{ position:relative; }
.ac-input{ width:100%; }
.ac-menu{ position:absolute; top:calc(100% + 4px); left:0; right:0; z-index:40;
  background:var(--paper, #fff); border:1.5px solid var(--track); border-radius:12px;
  box-shadow:0 14px 40px rgba(44,59,54,.18); padding:6px; max-height:260px; overflow-y:auto; }
.ac-item{ display:flex; align-items:center; gap:9px; width:100%; text-align:left;
  padding:9px 10px; border:none; background:none; border-radius:8px; cursor:pointer;
  font:inherit; font-size:.92rem; color:var(--ink); }
.ac-item:hover{ background:#EEF3EE; }
.ac-ico{ display:inline-flex; width:16px; height:16px; color:var(--green, #3E7C63); flex:none; }
.ac-ico svg{ width:16px; height:16px; }

/* ---- Round-22: translate-a-comment affordance ---- */
.cmt-translate{ border:none; background:none; cursor:pointer; padding:0; margin-left:4px;
  font:inherit; font-size:.8rem; font-weight:600; color:var(--green, #3E7C63); }
.cmt-translate:hover{ text-decoration:underline; }
.cmt-translate:disabled{ opacity:.6; cursor:default; text-decoration:none; }
.story-cmt small + .cmt-translate{ margin-left:8px; }

/* ---- Round-10: floating assistant ---- */
/* Round 224 (owner: "check every z-index so overlapping UIs stack by importance"): the button is
   page furniture, so it sits above the page and below EVERY bar, menu, sheet and dialog (the
   bottom bar is 1200; menus 1300; sheets 2000+). At 2500 it floated over the phone search sheet
   (2000), a community's search sheet (2300), the message menu and the composer's + menu (1300). */
.asst-fab{
  position:fixed; right:16px; top:50%; transform:translateY(-50%); z-index:1150;
  /* Round-89: a pill, not a circle — it holds the word "Ada" now. min-width keeps it from
     collapsing narrower than the old dot in languages that render the name shorter. */
  width:auto; min-width:52px; height:52px; padding:0 12px;
  border-radius:999px; border:0; cursor:pointer;
  font-size:.98rem; font-weight:400; font-style:italic; letter-spacing:.01em;
  font-family:var(--font, inherit);
  display:inline-flex; align-items:center; justify-content:center;
  background:linear-gradient(135deg, var(--deep, #4F8577), var(--accent, #618D7C)); color:#fff;
  box-shadow:0 8px 26px rgba(44,59,54,.4);
  transition:transform .15s, box-shadow .15s;
}
.asst-fab:hover{ transform:translateY(-50%) scale(1.07); box-shadow:0 10px 30px rgba(44,59,54,.5); }

/* Round-89: on the live map Ada joins the right-hand stack, so it takes that stack's exact
   geometry — 44px round, matching .right-stack .map-card.min — and the map palette's tan.
   A wider, greener, differently-shaped button sitting among the map cards reads as something
   bolted on rather than one of the map's own controls. The label shrinks to fit the circle;
   it stays the word rather than an icon, because "Ada" is the thing people are told to ask. */
.asst-fab.asst-fab-map{
  width:44px; min-width:44px; height:44px; padding:0; border-radius:50%;
  background:var(--tan-light, #E3C9A5); color:var(--ink, #2C3B36);
  font-size:.74rem; letter-spacing:0;
}
.asst-fab.asst-fab-map .asst-lock{ font-size:.6rem; }
@media (max-width:900px){
  /* Round-95k: at 360dp the mid-screen pill sat on top of every hero paragraph (measured on
     the G6). Content pages anchor it lower-right — above the stories ＋ FAB — while the map
     keeps its curated position (the html.pg-map / body:has(#map) overrides outrank this). */
  .asst-fab{ top:auto; bottom:calc(150px + var(--safe-bottom, 0px)); transform:none; }
  .asst-fab:hover{ transform:scale(1.07); }
}
.asst-fab.open{ display:none; } /* round-11: one close button only — the drawer's */
/* Round-142f: the visual hold for a click made before the circuit attached (site.js aquaAda) —
   the drawer is open and the button gone from the moment of the click, not the moment the
   server hears about it. Released once the component has rendered the real open state. */
html.ada-pending .asst-fab{ display:none; }
html.ada-pending .asst-drawer{ transform:translateX(0); }
/* round-71: signed-out visitors see the FAB locked — it links to sign-in */
a.asst-fab-locked{ display:inline-flex; align-items:center; justify-content:center; text-decoration:none; }
.asst-lock{
  position:absolute; right:-3px; bottom:-3px; font-size:.72rem; line-height:1;
  background:#F3F4F1; border-radius:50%; padding:2.5px; box-shadow:0 2px 6px rgba(44,59,54,.3);
}
.asst-close{
  width:34px; height:34px; flex:0 0 34px; border-radius:50%; border:0; cursor:pointer;
  padding:0; line-height:1;
  background:#F1F5F1; color:var(--ink, #2C3B36); font-size:1rem;   /* round-141c: dark on white head */
  display:inline-flex; align-items:center; justify-content:center;
}
.asst-close:hover{ background:#E7EDE6; }
.asst-drawer{
  position:fixed; top:0; right:0; bottom:0; z-index:2400;
  width:min(430px, 100vw); background:var(--paper, #FBFBF8);
  border-left:1.5px solid var(--track, #ADC3B9);
  display:flex; flex-direction:column;
  /* Round-175: CLOSED means no shadow and not painted. The -14px/44px shadow reached ~40px
     into the viewport from its off-screen parking spot and drew a grey band over the search
     sheet (z 2000 < 2400) — the "accent background" on the G6. visibility waits for the slide
     to finish so closing still animates. */
  box-shadow:none; visibility:hidden;
  transform:translateX(105%);
  transition:transform .28s cubic-bezier(.2,.8,.25,1), box-shadow .28s ease, visibility 0s linear .28s;
}
.asst-drawer.open, html.ada-pending .asst-drawer{
  transform:translateX(0); visibility:visible; box-shadow:-14px 0 44px rgba(44,59,54,.3);
  transition:transform .28s cubic-bezier(.2,.8,.25,1), box-shadow .28s ease, visibility 0s;
}
/* Round-94m: no phone-specific drawer sizing any more. The keyboard shrinks the VIEWPORT
   itself (native WebView margin in-app, interactive-widget=resizes-content in Safari), so
   a plain top:0/bottom:0 panel is already right — the round-94d --vvh override raced the
   native resize and made the drawer "inconsistent, sometimes disappearing". */
/* Round-141c (design ada/01): the head is WHITE now, with the logo-mark avatar and a subtitle. */
.asst-drawer-head{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:14px 16px; border-bottom:1px solid #E7EDE6;
  background:#fff; color:var(--ink, #2C3B36);
}
.asst-ada-id{ display:flex; align-items:center; gap:11px; min-width:0; }
.asst-ada-av{ width:36px; height:36px; border-radius:50%; background:#fff; border:1px solid #E4EBE5;
  display:flex; align-items:center; justify-content:center; box-shadow:0 2px 8px rgba(44,59,54,.08); flex:0 0 auto; }
.asst-ada-av svg{ width:22px; height:22px; transform:rotate(180deg); }
.asst-ada-name b{ font-size:.98rem; display:block; line-height:1.15; }
.asst-ada-name small{ font-size:.72rem; color:var(--ink-soft, #5C6E66); }
.asst-chat{ display:flex; flex-direction:column; flex:1; min-height:0; }
.asst-toolbar{ display:flex; align-items:center; gap:8px; padding:10px 14px; border-bottom:1px dashed var(--track, #ADC3B9); }
.asst-history{
  display:flex; flex-direction:column; max-height:220px; overflow-y:auto;
  border-bottom:1px dashed var(--track, #ADC3B9); background:var(--mintbg, #EAF1EC);
}
.asst-hist-row{
  display:flex; flex-direction:column; align-items:flex-start; gap:2px; text-align:left;
  border:0; background:none; cursor:pointer; padding:9px 16px; font-family:var(--font);
  border-bottom:1px dashed var(--track, #ADC3B9);
}
.asst-hist-row:hover{ background:rgba(97,141,124,.14); }
.asst-hist-row.sel{ background:rgba(97,141,124,.2); }
.asst-hist-row small{ color:var(--ink-soft, #55635C); font-size:.72rem; }

/* round-11: assistant tables — proper formatting + horizontal scroll */
.msg-bubble .tbl-scroll{ overflow-x:auto; max-width:100%; margin:8px 0; }
.msg-bubble table{ border-collapse:collapse; font-size:.82rem; white-space:nowrap; }
.msg-bubble th, .msg-bubble td{ border:1px solid var(--track, #ADC3B9); padding:5px 10px; text-align:left; }
.msg-bubble th{ background:var(--deep, #4F8577); color:#F3F4F1; font-weight:700; }
.msg-bubble.mine th{ background:rgba(243,244,241,.25); }
.msg-bubble tbody tr:nth-child(even){ background:rgba(97,141,124,.07); }
.asst-scroll{ flex:1; min-height:220px; }
/* Round-141c (design ada/01): Ada's user bubble is a light neutral accent, not the Messages
   mint — scoped here so the messenger thread keeps its own colour. */
.asst-scroll .msg-bubble.mine{ background:#EAEFEA; }
.asst-drawer .asst-scroll{ max-height:none; min-height:0; }  /* same shrink rule as .msg-scroll — the 220px page-panel minimum would push the drawer’s composer out when the keyboard shrinks it */
.page .asst-scroll{ max-height:calc(var(--app-h, 100vh) * 0.52); }

/* ============================================================
   Round-95: aquaComposerSheet — full-screen mobile composer.
   Facebook-compose layout: top bar (Cancel · title · Post), the
   textarea owns the middle, tools row at the bottom. Fixed inset:0
   — the viewport itself shrinks for the keyboard, so no height
   math, ever (round-94m). z: backdrop 3090 / sheet 3100 — above
   the lightbox (3000, the sheet opens FROM its comment rail),
   below the emoji panel (3200, which must overlay the sheet).
   ============================================================ */
/* ============ Round-96g: /alerts-setup as the 09-chat-ada wizard ======================
   Ada-led chat onboarding. Accent (not the mockup's amber) for the avatar + CTAs — the
   user's call. sensing.js owns [data-setup-state]'s class list ("card setup-state warn|ok"),
   so the readout is styled through those exact classes. */
/* Round-96h: mockup-faithful pacing — ONE screen at a time, viewport-fit, NO scrolling.
   The intro turn (the disclosure) shows until Get started adds .wz-started (sensing.js
   also adds it when the permission is already "always"); the state turns are gated with
   !important because sensing.js drives their inline display. Each turn is a column with
   its CTA pinned at the bottom, mockup-style; .wz-bubs may scroll internally as a safety
   valve on tiny screens, but the copy is sized not to need it. */
.wz-section{ padding-top:24px; padding-bottom:0; }
html.in-app .wz-section{ padding-top:calc(var(--safe-top, 0px) + 14px); }
/* Round-96n2 (G6: "Get started is below the screen"): in-app, the wizard page must NOT
   reserve header space. Round-93b's app-shell style sets :root{--header-h:0} but the
   round-84 rule `html.in-app:not(.signup-bar-on){--header-h:calc(64px + safe-top)}` is
   more specific and wins, leaving 88dp of clearance for a header that is not there —
   which pushed the viewport-fit wizard's CTA below the fold. Scoped, explicit zero. */
html.in-app .wz-page{ padding-top:0; }
.wz-chat{
  max-width:480px; margin:0 auto; display:flex; flex-direction:column;
  height:calc(var(--app-h, 100vh) - var(--safe-top, 0px) - 46px);
  height:calc(var(--vvh, var(--app-h, 100dvh)) - var(--safe-top, 0px) - 46px);   /* round-97 */
  overflow:hidden;
}
.wz-chat:not(.wz-started) [data-setup-when]{ display:none !important; }
.wz-chat.wz-started .wz-intro{ display:none; }
.wz-turn{ flex:1; min-height:0; display:flex; flex-direction:column; }
.wz-bubs{ flex:1; min-height:0; overflow-y:auto; display:grid; gap:10px; align-content:start; }
.wz-turn .wz-cta, .wz-turn .wz-acts{ margin-top:14px; }
.wz-chat-head{
  display:flex; gap:12px; align-items:center; padding:4px 4px 14px;
  border-bottom:1px solid var(--track, #ADC3B9); margin-bottom:14px;
}
.wz-ada{
  width:46px; height:46px; border-radius:50%; background:var(--accent, #618D7C); color:#fff;
  font-weight:700; font-size:.8rem; display:flex; align-items:center; justify-content:center;
  flex:0 0 46px;
}
.wz-who b{ display:block; color:var(--ink, #2C3B36); font-size:.98rem; }
.wz-who span{ font-size:.76rem; color:var(--sage2, #8FA99B); }
/* Round-96n (user pick, from the 09-chat-ada mockup): the status is the 03-step-tracker
   card — the grant chain as two steps with a progress bar. sensing.js still owns
   [data-setup-state] (now the card's caption line) and stamps wz-lv-* on .wz-chat, which
   is all the steps/bar/dots below read. Plain class chains only — no :has (G6). */
.st-root{ background:#fff; border:1px solid #E4E8E2; border-radius:16px; padding:13px 16px 9px; margin-bottom:14px; }
.st-steps{ display:flex; align-items:center; }
.st-step{ display:flex; flex-direction:column; align-items:center; gap:5px; flex:0 0 auto; width:96px; }
.st-n{
  width:26px; height:26px; border-radius:50%; background:#EDF2EE; color:#8FA99B;
  font-size:.72rem; font-weight:800; display:flex; align-items:center; justify-content:center;
  border:2px solid #E0E5DF;
}
.st-step > span{ font-size:.62rem; color:#8FA99B; text-align:center; line-height:1.25; }
/* step 1: "now" until a grant lands, then done */
.wz-chat .st-s1 .st-n{ border-color:#C89B54; color:#C89B54; background:#FBF3E2; }
.wz-chat .st-s1 > span{ color:#7A5B22; font-weight:700; }
.wz-chat.wz-lv-whileusing .st-s1 .st-n, .wz-chat.wz-lv-always .st-s1 .st-n{
  background:#4F8577; border-color:#4F8577; color:#fff; }
.wz-chat.wz-lv-whileusing .st-s1 > span, .wz-chat.wz-lv-always .st-s1 > span{ color:#1F5C44; font-weight:400; }
/* step 2: idle, then "now" at whileusing, done at always */
.wz-chat.wz-lv-whileusing .st-s2 .st-n{ border-color:#C89B54; color:#C89B54; background:#FBF3E2; }
.wz-chat.wz-lv-whileusing .st-s2 > span{ color:#7A5B22; font-weight:700; }
.wz-chat.wz-lv-always .st-s2 .st-n{ background:#4F8577; border-color:#4F8577; color:#fff; }
.wz-chat.wz-lv-always .st-s2 > span{ color:#1F5C44; }
.st-bar{ flex:1; height:3px; background:#E0E5DF; border-radius:99px; position:relative; top:-11px; overflow:hidden; }
.st-bar i{ display:block; height:100%; background:#4F8577; width:0; border-radius:99px; }
.wz-chat.wz-lv-whileusing .st-bar i{ width:50%; }
.wz-chat.wz-lv-always .st-bar i{ width:100%; }
/* the caption is the SAME [data-setup-state] element sensing.js has always written */
.wz-chat .setup-state{
  border:0; background:none; box-shadow:none; border-radius:0; padding:7px 0 0;
  margin:0; text-align:center; font-size:.76rem; color:var(--sage2, #8FA99B);
}
.wz-chat .setup-state.warn{ background:none; border:0; color:#A98F5E; }
.wz-chat .setup-state.ok{ background:none; border:0; color:#1F5C44; font-weight:700; }
/* pager dots (mockup): decorative progress on the same classes; active = accent-gradient pill */
.wz-dots{ display:flex; gap:7px; justify-content:center; padding:12px 0 2px; }
.wz-dots span{ width:7px; height:7px; border-radius:50%; background:#D7DED4; }
.wz-chat:not(.wz-started) .wz-dots span:nth-child(1),
.wz-chat.wz-started.wz-lv-none .wz-dots span:nth-child(2),
.wz-chat.wz-started.wz-lv-whileusing .wz-dots span:nth-child(3),
.wz-chat.wz-lv-always .wz-dots span:nth-child(4){
  background:linear-gradient(135deg, #4F8577, #8FB8A6); width:22px; border-radius:99px;
}
.wz-chat.wz-started .wz-dots span:nth-child(1),
.wz-chat.wz-lv-whileusing .wz-dots span:nth-child(2),
.wz-chat.wz-lv-always .wz-dots span:nth-child(2),
.wz-chat.wz-lv-always .wz-dots span:nth-child(3){
  background:linear-gradient(135deg, #7FA893, #B9CEC2);
}
.wz-chat.wz-lv-always .wz-dots span:nth-child(1){ background:linear-gradient(135deg, #7FA893, #B9CEC2); width:7px; border-radius:50%; }
@keyframes wzPop{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; } }
.wz-bubs .wz-b{ animation:wzPop .3s ease; }
.wz-b{
  max-width:88%; background:#fff; border:1px solid #E4E8E2; border-radius:4px 18px 18px 18px;
  padding:13px 15px; font-size:.92rem; line-height:1.55; color:#33403A;
  box-shadow:0 2px 8px rgba(44,59,54,.05); justify-self:start;
}
.wz-b b{ color:var(--ink, #2C3B36); }
.wz-hint{ font-size:.82rem; color:#7E8D85; font-style:italic; }
.wz-ol{ margin:0 0 0 18px; padding:0; line-height:1.7; }
.wz-cta{
  display:block; width:100%; border:0; background:var(--accent, #618D7C); color:#fff;
  font-family:var(--font); font-weight:800; font-size:.98rem; text-align:center;
  padding:15px; border-radius:999px; cursor:pointer; text-decoration:none;
  box-shadow:0 8px 20px rgba(97,141,124,.3);
}
.wz-cta:hover{ text-decoration:none; color:#fff; filter:brightness(1.05); }
.wz-acts{ display:grid; gap:10px; }
.wz-ghost{
  display:block; text-align:center; padding:12px; font-size:.9rem; color:var(--deep, #4F8577);
  border:1px solid var(--track, #ADC3B9); border-radius:999px; text-decoration:none; background:#fff;
}

/* ============ Round-96q: the wizard OWNS the screen (design 06), phone and desktop ======
   A pinned panel between the header and the bottom of the VISUAL viewport (--vvh, kept
   fresh by site.js), so an open keyboard shortens it instead of hiding the composer. The
   page itself never scrolls; a dense step (the map one) scrolls inside its own box. */
.rpt-full{
  /* Round-97h: + --vvt, the visual-viewport pan. iOS slides the visual viewport down to reveal
     the caret while position:fixed stays anchored to the LAYOUT viewport, so without this the
     whole panel rides ~200px off the top of the screen the moment the keyboard opens (measured
     on an iPhone: ot=200). 0 on Android and desktop. */
  position:fixed; left:0; right:0; top:calc(var(--header-h) + var(--vvt, 0px)); padding-top:0; z-index:900;
  height:calc(var(--app-h, 100vh) - var(--header-h));                    /* pre-108 engines (G6) */
  height:calc(var(--vvh, var(--app-h, 100dvh)) - var(--header-h));
  overflow:hidden; display:flex; flex-direction:column; background:#fff;   /* user call */
}
/* on white, the white tappable surfaces need their own edge to read as surfaces */
.rpt-full .wz2-svc .chip, .rpt-full .wz2-issb,
.rpt-full .wz2-sev .chip, .rpt-full .wz2-vis .chip{ box-shadow:0 1px 3px rgba(44,59,54,.07); }
.rpt-full .hero-inner, .rpt-full .rpt-side{ display:none; }
.rpt-full > .section{ flex:1; min-height:0; padding:0; display:flex; }
.rpt-full > .section > .container{
  flex:1; min-height:0; display:flex; flex-direction:column;
  max-width:640px; padding-top:16px; padding-bottom:16px;
}
/* !important earns its place: the grid carries an inline align-items:start (right for the
   two-column page, fatal here — the card stops at its content and the footer floats mid-air) */
.rpt-full .grid-2{ display:flex; flex:1; min-height:0; gap:0; align-items:stretch !important; }
/* the card chrome goes: at full width the border and shadow just draw a box round the page */
.rpt-full .form-card{
  flex:1; min-height:0; display:flex; flex-direction:column;
  background:transparent; border:0; box-shadow:none; padding:0;
}
.rpt-full form{ flex:1; min-height:0; display:flex; flex-direction:column; }
.rpt-full .wz2{ flex:1; min-height:0; display:flex; flex-direction:column; }
.rpt-full .wz2-step.on{
  flex:1; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;
  /* Round-96s (user call): iOS-style scrollbar — thin, rounded, translucent. Android's
     WebView draws a chunky opaque one by default. Both syntaxes: the ::-webkit rules are
     what the G6 (Chrome 101) understands; scrollbar-width lands on newer engines. */
  scrollbar-width:thin; scrollbar-color:rgba(44,59,54,.22) transparent;
}
.rpt-full .wz2-step.on::-webkit-scrollbar{ width:4px; }
.rpt-full .wz2-step.on::-webkit-scrollbar-track{ background:transparent; }
.rpt-full .wz2-step.on::-webkit-scrollbar-thumb{ background:rgba(44,59,54,.22); border-radius:99px; }
@media (max-width:700px){
  /* the scroll box runs to the SCREEN edge (its padding holds the content in), so the bar
     sits at the very edge instead of floating a container's width inside it */
  .rpt-full > .section > .container{ padding-left:0; padding-right:0; }
  .rpt-full .wz2-head, .rpt-full .wz2-dots, .rpt-full .wz2-foot,
  .rpt-full .form-card > .sp-alertbox, .rpt-full .form-card > p.hint{
    padding-left:16px; padding-right:16px;
  }
  /* Round-97e: .wz2-need is a bordered amber BOX, so the padding this used to share with the
     flowing-text rules above only inset its words — the box itself still touched both screen
     edges. It needs margin, and keeps its own 10px 12px padding. */
  .rpt-full .wz2-need{ margin-left:16px; margin-right:16px; }
  .rpt-full .wz2-step.on{ padding-left:16px; padding-right:16px; }
}
/* a step's SECOND element (hidden uploader input, the submit bar) is content-sized: two
   flex:1 siblings would halve the step and clip what is above */
.rpt-full .wz2-step.wz2-aux.on{ flex:0 0 auto; overflow:visible; }
/* with the step now filling the screen, the composer gets room to be a composer */
.rpt-full .wz2-step.on .field .composer textarea{ min-height:170px; }
/* Round-97c (the user's model): while the keys are up, the text box sits ON the keyboard.
   Everything that is guidance rather than input steps aside for the duration — the coaching line,
   the evidence-limits blurb, the question label (the placeholder asks it anyway) and the
   attachment previews — and so does the Back/Next footer, which is what frees the room for the box
   to keep its FULL height. The step then lays out as a column with the field pushed to the bottom,
   so the composer's lower edge meets the top of the keyboard.

   History, so none of it is retried: flex-FILLING the step gave the composer a fixed height and the
   in-frame tiles then painted over the button strip; shrinking the textarea (96px, then 72px, then
   44px with compacted thumbnails) never fit either, because the arithmetic does not work on a
   ~130px step. Removing things beats shrinking them, and the tiles left the frame entirely
   (Report.razor, round-97b). Nothing here touches the textarea's height. */
html.kb-open .rpt-full .wz2-step.on{ display:flex; flex-direction:column; }
html.kb-open .rpt-full .wz2-step.on .field{ margin-top:auto; margin-bottom:0; }  /* bottom-anchored */
/* ...and the last white band under the border goes with it: .field carries an 18px bottom margin
   (style.css) and the wizard's container another 16px of padding, which together left the box
   floating ~34px above the keys instead of resting on them. */
html.kb-open .rpt-full > .section > .container{ padding-bottom:0; }
html.kb-open .rpt-full .wz2-step.on .wz2-hint{ display:none; }
html.kb-open .rpt-full .wz2-step.on .field > .wz2-q{ display:none; }
html.kb-open .rpt-full .wz2-step.on .field .composer-foot{ display:none; }
html.kb-open .rpt-full #upTiles{ display:none; }
html.kb-open .rpt-full .wz2-foot{ display:none; }
.wz2-need{
  flex:0 0 auto; margin:10px 0 0; font-size:.88rem; font-weight:700; color:#96661C;
  background:#FBF3E2; border:1px solid #E8CD9A; border-radius:12px; padding:10px 12px;
}
.rpt-full .wz2-foot{ flex:0 0 auto; }
/* the submit step's sticky bar has a pinned footer of its own here — plain flow instead */
.rpt-full .submit-bar{ position:static; box-shadow:none; padding:0; }
@media (min-width:900px){
  /* desktop: the same single column, centred — the mockup's browser view */
  .rpt-full > .section > .container{ padding-top:28px; padding-bottom:28px; }
  .rpt-full .wz2 .wz2-q{ font-size:1.45rem; }
}

/* ============ Round-96p: /report as design 06 — one question per screen ================
   Public create flow pages; edit + staff get .wz2-classic (every step visible at once).
   Steps hide by CLASS, never removal — the uploader's #upInput must stay in the DOM. */
.wz2-step{ display:none; }
.wz2-step.on, .wz2-classic .wz2-step{ display:block; animation:wzPop .25s ease; }
.wz2-classic .wz2-step{ animation:none; }
/* Round-96t: the wizard hides the site header and carries its own way out. The panel then
   starts at the SAFE-TOP inset (in-app the status bar sits there; on the web it is 0) —
   set on .rpt-full directly, because html.in-app's --header-h rule out-specifies :root. */
html.pg-rpt .site-header{ display:none; }
/* Round-142c: the same, without waiting for any script — engines with :has() (Chrome 105+,
   Safari 15.4+) hide the header and size the panel the moment the wizard's markup exists,
   so the slide-up never plays against a header that is about to disappear. */
html:has(.rpt-full) .site-header{ display:none; }
html:has(.rpt-full), html:has(.rpt-full) body{ background:#fff; }
html:has(.rpt-full) .rpt-full{
  /* Round-206: + --toast-room — the offline toast's height, when it sits at the top of this
     form (it cannot be dismissed, so the form moves down rather than being covered). 0 online. */
  top:calc(var(--safe-top, 0px) + var(--toast-room, 0px)); padding-top:0;
  height:calc(var(--app-h, 100vh) - var(--safe-top, 0px) - var(--toast-room, 0px));
  height:calc(var(--vvh, var(--app-h, 100dvh)) - var(--safe-top, 0px) - var(--toast-room, 0px));
}
/* Round-202: and NO SIGN-UP BAR over it. The panel above starts at the safe-area inset because
   the wizard hides the site header and owns the screen — but #signupBar is z-index 1200 and was
   still drawn on top of it. Measured on production at 375px wide: the bar is 112px tall and the
   panel starts at 0, so it covered the service chips a reporter has to tap first.

   Hidden, not reserved for: reserving would hand a form that deliberately owns the screen a
   112px band of somebody else's invitation. AppShellPaths already lists "report" under Tasks —
   "These own the screen: no bar, whatever else is true of them" — and that judgement simply had
   not reached this element. "Sign up free" over a form somebody is using to report an emergency
   is the wrong thing to say at that moment.

   Both selectors for the reason round 142c gives: html.pg-rpt needs site.js to have run,
   html:has(.rpt-full) does not, so the bar cannot flash over the wizard on a slow first paint. */
html.pg-rpt #signupBar, html:has(.rpt-full) #signupBar{ display:none !important; }
/* the page behind the panel is white too — otherwise the safe-area strip under the phone's
   status bar (the panel starts BELOW it) stays the site's cream and reads as a stray band */
html.pg-rpt, html.pg-rpt body{ background:#fff; }
html.pg-rpt .rpt-full{
  top:calc(var(--safe-top, 0px) + var(--toast-room, 0px)); padding-top:0;   /* round-206: see html:has(.rpt-full) */
  height:calc(var(--app-h, 100vh) - var(--safe-top, 0px) - var(--toast-room, 0px));
  height:calc(var(--vvh, var(--app-h, 100dvh)) - var(--safe-top, 0px) - var(--toast-room, 0px));
}
.wz2-head{ display:flex; align-items:center; gap:10px; margin-bottom:14px; }
.wz2-head .wz2-title{ flex:1; margin-bottom:0; }
.wz2-close{
  flex:0 0 auto; width:34px; height:34px; border-radius:50%; display:flex;
  align-items:center; justify-content:center; text-decoration:none; cursor:pointer;
  color:var(--sage2, #8FA99B); font-size:1.05rem; background:#F3F4F1;
  border:0; font-family:var(--font);   /* it is a <button> now */
}
.wz2-close:hover{ text-decoration:none; color:var(--ink, #2C3B36); }

/* the screen's name — quiet on purpose: the step's question is the loud thing */
.wz2-title{
  font-size:.72rem; font-weight:800; letter-spacing:2px; text-transform:uppercase;
  color:var(--sage2, #8FA99B); margin-bottom:10px;
}
.wz2-dots{ display:flex; gap:6px; margin-bottom:18px; }
.wz2-dots span{ flex:1; height:5px; border-radius:999px; background:#E0E5DF; }
.wz2-dots span.on{ background:var(--accent, #618D7C); }
.wz2 .wz2-q{ font-size:1.25rem; font-weight:800; color:#1F2A24; line-height:1.3; }
.wz2-classic .wz2-q{ font-size:inherit; font-weight:600; }   /* classic keeps ordinary labels */
.wz2-hint{ margin:4px 0 12px; }
/* service picker as tiles (wizard only — classic keeps the chip row) */
.wz2:not(.wz2-classic) .wz2-svc{ display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin-top:10px; }
@media (min-width:700px){ .wz2:not(.wz2-classic) .wz2-svc{ grid-template-columns:repeat(3,1fr); } }
.wz2:not(.wz2-classic) .wz2-svc .chip{
  display:grid; gap:6px; justify-items:center; padding:16px 8px; border-radius:16px;
  font-size:.92rem; border:1.5px solid #E0E5DF; background:#fff;
}
.wz2:not(.wz2-classic) .wz2-svc .chip .ico-t{ font-size:1.5rem; }
/* the emoji is a SILHOUETTE painted by text-shadow, and .chip.sel paints it paper-white
   for the classic accent-filled chip — on the wizard's white tile that is invisible.
   Selected keeps the service colour, like the label does. */
.wz2:not(.wz2-classic) .wz2-svc .chip.sel .ico-t{ text-shadow:0 0 0 var(--c, var(--accent, #618D7C)); }
.wz2:not(.wz2-classic) .wz2-svc .chip.sel{
  /* the classic .chip.sel paints accent-on-white-text — on a white tile that is white on
     white. Selected tile = coloured border + coloured label, body stays white. */
  border-color:var(--c, var(--accent)); box-shadow:0 8px 20px rgba(44,59,54,.08);
  background:#fff; color:var(--c, var(--accent)); font-weight:700;
}
/* issue types as tappable rows */
.wz2-iss{ display:grid; gap:8px; margin-top:10px; }
.wz2-issb{
  border:1.5px solid #E0E5DF; background:#fff; border-radius:14px; padding:13px 15px;
  text-align:left; cursor:pointer; font-size:.92rem; color:#2C3B36; font-family:var(--font);
}
.wz2-issb.on{ border-color:var(--accent, #618D7C); background:#EFF5F1; font-weight:700; }
.wz2-issb.skip{ color:#8FA99B; font-style:italic; }
/* danger + visibility chips stack full-width as cards in the wizard */
.wz2:not(.wz2-classic) .wz2-sev, .wz2:not(.wz2-classic) .wz2-vis{ display:grid; gap:8px; margin-top:10px; }
.wz2:not(.wz2-classic) .wz2-sev .chip, .wz2:not(.wz2-classic) .wz2-vis .chip{
  width:100%; text-align:left; padding:14px 16px; border-radius:14px; font-size:.95rem;
}
/* the range picker */
.wz2-cap{ font-size:.82rem; font-weight:700; color:#2C3B36; margin:12px 0 8px; }
.wz2-map{ height:230px; border-radius:14px; border:1.5px solid var(--track, #ADC3B9); overflow:hidden; }
.wz2-slider{ display:flex; align-items:center; gap:10px; margin-top:10px; }
.wz2-slider span{ font-size:.78rem; color:#8FA99B; }
.wz2-slider input[type=range]{ flex:1; accent-color:var(--accent, #618D7C); }
.wz2-slider b{ font-size:.84rem; color:#2C3B36; min-width:52px; text-align:right; }
/* the quiet "there is more here" affordance under the map */
.wz2-link{
  border:0; background:none; padding:10px 0 0; cursor:pointer; font-family:var(--font);
  font-size:.86rem; font-weight:700; color:var(--deep, #4F8577); text-decoration:underline;
}
/* the more-details fold opens flat inside its step */
.wz2:not(.wz2-classic) .more-details summary{ display:none; }
.wz2-step-title{ display:block; margin-bottom:6px; }
/* wizard footer */
.wz2-foot{ display:flex; gap:12px; align-items:center; margin-top:16px; }
.wz2-ghost{
  border:0; background:none; color:#93A198; font-size:.9rem; padding:14px 6px;
  cursor:pointer; font-family:var(--font);
}
.wz2-next{
  flex:1; border:0; background:var(--accent, #618D7C); color:#fff; font-weight:800;
  font-size:1rem; padding:15px; border-radius:16px; cursor:pointer; font-family:var(--font);
}

html.composer-open, html.composer-open body{ overflow:hidden; }
.cs-backdrop{ position:fixed; inset:0; z-index:3090; background:rgba(24,32,29,.45); }
.cs-sheet{
  position:fixed; inset:0; top:var(--vvt, 0px); z-index:3100;   /* round-97h: iOS pan */
  height:var(--app-h, 100vh); height:var(--vvh, var(--app-h, 100dvh));   /* round-97: keyboard-aware, see .omni-sheet */
  display:grid; grid-template-rows:auto auto minmax(0,1fr) auto;  /* head | reply chip | body | tools */
  background:var(--paper, #FBFBF8);
  padding:calc(var(--safe-top, env(safe-area-inset-top, 0px))) 0 0;
  animation:csIn .22s cubic-bezier(.2,.8,.25,1);
}
@keyframes csIn{ from{ transform:translateY(100%); } to{ transform:translateY(0); } }
/* children are placed EXPLICITLY — the omni-sheet lesson: a [hidden] sibling (the reply
   chip) generates no grid item, and auto-placement then shifts every later row up, handing
   the 1fr body row to the tools strip. */
.cs-head{
  grid-row:1; position:relative;
  display:flex; align-items:center; gap:10px; padding:16px 14px 10px;
  border-bottom:1px solid var(--track, #ADC3B9);
}
/* Round-142 (P2.5): the pill that says "this sheet drags". Sits on whichever header is the
   handle (aquaDragDismiss adds .sheet-handle); the header itself takes the vertical pan so the
   body underneath keeps its own scroll. Round-142 P3: will-change only while the sheet is
   actually being handled — never permanently (it pins a compositor layer for nothing). */
.sheet-grab{ position:absolute; left:50%; top:6px; width:36px; height:4px; border-radius:2px;
  background:var(--track, #ADC3B9); transform:translateX(-50%); pointer-events:none; }
.sheet-handle{ touch-action:pan-x; cursor:grab; user-select:none; -webkit-user-select:none; }
.sheet-handle:active{ cursor:grabbing; }
.cs-sheet[style*="transform"], .lb-side[style*="transform"]{ will-change:transform; }
/* the comments panel is only a sheet on phones — as the desktop rail its header is a plain header */
.lightbox:not(.comments-only) .lb-side-head .sheet-grab{ display:none; }
.lightbox:not(.comments-only) .lb-side-head.sheet-handle{ cursor:default; touch-action:auto; user-select:auto; }
@media (min-width:901px){ .lb-side-head .sheet-grab{ display:none; } .lb-side-head.sheet-handle{ cursor:default; touch-action:auto; } }
.cs-cancel{
  width:40px; height:40px; border-radius:50%; border:1.5px solid #F0EEE8; background:#fff;
  font-size:1rem; color:var(--ink, #2C3B36); cursor:pointer; flex:0 0 auto;
}
.cs-title{ flex:1; text-align:center; font-weight:700; color:var(--ink, #2C3B36); font-size:1rem;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cs-send{
  flex:0 0 auto; border:0; border-radius:999px; padding:9px 18px; cursor:pointer;
  background:var(--accent, #618D7C); color:#fff; font-family:var(--font); font-weight:700;
}
.cs-send:disabled{ opacity:.45; cursor:default; }
.cs-reply-chip{
  grid-row:2;
  display:flex; align-items:center; gap:8px; margin:8px 14px 0; padding:6px 12px;
  background:var(--mintbg, #EAF1EC); border-radius:10px; font-size:.85rem; color:var(--ink-soft, #55635C);
}
.cs-reply-chip[hidden]{ display:none; }
.cs-reply-x{ margin-left:auto; border:0; background:none; cursor:pointer; color:inherit; }
/* the .compose base gives the textarea its border/padding; here it fills the row instead */
.cs-form{ grid-row:3; margin:0; padding:12px 14px; display:flex; min-height:0; }
.cs-form textarea{
  flex:1; min-height:0; height:auto; resize:none; border:1.5px solid var(--track, #ADC3B9);
  border-radius:14px; padding:12px; font-family:var(--font); background:#fff;
}
.cs-form textarea:focus{ outline:none; border-color:var(--accent, #618D7C); }
.cs-error{ position:absolute; left:22px; bottom:2px; margin:0; color:#8e2a1f; font-size:.82rem; }
.cs-form{ position:relative; }
.cs-tools{
  grid-row:4;
  display:flex; align-items:center; gap:10px; padding:8px 16px;
  padding-bottom:calc(10px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  border-top:1px solid #F0EEE8;
}
.cs-tools .emoji-btn{ border:0; background:none; font-size:1.3rem; cursor:pointer; padding:4px; }
.cs-count{ margin-left:auto; font-size:.8rem; color:var(--sage2, #8FA99B); }

/* ============================================================
   Round-95i: the EDITORIAL incident panel (design final-media-02-hero).
   #stationPanel.ed — hero carousel / serif head / service-bordered
   description / hairline actions band / Comments·Timeline·Details tabs
   / pinned composer card. Stations and other panels keep the classic
   shell (the .ed class is added only by _openIncidentPanel).
   ============================================================ */
.station-panel.ed .sp-head{ background:none; padding:0; height:0; overflow:visible; z-index:6; }
.station-panel.ed .sp-head #spPill, .station-panel.ed .sp-head h2, .station-panel.ed .sp-head .sub{ display:none; }
.station-panel.ed .sp-close{
  top:14px; right:14px; background:#fff; color:var(--ink, #2C3B36); border:0;
  box-shadow:0 2px 10px rgba(0,0,0,.18);
  /* Round-97l: the SIZE deliberately does not live here any more. This selector is (0,3,0) and
     the shared phone rule at the end of this file is (0,2,0), so a size set here silently beat
     the shared one and left this ✕ a different size from every other — the very inconsistency
     that rule exists to remove. Only the look (white on a photograph) is this variant's business.
     On desktop it falls back to the shared 34px, matching every other desktop close. */
}
.station-panel.ed #spBody{
  padding:0; min-height:0; background:var(--paper, #F3F4F1);
  display:grid; grid-template-rows:auto auto auto auto auto minmax(0,1fr) auto;
  overflow:hidden;
}
/* Round-97: the panel is full-screen on a phone, so it runs under the status bar — and this
   variant collapses .sp-head to height:0, which means the top inset the head carries for the
   station variant never applies here. Without these two rules the close button and the hero's
   service chips sit directly under the clock, battery and signal icons (seen on the G6).
   #spBody is paper and so is the panel, so the inset reads as a clean spacer rather than a seam.
   --safe-top before env(): Android's WebView leaves env() at 0 (no cutout), and the native
   bridge fills --safe-top instead; env() is the mobile-web-on-iOS fallback. */
@media (max-width:720px){
  /* #spBody keeps the inset so a panel with NO media still clears the clock; the hero cancels it
     for itself below, which is what lets the picture run under the status bar. */
  .station-panel.ed #spBody{ padding-top:var(--safe-top, env(safe-area-inset-top, 0px)); }
  /* Round-97e: the ✕ and the service chips share ONE band, so their centres line up no matter
     what height the chips turn out to be. Matching their `top` alone cannot align them — the
     button is 51px and a chip is roughly half that, which is why the ✕ read as sitting low. */
  .station-panel.ed .sp-close{
    top:calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 6px);
    display:flex; align-items:center; justify-content:center; }
  /* Round-97d: a third of the panel on a phone. --vvh is the panel's own height (site.js keeps
     it true on every engine), so this tracks the real viewport rather than a guessed number. The
     negative margin pulls the picture up THROUGH #spBody's inset and behind the status bar, and
     the height adds that inset back so the visible picture is still two thirds. */
  .station-panel.ed .ed-hero{
    margin-top:calc(-1 * var(--safe-top, env(safe-area-inset-top, 0px)));
    height:calc(var(--vvh, var(--app-h, 100vh)) / 3 + var(--safe-top, env(safe-area-inset-top, 0px)));
  }
  /* The chips sat at the very top of the hero, which is now under the clock. Qualified with
     .station-panel.ed on purpose: a media query adds NO specificity, and the base .ed-hero-chips
     rule is further down this file, so an unqualified override here loses on source order — which
     is exactly what happened first time (the chips still sat under the status icons). */
  .station-panel.ed .ed-hero-chips{
    top:calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 6px);
    height:51px; align-items:center; }
  /* Round-97j, measured on an iPhone 11: with the keys up the panel is ~320px tall and #spBody is
     overflow:hidden, so its LAST grid row — the comment composer — was clipped away completely and
     the sheet had to be dragged upward to reach it. While typing, the rows that are context rather
     than conversation step aside: the hero, the description quote and the reactions strip. The
     headline stays, because you should be able to see what you are commenting on, and the comment
     list is minmax(0,1fr) so it absorbs whatever is left. */
  html.kb-open .station-panel.ed .ed-hero,
  html.kb-open .station-panel.ed .ed-desc,
  html.kb-open .station-panel.ed .ed-acts{ display:none; }
  /* Round-95l draws the status icons DARK for the site's light pages. Over a photo they can
     disappear entirely, so lay a short white scrim behind just that band — enough to keep the
     clock and battery readable, short enough that it reads as light falling on the image. */
  .station-panel.ed .ed-hero::before{
    content:""; position:absolute; left:0; right:0; top:0; z-index:1; pointer-events:none;
    height:calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 10px);
    background:linear-gradient(rgba(255,255,255,.78), rgba(255,255,255,0));
  }
}
/* Round-97d: a quarter of the drawer on desktop (it runs from the header to the bottom); phones
   override to two thirds above. object-fit:cover on the img means the media fills this height and
   is cropped left/right instead — full height showing, which is what a landscape photo of a
   flooded street needs. */
.ed-hero{ grid-row:1; position:relative; overflow:hidden; cursor:pointer;
  height:calc((var(--app-h, 100vh) - var(--header-h, 64px)) / 4); }
.station-panel.ed .ed-hero img{ width:100%; height:100%; object-fit:cover; display:block; }
.ed-hero[hidden]{ display:none; }
/* Round-142t: the action-strip skeleton is ONE row the height of the real strip */
.ed-acts [aria-busy]{ min-height:40px; display:flex; align-items:center; }
.ed-acts [aria-busy] .skel-line + .skel-line{ display:none; }
/* Round-142s: the reserved hero — the photo's dominant colour until its bytes land */
.ed-hero.ph{ background:var(--ph, #DCE4DE); }
.ed-play{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; pointer-events:none; }
/* round-152b (owner): the outline play icon in the theme colour, not a text glyph. It rides on a
   white disc because the frame behind it is a photograph — a green outline alone disappears over
   half of them, and the disc is the same affordance .ed-mnav already uses on this hero. */
.ed-play::before{ content:""; position:absolute; width:54px; height:54px; border-radius:50%;
  background:rgba(255,255,255,.92); box-shadow:0 4px 16px rgba(20,28,25,.28); }
.ed-play svg{ position:relative; width:26px; height:26px; margin-left:3px;
  color:var(--deep, #4F8577); stroke:currentColor; }
.ed-hero-chips{ position:absolute; top:10px; left:12px; display:flex; gap:8px; }
.ed-mnav{ position:absolute; top:50%; transform:translateY(-50%); left:8px; width:30px; height:30px;
  border-radius:50%; border:0; background:rgba(255,255,255,.85); cursor:pointer; font-size:1rem; }
.ed-mnav.r{ left:auto; right:8px; }
/* Round-142e: the hero swipes (map-engine._loadMedia) — vertical stays the panel's scroll;
   on phones the ‹ › give way to the gesture and the dots say where you are */
.ed-hero{ touch-action:pan-y; }
.ed-hero img{ -webkit-user-drag:none; user-drag:none; will-change:transform; }
@media (max-width:900px){ .ed-mnav{ display:none; } }
.ed-dots{ position:absolute; bottom:8px; left:0; right:0; display:flex; gap:5px; justify-content:center; }
.ed-dots i{ width:7px; height:7px; border-radius:50%; background:rgba(255,255,255,.5); }
.ed-dots i.on{ background:#fff; }
.ed-chip{ font-size:.66rem; font-weight:800; letter-spacing:1px; border-radius:999px; padding:3px 11px;
  color:#fff; text-transform:uppercase; }
.ed-head{ grid-row:2; position:relative; padding:14px 18px 10px;
  border-bottom:1px solid rgba(44,59,54,.08); }
.has-hero .ed-head .ed-chips{ display:none; }      /* the hero overlay carries them */
.ed-chips{ display:flex; gap:8px; margin-bottom:8px; }
.ed-hl{ font-family:Georgia, "Times New Roman", serif; font-size:1.08rem; line-height:1.35;
  margin:0; padding-right:34px; color:var(--ink, #2C3B36); text-transform:none;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.ed-meta{ font-size:.78rem; color:var(--ink-soft, #55635C); margin-top:5px; font-style:italic; }
.ed-src{ font-size:.76rem; color:var(--ink-soft, #55635C); margin-top:4px; }
.ed-src b{ color:var(--deep, #4F8577); }
.ed-desc{ grid-row:3; margin:10px 18px; font-size:.9rem; line-height:1.55; color:#3E4A44;
  border-left:3px solid var(--accent, #618D7C); padding-left:12px; cursor:pointer; }
.ed-desc .js-cmt-body{ display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden; }
.ed-desc.open .js-cmt-body{ display:block; -webkit-line-clamp:unset; max-height:calc(var(--app-h, 100vh) * 0.4); overflow-y:auto; }
.ed-acts{ grid-row:4; margin:0 18px; padding:2px 0;
  border-top:1px solid rgba(44,59,54,.08); border-bottom:1px solid rgba(44,59,54,.08); }
.ed-acts .story-actions{ margin:6px 0; border:0; padding:0; }
.ed-tabs{ grid-row:5; display:flex; gap:18px; padding:9px 18px 0; border-bottom:1px solid #E7EBE6; }
.ed-tabs span{ padding-bottom:8px; font-size:.82rem; font-weight:700; color:var(--ink-soft, #55635C); cursor:pointer; }
.ed-tabs span.on{ color:var(--deep, #4F8577); border-bottom:2.5px solid var(--deep, #4F8577); }
.ed-panes{ grid-row:6; overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch;
  /* Round-97m: 20px at the foot, not 12. Scrolled to the end, the last comment used to stop
     one hair above the composer card and read as collided rather than adjacent. */
  padding:12px 18px 20px; }
.ed-pane{ display:none; } .ed-pane.on{ display:block; }
/* comments in the editorial voice: white shadowed avatars on a thread rail */
.station-panel.ed .cmt-thread{ position:relative; }
.station-panel.ed .cmt-thread::before{ content:""; position:absolute; left:15px; top:38px; bottom:6px;
  width:2px; background:#E4EAE4; }
.station-panel.ed .cmt-thread:last-child::before{ display:none; }
.station-panel.ed .cmt-av, .station-panel.ed .cmt-av-fallback{
  width:32px; height:32px; flex:0 0 32px; border-radius:50%;
  box-shadow:0 2px 8px rgba(0,0,0,.1); position:relative; z-index:1;
}
.station-panel.ed .cmt-av-fallback{ background:#fff; color:var(--deep, #4F8577); }
/* timeline pane: the same rail language with dots */
.station-panel.ed .tl-row{ position:relative; padding:0 0 14px 30px; font-size:.86rem; }
.station-panel.ed .tl-row::before{ content:""; position:absolute; left:6px; top:5px; width:10px; height:10px;
  border-radius:50%; background:#fff; box-shadow:0 0 0 2px var(--accent, #618D7C); }
.station-panel.ed .tl-row::after{ content:""; position:absolute; left:10px; top:18px; bottom:0; width:2px; background:#E4EAE4; }
.station-panel.ed .tl-row:last-child::after{ display:none; }
/* the pinned composer: the floating white card */
.ed-compose{ grid-row:7; margin:0 12px 12px !important; padding:10px 12px !important;
  background:#fff; border-radius:16px; box-shadow:0 8px 26px rgba(44,59,54,.14);
  position:relative; }
/* Round-97m: the composer is a pinned card over a scrolling list, and the two met with nothing
   between them — so a comment scrolling past looked like it had crashed into the card rather
   than passing behind it. This is the affordance every messaging app uses: an 18px fade in the
   panel's own paper, drawn immediately ABOVE the card, so content dissolves under it. It also
   tells you, without a scrollbar, that there is more above. Bleeds past the card's 12px side
   margins so the fade spans the full panel width. */
.ed-compose::before{
  content:""; position:absolute; left:-12px; right:-12px; bottom:100%; height:18px;
  background:linear-gradient(to top, var(--paper, #F3F4F1), rgba(243,244,241,0));
  pointer-events:none;
}
.ed-compose textarea{ width:100%; min-height:48px; border:0 !important; padding:4px 2px !important;
  background:none !important; font-family:var(--font); resize:none; }
.ed-compose textarea:focus{ outline:none; }
.ed-tools{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:2px; }
.ed-tools .emoji-btn{ border:0; background:none; font-size:1.2rem; cursor:pointer; padding:0; }
.ed-count{ color:var(--ink-soft, #55635C); font-size:.72rem; }
.ed-compose .compose-send{ position:static; border-radius:999px; padding:8px 20px; }
.ed-compose .cmt-replying{ margin-bottom:6px; }
html.in-app .station-panel.ed .ed-compose{ margin-bottom:calc(12px + var(--safe-bottom, 0px)) !important; }

/* ---- Round-10: emoji panel, role badges, comment edit ---- */
.emoji-panel{
  position:fixed; z-index:3200; display:grid; grid-template-columns:repeat(8, 1fr); gap:2px;
  width:264px; background:var(--paper, #FBFBF8); border:1.5px solid var(--track, #ADC3B9);
  border-radius:14px; padding:8px; box-shadow:0 14px 36px rgba(44,59,54,.28);
}
.emoji-panel button{
  border:0; background:none; cursor:pointer; font-size:1.15rem; padding:3px; border-radius:8px;
}
.emoji-panel button:hover{ background:var(--mintbg, #EAF1EC); }

.role-badge{ cursor:help; font-size:.85rem; }
.cmt-error{ color:#8e2a1f; font-size:.82rem; margin-top:6px; }

/* round-12b: custom from → to date range inside the pin-filters popover */
.pin-filters .tf-range{
  border-top:1px solid var(--track, #ADC3B9); margin-top:6px; padding-top:9px;
  display:flex; flex-direction:column; gap:7px;
}
.pin-filters .tf-lab{ display:flex; align-items:center; justify-content:space-between; gap:10px; font-size:.85rem; }
   icon and highlight color inside the native popup (color-scheme + accent-color) */
.pin-filters .tf-lab input{
  border:1.5px solid var(--track, #ADC3B9); border-radius:8px; padding:5px 7px;
  font-family:var(--font); font-size:.78rem; font-weight:600;
  background:var(--cream, #F3EFE7); color:var(--deep, #4F8577); cursor:pointer;
  color-scheme:light; accent-color:var(--accent, #618D7C);
}
.pin-filters .tf-lab input:focus{ outline:none; border-color:var(--accent, #618D7C); background:#fff; }
.pin-filters .tf-lab input::-webkit-calendar-picker-indicator{
  cursor:pointer; opacity:.9;
  /* tint the black glyph to the sage accent (#618D7C) */
  filter:invert(53%) sepia(11%) saturate(760%) hue-rotate(107deg) brightness(93%) contrast(87%);
}
.pin-filters .tf-lab input::-webkit-datetime-edit-fields-wrapper{ color:var(--deep, #4F8577); }
.pin-filters .tf-lab input::-webkit-datetime-edit-hour-field:focus,
.pin-filters .tf-lab input::-webkit-datetime-edit-minute-field:focus,
.pin-filters .tf-lab input::-webkit-datetime-edit-day-field:focus,
.pin-filters .tf-lab input::-webkit-datetime-edit-month-field:focus,
.pin-filters .tf-lab input::-webkit-datetime-edit-year-field:focus{
  background:var(--accent, #618D7C); color:#fff; border-radius:4px;
}
.pin-filters [data-range-clear]{
  align-self:flex-start; border:1.5px solid var(--track, #ADC3B9); border-radius:999px;
  background:none; color:var(--ink-soft, #55635C); font-family:var(--font); font-size:.74rem;
  font-weight:700; padding:4px 10px; cursor:pointer;
}
.pin-filters [data-range-clear]:hover{ border-color:var(--accent, #618D7C); color:var(--deep, #4F8577); }

/* round-11: pin-filter popover above the time bands */
.pin-filters{
  position:fixed; bottom:64px; left:50%; transform:translateX(-50%); z-index:960;
  background:var(--paper, #FBFBF8); border:1.5px solid var(--track, #ADC3B9); border-radius:14px;
  padding:12px 16px; box-shadow:0 12px 34px rgba(44,59,54,.28);
  display:flex; flex-direction:column; gap:7px; min-width:240px;
}
.pin-filters h4{ font-size:.72rem; letter-spacing:1.5px; text-transform:uppercase; color:var(--sage2, #8FA99B); }
.pin-filters label{ display:flex; align-items:center; gap:8px; font-size:.9rem; font-weight:600; cursor:pointer; }
.pin-filters label input{ width:auto; }
.pin-filters small{ color:var(--ink-soft, #55635C); font-size:.74rem; max-width:240px; }
@media (max-width: 720px){
  .pin-filters{ bottom:70px; left:50%; transform:translateX(-50%); max-width:calc(100vw - 20px); }
}
.cmt-edit{
  width:100%; margin:6px 0; padding:8px 10px; border:1.5px solid var(--accent, #618D7C);
  border-radius:10px; font-family:var(--font); font-size:.92rem; min-height:60px;
}

/* ---- Country tabs on About/Learn/Partners (round-9) ---- */
.country-tabs{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  background:var(--paper, #FBFBF8); border:1.5px solid var(--track, #ADC3B9);
  border-radius:14px; padding:10px 14px;
}
.ct-label{ font-size:.8rem; font-weight:800; letter-spacing:.6px; text-transform:uppercase; color:var(--sage2, #8FA99B); }
.ct-tab{
  padding:6px 14px; border-radius:999px; border:1.5px solid var(--track, #ADC3B9);
  font-size:.88rem; font-weight:700; color:var(--ink-soft, #55635C);
}
.ct-tab:hover{ border-color:var(--accent, #618D7C); text-decoration:none; }
.ct-tab.on{ background:var(--accent, #618D7C); border-color:var(--accent, #618D7C); color:#fff; }
.ct-note{ font-size:.78rem; color:var(--ink-soft, #55635C); flex-basis:100%; }

/* Round-83: pills on desktop, type-ahead on mobile.
   152 enabled countries render 152 pills. On a wide screen that is a useful glance at who is
   live; on a phone it is a wall of wrapped chips that pushes the actual page below several
   screens of navigation. The picker is the same control the admin scope switcher uses, so it
   is one thing to learn rather than two. */
.ct-pick{ display:none; }
@media (max-width: 720px){
  .ct-pick{ display:block; flex:1 1 100%; min-width:0; }
  .country-tabs .ct-tab{ display:none; }
  .country-tabs{ gap:6px; }
  /* Room to read a country name, and never wider than the screen. */
  .ct-pick .cpick-list{ width:100%; max-width:calc(100vw - 40px); }
}

/* rendered per-country markdown */
.md-body h1, .md-body h2, .md-body h3{ color:var(--ink, #2C3B36); margin:18px 0 8px; }
.md-body p{ margin:8px 0; color:#3d4c46; }
.md-body ul, .md-body ol{ margin:8px 0 8px 22px; }
.md-body a{ color:var(--deep, #4F8577); }
.md-body img{ border-radius:12px; }

/* ---- Virtualized-list loading skeleton (round-9) ---- */
.skeleton{
  background:linear-gradient(90deg, var(--mintbg, #EAF1EC) 25%, #f5f8f5 37%, var(--mintbg, #EAF1EC) 63%);
  background-size:400% 100%;
  animation:skeleton-shimmer 1.3s ease infinite;
  border-radius:12px; border:1px solid var(--track, #ADC3B9);
}
@keyframes skeleton-shimmer{ 0%{ background-position:100% 50%; } 100%{ background-position:0 50%; } }

/* ---- Round-142 (motion P0): skeleton primitives ----
   Pure CSS, because a placeholder has to exist in three places a Razor component cannot reach:
   the static prerender (before any circuit), JS-built DOM (stories, the incident panel) and
   Razor. The ON switch is aria-busy="true" on the REGION — the same attribute that tells a
   screen reader to hold its announcements — so a skeleton is painted only while its region
   says it is busy and disappears the moment the data lands, with nothing to call. The model is
   .map-skel, which ends itself when Leaflet takes the element. */
[aria-busy="true"] .skel{ position:relative; overflow:hidden; background:var(--mint, #E7EDE6); border-radius:8px; }
[aria-busy="true"] .skel::after{
  content:""; position:absolute; inset:0; transform:translateX(-100%);
  background:linear-gradient(100deg, transparent 20%, var(--skel-hi, rgba(255,255,255,.55)) 50%, transparent 80%);
  animation:aq-sweep var(--skel-period, 1.4s) ease-in-out infinite;
}
.skel-line{ height:.9em; width:var(--w, 100%); margin:.35em 0; }
.skel-line.t{ height:1.15em; }
.skel-circle{ width:var(--w, 40px); aspect-ratio:1; border-radius:50% !important; flex:0 0 auto; }
.skel-block{ width:var(--w, 100%); aspect-ratio:var(--ar, 16/10); }
.skel-row{ display:flex; gap:12px; align-items:flex-start; padding:10px 0; }
.skel-row > div{ flex:1; min-width:0; }
:not([aria-busy="true"]) > .skel, :not([aria-busy="true"]) > * .skel{ display:none; }
[aria-busy="true"] .skel{ display:block; }
[aria-busy="true"] .skel-row{ display:flex; }

/* Round-142 (P3): entry via @starting-style where the browser has it (Chrome 117+, Safari
   17.5+). These overlays are JS-owned, so a transition is safe; the one-shot keyframes above
   remain the fallback everywhere else, and both land on the same resting frame. */
@supports (transition-behavior: allow-discrete) {
  .gate-backdrop, .cs-backdrop{ transition:opacity var(--dur-2, 180ms) var(--ease-out, ease-out), display var(--dur-2, 180ms) allow-discrete; }
  @starting-style { .gate-backdrop, .cs-backdrop{ opacity:0; } }
  .cs-sheet{ animation:none; transition:transform var(--dur-3, 260ms) var(--ease-out, ease-out), display var(--dur-3, 260ms) allow-discrete; }
  @starting-style { .cs-sheet{ transform:translateY(100%); } }
}
/* Round-142 (P3): cross-document view transitions for the FULL loads (language switch, login,
   the data-enhance-nav=false links, a reconnect reload) — opt-in behind html.vt-on until the
   WebKit probe has measured it inside the WebView. Enhanced navigations are untouched. */
html.vt-on{ view-transition-name:root; }
@view-transition{ navigation:auto; }
html:not(.vt-on)::view-transition-old(root), html:not(.vt-on)::view-transition-new(root){ animation:none; }
/* Round-142 (P3): compositor hints only while the JS-owned panels are on the move. */
.station-panel.open, .asst-drawer.open{ will-change:transform; }

/* Round-142 (P2.5): feedback on TOUCH, not on release. Every native app answers a finger within
   a frame; eight :active rules in six thousand lines meant this one mostly did not. The press
   is a transform on :active — no class, no JS, so nothing for a Blazor patch to strip — and
   it is the same scale everywhere so the whole app has one hand-feel. Excluded: inputs, the
   map (Leaflet owns its pointer), and anything already disabled. */
.btn:active:not([disabled]), .pill-svc:active, .story-act:active, .sn2-acts button:active,
.cpage2-tab:active, .base-switch button:active, .stories-scope button:active, .nav a:active,
.sn2-c:active, .story-card .story-map:active, .asst-fab:active{
  transform:scale(.97); transition:transform var(--dur-1, 120ms) var(--ease-out, ease-out);
}
/* Round-142 (P2.5): the reaction burst — a ring that pops from the button once on the way to
   "on", spring-eased, under 400 ms, never blocking. JS-owned class (stories.js), one-shot. */
.story-act{ position:relative; }
.story-act.burst::before{
  content:""; position:absolute; inset:-6px; border-radius:999px; pointer-events:none;
  border:2px solid var(--svc, var(--accent, #618D7C));
  animation:actBurst var(--dur-4, 420ms) var(--ease-spring, ease-out) both;
}
@keyframes actBurst{ from{ transform:scale(.6); opacity:.9 } to{ transform:scale(1.5); opacity:0 } }

/* Round-142 (motion P0): THE busy button. ~45 buttons set disabled="@_busy" and two of them
   showed anything. The label stays in the DOM (no width jump, the accessible name survives) and
   goes transparent; a ring spins over it. `busy` is a class toggled BY BLAZOR, so this is an
   animation, never a transition — see the trap notes at .adm-exp / acSheetIn / asetPaneIn. */
.btn.busy, button.busy{ position:relative; color:transparent !important; pointer-events:none; }
.btn.busy > *, button.busy > *{ visibility:hidden; }
.btn.busy::after, button.busy::after{
  content:""; position:absolute; inset:0; margin:auto; width:16px; height:16px;
  border:2px solid rgba(255,255,255,.35); border-top-color:#fff; border-radius:50%;
  animation:geospin .8s linear infinite;
}
.btn-outline-dark.busy::after, .btn-outline.busy::after, .o.busy::after{ border-color:rgba(44,59,54,.25); border-top-color:var(--deep, #4F8577); }

/* Round-142 (motion P0): the band strip's loading state. map-engine has toggled
   #timeSwitch.loading since round-81 with no rule to match — a silent class. JS-owned, so a
   transition is safe; the sweep along the strip's bottom edge is the shared aq-sweep. */
.base-switch{ position:relative; }
.base-switch.loading{ cursor:progress; }
.base-switch.loading::after{
  content:""; position:absolute; left:8px; right:8px; bottom:0; height:2px; overflow:hidden;
  background:linear-gradient(90deg, transparent, var(--green, #1FA97A), transparent);
  transform:translateX(-100%); animation:aq-sweep 1.1s ease-in-out infinite; border-radius:2px;
}

/* ---- Private messaging (§60) ---- */
/* Round-94: design 02 "messenger warm" — WhatsApp-familiar messages. White list with large
   avatars and green time stamps; the thread sits on a patterned warm wallpaper with flat
   shadowed bubbles (white theirs, pale-green mine). Same markup and handlers as before. */
/* Full bleed below the nav: the page IS the messenger. --header-h already includes the
   sign-up bar when it shows; dvh keeps mobile URL-bar resizing honest. */
.msg-page{ padding:var(--header-h) 0 0; }   /* clear the fixed header, nothing else */
body:has(.msg-page) .site-footer, body:has(.msg-page) .cov-strip{ display:none; }
.msg-shell{
  display:grid; grid-template-columns:340px 1fr; gap:0; align-items:stretch;
  height:calc(var(--app-h, 100vh) - var(--header-h)); height:calc(var(--app-h, 100dvh) - var(--header-h)); border:0; border-radius:0; overflow:hidden;
  background:#fff;
  /* the mockup's frame: centred on wide screens, the page paper showing at the sides */
  max-width:1180px; margin:0 auto;
}
/* no side borders on the shell — the white page does the framing */
.msg-head{ padding:16px 16px 0; }
.msg-head h1{ margin:0; font-size:1.35rem; }
.msg-head .sub{ font-size:.8rem; color:var(--ink-soft, #55635C); margin-top:1px; }
.msg-search{ padding:12px 12px 8px; }
.msg-search input{
  width:100%; border:0; border-radius:10px; padding:9px 14px; font-family:var(--font);
  font-size:.9rem; background:var(--bg, #F3F4F1); color:var(--ink, #2C3B36);
}
.msg-search input:focus{ outline:2px solid var(--accent, #618D7C); }
.msg-list{
  background:#fff; border-right:1px solid #F0EEE8; border-radius:0;
  overflow-y:auto;
}
.msg-row{
  display:flex; align-items:center; gap:12px; padding:12px 14px;
  border-bottom:1px solid #F0EEE8; color:var(--ink, #2C3B36);
}
.msg-row .cmt-av, .msg-row .cmt-av-fallback{ width:50px; height:50px; flex:0 0 50px; font-size:1.05rem; }
.msg-row:hover{ background:#F7F8F5; text-decoration:none; }
.msg-row.sel{ background:#F2F6F2; }
.msg-meta{ flex:1; min-width:0; display:flex; flex-direction:column; gap:1px; }
.msg-meta small{ color:var(--ink-soft, #55635C); font-size:.84rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.msg-row.unread .msg-meta b, .msg-row.unread .msg-meta small{ color:var(--ink, #2C3B36); font-weight:700; }
.msg-side{ display:flex; flex-direction:column; align-items:flex-end; gap:5px; flex:0 0 auto; }
.msg-when{ font-size:.72rem; font-weight:700; color:var(--accent, #618D7C); }
.msg-dot{ width:12px; height:12px; border-radius:50%; background:#C0392B; flex:0 0 auto; }

.msg-thread{
  border:0; border-radius:0; display:flex; flex-direction:column; min-height:0;
  background:var(--bg, #F3F4F1) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="140" height="140" opacity="0.06"><path d="M20 20l14 0M60 50a10 10 0 1 0 20 0M100 100l12 12M30 110a8 8 0 1 1 16 0" stroke="%232C3B36" fill="none" stroke-width="3"/></svg>');
}
.msg-thread-head{
  display:flex; align-items:center; gap:10px; padding:10px 16px;
  border-bottom:1px solid #F0EEE8; background:#fff;
  position:sticky; top:0; z-index:3;   /* stays put however the page moves on phones */
}
/* a long counterpart name never wraps — one line, ellipsised */
.msg-thread-head > a{ flex:1 1 auto; min-width:0; }
.msg-head-id{ min-width:0; flex:1; }
.msg-head-id b{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:block; }
.msg-back{ display:none; padding:2px 8px 2px 0; color:var(--deep, #4F8577); }
.msg-back svg{ width:36px; height:36px; display:block; }  /* thin 1.6 stroke chevron */
/* the mockup's icobtn: 40px circle, hairline #E3E6E1 border, the ⋮ glyph in ink */
.msg-kebab{
  width:40px; height:40px; border-radius:50%; border:1.5px solid #E3E6E1; background:#fff;
  display:inline-flex; align-items:center; justify-content:center; color:var(--ink, #2C3B36); cursor:pointer;
  font-size:1.25rem; font-weight:700; line-height:1;
}
.msg-kebab:hover{ background:var(--mintbg, #EAF1EC); }
.msg-menu-wrap{ position:relative; display:inline-flex; }
.msg-menu-backdrop{ position:fixed; inset:0; background:transparent; border:0; z-index:1290; cursor:default; }
.msg-menu{
  position:absolute; top:calc(100% + 6px); right:0; z-index:1300; min-width:180px;
  background:#fff; border:1px solid var(--track, #ADC3B9); border-radius:12px;
  box-shadow:0 14px 36px rgba(44,59,54,.22); padding:6px; display:flex; flex-direction:column;
}
.msg-menu > *{
  display:block; text-align:left; border:0; background:transparent; padding:9px 12px;
  border-radius:8px; font-family:var(--font); font-size:.9rem; color:var(--ink, #2C3B36); cursor:pointer;
}
.msg-menu > *:hover{ background:var(--mintbg, #EAF1EC); text-decoration:none; }
.msg-tag{
  display:block; font-size:.72rem; font-weight:800; line-height:1.25;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.tag-agency{ color:#C0392B; }
.tag-platform{ color:var(--deep, #4F8577); }
.tag-community{ color:#6B5B95; }
.msg-head-id{ display:flex; flex-direction:column; min-width:0; }
/* the chat header's avatar matches the sidebar rows */
.msg-thread-head .cmt-av, .msg-thread-head .cmt-av-fallback{ width:50px; height:50px; flex:0 0 50px; font-size:1.05rem; border-radius:50%; object-fit:cover; }
.msg-head-id .msg-tag{ font-size:.7rem; }
/* min-height:0 is load-bearing: a flex item's default minimum is its CONTENT height, so
   without it the message list refuses to shrink when the keyboard shrinks the shell and
   the composer is pushed out through overflow:hidden — "the textarea doesn't show". The
   bug was invisible until the keyboard bridge made the shell actually shrink in-app. */
.msg-scroll{ flex:1; min-height:0; overflow-y:auto; padding:18px; display:flex; flex-direction:column; gap:8px; }
.msg-bubble{
  max-width:75%; background:#fff; border:none; box-shadow:0 1px 1px rgba(0,0,0,.12);
  border-radius:4px 10px 10px 10px; padding:8px 12px; align-self:flex-start;
}
.msg-bubble.mine{
  background:#DFF0DC; color:var(--ink, #2C3B36); border:none;
  border-radius:10px 4px 10px 10px; align-self:flex-end;
}
.msg-bubble p{ font-size:.95rem; white-space:pre-wrap; word-break:break-word; }
.msg-bubble small{ display:block; margin-top:3px; font-size:.68rem; color:#5B7A5B; text-align:right; }
.msg-compose{ display:flex; gap:8px; padding:12px 14px; border-top:1px solid #F0EEE8; background:#fff; }
.msg-compose textarea{
  flex:1; min-width:0; resize:none; border:1.5px solid #F0EEE8; border-radius:14px;
  padding:9px 12px; font-family:var(--font); font-size:.95rem; background:var(--bg, #F3F4F1);
}
.msg-compose textarea:focus{ outline:none; border-color:var(--accent, #618D7C); background:#fff; }
.msg-thread .compose{ background:#fff; }
/* the messenger owns the viewport — the DOCUMENT never scrolls here, so the fixed nav and the
   thread head cannot be scrolled away on phones. Panes scroll internally. */
/* Round-108: two faults, one symptom — with the keyboard up you could still swipe the whole
   screen and drag blank space up under the composer.

   1. THE HEIGHT CAME FROM dvh. Neither 100vh nor 100dvh shrinks when a soft keyboard shrinks
      the viewport, and round-94m's note that "100dvh shrinks" when the WKWebView is resized
      does not hold in practice — measured on an iPhone with the keys up, the body stayed
      taller than the area showing it, and a document taller than its viewport scrolls. --vvh
      is read from window.visualViewport and is ground truth on both paths (the native bridge
      publishes it in applyKb, visualViewport publishes it in apply). It is the same rule
      already followed by every sheet on the site; the messenger was never converted.
   2. overflow:hidden WAS ON body ALONE. On iOS the scroll propagates straight to the
      documentElement, so a body-only lock is advisory. <html> has to carry it as well.

   overscroll-behavior:none stops the rubber-band drag that survives both of the above.

   The fallback is 100vh, not 100dvh: dvh landed in Chrome 108 and the G6's WebView is 101,
   where `var(--vvh, 100dvh)` is a PARSE ERROR that drops the whole declaration — round-96d
   found that the hard way when the shell lost its height entirely and the composer fell below
   the fold. The fallback should almost never be reached (site.js seeds --vvh on load and after
   every enhanced navigation), but a fallback that breaks the engine it exists for is no
   fallback. overflow and overscroll-behavior are separate declarations and survive either way. */
html.pg-msg, html:has(.msg-page){
  height:var(--vvh, var(--app-h, 100vh)); overflow:hidden; overscroll-behavior:none; }
html.pg-msg body{ height:var(--vvh, var(--app-h, 100vh)); overflow:hidden; overscroll-behavior:none;
  background:var(--cream, #F1EBE4); }   /* :has twin (pre-105); round-142f: cream, like every page */
body:has(.msg-page){ height:var(--vvh, var(--app-h, 100vh)); overflow:hidden; overscroll-behavior:none;
  background:var(--cream, #F1EBE4); }
/* Round-94d: nothing ever scrolls under the header on the messenger (the shell starts below
   it), so its backdrop blur has nothing to blur — it was pure GPU cost, re-rendered on every
   glued frame while the keyboard moved. Solid paper instead. */
html.pg-msg .site-header{ backdrop-filter:none; -webkit-backdrop-filter:none; background:#fff; }
body:has(.msg-page) .site-header{ backdrop-filter:none; -webkit-backdrop-filter:none; background:#fff; }  /* the PAGE around the centred shell is white; the thread keeps its warm wallpaper */
html:has(.msg-page){ background:var(--cream, #F1EBE4); }   /* round-142f: cream, like every page */
.msg-empty{ flex:1; display:flex; align-items:center; justify-content:center; }
@media (max-width: 820px){
  .msg-shell{ grid-template-columns:1fr; }
  /* Round-94d: THE iOS FOCUS-ZOOM RULE, SITE-WIDE. iOS zooms the page toward any focused
   field whose font is under 16px, and that zoom is also what drags the fixed nav off-screen.
   Round-94 fixed it for ONE composer (.msg-thread .compose) and the audit then found 13 more
   sub-16px controls — Ada's drawer (.compose at .92rem), comment boxes, story compose, every
   search input. Chasing them selector by selector loses to the next new control, so: on any
   touch/small screen, every text-entry control is at least 16px. Desktop keeps its compact
   forms. Checkboxes/radios/ranges/files never summon the keyboard and keep their looks. */
@media (max-width:900px), (pointer:coarse){
  textarea, select,
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="button"]):not([type="submit"]){
    font-size:16px !important;
  }
}

/* iOS Safari zooms any focused field under 16px/* iOS Safari zooms any focused field under 16px — the one cause of "the page enlarges when
     I tap the message box". 16px exactly. The thread's composer is the GENERIC .compose (the
     first fix targeted .msg-compose, which this page never uses — hence the zoom survived). */
  .msg-thread .compose textarea, .msg-search input{ font-size:16px !important; }
  /* pin the shell itself so iOS keyboard panning has no document to drag around. Its height
     follows the VISUAL viewport (--vvh, set from window.visualViewport in site.js): when the
     keyboard opens the shell shrinks with it, keeping the composer above the keys. */
  .msg-page .msg-shell{ position:fixed; left:0; right:0; top:var(--header-h); bottom:auto; max-width:none;
    height:calc(var(--vvh, var(--app-h, 100dvh)) - var(--header-h)); z-index:2600; }
  /* the chat owns the phone screen — Ada's FAB (z 2500) stays behind it, never over the
     composer or send button */
  html.pg-msg .asst-fab{ z-index:1150; }
  body:has(.msg-page) .asst-fab{ z-index:1150; }
  /* Round-94d: THE SHAKE. site.js glues the FAB to the visual viewport by writing its
     transform every frame the keyboard moves — and the base rule transitions transform over
     .15s, so each write started an animation toward a target that had already moved. An
     element permanently chasing a moving target with a 150ms lag IS the wobble. No transform
     transition where the glue drives it. */
  body:has(.msg-page) .asst-fab{ transition:box-shadow .15s; }
  .msg-shell.thread-open .msg-list{ display:none; }
  .msg-shell:not(.thread-open) .msg-thread{ display:none; }  /* full-bleed: the empty pane is noise on a phone */
  .msg-back{ display:inline-flex; }
}
/* Round-94: the in-app shell must follow the VISUAL viewport too — this exact rule outranked
   the keyboard fix (html.in-app adds specificity) and kept the composer under the keys in the
   WebView. The home-bar clearance moves to the composer's padding, where an open keyboard
   simply renders it harmless. */
html.in-app .msg-shell{ height:calc(var(--vvh, var(--app-h, 100dvh)) - var(--header-h)); }
html.in-app .msg-thread .compose{ padding-bottom:calc(12px + var(--safe-bottom, 0px)); }

/* §58 "not live here yet" banner */
.geo-banner{
  position:fixed; top:calc(var(--header-h) + 8px); left:50%; transform:translateX(-50%);
  z-index:1150; display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  max-width:min(640px, calc(100vw - 20px));
  background:rgba(243,244,241,.97); border:1.5px solid var(--track, #ADC3B9); border-radius:14px;
  padding:10px 14px; box-shadow:0 8px 26px rgba(44,59,54,.25); font-size:.92rem;
}
.geo-banner .geo-close{
  background:none; border:0; cursor:pointer; color:var(--ink-soft, #55635C);
  font-size:1rem; padding:4px 6px; border-radius:6px;
}
.geo-banner .geo-close:hover{ background:rgba(97,141,124,.12); }
@media (max-width: 1000px){
  .geo-banner{ top:calc(var(--header-h) + 52px); } /* below the chips row */
}

/* SafeNet subject pins: profile photo (or emoji) in a ringed circle */
.sn-pin{
  width:44px; height:44px; border-radius:50%; overflow:hidden;
  background:#F3F4F1; border:3px solid var(--deep, #4F8577);
  box-shadow:0 3px 12px rgba(44,59,54,.45);
  display:flex; align-items:center; justify-content:center; font-size:22px;
}
.sn-pin img{ width:100%; height:100%; object-fit:cover; }
.sn-pin.asset{ border-color:var(--tan, #BC8A52); }
.sn-pin.danger{ border-color:#C0392B; animation:sevpulse 1.6s ease-in-out infinite; }
/* Round-85: freshness lives in the RING, extending this established pin rather than a second
   competing .sn-pin block (which is what the first attempt did — p4.css loads later and simply
   overrode it). A glance at the map now answers the same question the list answers in words. */
.sn-pin[style*="--ring"]{ border-color:var(--ring); }
/* Paused says so in the pin itself, not only in a popup nobody opens. */
.sn-paused{ filter:grayscale(1); border-style:dashed !important; }
.map-card.min{ width:auto; right:auto; padding:10px 12px; } /* #weatherCard keeps its right anchor (id wins) */
.map-card.min .card-body{ display:none; }
.map-card.min .card-head h2, .map-card.min .card-head h3, .map-card.min .card-caret{ display:none; }
.map-card.min .card-head{ gap:0; }
/* round-15c/15e: minimized chips in the right column are 44px circles (like the ✨
   button), and expanding/collapsing MORPHS between chip and card — width, height and
   corner radius animate while the content fades in a beat later. */
.right-stack .map-card.collapsible{
  width:250px; max-height:480px; overflow:hidden; flex:0 0 auto;
  transition:width .32s cubic-bezier(.2,.8,.25,1), max-height .32s cubic-bezier(.2,.8,.25,1),
             border-radius .32s ease, padding .32s ease;
}
.right-stack .map-card.collapsible .card-body{
  display:block; opacity:1; transition:opacity .2s ease .12s;
}
/* Round 225 (owner, desktop: "the layers panel when open doesn't scroll"): the open card is capped
   at 480px with overflow hidden (the chip-to-card morph needs it), and the Layers card is taller
   than that, so its last rows could not be reached. The card becomes a column and its BODY scrolls
   inside the cap; the head stays put. */
@media (min-width:721px){
  .right-stack .map-card.collapsible:not(.min){ display:flex; flex-direction:column; }
  .right-stack .map-card.collapsible:not(.min) .card-body{
    flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain;
  }
}
.right-stack .map-card.collapsible .card-head h2,
.right-stack .map-card.collapsible .card-head h3,
.right-stack .map-card.collapsible .card-caret{
  opacity:1; white-space:nowrap; transition:opacity .18s ease .12s;
}
.right-stack .map-card.min{
  width:44px; max-height:44px; padding:12px; border-radius:50%;
}
.right-stack .map-card.min .card-body{ display:block; opacity:0; transition-delay:0s; }
.right-stack .map-card.min .card-head h2,
.right-stack .map-card.min .card-head h3,
.right-stack .map-card.min .card-caret{ display:inline-flex; opacity:0; transition-delay:0s; }
.right-stack .map-card.min .card-head{ gap:8px; }
.right-stack .map-card.min .card-ico svg{ width:20px; height:20px; }

/* ---- Map type inside the grid menu (populated by map-engine on the live map) ---- */
.grid-menu .grid-base{ display:none; grid-column:1/-1; border-top:1px solid var(--track, #ADC3B9); margin-top:6px; padding-top:10px; }
.grid-menu .grid-base.has-base{ display:block; }   /* :has twin (pre-105 engines) */
.grid-menu .grid-base:has(#baseSwitch:not(:empty)){ display:block; }
.grid-base-title{
  font-size:.68rem; font-weight:800; letter-spacing:1.5px; text-transform:uppercase;
  color:var(--sage2, #8FA99B); margin-bottom:6px;
}
.grid-menu .base-switch{
  position:static; transform:none; display:flex; box-shadow:none;
  border:1px solid var(--track, #ADC3B9); border-radius:999px; overflow:hidden;
}
.grid-menu .base-switch button{ flex:1; background:#FBFBF8; color:var(--ink-soft, #55635C); padding:8px 4px; font-size:.78rem; }
.grid-menu .base-switch button + button{ border-left:1px solid var(--track, #ADC3B9); }
.grid-menu .base-switch button.on{ background:var(--accent, #618D7C); color:#fff; }

/* chips row drops below the bar once the header gets crowded.

   Round-83: raised from 1000px to 1200px. The desktop row sits BETWEEN the search box and the
   right-hand card column — left:calc(400px + 42px), right:370px — so its width is whatever is
   left over. On an iPad Pro in portrait (1024px) that leftover is 1024 - 442 - 370 = 212px: the
   service chips were squeezed to almost nothing, and the "N layers hidden" button (which lives
   inside .svc-wrap) was pushed out of the row and up against "Report an issue" in the header.
   Both reported symptoms are the same 212px.

   1200 is where the leftover first exceeds ~390px, which is the point the row can show several
   chips and still breathe. Below that, the full-width row under the header is simply better.

   Round-145g (owner's report): 1520, not 1200. With the honest 440px reserve the leftover at
   1200–1500px is 300–560px, and six labelled chips need ~640 — the row scrolled and its last
   chip sat clipped flush against "Report an issue", which reads as an overlap. 1520 is where
   the leftover first holds the whole labelled row; under that the full-width row is used.
   Round-146g (owner's snapshot at ~1520px, signed in): 1600 — signed in the row has a SafeNet
   chip too and the cluster is wider (Report + bell + messages + avatar + grid), so the honest
   reserve is 500px and the labelled row first fits at ~1600. */
@media (max-width: 1600px){
  .svc-wrap{ top:calc(var(--header-h) + 6px); left:8px; right:8px; }
  .net-stats{ top:calc(var(--header-h) + 56px); }
  /* Round-94 (revised twice): zoom-only container lives bottom-left on every width now —
     see style.css .map-actions. No per-breakpoint override needed. */
}

/* ---- RTL (Arabic) corrections: flip physical positioning flexbox/logic can't ---- */
/* Leaflet's overlay geometry ASSUMES LTR: .leaflet-tooltip/.leaflet-popup are position:absolute
   with no left/top and are placed purely by transform, so their origin is the element's static
   position. Under dir=rtl that static position resolves from the RIGHT edge, and every tooltip
   lands horizontally offset from its pin. Leaflet's own CSS never sets `direction`, so it
   inherits rtl from <html>. Keep the map's internals LTR and flip only the text back. */
html[dir="rtl"] .leaflet-container{ direction:ltr; }
html[dir="rtl"] .leaflet-tooltip,
html[dir="rtl"] .leaflet-popup-content{ direction:rtl; text-align:right; }
/* nav: push the icon cluster to the visual start (which is the LEFT in RTL) */
html[dir="rtl"] .nav-right{ margin-left:0; margin-right:auto; }
html[dir="rtl"] .card-caret{ margin-left:0; margin-right:auto; }
/* live-map service-chip row sits to the LEFT of the (now right-aligned) search box */
html[dir="rtl"] .svc-wrap{
  left:440px; right:calc(var(--msearch-w, 400px) + 42px);   /* round-137: honest cluster reserve */
}
html[dir="rtl"] .svc-pills{ justify-content:flex-start; }
/* overflow arrows: mirror both the gaps and the glyphs */
html[dir="rtl"] .pill-arrow.left{ margin-right:0; margin-left:6px; }
html[dir="rtl"] .pill-arrow.right{ margin-left:0; margin-right:6px; }
html[dir="rtl"] .pill-arrow.left svg{ transform:rotate(180deg); }
html[dir="rtl"] .pill-arrow.right svg{ transform:none; }
/* right-hand map card stack + assistant FAB move to the LEFT edge */
html[dir="rtl"] .right-stack{ right:auto; left:14px; align-items:flex-start; padding-left:0; padding-right:6px; }
html[dir="rtl"] body:has(#map) .asst-fab{ right:auto; left:16px; }
html[dir="rtl"] .msearch{ padding:5px 10px 5px 8px; }
/* omnibox result dropdown + search text align to the right */
html[dir="rtl"] .omni-results{ text-align:right; }
@media (max-width: 720px){
  html[dir="rtl"] .svc-wrap{ left:8px; right:8px; }
}

/* build r14b-2 */

/* OAuth verification / branding: app name + privacy/terms, styled like a Leaflet/OSM/Carto
   attribution bar and flush to the bottom-left corner of the map (no gap to the edges). */
.map-legal{
  position:fixed; left:0; bottom:0; margin:0; z-index:850;
  display:flex; align-items:center; gap:7px;
  padding:2px 8px; background:rgba(251,251,248,.82);
  border-radius:0 5px 0 0; font-size:11px; line-height:1.5; color:#3a4a44;
}
.map-legal .ml-brand{ font-weight:700; color:var(--deep, #4F8577); }
.map-legal a{ color:#3a4a44; text-decoration:none; }
.map-legal a:hover{ text-decoration:underline; }

/* Service-coloured cluster pill: each per-service cluster shows its own colour + count, so an area
   with several services reads as a group of differently-coloured count pills. */
.svc-cluster{
  display:flex; align-items:center; justify-content:center;
  min-width:28px; height:28px; padding:0 7px; border-radius:999px;
  color:#F3F4F1; font-weight:800; font-size:.82rem; font-family:var(--font);
  box-shadow:0 3px 10px rgba(44,59,54,.4); border:2px solid rgba(255,255,255,.65);
}
.svc-cluster.unv{ background:#FBFBF8; border-style:dashed; box-shadow:0 3px 10px rgba(44,59,54,.25); }

/* Round-38: Ada map-layer chips — bottom-left above the legal strip; click toggles a layer */
#adaLayers{
  position:fixed; left:8px; bottom:32px; z-index:860;
  display:flex; flex-wrap:wrap; gap:6px; max-width:min(60vw, 420px);
}
.ada-chip{
  border:1.5px solid var(--accent, #618D7C); border-radius:999px; cursor:pointer;
  background:rgba(251,251,248,.94); color:var(--deep, #4F8577);
  font-family:var(--font); font-weight:700; font-size:.72rem; padding:4px 10px;
  box-shadow:0 3px 10px rgba(44,59,54,.18);
  max-width:220px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.ada-chip:not(.on){ opacity:.45; border-style:dashed; }
/* Round 225: an Ada chip whose layer is on the map reads as pressed; tap again to take it off */
a.chip[data-ada-map][data-layer="on"]{ background:var(--deep, #4F8577); color:#fff; border-color:var(--deep, #4F8577); }
a.chip[data-ada-map][data-layer="on"] svg{ stroke:#fff; }
a.chip[data-ada-map][data-layer="off"]{ opacity:.7; border-style:dashed; }
/* Round 225: wide and short views — top left, under the service pills (map-engine _placeAdaChips sets top) */
#adaLayers.ada-top:not(#_){ bottom:auto; left:12px; right:auto; justify-content:flex-start; max-width:min(70vw, 640px); }

/* Round-38: tighter Ada toolbar chips + theme-coloured icons */
.asst-toolbar .chip{ padding:4px 10px; display:inline-flex; align-items:center; gap:6px; }
.asst-toolbar .chip svg{ width:14px; height:14px; stroke:var(--deep, #4F8577); flex:0 0 auto; }
.asst-toolbar .chip.sel svg{ stroke:#fff; }

/* Round-91: the community's own geofence IS the hero background.
   It used to sit in a card beside the intro, which made the page's most distinctive thing — the
   actual shape of the place — compete with the text for a column. Behind the words it does the
   job a hero image does, and it is the real boundary rather than a stock photograph.

   The map is inert: aquaBoundary builds it with every interaction handler off, and pointer-events
   are dropped so it cannot swallow a tap meant for a button underneath the cursor. */
.cpage-hero{ display:grid; gap:24px; align-items:center; position:relative; z-index:1; }
.cpage-boundary{
  position:absolute; inset:0; width:100%; height:100%; z-index:0;
  pointer-events:none; opacity:.55; filter:saturate(.75);
}
/* Text over a map needs a floor under it. The veil is the hero's own gradient at high alpha down
   the left, fading right, so the words keep their contrast while the shape stays legible. */
.cpage-veil{
  position:absolute; inset:0; z-index:0; pointer-events:none;
  /* Round-91b/c: lightened 30% twice over — .94/.86/.55/.42 → .66/.60/.39/.29 → .46/.42/.27/.20. The contrast
     the text lost is given back by a shadow on the text itself rather than by hiding the map —
     a veil dark enough to guarantee legibility everywhere had made the map decorative. */
  background:
    linear-gradient(100deg, rgba(44,59,54,.46) 0%, rgba(44,59,54,.42) 38%, rgba(44,59,54,.27) 70%, rgba(44,59,54,.20) 100%),
    radial-gradient(120% 80% at 15% 40%, rgba(44,59,54,.27), transparent 70%);
}
@media (max-width: 899px){
  /* On a phone the text fills the width, so the veil has to be flat rather than directional. */
  .cpage-veil{ background:linear-gradient(180deg, rgba(44,59,54,.43), rgba(44,59,54,.46)); }
  .cpage-boundary{ opacity:.45; }
}

/* The shadow does the work the veil used to. Two layers: a tight one for edge definition against
   a light tile, and a wide soft one that darkens the area immediately around the glyphs, which is
   what keeps text readable over a busy map rather than over a flat colour. */
/* .cset-hero is the settings header: the same layering and shadows, but NOT .cpage-hero's grid,
   which would space a stack of four short lines out like columns. */
.cset-hero{ position:relative; z-index:1; }
.cpage-hero h1, .cpage-hero .ctype, .cpage-hero .lead, .cpage-hero .kicker,
.cset-hero h1, .cset-hero .lead, .cset-hero .kicker{
  text-shadow:0 1px 2px rgba(20,28,25,.85), 0 2px 10px rgba(20,28,25,.75), 0 4px 22px rgba(20,28,25,.55);
}
/* Buttons carry their own background or border, so a shadow on their label only muddies it. */
.cpage-hero .btn, .cset-hero .btn{ text-shadow:none; }

/* Round-91: the type line under the name was var(--sage2) on the dark hero — near enough to the
   background to be unreadable. Cream is the hero's own light tone. */
.cpage-hero .ctype{ color:var(--cream, #F1EBE4) !important; opacity:.92; }

/* Round-91: the safety score, in a card of its own above the counts. It is the one number on the
   page that is a judgement rather than a tally, and the factors under it are what makes it
   actionable rather than a rating. */
.cpage-score{
  display:flex; flex-wrap:wrap; align-items:center; gap:14px 22px;
  background:var(--paper, #F3F4F1); border-radius:var(--radius, 14px);
  border-left:6px solid var(--score, #4F8577); padding:16px 20px; margin-bottom:16px;
  box-shadow:var(--shadow-sm, 0 6px 18px rgba(44,59,54,.08));
}
.cpage-score .n{ font-size:2.1rem; font-weight:800; line-height:1; color:var(--score, #4F8577); }
.cpage-score .band{ font-weight:800; color:var(--score, #4F8577); letter-spacing:.3px; }
.cpage-score .lbl{ font-size:.85rem; color:var(--ink-soft, #5A6B63); }
.cpage-score .why{ flex:1 1 260px; font-size:.85rem; color:var(--ink-soft, #5A6B63); margin:0; }
/* Round-39b: community-page badges read better on the hero gradient with a solid white base */
.cpage-hero .pill{ background:#fff; }

/* story action icons follow the theme (silhouette technique) instead of full-colour emoji */
.story-src{ display:block; font-size:.8rem; color:var(--ink-soft, #55635C); margin-top:6px; }
.story-act .ico-t{ text-shadow:0 0 0 var(--deep, #4F8577); }
/* round-71f: selected buttons have the ACCENT background — an accent silhouette vanished
   into it (invisible like/dislike icons); white matches the button's text (round-57 rule) */
.story-act.on .ico-t{ text-shadow:0 0 0 #fff; }
/* round-71d: outline SVG glyphs inside action/share buttons follow the button colour */
.ico-svg{ width:15px; height:15px; vertical-align:-2px; display:inline-block; }

/* ── round-80: country type-ahead picker ────────────────────────────────────
   Opening the platform worldwide made every country control a 142-item list.
   Reuses the form-field look so it sits naturally beside other inputs. */
.cpick { position: relative; }
.cpick-input { width: 100%; }
.cpick-list {
  position: absolute; z-index: 60; left: 0; right: 0; top: calc(100% + 4px);
  margin: 0; padding: 4px; list-style: none;
  max-height: 280px; overflow-y: auto;
  background: var(--surface, #fff); border: 1px solid var(--track);
  border-radius: 10px; box-shadow: 0 10px 28px rgba(0,0,0,.18);
}
.cpick-opt {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 10px; border-radius: 7px; cursor: pointer;
}
.cpick-opt small { color: var(--ink-soft); font-size: .78rem; }
.cpick-opt:hover, .cpick-opt.on { background: var(--track); }
.cpick-off {
  margin-left: auto; font-size: .72rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-soft);
}
.cpick-empty { padding: 10px; color: var(--ink-soft); font-size: .88rem; }

/* ── round-80: no focus ring on headings after navigation ────────────────────
   Blazor's enhanced navigation moves focus to the page heading so screen readers
   announce the new page. That is correct behaviour, but the browser then paints a
   :focus-visible ring on an element the user never tabbed to, so every page load
   appeared to start with an outlined H1. Suppress it ONLY for programmatically
   focused, non-interactive targets — real controls keep their focus ring. */
h1:focus, h1:focus-visible,
[tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none; box-shadow: none; }

/* ── round-80: coverage strip under the header ──────────────────────────────
   One quiet line, not a shouting banner: it must be readable without competing
   with the emergency guidance that appears at the point of reporting. */
.cov-strip{
  display:flex; align-items:center; gap:10px; justify-content:center;
  padding:7px 14px; font-size:.86rem; line-height:1.35;
  background:var(--tan, #E8D9BE); color:#FFFFFF;
  border-bottom:1px solid rgba(0,0,0,.08);
}
.cov-strip a{ color:inherit; font-weight:700; text-decoration:underline; }
.cov-close{
  margin-left:4px; background:none; border:0; cursor:pointer;
  font-size:.95rem; line-height:1; color:inherit; opacity:.6; padding:2px 4px;
}
.cov-close:hover{ opacity:1; }
html.cov-strip-off .cov-strip{ display:none; }

/* Round-83: the strip was rendering BEHIND the fixed header.
   MainLayout puts it between </header> and @Body, so it is an ordinary in-flow block starting at
   y=0 — but the header is position:fixed and `.page` compensates for that with its own
   padding-top. The strip therefore sat underneath the header, invisible, while its height became
   dead space between the nav and the page heading. Measured: strip top 0, entirely covered,
   64px of blank below the header. It only showed signed IN because the strip resolves the
   country from the account — signed out, GeoIP usually cannot, so it never rendered.

   Fix: push the strip below the fixed header and let it provide the offset `.page` was providing,
   instead of both reserving the same space. Verified in both states — signed out (header 157)
   and signed in (header 64) — strip fully visible, zero gap, no margin collapse.

   :has() so the page only drops its own reservation on the pages that actually have a strip;
   where it is absent nothing changes, and a browser without :has() keeps today's behaviour
   rather than losing the header offset entirely. */
.cov-strip{ margin-top:var(--header-h); }
body:has(.cov-strip) main.page{ padding-top:0; }

/* Round 224 (owner, on the Android app): the strip was in the page but hidden under the report
   form, which is fixed and owns the screen — on the one page it matters most. It sits at the
   form's top edge (below the status bar) and the form starts under it, as it already moves for the
   offline toast; site.js measures the strip as --cov-h. (224b: the strip is drawn on Report only.) */
html.pg-rpt .cov-strip, html:has(.rpt-full) .cov-strip{
  position:fixed; left:0; right:0; top:calc(var(--safe-top, 0px) + var(--toast-room, 0px)); margin-top:0; z-index:901;
}
html.pg-rpt .rpt-full:not(#_), html:has(.rpt-full) .rpt-full:not(#_){
  top:calc(var(--safe-top, 0px) + var(--toast-room, 0px) + var(--cov-h, 0px));
  height:calc(var(--app-h, 100vh) - var(--safe-top, 0px) - var(--toast-room, 0px) - var(--cov-h, 0px));
  height:calc(var(--vvh, var(--app-h, 100dvh)) - var(--safe-top, 0px) - var(--toast-room, 0px) - var(--cov-h, 0px));
}

/* ── round-80: admin rail + hub icons follow the theme ───────────────────────
   Emoji rendered in the OS's own palette, so the rail never matched the product
   and looked pasted in. Outline icons stroke with currentColor, so they inherit
   the link colour, the hover state and the active state with no extra rules. */
.adm-ico{ display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; }
.adm-ico svg{ width:18px; height:18px; display:block; }
.adm-sidebar .adm-link{ color:var(--ink-soft, #5A6B63); }
.adm-sidebar .adm-link .adm-ico{ color:var(--accent, #618D7C); transition:color .15s; }
.adm-sidebar .adm-link:hover{ color:var(--ink, #23312C); }
.adm-sidebar .adm-link:hover .adm-ico,
.adm-sidebar .adm-link.on .adm-ico{ color:var(--accent-strong, #4F8577); }
/* Round-82: the active rail item sits on a filled accent pill, so --ink (near-black) on it was
   the low-contrast pairing. White is the readable one, and the icon has to come with it —
   .adm-link.on .adm-ico above sets its own colour and would otherwise stay green on green. */
.adm-sidebar .adm-link.on{ color:#fff; font-weight:700; }
.adm-sidebar .adm-link.on .adm-ico{ color:#fff; }

.adm-card-ico{ display:inline-flex; vertical-align:-3px; color:var(--accent, #618D7C); }
.adm-card-ico svg{ width:20px; height:20px; }

/* Round-81: audit log row clamp. <Virtualize ItemSize="44"> sizes its spacers from that number,
   so a row that renders taller makes the measured and computed scroll heights disagree and the
   table flickers as it recalculates. Every cell is held to one line; the Details text keeps its
   full value in a title attribute rather than being lost. */
table.pro.audit-log tbody tr{ height:44px; }
table.pro.audit-log tbody td{ padding-top:0; padding-bottom:0; height:44px; overflow:hidden; white-space:nowrap; }
table.pro.audit-log tbody td .clip{
  display:block; max-width:42ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* the actor cell carries a 26px avatar — keep it from pushing the row past the promised height */
table.pro.audit-log tbody td img,
table.pro.audit-log tbody td span[style*="border-radius:50%"]{ vertical-align:middle; }
@media (max-width:900px){ table.pro.audit-log tbody td .clip{ max-width:24ch; } }

/* Round-81: tag / keyword search inside the pin-filters popover */
.pin-filters .tf-tag{ border-top:1px solid var(--track); margin-top:8px; padding-top:8px; }
.pin-filters .tf-tag input[type="search"]{
  width:100%; box-sizing:border-box; padding:7px 10px; border:1px solid var(--track);
  border-radius:9px; font-size:.9rem; background:var(--paper, #fff); color:var(--ink);
}
.pin-filters .tf-tag small{ display:block; margin-top:5px; color:var(--ink-soft); font-size:.76rem; line-height:1.35; }

/* Round-81: keyword box in the stories filter row */
.stories-q{ display:flex; margin:0 0 8px; }
.stories-q input[type="search"]{
  width:100%; max-width:420px; padding:7px 12px; border:1px solid var(--track);
  border-radius:999px; font-size:.9rem; background:var(--paper, #fff); color:var(--ink);
}
.stories-q input[type="search"]:focus{ outline:2px solid var(--accent, #618D7C); outline-offset:1px; }
@media (max-width:640px){ .stories-q input[type="search"]{ max-width:none; } }

/* Round-81: time bands the viewer's plan does not cover — visibly closed, still clickable so the
   click can explain why rather than doing nothing. */
#timeSwitch button.locked, .stories-time button.locked{ opacity:.55; cursor:pointer; }
#timeSwitch button.locked:hover, .stories-time button.locked:hover{ opacity:.8; }

/* Round-81 (a): the history upsell must clear the Ada FAB.
   .asst-fab is z-index:2500 and sits mid-right, directly over the panel's ✕ — so the panel
   opened but could not be closed. It outranks the FAB rather than the FAB being lowered.
   (Round-81 (i) has since dropped the FAB to 900 *on the map*, so that collision is gone there —
   but this rule still earns its place: off-map the FAB is still 2500, and 2600 is also what
   keeps the panel above its own .gate-backdrop at 2590.) */
.partner-panel{ z-index:2600; }

/* Round-81 (b): time bands must stay on ONE line on mobile. Adding " 🔒" to four of six labels
   pushed them to wrap, so the strip grew to two rows and the lock read as a separate item. */
#timeSwitch button, .stories-time button{ white-space:nowrap; }
@media (max-width:640px){
  #timeSwitch, .stories-time{
    max-width:calc(100vw - 16px); overflow-x:auto; flex-wrap:nowrap;
    scrollbar-width:none; -webkit-overflow-scrolling:touch;
  }
  #timeSwitch::-webkit-scrollbar, .stories-time::-webkit-scrollbar{ display:none; }
  /* :not(.time-arrow) — the arrows are buttons inside the same host, so this ID-specificity rule
     was beating .time-arrow's own padding:0 and giving the 28px circles 20px of side padding,
     which squashed the chevron off-centre. They size themselves below. */
  #timeSwitch button:not(.time-arrow), .stories-time button:not(.time-arrow){
    flex:0 0 auto; padding:8px 10px; font-size:.78rem;
  }
  /* the arrows' own mobile size is set with the rest of their styling further down — a media
     query adds no specificity, so setting it here would lose to the later .time-arrow block. */
}

/* Round-81 (c): the custom-range block when history is capped */
.pin-filters .tf-range.capped h4::after{ content:" 🔒"; }
.pin-filters .tf-range .tf-cap{
  display:block; margin-top:6px; color:var(--ink-soft); font-size:.76rem; line-height:1.35;
}

/* Round-81 (d): the upsell heading sits on the dark panel gradient, where the inherited heading
   colour renders too dark to read. The card body already uses --paper; the heading did not. */
.partner-panel h3{ color:#fff; }
.partner-panel .sp-close{ color:#fff; opacity:.85; }
.partner-panel .sp-close:hover{ opacity:1; }

/* Round-81 (e): the gate modal gets the reconnect modal's treatment — a dimmed, blurred backdrop
   behind it and an accent edge. Without it the panel floats over a live map with no visual
   separation, so the eye keeps reading the map underneath instead of the message. */
.gate-backdrop{
  position:fixed; inset:0; z-index:2590;
  background:rgba(44,59,54,.38); backdrop-filter:blur(2px);
  animation:gateFade .18s ease-out;
}
@keyframes gateFade{ from{ opacity:0 } to{ opacity:1 } }
.partner-panel{ border-left:3px solid var(--accent, #618D7C); }

/* Round-81 (f): scroll affordance on the time bands. Reuses the service row's arrow styling —
   same problem, same answer — and only appears when the strip actually overflows. */
/* Round-81: STICKY children of the strip, not fixed elements placed by JS. Fixed arrows had to be
   re-positioned from the strip's rect on every scroll and resize, and a mobile browser collapsing
   its URL bar fires resize continuously while you scroll — visible jitter. Sticky lets the
   browser hold them against the strip's own edges, and because a sticky box keeps its place in
   the flow they reserve their own room instead of covering the first and last bands. */
/* Round-81 (f2): white disc, accent chevron, glyph centred, label-sized type.
   - align-self:center — the band buttons are taller than 28px, so without this the discs sit on
     the strip's cross-axis start instead of level with the labels they scroll.
   - line-height:1 + padding:0 — "‹" carries its own leading and side bearing; both push the
     glyph off the disc's centre unless the box is collapsed onto it.
   - font-size matches .base-switch button (.82rem, .78rem on mobile) so the chevron reads as the
     same type as the band labels rather than as a separate piece of furniture. */
/* Selector is .base-switch button.time-arrow, not .time-arrow: the arrows are <button>s inside
   .base-switch, and style.css's `.base-switch button` (0,0,1,1) outranks a lone class (0,0,1,0).
   Every declaration below — padding, background, colour, font-size — was being overridden by it,
   so the discs rendered transparent with the bands' pale label colour and the bands' 14px side
   padding. Beating it needs equal-or-greater specificity, not just a later rule. */
.base-switch button.time-arrow{
  position:sticky; flex:0 0 auto; align-self:center; z-index:2;
  /* 30px disc / 1.5px border / 16px caret — the .story-arrow figures, so the two scroll
     affordances on a phone are the same control at the same size. Colour stays accent-on-white
     rather than the services row's --deep, because these sit on the dark band strip. */
  width:30px; height:30px; border-radius:50%; padding:0;
  border:1.5px solid var(--accent, #618D7C); background:#fff; color:var(--accent, #618D7C);
  display:none; align-items:center; justify-content:center; cursor:pointer;
  line-height:1;
  box-shadow:0 3px 10px rgba(44,59,54,.25);
}
.base-switch button.time-arrow svg{ width:16px; height:16px; display:block; }
.base-switch button.time-arrow.right svg{ transform:rotate(180deg); }
.base-switch button.time-arrow:hover{ background:var(--accent, #618D7C); color:#fff; }
.time-arrow.left{ left:2px; }
.time-arrow.right{ right:2px; }
.base-switch button.time-arrow.show{ display:inline-flex; }
/* no mobile font-size override any more: the caret is an SVG sized in px, not type. */
/* round-81: no desktop kill-switch — the arrows are driven by whether the strip actually
   overflows, which can happen in a narrow desktop window too. */

/* Round-81 (g): scroll arrows live INSIDE the strip on phones.
   The bottom row is [time bands][report ✚]. Placing the arrows just outside the strip put the
   right one on top of the report button on an iPhone SE — and shrinking the strip did not help,
   because the row is centred so the report button simply moved left with it. Overlaying the
   strip's own ends is immune to how the row lays out: the arrows can never reach a sibling
   control. The strip gives up a little width and gains end padding so the arrows do not sit on
   the first and last bands. */
@media (max-width:640px){
  /* Round-82: .stories-time joins the rule — the stories strip now shares the map's .time-row and
     therefore has the same report button beside it to leave room for. */
  #timeSwitch, .stories-time{ max-width:calc(100vw - 96px); margin-right:18px; transition:padding .12s ease; }
  /* the padding itself is applied inline by _wireTimeArrows, alongside the arrow that needs it */
}

/* Round-81 (h): lift the bottom controls clear of the map attribution on phones.
   .time-row sat at bottom:14px (spanning ~613-653 on an iPhone SE) while .map-legal occupies
   647-667 — they overlapped by 6px, so a thumb aimed at the band strip or the report button
   frequently landed on an OpenStreetMap link and navigated away from the map. */
@media (max-width:720px){
  .time-row{ bottom:calc(27px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))); }

  /* The attribution TEXT stays — OpenStreetMap, Leaflet and CARTO all require visible credit —
     but its links stop accepting taps, so a near miss on the controls above does nothing instead
     of leaving the page. Desktop keeps them clickable.

     TWO separate footers sit down there and the first pass only caught one: .map-legal is ours
     (Privacy, Terms) while .leaflet-control-attribution is Leaflet's own, holding the
     Leaflet / OpenStreetMap / CARTO links. Those were the ones still stealing taps. */
  .map-legal a,
  .leaflet-control-attribution a{ pointer-events:none; text-decoration:none; }
}

/* Round-81 (i): reserved space for the band-strip scroll arrows. Padding on the flex +
   overflow-x:auto strip provably did not apply, so the arrows covered the first and last
   controls; these spacers are real flex children and cannot be collapsed away. */


/* Round-81 (j): the stories filter bar is FIXED under the nav, not sticky.
   Sticky meant it travelled with the document until it reached its offset, so on a phone it
   visibly slid as you scrolled back to the top. Fixed pins it under the header for good; the
   feed gains matching top padding so the first card is not hidden behind it. */
.stories-filters{ position:fixed; left:0; right:0; top:var(--header-h); }
.stories-wrap{ padding-top:var(--stories-filters-h, 24px); }   /* JS measures the exact clearance */
/* round-81: the 12px breathing gap under the fixed filter bar was four times more than the
   design wants on a phone, where vertical space is the scarce thing */

/* Round-81 (k): the stories feed has its own bottom furniture (time bands, gear) and an infinite
   scroll that never reaches a natural end — a site footer below it is unreachable in practice and
   competes with the fixed controls for the same thumb space. */
.stories-page ~ .site-footer,
body:has(.stories-page) .site-footer{ display:none; }


/* Round-82 P4: discreet mode.
   For someone hiding from an intruder, a phone screen is a lamp pointed at their hiding place.
   Dim hard, drop the whites, and remove the red the help panel otherwise uses to draw the eye —
   in this one situation being noticed is the danger, not being missed.
   filter on <html> so it covers the map canvas and every panel at once, including anything the
   map draws after this class is set. */
html.aqua-discreet{ filter:brightness(.32) saturate(.6); }
html.aqua-discreet #helpPanel{ border-color:var(--track, #ADC3B9) !important; }
html.aqua-discreet #helpSend{ background:var(--deep, #4F8577) !important; }
/* A dimmed screen must not also be an unreadable one — keep the text the person is typing legible
   inside the panel while everything around it recedes. */
html.aqua-discreet #helpPanel, html.aqua-discreet #helpPanel *{ filter:brightness(1.6); }
@media (prefers-reduced-motion:reduce){ html.aqua-discreet{ transition:none; } }


/* Round-82: low-power mode. Everything that costs battery and is not the beacon goes away —
   animation, shadows, blur, the assistant. A phone at 12% is a phone that has to last until a
   crew arrives, and being findable matters more than being able to browse. */
html.aqua-lowpower *,
html.aqua-lowpower *::before,
html.aqua-lowpower *::after{ animation:none !important; transition:none !important; }
html.aqua-lowpower .asst-fab, html.aqua-lowpower .right-stack,
html.aqua-lowpower .leaflet-tile-pane{ display:none !important; }
html.aqua-lowpower .card, html.aqua-lowpower .sp-alertbox{ box-shadow:none !important; backdrop-filter:none !important; }


/* ================= Round-82 (#12): metrics visuals =================
   Sequential single hue throughout, because every measure here is MAGNITUDE — how many, how
   often, what share. None of it is identity, so categorical colour would be decoration that
   buries the one number that matters.

   The ramp was re-stepped rather than taken from the brand scale directly: the natural
   --water/--sage/--accent/--deep steps land in a lightness band where neither ink nor paper
   reaches 4.5:1, and a retention grid carries a number in EVERY cell. So the whole ramp stays
   above that band — light to mid, ink labels throughout, verified 5.35:1 at the darkest step.
   The site has no prefers-color-scheme anywhere, so one mode is the honest answer here. */
:root{
  --viz-1:#F2F6F1; --viz-2:#DCE9E0; --viz-3:#C2D9CB; --viz-4:#A8C8B6; --viz-5:#8FB8A3;
  --viz-track:#EDF1EC; --viz-ink:#2C3B36;
}
.viz-fig{ margin:0 0 30px; }
.viz-fig figcaption{ font-size:.86rem; color:var(--ink-soft,#5C6E66); margin:0 0 10px; max-width:640px; }

/* --- horizontal bars: 4px rounded data-end, anchored to the baseline --- */
.viz-bars{ display:grid; grid-template-columns:minmax(90px,auto) 1fr auto; gap:8px 12px; align-items:center; }
.viz-bars .lbl{ font-size:.88rem; color:var(--ink,#2C3B36); }
.viz-bars .track{ background:var(--viz-track); border-radius:4px; height:22px; position:relative; overflow:hidden; }
.viz-bars .fill{ height:100%; border-radius:0 4px 4px 0; background:var(--viz-5); }
.viz-bars .val{ font-size:.88rem; font-variant-numeric:tabular-nums; color:var(--ink,#2C3B36); }

/* --- meter: a single ratio against its limit, on the same ramp --- */
.viz-meter{ background:var(--viz-track); border-radius:999px; height:14px; overflow:hidden; max-width:420px; }
.viz-meter > span{ display:block; height:100%; background:var(--viz-5); border-radius:999px; }

/* --- retention heatmap. 2px surface gap between cells, per the mark spec: adjacent fills must
       never touch, or the eye reads two cells as one block. --- */
.viz-heat{ border-collapse:separate; border-spacing:2px; font-size:.86rem; }
.viz-heat th{ font-weight:600; color:var(--ink-soft,#5C6E66); font-size:.8rem; padding:4px 8px; text-align:left; }
.viz-heat td{ padding:8px 10px; border-radius:4px; text-align:center; font-variant-numeric:tabular-nums;
              color:var(--viz-ink); min-width:56px; }
.viz-heat td.empty{ background:transparent; color:var(--ink-soft,#5C6E66); }
.viz-heat td.wk{ background:transparent; text-align:left; white-space:nowrap; color:var(--ink,#2C3B36); }
/* --- legend: identity is never colour-alone, so the scale is spelled out --- */
.viz-legend{ display:flex; align-items:center; gap:6px; font-size:.8rem; color:var(--ink-soft,#5C6E66); margin-top:10px; }
.viz-legend i{ width:26px; height:12px; border-radius:3px; display:inline-block; }
@media (max-width:640px){ .viz-bars{ grid-template-columns:minmax(72px,auto) 1fr auto; } }


/* Round-82 (#9): nested rail children.
   Indented under their parent and set a step quieter, so the eye reads "this belongs to the thing
   above it" without needing a connector line. No icon: the parent's icon already identifies the
   section, and a second one at child level competes with it for the same meaning. */
.adm-sidebar .adm-link.adm-sub{
  padding-left:34px; font-size:.88rem; color:var(--ink-soft,#5A6B63);
  position:relative;
}
.adm-sidebar .adm-link.adm-sub::before{
  content:""; position:absolute; left:20px; top:50%; width:6px; height:1px;
  background:var(--track,#ADC3B9);
}
/* Round-89: an active CHILD is bold accent TEXT — never the filled pill its parent gets.
   It was white-on-accent because children used to inherit the parent's solid background; with
   that removed, white text on a light rail is invisible. Two stacked solid blocks also read as
   two peers, which is exactly the hierarchy the indent and the tick exist to deny. */
.adm-sidebar .adm-link.adm-sub.on{ background:transparent; color:var(--accent, #618D7C); font-weight:700; }
.adm-sidebar .adm-link.adm-sub.on:hover{ background:var(--mintbg); }
.adm-sidebar .adm-link.adm-sub.on::before{ background:var(--accent, #618D7C); }


/* Round-82: attention badges on the admin rail.
   Two levels, because two things are not equally urgent. A backlog of reports to review is work;
   an open help request is a person waiting. The review badge is the platform accent, the help
   badge is the impact red and pulses — reserved for that one case so the colour keeps meaning
   something. Numbers are shown, never colour alone, and >99 collapses so the rail cannot widen. */
.adm-badge{
  margin-left:auto; min-width:20px; padding:1px 6px; border-radius:999px;
  background:var(--accent,#618D7C); color:#fff;
  font-size:.72rem; font-weight:700; line-height:1.5; text-align:center;
  font-variant-numeric:tabular-nums; flex:0 0 auto;
}
.adm-badge.urgent{ background:var(--st-impact,#C0392B); --pulse-min:.55; animation:aq-pulse 2.2s ease-in-out infinite; }
.adm-link.on .adm-badge{ background:#fff; color:var(--accent,#618D7C); }
.adm-link.on .adm-badge.urgent{ background:#fff; color:var(--st-impact,#C0392B); }
/* the link is a flex row so the badge can sit hard right */
.adm-sidebar .adm-link{ display:flex; align-items:center; gap:8px; }


/* Round-82: account tab strip. Horizontal rather than a left rail, because these pages keep the
   public shell — someone changing a notification setting is still browsing the site. Scrolls
   sideways on a phone like the map's service chips: five tabs do not fit on an iPhone SE, and
   wrapping to two rows pushes the actual setting below the fold. */
/* Round-83: the wrapper carries the overflow arrows and the border, so the border no longer
   scrolls with the tabs — a border on the scroller itself shifted a hairline under the thumb on
   every drag, which is half of what "shaky" was. */
.acct-tabs-wrap{
  display:flex; align-items:center;
  border-bottom:1px solid var(--track,#ADC3B9); margin-bottom:22px;
}
.acct-tabs{
  display:flex; gap:6px; overflow-x:auto; scrollbar-width:none;
  flex:1 1 auto; min-width:0; padding-bottom:0;
  /* iOS: keep the horizontal rubber-band inside this strip. Without contain, an over-scroll at
     either end propagates to the page and to Safari's back-swipe, which is what made the row
     feel like it was fighting the thumb. */
  overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;
  touch-action:pan-x;
  scroll-behavior:smooth;
}
.acct-tabs::-webkit-scrollbar{ display:none; }
.acct-tab{
  position:relative; flex:0 0 auto; padding:9px 14px; font-size:.92rem; font-weight:600;
  white-space:nowrap; color:var(--ink-soft,#5C6E66); text-decoration:none;
  border-bottom:2px solid transparent; margin-bottom:-1px;
  /* iOS paints a grey flash on tap that reads as the row jumping */
  -webkit-tap-highlight-color:transparent;
}
/* The wiggle. The active tab goes from 600 to 700, which re-measures the text, which re-lays out
   every tab after it — so selecting a tab shifted the row under the finger. This reserves the
   BOLD width on every tab permanently: an invisible copy of the label at weight 700 sets the
   box, and the visible text sits on top of it. Geometry now never changes. */
.acct-tab::before{
  content:attr(data-label); display:block; height:0; overflow:hidden;
  font-weight:700; visibility:hidden; pointer-events:none;
}
.acct-tab:hover{ color:var(--ink,#2C3B36); text-decoration:none; }
/* The active tab is marked by an underline AND weight, never colour alone. */
.acct-tab.on{ color:var(--deep,#4F8577); border-bottom-color:var(--deep,#4F8577); font-weight:700; }
/* The arrows sit level with the tabs and clear the 1px rule the wrapper owns. */
.acct-tabs-wrap .pill-arrow{ margin-bottom:2px; }


/* Round-135: the round-82 .hidden-layers notice is gone (user request) — the dimmed pills are
   the off-state now. */


/* Round-82: local-resource pins (defibrillators, extinguishers, hydrants, shelters).
   A white disc with a coloured ring, deliberately quieter than an incident pin: these are
   standing facts about a place, not things happening right now, and they must never be mistaken
   for an alert. The ring carries freshness — green confirmed recently, amber ageing, grey over a
   year old, red reported missing. */
.res-pin{ background:none; border:0; }
.res-dot{
  display:flex; align-items:center; justify-content:center;
  width:30px; height:30px; border-radius:50%;
  background:#FFF; border:2.5px solid #2F6F5E;
  font-size:15px; line-height:1;
  box-shadow:0 2px 6px rgba(44,59,54,.28);
}
/* round-143: the pin's outline icon takes the band colour; a missing one is dashed */
.res-dot svg{ width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.res-dot.gone{ border-style:dashed; }
.res-pop-i svg{ width:16px; height:16px; vertical-align:-3px; fill:none; stroke:currentColor; stroke-width:2; }
.res-pop-t{ margin-top:3px; font-size:.76rem; color:#6B726B; }
.res-pop{ font-family:var(--font); font-size:.86rem; color:#2C3B36; }
.res-pop-h{ font-size:.95rem; margin-bottom:2px; }
.res-pop-k{ color:#6B726B; font-size:.78rem; text-transform:uppercase; letter-spacing:.04em; }
.res-pop-a{ margin-top:6px; }
.res-pop-n{ margin-top:4px; color:#4A524A; }
.res-pop-f{ margin-top:7px; font-weight:600; font-size:.8rem; }
.res-pop-dir{ display:inline-block; margin-top:7px; color:#4F8577; font-weight:600; }
.res-pop-btns{ display:flex; gap:6px; margin-top:10px; }
.res-pop-btns button{
  flex:1; cursor:pointer; font-family:var(--font); font-weight:600; font-size:.8rem;
  padding:7px 8px; border-radius:8px; border:1px solid #C9CEC7; background:#F7F8F5; color:#2C3B36;
}
.res-pop-btns button:hover:not(:disabled){ background:#EDF0EA; }
.res-pop-btns button:disabled{ opacity:.5; cursor:default; }
/* "Not there" is the destructive-sounding one but it is the MOST valuable report on this layer —
   it is the only thing that stops the next person making a wasted run. Never styled as a warning. */
.res-pop-msg:not(:empty){ margin-top:8px; font-size:.8rem; color:#4F8577; font-weight:600; }

/* /resources — add on the left, what is already mapped here on the right. Side by side rather
   than stacked, because seeing the existing entries is what stops the same AED being added five
   times, and a list below the fold is a list nobody reads. */
.res-grid{ display:grid; grid-template-columns:1.1fr .9fr; gap:20px; align-items:start; }
@media (max-width:900px){ .res-grid{ grid-template-columns:1fr; } }
.res-pickmap{ height:280px; border-radius:12px; overflow:hidden; border:1px solid #D9DDD6; }
.res-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:12px; }
.res-list li{ display:flex; gap:11px; align-items:flex-start; }
.res-li-ico{
  flex:0 0 auto; display:flex; align-items:center; justify-content:center;
  width:34px; height:34px; border-radius:50%;
  background:#FFF; border:2.5px solid #2F6F5E; font-size:16px; line-height:1;
}
.res-li-body{ min-width:0; }
.res-li-btns{ display:flex; gap:6px; margin-top:6px; }
.res-li-btns button{
  cursor:pointer; font-family:var(--font); font-weight:600; font-size:.76rem;
  padding:5px 10px; border-radius:8px; border:1px solid #C9CEC7; background:#F7F8F5; color:#2C3B36;
}
.res-li-btns button:hover{ background:#EDF0EA; }

/* Round-83: the SLA clock.
   Three bands, and only two of them are loud. "ok" is deliberately quiet — if every item on the
   queue carries a coloured badge, the coloured badge stops meaning anything, and the one that is
   genuinely late reads the same as the ninety that are fine. */
.pill.sla-ok{ background:#E4E7E1; color:#4A524A; }
.pill.sla-ok .dot{ background:#8A8F87; }
.pill.sla-warn{ background:#FBEEDA; color:#8A5A12; }
.pill.sla-warn .dot{ background:#D9822B; }
.pill.sla-breach{ background:#F7DED8; color:#8E1B12; font-weight:700; }
.pill.sla-breach .dot{ background:#C0392B; }
.sla-banner{
  display:flex; gap:10px; flex-wrap:wrap; align-items:baseline;
  background:#F7DED8; border-left:4px solid #C0392B; padding:12px 16px;
}
.sla-banner b{ color:#8E1B12; }
.sla-banner span{ font-size:.88rem; color:#5A4340; }

/* Round-83: the agency operations map. */
.ops-map{ height:420px; border-radius:12px; overflow:hidden; border:1px solid #D9DDD6; }
@media (max-width:720px){ .ops-map{ height:320px; } }
.ops-legend{
  display:flex; flex-wrap:wrap; gap:6px 16px; margin-top:10px;
  font-size:.78rem; color:#4A524A;
}
.ops-legend span{ display:inline-flex; align-items:center; gap:6px; }
.ops-k{ width:11px; height:11px; border-radius:50%; display:inline-block; }
/* Age is drawn as a ring rather than another colour: severity already owns colour, and a second
   colour scale on the same marks is how a map stops being readable at a glance. */
.ops-k.ring{ background:transparent; border:2.5px solid #8E1B12; width:13px; height:13px; }
.ops-stale{ --pulse-min:.45; animation:aq-pulse 2.4s ease-in-out infinite; }

.ops-unit{ background:none; border:0; }
.ops-unit-dot{
  display:flex; align-items:center; justify-content:center;
  width:28px; height:28px; border-radius:8px;
  border:2px solid #FFF; font-size:14px; line-height:1;
  box-shadow:0 2px 7px rgba(44,59,54,.35);
}
.ops-placing{
  margin-top:10px; padding:9px 12px; border-radius:10px;
  background:#FBEEDA; color:#8A5A12; font-size:.86rem; font-weight:600;
}
.ops-pop{ font-family:var(--font); font-size:.86rem; color:#2C3B36; }
.ops-pop-h{ font-size:.96rem; }
.ops-pop-k{ color:#6B726B; font-size:.78rem; text-transform:uppercase; letter-spacing:.04em; margin-top:2px; }
.ops-pop-age{ margin-top:6px; font-weight:600; }
.ops-pop-age.stale{ color:#8E1B12; }
.ops-pop-n{ margin-top:5px; }
.ops-pop-n.muted{ color:#8A8F87; }
.ops-pop-link{ display:inline-block; margin-top:7px; color:#4F8577; font-weight:600; }

/* Round-83: the push ask.
   Bottom of the screen, above the thumb's reach on a phone, and deliberately NOT a modal — it
   appears immediately after the person completed something, and blocking that moment to demand a
   permission is how a banner becomes a Block. It can be ignored entirely and the page still works. */
.push-ask{
  position:fixed; left:50%; transform:translateX(-50%); bottom:18px; z-index:9000;
  width:min(440px, calc(100vw - 24px));
  display:flex; gap:12px; align-items:center; flex-wrap:wrap;
  background:#2C3B36; color:#F3F4F1;
  font-family:var(--font); font-size:.88rem; line-height:1.35;
  padding:13px 16px; border-radius:14px;
  box-shadow:0 10px 30px rgba(44,59,54,.34);
}
.push-ask-body{ flex:1 1 200px; }
.push-ask-btns{ display:flex; gap:8px; flex:0 0 auto; }
.push-ask-btns button{
  cursor:pointer; font-family:var(--font); font-weight:700; font-size:.82rem;
  padding:7px 13px; border-radius:999px; border:0;
}
.push-ask-btns button[data-push-yes]{ background:#4F8577; color:#F3F4F1; }
.push-ask-btns button[data-push-yes]:hover:not(:disabled){ background:#3F6E62; }
/* "Not now" is a real, easy choice — not a greyed-out afterthought. Making the decline hard is
   what turns a decline into a Block, and a Block cannot be undone from inside the app. */
.push-ask-btns button[data-push-no]{ background:transparent; color:#C3CCC6; text-decoration:underline; }
.push-ask-btns button:disabled{ opacity:.55; cursor:default; }
@media (max-width:520px){ .push-ask{ bottom:12px; font-size:.84rem; } }

/* Round-82: community issues. Status is a small filled pill rather than coloured text, so the
   state of a list of twenty is readable at a glance without reading any of them. */
.iss-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:14px; }
.iss-list li{ padding-bottom:12px; border-bottom:1px dashed #DDE1DA; }
.iss-list li:last-child{ border-bottom:0; padding-bottom:0; }
.iss-head{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.iss-status{
  flex:0 0 auto; font-size:.7rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  padding:3px 8px; border-radius:999px; background:#E4E7E1; color:#4A524A; white-space:nowrap;
}
.iss-status.acknowledged{ background:#E8EEF6; color:#2D568F; }
.iss-status.in_progress{ background:#FBEEDA; color:#8A5A12; }
.iss-status.resolved{ background:#E3F0E9; color:#2F6F5E; }
/* Declined is grey, not red. It is an honest answer, and colouring it as a failure is how you
   teach an assembly to leave things open for ever instead of saying no. */
.iss-status.rejected{ background:#EAEAE7; color:#75796F; }
.iss-reply{
  margin:8px 0 0; padding:9px 12px; border-radius:10px;
  background:#EAF2EE; font-size:.88rem;
}

/* Round-82: content-request thread. The platform's replies are the half a branch is waiting to
.creq-msg{ margin-top:10px; padding:9px 12px; border-radius:10px; background:#F5F6F3; }
.creq-msg.platform{ background:#EAF2EE; border-left:3px solid #4F8577; }
.iss-status.submitted{ background:#E4E7E1; color:#4A524A; }
.iss-status.needs_info{ background:#FBEEDA; color:#8A5A12; }
.iss-status.accepted{ background:#E8EEF6; color:#2D568F; }
.iss-status.done{ background:#E3F0E9; color:#2F6F5E; }
.iss-status.declined{ background:#EAEAE7; color:#75796F; }

/* Round-82: emergency plan. Progress, never a score — this is not a test, and a household with
   one good line is enormously better off than one with none. */
.plan-progress{ margin-bottom:20px; }
.plan-bar{ height:6px; border-radius:999px; background:#E4E7E1; overflow:hidden; margin-bottom:5px; }
.plan-bar span{ display:block; height:100%; background:#4F8577; transition:width .25s ease; }

/* The print sheet IS the deliverable for this page: a plan that only exists on a phone is
   unreadable on the night the phone is dead, wet, or left behind. Everything but the plan goes. */
@media print{
  .site-nav, .site-footer, .nav-grid, .asst-fab, .btn, .plan-progress, .hint, .sp-alertbox{ display:none !important; }
  .page, .section, .container, .card{ padding:0 !important; margin:0 !important; box-shadow:none !important; border:0 !important; }
  .field{ page-break-inside:avoid; margin-bottom:14px; }
  .field label{ font-weight:700; }
  /* Inputs print as their value only — a page of empty form boxes helps nobody at 2am. */
  .field input, .field textarea{ border:0 !important; padding:0 !important; resize:none; font-size:1rem; }
}

/* Round-83: duty roster. "Who is answerable right now" is the whole feature, so it gets the
   card at the top and the only colour on the page. Empty is amber, not red — nobody rostered is
   a gap worth closing, not a failure: the dispatcher still pages everyone. */
.duty-now{ border-left:4px solid #2F6F5E; }
.duty-now.empty{ border-left-color:#D9822B; background:#FBEEDA; }

/* Round-83: scene context on a queue card. Quiet by default; only the facts that change what
   somebody does get the alarm treatment, because if everything is urgent nothing is. */
.scene{ margin-top:10px; padding:9px 12px; border-radius:10px; background:#F5F6F3; }
.scene-f{ font-size:.87rem; margin-top:3px; }
.scene-f b{ color:#4F8577; }
.scene-f.alarm{ color:#8E1B12; }
.scene-f.alarm b{ color:#8E1B12; }

/* Round-83: device diagnostics. */
/* Round-142p: Check this device as design 01-scorecard — the ring, the verdict, the list */
.diag-score{ background:#fff; border-radius:22px; padding:22px 24px; display:grid; grid-template-columns:132px 1fr auto; gap:20px; align-items:center; box-shadow:0 2px 12px rgba(44,59,54,.08); }
.diag-ring{ width:132px; height:132px; border-radius:50%; background:conic-gradient(var(--deep, #4F8577) 0 calc(var(--pct, 0) * 1%), var(--mint, #E7EDE6) 0 100%); display:flex; align-items:center; justify-content:center; }
.diag-ring i{ width:104px; height:104px; border-radius:50%; background:#fff; display:flex; flex-direction:column; align-items:center; justify-content:center; font-style:normal; }
.diag-ring b{ font-size:1.9rem; line-height:1; font-variant-numeric:tabular-nums; } .diag-ring b span{ font-size:.95rem; color:var(--ink-soft, #5C6E66); font-weight:600; }
.diag-ring small{ color:var(--ink-soft, #5C6E66); font-size:.72rem; margin-top:3px; letter-spacing:.06em; text-transform:uppercase; }
.diag-verdict h1{ font-size:1.35rem; line-height:1.25; margin:2px 0 6px; } .diag-verdict p{ margin:0; font-size:.9rem; max-width:46ch; }
.diag-card{ margin-top:14px; background:#fff; border-radius:18px; overflow:hidden; }
.diag-row{ display:flex; gap:14px; align-items:center; padding:13px 18px; border-bottom:1px solid #EEF2EE; }
.diag-row:last-child{ border-bottom:0; }
.diag-row .ic{ width:38px; height:38px; border-radius:12px; background:var(--mint, #E7EDE6); display:flex; align-items:center; justify-content:center; color:var(--deep, #4F8577); flex:none; }
.diag-row .ic svg{ width:20px; height:20px; } .diag-row .tx{ flex:1; min-width:0; } .diag-row b{ display:block; font-size:.95rem; } .diag-row small{ color:var(--ink-soft, #5C6E66); font-size:.8rem; }
.diag-row .st{ color:#2E9E5B; flex:none; } .diag-row .st svg{ width:20px; height:20px; }
.diag-row.warn .st{ color:#D9922B; } .diag-row.warn .ic{ background:#FBEFD9; color:#8A5A10; }
.diag-row.fail .st{ color:#C0392B; } .diag-row.fail .ic{ background:#F8E1DD; color:#8E2A1D; }
.diag-row.info .st{ color:var(--track, #ADC3B9); }
.diag-row.skel-r{ min-height:65px; } .diag-row.skel-r .tx{ display:flex; flex-direction:column; gap:8px; }
.diag-ring.loading{ background:var(--mint, #E7EDE6); } .diag-row .st .nd{ display:block; width:10px; height:10px; border-radius:50%; background:var(--track, #ADC3B9); margin:5px; }
@media (max-width:640px){ .diag-score{ grid-template-columns:1fr; text-align:center; } .diag-ring{ margin:0 auto; } .diag-verdict p{ margin:0 auto; } }
/* Round-142q: the folds under the checks — About this app, Pair device — in the row grammar */
.diag-fold{ margin-top:12px; background:#fff; border-radius:18px; overflow:hidden; }
.diag-fold summary{ list-style:none; display:flex; gap:14px; align-items:center; padding:13px 18px; cursor:pointer; }
.diag-fold summary::-webkit-details-marker{ display:none; }
.diag-fold summary .ic{ width:38px; height:38px; border-radius:12px; background:var(--mint, #E7EDE6); display:flex; align-items:center; justify-content:center; color:var(--deep, #4F8577); flex:none; }
.diag-fold summary .ic svg{ width:20px; height:20px; } .diag-fold summary .tx{ flex:1; min-width:0; }
.diag-fold summary b{ display:block; font-size:.95rem; color:var(--ink, #2C3B36); } .diag-fold summary small{ color:var(--ink-soft, #5C6E66); font-size:.8rem; }
.diag-fold summary .chev{ color:var(--ink-soft, #5C6E66); flex:none; transition:transform .2s; } .diag-fold summary .chev svg{ width:20px; height:20px; }
.diag-fold[open] summary .chev{ transform:rotate(180deg); }
.diag-fold[open] summary{ border-bottom:1px solid #EEF2EE; }
.diag-fold-body{ padding:14px 18px 18px; }
.sn2-devcard-bare .sn2-devstat{ margin-top:2px; }
@media (prefers-reduced-motion:reduce){ .diag-fold summary .chev{ transition:none; } }
/* the grid menu's Check-this-device line: a pending count, and the paired asset */
.grid-menu a{ position:relative; flex-wrap:wrap; }
.grid-badge{ margin-left:auto; min-width:22px; height:22px; border-radius:999px; background:#D9922B; color:#fff; font-size:.72rem; font-weight:800; display:inline-flex; align-items:center; justify-content:center; padding:0 6px; }
.grid-sub{ flex-basis:100%; color:var(--deep, #4F8577); font-size:.74rem; font-weight:700; padding-left:34px; margin-top:-4px; }
/* Round-142p: the emergency plan — design 04-checklist to read, design 08-step-wizard to edit */
.plan-kicker{ color:var(--tan, #BC8A52); font-weight:800; letter-spacing:1.4px; }
.plan-h1{ font-size:1.7rem; line-height:1.15; margin:4px 0 12px; }
.plan-review{ background:#FBEFD9; color:#8A5A10; border-radius:14px; padding:12px 14px; font-size:.88rem; display:flex; justify-content:space-between; align-items:center; gap:12px; margin:0 0 14px; }
.btn.btn-tan{ background:var(--tan, #BC8A52); color:#fff; }
.plan-bar2{ display:flex; align-items:center; gap:14px; background:#fff; border-radius:16px; padding:12px 16px; margin:0 0 14px; }
.plan-bar2 .p{ flex:1; height:10px; border-radius:5px; background:var(--mint, #E7EDE6); overflow:hidden; } .plan-bar2 .p i{ display:block; height:100%; background:var(--deep, #4F8577); transition:width .3s; }
.plan-bar2 b{ font-variant-numeric:tabular-nums; white-space:nowrap; }
.plan-item{ width:100%; text-align:left; background:#fff; border:0; border-radius:16px; padding:14px 16px; margin:0 0 10px; display:grid; grid-template-columns:30px 1fr; gap:12px; align-items:start; cursor:pointer; font-family:inherit; color:var(--ink, #2C3B36); }
.plan-item:hover, .plan-item:focus-visible{ box-shadow:0 0 0 2px var(--track, #ADC3B9) inset; outline:none; }
.plan-item .cb{ width:26px; height:26px; border-radius:50%; border:2px solid var(--track, #ADC3B9); display:flex; align-items:center; justify-content:center; color:#fff; margin-top:2px; }
.plan-item .cb svg{ width:16px; height:16px; } .plan-item.done .cb{ background:#2E9E5B; border-color:#2E9E5B; }
.plan-item .tx{ display:flex; flex-direction:column; gap:4px; min-width:0; } .plan-item b{ font-size:1rem; }
.plan-item .ans{ font-size:.95rem; white-space:pre-wrap; } .plan-item.todo .ans{ color:var(--ink-soft, #5C6E66); font-style:italic; }
.plan-item .hint{ color:var(--ink-soft, #5C6E66); font-size:.78rem; }
.plan-steps{ display:flex; gap:8px; align-items:center; margin:10px 0 18px; }
.plan-steps span{ width:30px; height:30px; border-radius:50%; background:var(--mint, #E7EDE6); color:var(--ink-soft, #5C6E66); display:inline-flex; align-items:center; justify-content:center; font-weight:800; font-size:.82rem; flex:none; }
.plan-steps span.done{ background:#2E9E5B; color:#fff; } .plan-steps span.on{ background:var(--deep, #4F8577); color:#fff; }
.plan-steps i{ flex:1; height:3px; background:var(--mint, #E7EDE6); }
.plan-card{ background:#fff; border-radius:22px; padding:26px; box-shadow:0 4px 18px rgba(44,59,54,.08); }
.plan-card .ic{ width:52px; height:52px; border-radius:14px; background:var(--mint, #E7EDE6); color:var(--deep, #4F8577); display:flex; align-items:center; justify-content:center; margin-bottom:12px; }
.plan-card .ic svg{ width:26px; height:26px; }
.plan-card .q{ display:block; font-size:1.35rem; line-height:1.25; font-weight:800; margin:4px 0 8px; }
.plan-card input, .plan-card textarea{ width:100%; font:inherit; font-size:1rem; border:1.5px solid var(--track, #ADC3B9); border-radius:12px; padding:12px 14px; }
.plan-acts{ display:flex; justify-content:space-between; gap:10px; margin-top:18px; flex-wrap:wrap; }
/* Round-142t (audit): reservations for late content */
#storyTime .base-switch, .time-row .base-switch{ min-height:36px; }
.cdir-count{ min-height:1.5em; }
.dash-note{ min-height:1.6em; }
.ahelp-att img, .ahelp-att video, .ahelp-media img, .ahelp-media video{ aspect-ratio:4 / 3; object-fit:cover; }
.astn-photo img{ height:190px; width:100%; object-fit:cover; }
.diag-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:10px; }
.diag-list li{ display:flex; gap:11px; align-items:flex-start; }
.diag-ico{
  flex:0 0 auto; width:22px; height:22px; border-radius:50%; color:#FFF;
  display:flex; align-items:center; justify-content:center; font-size:.76rem; font-weight:700;
  background:#8A8F87;
}
.diag-ok .diag-ico{ background:#2F6F5E; }
.diag-warn .diag-ico{ background:#D9822B; }
.diag-fail .diag-ico{ background:#C0392B; }

/* Round-83: legacy page content, shown so it stops being invisible. Monospace and scrollable —
   it is raw markdown being read for migration, not prose being presented. */
.legacy-body{
  margin:8px 0 0; padding:9px 12px; border-radius:8px; background:#F5F6F3;
  font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace; color:#4A524A;
  white-space:pre-wrap; max-height:170px; overflow:auto;
}

/* Round-130: the round-83 share toggle + #sharePanel are gone — #shareBtn is a plain .story-act
   in the reaction row that opens the OS share sheet directly. */

/* ── Round-83: the horizontal-scroll fix ────────────────────────────────────
   Reported on /account/channels at 375px (iPhone SE), and it was never that page's fault.

   `table.pro` carries min-width:520px so its columns stay readable, and every such table sits in
   `.table-wrap{ overflow-x:auto }` — a scroller whose whole job is to absorb that. It never got
   the chance. Grid and flex children default to `min-width:auto`, meaning they refuse to shrink
   below their content's intrinsic minimum, so the wrapper was forced to 520px, which widened its
   grid track, which widened the grid, which gave the PAGE a horizontal scrollbar while the
   scroller sat there unused at full width.

   Measured before: wrapper 520px inside a 375px viewport. After: 281px, with the table scrolling
   inside it as designed.

   This is the root cause, not a mask. Deliberately NOT `body{ overflow-x:hidden }` — that hides
   the next instance of this bug instead of surfacing it, and on iOS it can break position:sticky.
   Anything that overflows after this is a real defect and should still be visible as one. */
.grid > *,
/* Every OTHER grid container in the product needs the same permission. Found the hard way:
   .res-grid was missed, so /volunteer's 442px week-hour grid could not shrink and the page
   scrolled sideways even though .vol-grid already had overflow-x:auto and min-width:0 —
   the scroller was willing, its grandparent was not. */
.res-grid > *, .cpage-hero > *, .up-grid > *, .stats-row > *, .net-grid > *,
.sp-metrics > *, .foot-grid > *, .impact-strip > *{ min-width:0; }
.table-wrap{ min-width:0; }
/* Same reasoning one level down: a flex row holding a scroller has to let it shrink. */
.form-card .table-wrap, .card .table-wrap{ min-width:0; }

/* ── Round-83: the hidden attribute must actually hide ──────────────────────
   The "You appear to be in the affected area / Mark yourself safe / I'm not safe" prompt was
   shown to everybody, everywhere, regardless of the location gate — and the gate was never the
   problem. #checkinBox carried `hidden` AND an inline `display:flex`. The hidden attribute is
   only `[hidden]{display:none}` in the UA stylesheet, and an inline style beats it every time,
   so the element announced itself while its own attribute said otherwise.

   Two rules: give the box its flex layout only when it is NOT hidden, and make the attribute
   authoritative everywhere so the next element that does this cannot fail the same way. This
   was a safety bug — the prompt asserts a person's location — so it gets the belt AND braces. */
#checkinBox:not([hidden]){ display:flex; }
[hidden]{ display:none !important; }

   list used to print — an emoji ignores the theme, renders differently on every platform, and
   sat next to whatever glyph the sender had already put at the front of the title. */
.notif-ico{ display:inline-flex; align-items:center; justify-content:center; width:22px; height:22px; }
.notif-ico svg{ width:22px; height:22px; stroke:currentColor; fill:none; }

/* Round-87: the wizard's state banner. Colour carries the meaning at a glance; the text says it
   precisely, because "amber" is not an instruction. */
.setup-state{ border-left:4px solid var(--track); }
.setup-state.ok{ border-left-color:#1FA97A; }
.setup-state.warn{ border-left-color:#E8A33D; }

/* Round-89: the cedi equivalent under a dollar price. Deliberately quiet — the dollar figure
   is the price, this line is the reassurance that nothing unexpected happens at checkout. */
.plan-card .price-alt{ display:block; margin-top:2px; font-size:.82rem; font-weight:600;
                       color:var(--ink-soft, #55635C); letter-spacing:0; }

/* Round-90: disclosure carets on rail entries that have sub-pages (admin AND org rails).
   The row, not the link, is now the active pill. Otherwise the caret sat outside the green
   block it belongs to, reading as a control for the rail rather than for that entry. */
.adm-sidebar .adm-branch{ display:flex; align-items:center; border-radius:9px; }
.adm-sidebar .adm-branch:hover{ background:var(--mintbg); }
.adm-sidebar .adm-branch.on{ background:var(--accent, #618D7C); }
.adm-sidebar .adm-branch .adm-link{ flex:1 1 auto; min-width:0; }
/* the link keeps its text colour and loses only its background, which the row now draws */
.adm-sidebar .adm-branch .adm-link,
.adm-sidebar .adm-branch .adm-link:hover,
.adm-sidebar .adm-branch .adm-link.on{ background:transparent; }

.adm-sidebar .adm-exp{
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  width:26px; height:26px; margin-inline-end:4px; padding:0;
  background:none; border:0; border-radius:7px; cursor:pointer;
  color:var(--sage2, #8FA99B);
}
.adm-sidebar .adm-exp:hover{ background:rgba(0,0,0,.07); color:var(--ink, #23312C); }
.adm-sidebar .adm-exp:focus-visible{ outline:2px solid var(--accent, #618D7C); outline-offset:1px; }
/* NO transition on this transform, deliberately. With `transition:transform .18s` the caret was
   right on first paint and then stuck: clicking one left it at the identity matrix — visually
   unturned — for as long as you cared to wait. Measured, not guessed: the same click with the
   transition removed from that one button lands on rotate(-90deg) correctly, so it is the
   transition being interrupted by Blazor's DOM patch mid-flight, not a stale style. A caret that
   sometimes fails to turn is worse than one that never animates, and a disclosure arrow snapping
   is the normal behaviour anyway.
   caretleft points ◀: 180° is ▶ (shut) and -90° is ▼ (open, aimed at the children below). */
.adm-sidebar .adm-exp svg{ width:13px; height:13px; transform:rotate(180deg); }
.adm-sidebar .adm-exp[aria-expanded="true"] svg{ transform:rotate(-90deg); }
/* Under RTL the shut caret points ◀, into the text it belongs to; open is ▼ either way. */
html[dir="rtl"] .adm-sidebar .adm-exp svg{ transform:none; }
html[dir="rtl"] .adm-sidebar .adm-exp[aria-expanded="true"] svg{ transform:rotate(-90deg); }
/* on the active row the caret sits on accent, so it borrows the row's white */
.adm-sidebar .adm-branch.on .adm-exp{ color:#fff; }
.adm-sidebar .adm-branch.on .adm-exp:hover{ background:rgba(255,255,255,.2); color:#fff; }

/* Round-90: the "find the real neighbourhood" results.
   A list under the box rather than a native <select>: the rows carry a size, a member count and
   an Open link, none of which fit in an <option>, and the whole point is that a taken area is
   visibly taken rather than quietly missing. */
.acom-hits{
  margin-top:6px; border:1px solid var(--track,#ADC3B9); border-radius:10px;
  background:var(--paper,#fff); overflow:hidden; max-height:280px; overflow-y:auto;
}
.acom-hit{
  display:flex; flex-wrap:wrap; align-items:center; gap:10px; width:100%; text-align:left;
  padding:9px 12px; border:0; border-bottom:1px solid var(--track,#ADC3B9);
  background:none; font:inherit; cursor:pointer;
}
.acom-hits .acom-hit:last-child{ border-bottom:0; }
.acom-hit:hover{ background:var(--mintbg,#EFF5F1); }
.acom-hit-name{ font-weight:700; color:var(--ink,#23312C); min-width:0; overflow-wrap:anywhere; }
.acom-hit-meta{ font-size:.8rem; color:var(--ink-soft,#5A6B63); min-width:0; overflow-wrap:anywhere; }
/* Round-91q: Open and Join belong together.
   The row is a wrapping flex line and the buttons were just two more items in it, so a long
   locality line pushed them apart — Open could end up on one row and Join on the next, reading as
   two unrelated controls rather than one pair. They are pushed to the end as a group, and given a
   shared height so neither sits proud of the other. */
/* A lone button in a row (admin's "taken" rows) still sits at the end on its own. Direct children
   only — inside .acom-hit-actions an auto margin would prise the pair apart again. */
.acom-hit > .btn{ margin-left:auto; }
.acom-hit-actions{
  margin-left:auto; display:flex; align-items:stretch; gap:6px; flex:0 0 auto;
}
.acom-hit-actions .btn{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:32px; white-space:nowrap; margin-left:0;
}
/* On a phone the name and the locality need the whole width, and two buttons pinned to the end of
   the same line ran off the right edge — the row does not scroll, so Join was simply cut off. The
   pair takes its own line and stays right-aligned, which keeps them adjacent without competing with
   the text for width. */
@media (max-width: 560px){
  .acom-hit-actions{ flex-basis:100%; margin-left:0; justify-content:flex-end; }
}
/* Taken: not a button, visibly not clickable, and it keeps its own way in via the Open link. */
.acom-hit.taken{ cursor:default; opacity:.72; }
.acom-hit.taken:hover{ background:none; }
.acom-hit.taken .acom-hit-meta{ color:var(--st-impact,#C0392B); font-weight:700; }

/* Round-90: the type label above a community's name. It is either the country's own level name
   ("District") or, for an unlinked community, the generic type — and those are held lower-case
   because the same two strings appear mid-sentence elsewhere. Capitalising the first letter here
   rather than storing two cased copies keeps ten languages from needing a second translation. */
.ctype::first-letter{ text-transform:uppercase; }

/* Round-91: the settings button on a community page when the member has muted something.
   It sits in the dark hero alongside .btn-outline, so it borrows that shape and changes only the
   colour — a muted service silences its emergencies, and the button is the only place that now
   says so. */
.btn-outline-danger{ background:transparent; color:#F5B7B1; border:2px solid #E6A9A2; }
.btn-outline-danger:hover{ background:rgba(230,169,162,.15); border-color:#F5B7B1; }

/* Round-121 (R6): the old jump-tab strip (.cpage-tabs/.cpage-anchor) lived here — retired
   with the anchor-scroll page; round-117 tabs are .cpage2-tabs with real navigation. */

/* Round-91m: a loading placeholder for any map container.
   Keyed on :not(.leaflet-container) — Leaflet adds that class to the element the instant it takes
   ownership, so the skeleton removes ITSELF with no JS to run, nothing to forget to call, and no
   way to leave a shimmer stuck over a map that has already drawn. It also covers the failure the
   round-91 work kept hitting: a container waiting on a slow script now looks like it is loading
   rather than looking broken. */
.map-skel:not(.leaflet-container){
  position:relative; overflow:hidden;
  background:linear-gradient(180deg, var(--track, #ADC3B9) 0%, rgba(173,195,185,.55) 100%);
}
.map-skel:not(.leaflet-container)::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(100deg, transparent 20%, rgba(255,255,255,.55) 50%, transparent 80%);
  transform:translateX(-100%);
  animation:aq-sweep var(--skel-period, 1.4s) ease-in-out infinite;
}
/* Round-226 (owner): the first-visit outline (site.js firstVisitOutline) — between the header
   (1100) and the phone bottom bar (1200), so both stay usable while a first visit loads. */
.nvskel{ position:fixed; left:0; right:0; top:var(--header-h, 64px); bottom:0; z-index:1150; overflow:hidden;
  padding-top:14px; animation:nvskelIn .12s ease-out both; }
.nvskel.out{ opacity:0; transition:opacity .18s ease; }
.nvskel-map{ padding-top:0; }
.nvskel .container{ max-width:760px; }
.nvskel-feed .story-card{ margin-bottom:14px; }
@keyframes nvskelIn{ from{ opacity:0; } to{ opacity:1; } }
@media (prefers-reduced-motion: reduce){ .nvskel{ animation:none; } }

/* Round-142 (motion P0): THE sweep — every shimmer in the app (map-skel, tiles-loading, .skel,
   the band strip's loading bar) is this one keyframe. */
@keyframes aq-sweep{ to{ transform:translateX(100%); } }
/* Round-130: the skeleton above ends the moment Leaflet takes the container — but on the
   community page's boundary maps (hero, settings, admin preview) that is long before the TILES
   arrive, leaving flat Leaflet grey that reads as broken. tiles-loading keeps the same sweep
   running on the live map's schedule: aquaBoundary adds it at build and removes it when the tile
   layer reports load (with a failsafe timeout), so the sheen ends on pixels, not on ownership. */
.leaflet-container.tiles-loading{ background:linear-gradient(180deg, var(--track, #ADC3B9) 0%, rgba(173,195,185,.55) 100%); }
.leaflet-container.tiles-loading::after{
  content:""; position:absolute; inset:0; z-index:800; pointer-events:none;
  background:linear-gradient(100deg, transparent 20%, rgba(255,255,255,.55) 50%, transparent 80%);
  transform:translateX(-100%);
  animation:aq-sweep var(--skel-period, 1.4s) ease-in-out infinite;
}
/* Round-92: the map on a saved place card. Short enough that the card still reads as a settings
   row rather than a map with some text under it, tall enough to place a junction. */
.place-map{
  height:170px; margin-top:12px; border-radius:12px;
  border:1.5px solid var(--track,#ADC3B9); overflow:hidden;
}
@media (max-width: 560px){ .place-map{ height:150px; } }
/* Round-91: honours the reader's own setting. A sweeping highlight behind text is exactly the
   kind of motion people turn this off for. */
@media (prefers-reduced-motion: reduce){
  .map-skel:not(.leaflet-container)::after,
  .leaflet-container.tiles-loading::after{ animation:none; opacity:.35; }
}

/* Round-94: the ⚙ filters/range button reads "on" when a custom range or pin filter is applied —
   white like the selected band, so the row tells the truth about what the map is filtered to.
   Round-141k: WITHOUT the green ring (user call) — white on the dark strip already says "on",
   exactly as the selected band does, and the ring made it the loudest thing in the row. */
#timeSwitch [data-pin-filters].on, .base-switch [data-pin-filters].on{
  background:#fff; color:var(--deep, #2C3B36); box-shadow:none;
}

/* Round-94: the incident panel borrows the stories action styling verbatim */
.inc-react .story-act{ padding:0; border:0; background:transparent; font-size:.85rem; font-weight:700; color:var(--ink-soft, #65756D); gap:5px; display:inline-flex; align-items:center; cursor:pointer; }
.inc-react .story-act .ico-t{ font-size:1rem; line-height:1; color:inherit; text-shadow:none; }
.inc-react .story-act .ico-t svg{ width:15px; height:15px; display:block; }
.inc-react .story-act:hover, .inc-react .story-act.on{ background:transparent; border:0; color:var(--accent, #618D7C); }

/* ── Round-94: notifications (/activity) — the composed "final-timeline" design ────────────
   04's timeline rail + 03's header and filter chips + 02's sticky day titles + 10's action
   pills. The green hero is gone; "Activity Center" keeps the hero's own deep green so the
   page still announces itself without a slab of colour. */
/* Round-94b: NO padding above the sticky block — with it, the block travelled those pixels
   before pinning and the gap under the nav visibly shrank. The spacing lives inside .ntf-top
   instead, so its natural position IS its stuck position and nothing moves. */
.ntf-wrap{ max-width:760px; padding-top:0; padding-bottom:60px; }
/* Round-94b sat the notifications page on white; round-142f (owner's call): cream, like every
   page — the cards still carry the colour (white for read, mint for unread). */
html.pg-ntf, html.pg-ntf body{ background:var(--cream, #F1EBE4); }   /* :has twin (pre-105) */
body:has(.ntf-wrap), html:has(.ntf-wrap){ background:var(--cream, #F1EBE4); }
.ntf-kicker{
  display:block; font-size:.72rem; font-weight:800; letter-spacing:2px; text-transform:uppercase;
  /* Round-94: the tan the kicker wore on the green hero. Its literal value there was
     --tan-light (#DEC29A), which is legible on dark green and nearly invisible on this light
     page — so the same token pair's darker sibling, --tan, carries the look at a readable
     weight. */
  color:var(--tan, #BC8A52); margin-bottom:4px;
}
.ntf-head{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:0 0 14px; }
.ntf-head h1{ margin:0; font-size:clamp(1.4rem, 3vw, 1.7rem); }
/* Round-96h: caret that folds the header tools + filter chips. caretleft rotated:
   open = up (points at what collapses), closed = down. Deliberately NO transition —
   the class is Blazor-toggled (round-93 stuck-transition trap). */
.ntf-caret{
  border:0; background:#EDF2EE; width:30px; height:30px; border-radius:50%; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; color:var(--deep, #4F8577);
}
.ntf-caret svg{ width:16px; height:16px; transform:rotate(-90deg); }   /* down (closed) */
.ntf-caret.open svg{ transform:rotate(90deg); }                        /* up (open) */
.ntf-new{ background:#C0392B; color:#fff; border-radius:999px; font-size:.72rem; font-weight:800; padding:2px 9px; }
.ntf-head-actions{ margin-left:auto; display:flex; gap:8px; flex-wrap:wrap; }
.ntf-pill{
  display:inline-flex; align-items:center; gap:6px; border:1.5px solid var(--track, #ADC3B9);
  background:#fff; border-radius:999px; padding:7px 14px; font-family:var(--font);
  font-size:.82rem; font-weight:700; color:var(--ink, #2C3B36); cursor:pointer;
}
.ntf-pill svg{ width:15px; height:15px; }
.ntf-pill:hover{ border-color:var(--accent, #618D7C); color:var(--deep, #4F8577); text-decoration:none; }
.ntf-pill.pri{ background:var(--c, #618D7C); border-color:var(--c, #618D7C); color:#fff; }
.ntf-pill.pri:hover{ color:#fff; filter:brightness(1.06); }
.ntf-more{ display:block; margin:16px auto 0; }

/* Round-94b: the kicker/title/filters block stays under the nav; JS publishes its height as
   --ntf-top-h so the day titles can dock exactly beneath it. */
.ntf-top{
  position:sticky; top:var(--header-h); z-index:3;
  background:var(--cream, #F1EBE4); padding-top:22px; margin:0 -4px; padding-inline:4px;   /* round-142f: the page is cream */
  /* round-150e: the head's 14px used to be a bottom MARGIN that collapsed out of this box, so the
     block's measured height (and the day titles' dock) stopped above an unpainted strip */
  padding-bottom:14px;
}
.ntf-top .ntf-head{ margin-bottom:0; }
.ntf-top .ntf-chips-wrap{ padding-bottom:0; }
.ntf-chips-wrap{ display:flex; align-items:center; gap:6px; min-width:0; padding-bottom:8px; }
.ntf-arrow{
  flex:0 0 auto; width:30px; height:30px; border-radius:50%; cursor:pointer;
  border:1.5px solid var(--track, #ADC3B9); background:#fff; color:var(--deep, #4F8577);
  display:none; align-items:center; justify-content:center;
}
.ntf-arrow svg{ width:15px; height:15px; }
.ntf-arrow.right svg{ transform:rotate(180deg); }
.ntf-arrow.show{ display:inline-flex; }
.ntf-arrow:hover{ border-color:var(--accent, #618D7C); background:var(--mintbg, #EAF1EC); }
/* NB: no `scroll-behavior:smooth` here — with it, programmatic scrolls (scrollBy, even a plain
   scrollLeft assignment) animated and were cancelled before moving, so the arrows did nothing.
   The JS passes behavior:"smooth" per call instead, which is not affected. */
.ntf-chips{ display:flex; gap:8px; overflow-x:auto; margin:0; scrollbar-width:none; padding-bottom:2px; }
.ntf-chips::-webkit-scrollbar{ display:none; }
.ntf-chips::-webkit-scrollbar{ display:none; }
.ntf-chip{
  flex:0 0 auto; border:1.5px solid var(--track, #ADC3B9); background:#fff; border-radius:999px;
  padding:6px 14px; font-family:var(--font); font-size:.85rem; font-weight:700;
  color:var(--ink, #2C3B36); white-space:nowrap; cursor:pointer;
}
.ntf-chip.on{ background:var(--deep, #4F8577); color:#fff; border-color:var(--deep, #4F8577); }
.ntf-chip .b{ background:#C0392B; color:#fff; border-radius:999px; font-size:.68rem; padding:0 6px; margin-left:5px; }

.ntf-day{
  font-size:.72rem; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink-soft, #55635C); margin:0 0 10px;
  /* Round-150f: the in-flow day titles are plain separators now. The sticky one is
     .ntf-day-current INSIDE the sticky title block — one sticky element, no measured offset,
     so neither WebKit's margin quirk nor the G6's WebView can open a strip under the block. */
  position:static; background:transparent; padding:24px 0 6px;
}
.ntf-day-current{ padding:10px 0 0; margin:0; min-height:1.6em; visibility:hidden; }
.ntf-top.docked .ntf-day-current{ visibility:visible; }
.ntf-top{ transition:box-shadow .18s ease; }
.ntf-top.docked{ box-shadow:0 10px 12px -10px rgba(44,59,54,.35); }
.ntf-tl{ position:relative; padding-left:56px; }
.ntf-tl:before{ content:""; position:absolute; left:22px; top:0; bottom:0; width:2px; background:var(--track, #ADC3B9); opacity:.5; }
.ntf-ev{ position:relative; margin-bottom:14px; }
.ntf-ev .node{
  position:absolute; left:-56px; top:2px; width:44px; height:44px; border-radius:50%;
  display:flex; align-items:center; justify-content:center; color:#fff; background:var(--c, #618D7C);
  border:3px solid #fff;   /* the rail passes behind the node; the ring hides the seam */
}
.ntf-ev .node svg{ width:20px; height:20px; }
.ntf-ev .c{ background:#fff; border:1px solid var(--track, #ADC3B9); border-radius:14px; padding:12px 14px; box-shadow:0 1px 3px rgba(44,59,54,.06); }
.ntf-ev.unread .c{ background:var(--mintbg, #EAF1EC); border-color:#CBDCD2; }
/* Round-94b: each card wears a WASH of its own type colour — a fire alert reads red-ish, a
   flood blue-ish, at a glance, without shouting. Unread doubles the wash rather than switching
   to mint, so "new" and "what kind" stay two separate signals. Guarded: browsers without
   color-mix keep the plain white/mint above. */
@supports (background: color-mix(in srgb, red 10%, white)){
  .ntf-ev .c{
    background:#F1EEE7; background:color-mix(in srgb, var(--c) 7%, #fff);
    border-color:color-mix(in srgb, var(--c) 24%, #fff);
  }
  .ntf-ev.unread .c{
    background:#F1EEE7; background:color-mix(in srgb, var(--c) 15%, #fff);
    border-color:color-mix(in srgb, var(--c) 40%, #fff);
  }
}
.ntf-ev-top{ display:flex; align-items:center; gap:8px; }
.ntf-ev-top .kind{ font-size:.72rem; font-weight:800; letter-spacing:.04em; text-transform:uppercase; color:var(--c, #618D7C); }
.ntf-ev-top .when{ margin-left:auto; font-size:.75rem; color:var(--ink-soft, #55635C); white-space:nowrap; }
.ntf-ev-top .dot{ width:9px; height:9px; border-radius:50%; background:#C0392B; flex:0 0 auto; }
.ntf-ev b{ display:block; margin:2px 0; }
.ntf-ev p{ margin:2px 0 8px; color:var(--ink-soft, #55635C); font-size:.9rem; }
.ntf-acts{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
@media (max-width:560px){
  .ntf-tl{ padding-left:46px; }
  .ntf-tl:before{ left:17px; }
  .ntf-ev .node{ left:-46px; width:36px; height:36px; }
  .ntf-ev .node svg{ width:17px; height:17px; }
  .ntf-head-actions{ margin-left:0; width:100%; }
}


/* Round-97l's 51px phone rule was replaced by the round-175 close-button block at the end of this file. */


/* ============ Round-98: the bottom-sheet entrance, shared ============
   The incident details panel has always slid up from the bottom on a phone and back down on
   close (.station-panel translateY + transition). The report wizard and the comments modal
   appeared instantly, which made them feel like a different app. Same motion, same curve.

   KEYFRAMES, not a transition on a toggled class, and deliberately so: .rpt-full's state lives
   in an <html> class, and an enhanced navigation can rebuild <html>'s attributes mid-flight
   (round-96d). A transition whose "visible" state depends on a class that can vanish leaves the
   panel stuck OFF-SCREEN; a one-shot entrance animation cannot — its resting state is visible,
   and losing the class costs at most the animation. The exit class is removed by the navigation
   that follows it, so it cannot linger either. */
@keyframes acSheetIn  { from { transform:translateY(100%); } to { transform:translateY(0); } }
@keyframes acSheetOut { from { transform:translateY(0); } to { transform:translateY(100%); } }

@media (max-width:720px){
  .rpt-full{ animation:acSheetIn .28s cubic-bezier(.2,.8,.25,1) both; }
}
/* Round-142c: the exit is a short fade on every width — the navigation back starts at the same
   instant (aquaCloseReport), and a slide with nothing underneath was a wait dressed as motion. */
@keyframes rptFade { to { opacity:0; } }
html.pg-rpt-out .rpt-full{ animation:rptFade .14s ease-out both; pointer-events:none; }
@media (max-width:900px){
  .lightbox.comments-only.open .lb-side{ animation:acSheetIn .28s cubic-bezier(.2,.8,.25,1) both; }
  .lightbox.comments-only.lb-closing .lb-side{ animation:acSheetOut .24s cubic-bezier(.4,0,.7,.2) both; }
}
/* Someone who has asked the OS for less motion gets the panel, not the journey. */
@media (prefers-reduced-motion: reduce){
  .rpt-full, html.pg-rpt-out .rpt-full,
  .lightbox.comments-only.open .lb-side,
  .lightbox.comments-only.lb-closing .lb-side{ animation:none !important; }
}


/* ============ Round-98d: /login — design 11 over a LIVE map ============
   The map is the page's floor: it spans the whole viewport, behind both columns. The pitch and
   the sign-in card float on it — side by side on desktop (04's arrangement), stacked on a phone
   with the pitch high under the nav and the card at the foot. One screen, no scroll. */
/* Round-199o: the header is reserved at BOTH ends, not just the top.
   The reservation exists so the pitch and the card cannot slide under the header — but the map
   runs edge to edge BEHIND the header (that is the whole "one surface" idea), so the reader's
   eye centres on the viewport, not on the box below the header. Reserving only at the top put
   the content exactly --header-h lower than the middle of what they can see: measured 260/196
   above/below at 1280x800 and 280/216 at 375x812, i.e. a 64 px dead band across the top at
   every width. Reported by the owner as "top space in login". */
.login-split{ position:relative; display:grid; grid-template-rows:1fr;
  height:var(--app-h, 100vh); height:var(--vvh, var(--app-h, 100vh));
  padding:var(--header-h) 0 var(--header-h) !important; overflow:hidden; }
/* …except where the screen is too short to give the space away. The page is overflow:hidden by
   design ("one screen, no scroll"), so on a landscape phone the second reservation would be
   taken out of the card itself rather than out of empty map. */
@media (max-height:640px){ .login-split{ padding-bottom:0 !important; } }
/* the map fills everything INCLUDING the header strip, so the page reads as one surface */
.ls-mapbg{ position:absolute; inset:0; z-index:0;
  background:linear-gradient(150deg,#EFEAE2 0%,#E4E9E2 42%,#D8E3DD 100%); }
.ls-mapbg .leaflet-container{ background:transparent; }
/* Legibility over live tiles, which we do not control the colour of. Strong at the top where the
   headline sits, strong again at the foot behind the card, and thin through the middle so the
   map is genuinely visible rather than implied. */
.ls-scrim{ position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(180deg, rgba(241,235,228,.55) 0%, rgba(241,235,228,.26) 32%,
    rgba(241,235,228,.20) 55%, rgba(241,235,228,.42) 80%, rgba(241,235,228,.66) 100%); }

/* Round-98j: the pitch is iPad-Pro-and-up only (user call) — below 1024px the screen is the map
   and the card, nothing else. Centred in its section rather than against the left edge, matching
   the card. The gradient stays on the full-width parent; only the inner block is capped. */
.ls-pitch{ display:none; position:relative; z-index:2; grid-row:1; justify-content:center;
  padding:14px 0 18px;
  background:linear-gradient(180deg, rgba(241,235,228,.97) 0%, rgba(241,235,228,.96) 58%,
    rgba(241,235,228,.80) 84%, rgba(241,235,228,0) 100%); }
.ls-pitch-in{ width:min(420px, calc(100vw - 32px)); }
.ls-pitch h2{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.4rem; line-height:1.14;
  margin:0 0 5px; max-width:17ch; }
.ls-pitch p{ color:var(--ink-soft, #5C6E66); font-size:.89rem; max-width:36ch; margin:0; }
.ls-stats{ display:grid; grid-template-columns:repeat(3,auto); justify-content:start; gap:0 18px;
  margin-top:12px; }
.ls-stats b{ display:block; font-family:var(--serif, Cambria, Georgia, serif); font-size:1.3rem; line-height:1.1; }
.ls-stats span{ display:block; font-size:.62rem; color:var(--sage2, #7B988C); letter-spacing:.5px;
  text-transform:uppercase; line-height:1.25; }

/* the card is the page on a phone: centred in what is left under the nav */
.ls-side{ position:relative; z-index:2; grid-row:1; display:grid; place-items:center;
  padding:14px 0; }
/* Round-98g: capped and centred at every width. Unbounded it filled an iPad edge to edge, which
   makes a sign-in form look like a page. min() keeps a 16px gutter on the narrowest phone and
   stops at 420px above that — roughly square against the card's own height, so it reads as a
   dialogue rather than a column. */
.ls-card{ background:#fff; width:min(420px, calc(100% - 32px)); margin:0 auto;
  border-radius:22px; padding:22px 20px 20px; box-shadow:0 16px 44px rgba(44,59,54,.20); }
.ls-card h1{ font-size:1.24rem; margin:0 0 4px; }
.ls-lead{ color:var(--ink-soft, #5C6E66); font-size:.84rem; margin:0 0 14px; }
.ls-prov{ display:flex; align-items:center; justify-content:center; gap:10px; width:100%;
  padding:13px 18px; border-radius:12px; border:0; cursor:pointer;
  font-family:var(--font); font-size:1rem; font-weight:700; text-decoration:none !important; }
.ls-prov svg{ width:20px; height:20px; flex:none; }
.ls-prov.g{ background:#fff; color:#3c4043;
  box-shadow:0 1px 3px rgba(44,59,54,.18), 0 0 0 1px rgba(44,59,54,.08); }
.ls-prov.f{ background:#1877F2; color:#fff; }
.ls-prov.a{ background:#000; color:#fff; }
.ls-prov + .ls-prov{ margin-top:10px; }
.ls-rev{ display:block; text-align:center; margin-top:16px; padding-top:14px;
  border-top:1px solid var(--track, #ADC3B9); font-size:.82rem; color:var(--ink-soft, #5C6E66); }

@media (min-width:1024px){
  /* 04's arrangement: the pitch and the card side by side, both centred, both over the map */
  .login-split{ grid-template-rows:1fr; grid-template-columns:1.05fr .95fr; align-items:center;
    height:auto; min-height:var(--app-h, 100vh); }
  .ls-scrim{ background:linear-gradient(90deg, rgba(241,235,228,.93) 0%, rgba(241,235,228,.72) 38%,
    rgba(241,235,228,.35) 56%, rgba(241,235,228,.55) 100%); }
  /* BOTH placements must be explicit. Giving only .ls-side a grid-row (to undo the phone's
     grid-row:3) makes the placement algorithm position it before the auto-placed pitch, so it
     took column 1 and the pitch was pushed to the right — the columns came out swapped. */
  .ls-pitch{ display:grid; grid-column:1; grid-row:1; padding:0 40px; background:none;
    align-content:center; }
  .ls-pitch-in{ width:auto; max-width:34ch; }
  .ls-pitch h2{ font-size:2.3rem; max-width:15ch; }
  .ls-pitch p{ font-size:1rem; }
  .ls-stats{ gap:0 26px; margin-top:18px; }
  .ls-stats b{ font-size:1.8rem; }
  .ls-stats span{ font-size:.68rem; }
  /* symmetric, so the card sits in the MIDDLE of its column the way the pitch now does in its
     own — the old 56/0 padding pulled it toward the map and the two columns read as unbalanced */
  .ls-side{ grid-column:2; grid-row:1; padding:48px; display:grid; place-items:center; }
  .ls-card{ width:min(400px, 100%); margin:0; padding:32px 30px;
    box-shadow:0 20px 60px rgba(44,59,54,.18); }
  .ls-card h1{ font-size:1.5rem; }
}

/* Round-98e: the reviewer access-code page carries no site nav. It is reached inside
   ASWebAuthenticationSession / Chrome Custom Tabs, where there is no browser chrome either and
   the surrounding site is not navigable — a header there offers links that lead out of the
   auth session. Browser back still works, and the form is the only thing to do on the page. */
html.pg-rev .site-header{ display:none; }
html.pg-rev .page{ padding-top:24px; }
html.pg-rev .site-footer{ display:none; }

/* ── round-107: the reviewer page on /login's surface ─────────────────────────────────────
   .login-split reserves --header-h for a header this page does not draw, so the map floor
   would start 64px down and leave a cream band across the top.
   Round-199o: both ends now, since the reservation is symmetric — a page with no header should
   give up no room to one at either end. */
html.pg-rev .login-split{ padding:0 !important; }

/* One column at every width — the pitch is /login's, not this page's. Without this the
   min-width:1024px rule below splits the grid in two and the card sits in the right half with
   an empty left half, which reads as a layout that lost something. */
.login-split.ls-solo{ grid-template-columns:1fr !important; }
.login-split.ls-solo .ls-side{ grid-column:1; }
/* the two-column scrim is a LEFT-to-right gradient built to darken behind the pitch; with one
   centred column it shades one side of the card for no reason. Back to the vertical one. */
@media (min-width:1024px){
  .login-split.ls-solo .ls-scrim{
    background:linear-gradient(180deg, rgba(241,235,228,.55) 0%, rgba(241,235,228,.26) 32%,
      rgba(241,235,228,.20) 55%, rgba(241,235,228,.42) 80%, rgba(241,235,228,.66) 100%); }
}

/* the card's own wordmark, in place of the old .kicker */
.ls-brand{ display:block; font-size:.68rem; letter-spacing:2px; text-transform:uppercase;
  font-weight:700; color:var(--sage2, #7B988C); margin-bottom:6px; }
/* matches .ls-prov's weight and radius so the card has one button language */
.ls-submit{ display:block; width:100%; margin-top:4px; padding:13px 18px; border:0;
  border-radius:12px; cursor:pointer; background:var(--accent, #618D7C); color:#fff;
  font-family:var(--font); font-size:1rem; font-weight:700; }
.ls-submit:hover{ filter:brightness(1.06); }
.ls-card .field{ margin-bottom:14px; }
.ls-card .field label{ display:block; font-size:.82rem; font-weight:700; margin-bottom:6px; }
.ls-card .field input{ width:100%; }

/* Round-98f: no sign-up bar on the sign-in page (user call). "Be the first to know — sign up
   free" is an invitation to do the exact thing this page already is, and on a phone it wraps to
   three lines and eats 93px of a screen we just spent two rounds fitting. Hiding it is not
   enough on its own: --header-h is widened by .signup-bar-on to reserve the bar's height, so
   that has to go back to the bare header or the page keeps a 93px hole where the bar was. */
html.pg-login #signupBar{ display:none !important; }
html.pg-login.signup-bar-on{ --header-h:64px; }

/* ── Round-199t: signing in owns the screen at EVERY width ──────────────────────────────────
   This treatment existed already but only inside the phones' media query, which is why desktop
   still carried the whole site header — brand, search, map, stories, Report, the bell, the
   avatar — over a page whose only job is two buttons. And the header reserved --header-h at the
   top, which is the "space at the top" the owner kept seeing: round 199o made that reservation
   symmetric so the card was centred, but the reservation itself was never the right answer on a
   page that draws no header worth reserving for.

   What is left is what the owner asked for: the way OUT (back, left) and the way ELSEWHERE
   (the launcher, far right). Nothing else — including the "Sign in" button, which on the sign-in
   page is a link to the page you are already looking at.

   Specificity, not order: `:not(#_)` costs an id, so these out-rank the phone rules further down
   the file (`.site-header:not(#_) .nav-right > a.nav-icon…`) without depending on where this
   block sits. html.pg-login is set by the server in App.razor — no :has(), which does nothing on
   the Chrome 101 baseline this app still supports. */
html.pg-login .site-header:not(#_){
  background:none; border:0; box-shadow:none;
  justify-content:space-between;
}
/* The row keeps its own box so the two survivors can sit at opposite ends; display:contents
   from the phone rules would hand them to the header and lose that. */
/* justify-content, not margin-left:auto: the row still stretches to fill what the header's
   space-between gives it (299px of a 375px phone), so pushing the ROW right does nothing — the
   launcher has to be pushed to the end of the row. Without this it sat at x=66, next to the back
   button, which is where it was found on the device. */
html.pg-login .site-header:not(#_) .nav-right{
  display:flex; justify-content:flex-end; align-items:center; gap:10px; margin-left:auto;
}
/* Everything except the launcher goes: the icons, Report, the bell, messages, the avatar, and
   the sign-in button. Listed as "every child, then put one back" so a nav item added later is
   hidden here by default rather than appearing on the sign-in page by accident. */
html.pg-login .site-header:not(#_) .brand,
html.pg-login .site-header:not(#_) .omni,
html.pg-login .site-header:not(#_) .nav-right > *{ display:none; }
html.pg-login .site-header:not(#_) .nav-right > .nav-grid{ display:inline-flex; }
html.pg-login .site-header:not(#_) .nav-back{
  display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; padding:0; border:0; border-radius:14px;
  background:#fff; color:var(--deep, #4F8577); cursor:pointer;
  box-shadow:0 3px 12px rgba(44,59,54,.16);
}
html.pg-login .site-header:not(#_) .nav-back svg{ width:26px; height:26px; }

/* Round-199v: and NO OFFSET for a bar that is not drawn.
   .site-header is position:fixed with `top:var(--signup-h)` (style.css), and html.signup-bar-on
   sets that to 46px. Round-98f hid the bar on this page and reset --header-h so the page kept no
   hole in its PADDING — but not the offset, so the header itself still started 46px down. That
   was invisible while the header had a background and a full row of its own; now that the page
   is two controls floating over the map it is naked space above the back button, which is what
   the owner kept seeing.

   The offset is overridden rather than the variable zeroed, because site.js writes the bar's
   measured height into --signup-h INLINE on <html> (site.js ~5589) and an inline custom property
   beats any stylesheet rule. padding-top keeps the notch honoured in the app, where the header
   would otherwise sit under the status bar. */
html.pg-login .site-header:not(#_){ top:0; padding-top:var(--safe-top, 0px); }

/* And with no header worth reserving for, the page stops reserving. The two controls float over
   the map the way the map's own header does, and the card centres in the whole screen — which
   is what finally removes the band at the top rather than balancing it. */
html.pg-login .login-split{ padding:0 !important; }
/* Round-110: and on the reviewer sign-in, for the same reason twice over. "Be the first to
   know — sign up free" invites the reader to do the thing the page already is, and this page
   is not even open to sign-ups: it takes an access code issued with a store submission. Both
   rules are needed here as well — hiding the bar without narrowing --header-h leaves its
   93px hole, which on a page whose card is vertically centred pushes the card off centre. */
html.pg-rev #signupBar{ display:none !important; }
html.pg-rev.signup-bar-on{ --header-h:64px; }


/* ============ Round-99: /account rebuilt as grouped rows (design 08) ============
   The eight collapsible .scard blocks became one scrolling list of grouped rows, because on
   a settings page the two things people actually do are "check whether I am covered" and
   "flip one switch". Accordions answered neither without a tap; rows answer the first at a
   glance and the second in place. Anything that cannot be a switch — a form, a long list, a
   consequential confirmation — opens as its own pane instead.

   The page keeps the site's cream: white groups on a tinted ground separate themselves, which
   is why they carry a soft shadow and no border. (Round-99b: this was briefly white, and the
   groups needed a drawn hairline to survive it. Cream is back, so the hairline went with it.)

   Ada is hidden here. The FAB is pinned to the right edge, which is exactly the column the
   switches now occupy, so on a phone it sat on top of one. */
html.pg-account .asst-fab, html.pg-account .asst-drawer{ display:none !important; }

/* the page's own heading, replacing the gradient hero: this is a settings list, and 100px of
   decorative banner pushed the first actual setting below the fold on a phone. */
.aset-h1{ font-size:1.6rem; line-height:1.2; margin-bottom:4px; }
.aset-lead{ color:var(--ink-soft); font-size:.92rem; margin-bottom:6px; }

.aset-wrap{ max-width:640px; margin:0 auto; }

/* group label — the small caps above each card */
.aset-gl{
  font-size:.72rem; text-transform:uppercase; letter-spacing:1.2px; color:var(--sage2);
  margin:22px 0 7px 4px; font-weight:700;
}
.aset-grp{
  background:#fff; border-radius:14px; overflow:hidden;
  box-shadow:0 3px 12px rgba(44,59,54,.07);
}

/* one row. <button>, <a> and <label> all use it, so every reset is spelled out. */
.aset-row{
  display:flex; align-items:center; gap:12px; width:100%; margin:0;
  text-align:left; font:inherit; color:inherit; background:none; border:0;
  border-bottom:1px solid rgba(173,195,185,.4); padding:14px 15px;
}
.aset-row:last-child{ border-bottom:0; }
.aset-row:hover{ text-decoration:none; color:inherit; }
button.aset-row:active, a.aset-row:active{ background:var(--mintbg,#E7EDE6); }
/* ...but not on the dark panel, where a mint tint flashes almost white. Round-104b. */
.aset-lead-card button.aset-row:active,
.aset-lead-card a.aset-row:active{ background:rgba(255,255,255,.06); }
.aset-row .t{ flex:1; min-width:0; }
.aset-row .v{ color:var(--ink-soft); font-size:.88rem; text-align:right; }
.aset-row .hint{ font-size:.76rem; color:var(--ink-soft); margin-top:2px; }
.aset-row .chev{ color:var(--track); font-size:1.15rem; line-height:1; flex:none; }
.aset-row.danger .t{ color:#C0392B; }
.aset-tick{ color:var(--accent); font-weight:700; flex:none; }

/* the switch is a real checkbox — keyboard-focusable, announced as a checkbox, and it
   still works if this stylesheet never arrives. */
.aset-sw{
  appearance:none; -webkit-appearance:none; flex:none; margin:0; cursor:pointer;
  width:42px; height:25px; border-radius:99px; background:var(--track); position:relative;
  transition:background .18s ease;
}
.aset-sw::after{
  content:""; position:absolute; top:3px; left:3px; width:19px; height:19px; border-radius:50%;
  background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.25); transition:left .18s ease;
}
.aset-sw:checked{ background:var(--accent); }
.aset-sw:checked::after{ left:20px; }
.aset-sw:focus-visible{ outline:2px solid var(--deep); outline-offset:2px; }

.aset-note{ font-size:.82rem; color:var(--ink-soft); margin:10px 4px 0; }
.aset-acts{ margin-top:18px; display:flex; flex-direction:column; gap:8px; }

/* ---- the detail pane ---------------------------------------------------------------
   Open state is Blazor's, so the entrance is a one-shot KEYFRAME, never a transition on a
   toggled class — same reasoning as acSheetIn above (round-96d): a transition whose visible
   state depends on a class can stick off-screen if that class is ever rebuilt, an entrance
   animation cannot. There is deliberately no exit animation: closing sets _pane to null, and
   holding the element on screen for another 240ms would add a second server round-trip to
   every "back" on a page where every interaction is already one. */
@keyframes asetPaneIn{ from{ transform:translateX(100%); } to{ transform:translateX(0); } }
.aset-pane{
  /* starts BELOW the fixed site header, never over it: a detail that swallows the top nav
     leaves no way anywhere except back, and on this page the nav is the only way out. */
  position:fixed; top:var(--header-h); right:0; bottom:0; left:0;
  z-index:1200; background:var(--cream); overflow:auto;
  animation:asetPaneIn .28s cubic-bezier(.2,.8,.25,1) both;
  -webkit-overflow-scrolling:touch;
}
.aset-ph{
  position:sticky; top:0; z-index:2; display:flex; align-items:center; gap:12px;
  padding:12px 14px; background:rgba(241,235,228,.96);
  border-bottom:1px solid var(--track);
}
.aset-ph h2{ font-size:1.05rem; margin:0; }
.aset-back{
  width:38px; height:38px; flex:none; border-radius:50%; border:0;
  background:#fff; color:var(--deep); font-size:1.5rem; line-height:1;
  display:grid; place-items:center; padding:0; box-shadow:0 2px 8px rgba(44,59,54,.1);
}
.aset-pb{ padding:6px 14px calc(46px + var(--safe-bottom,0px)); max-width:640px; margin:0 auto; }
.aset-pb>.aset-gl:first-child, .aset-pb>.aset-grp:first-child{ margin-top:10px; }
.aset-pb .field{ margin-bottom:14px; }


/* Only "caretleft" exists in OutlineIcons, so the row chevron is that glyph turned around.
   In Arabic the list runs the other way and the untouched glyph is already correct — which is
   why this is a rotation on the LTR case rather than a second icon pointing right. */
.aset-row .chev svg{ width:18px; height:18px; transform:rotate(180deg); }
[dir="rtl"] .aset-row .chev svg{ transform:none; }
[dir="rtl"] .aset-back svg{ transform:rotate(180deg); }
.aset-back svg{ width:20px; height:20px; }
.aset-row .t .hint{ display:block; }

/* ---- account sub-page header (round-99c) -------------------------------------------
   Replaces the gradient hero on /account/* — a back arrow and a title, nothing else. */
a.aset-back:hover{ text-decoration:none; }

/* ---- iPad and up: the split, like Settings ------------------------------------------
   768px is iPad portrait. Below it the detail is a full-screen overlay that slides in over
   the list (a phone has room for one thing); at and above it the SAME markup becomes the
   right-hand column, so there is one set of panes, not two. The animation is dropped here
   on purpose: a pane sliding in from the right edge of a column it already occupies reads
   as a glitch, not as motion. */
.aset-empty{ display:none; }

@media (min-width:768px){
  .aset-split{
    display:grid; grid-template-columns:minmax(280px,360px) 1fr; gap:26px;
    align-items:start; max-width:1040px; margin:0 auto;
  }
  .aset-wrap{ max-width:none; }
  .aset-pane{
    position:static; z-index:auto; overflow:visible; animation:none;
    background:#fff; border-radius:16px; box-shadow:0 3px 12px rgba(44,59,54,.07);
    /* the list is long and the detail is short — keep the detail in view while the list
       scrolls past it, the way a settings split behaves */
    position:sticky; top:calc(var(--header-h) + 16px);
  }
  .aset-ph{
    position:static; background:transparent; padding:16px 18px 14px;
    border-bottom:1px solid rgba(173,195,185,.5);
  }
  /* in the split the list is already on screen, so neither back arrow is needed */
  .aset-pane .aset-back{ display:none; }
  .aset-pb{ padding:14px 18px 22px; max-width:none; }
  .aset-empty{
    display:grid; place-items:center; min-height:220px; padding:24px;
    background:#fff; border-radius:16px; box-shadow:0 3px 12px rgba(44,59,54,.07);
    color:var(--ink-soft); font-size:.92rem; text-align:center;
    position:sticky; top:calc(var(--header-h) + 16px);
  }
  .aset-row.on{ background:var(--mintbg,#E7EDE6); }
  .aset-row.on .t{ font-weight:700; }
}

/* ---- a sub-page hosted inside the split (round-99d) ---------------------------------
   The sub-page's own markup keeps its .container wrappers; inside a split column those
   would re-centre at 1120px and blow the column apart, so they are neutralised here
   rather than surgically removed from five working pages. */
.aset-split .container{ max-width:none; padding-left:0; padding-right:0; }
.aset-split .section{ padding:8px 0 24px; }

/* Below iPad the list is not shown on a sub-page — the sub-page IS the screen, and its
   back arrow is the way out. */
.aset-split-sub .aset-wrap{ display:none; }

@media (min-width:768px){
  /* On a tablet the list returns as the left column and the back arrow goes: the place it
     would take you back to is already on screen, which is exactly why iPad Settings has no
     back button at this level. */
  .aset-split-sub .aset-wrap{ display:block; }
}

/* A sub-page hosted in the split has roughly half the width it was written for, so its own
   two-column grids have to fold. They only get to be two columns again when the window is
   wide enough for the list AND two columns beside it. */
@media (max-width:1099px){
  .aset-split .grid-2, .aset-split .grid-3{ grid-template-columns:1fr; }
}

/* Present for assistive tech, absent on screen. Not display:none — that removes it from the
   accessibility tree too, which defeats the point of keeping the heading at all. */
.aset-sr{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}

/* ---- a value that will not fit drops to a second line (round-99f) -------------------
   Not a container query: the WebView baseline here is Chrome 101 and those landed in 105.
   Flex wrapping gets the same result on every browser we ship to — the label claims 55% of
   the row, and a value that cannot fit in what is left moves under it instead of squeezing
   the label into a three-line stack. The value cannot shrink (flex-shrink:0), which is what
   makes it wrap to a new line rather than reflow in place. */
.aset-row{ flex-wrap:wrap; }
.aset-row .t{ flex:1 1 auto; min-width:55%; }
.aset-row .v{ flex:0 0 auto; max-width:100%; margin-left:auto; overflow-wrap:anywhere; }

/* The chevron is furniture, not content, so it must not take part in the wrap. Left in the
   flex flow it wrapped onto a THIRD line of its own once the value moved down — an arrow
   floating under a phone number. Pinned to the right edge it stays put whether the row is one
   line or two, and only .nav rows pay the padding, so a switch still sits where it did. */
.aset-row.aset-nav{ position:relative; padding-right:36px; }
.aset-row.aset-nav .chev{ position:absolute; right:13px; top:50%; transform:translateY(-50%); }

/* ---- the privacy panel (round-99g, design 07) ---------------------------------------
   Dark, at the top, before anything else. This is the group that decides whether a person
   can be identified from a report, and on a platform where people report armed robbery and
   illegal mining it should not look like a preference among preferences.

   The switches invert here: on a dark ground the normal grey "off" track reads as ON, which
   on these four toggles is the most dangerous possible misreading. */
.aset-lead-card{
  display:block; background:#2C3B36; color:#fff; border-radius:20px;
  padding:20px 18px; margin-bottom:8px;
}
.aset-lead-card h2{ font-family:var(--serif,Cambria,Georgia,serif); font-size:1.28rem;
  line-height:1.25; margin:0 0 6px; color:#fff; }
.aset-lead-card>p{ color:#CBD9D1; font-size:.9rem; margin:0 0 14px; }
.aset-lead-card .aset-grp{ background:transparent; box-shadow:none; border-radius:0; }
.aset-lead-card .aset-row{ border-bottom-color:rgba(255,255,255,.14); padding:13px 0; }
.aset-lead-card .aset-row .hint{ color:#9FB8AC; }
.aset-lead-card .aset-sw{ background:rgba(255,255,255,.2); }
.aset-lead-card .aset-sw:checked{ background:#7FB79F; }
.aset-lead-card .aset-sw:focus-visible{ outline-color:#CBD9D1; }

/* Sign out sits in the account card now. It is still the one destructive-ish row there, so
   it reads as an action rather than as another setting. */
.aset-signout .t{ color:var(--deep); font-weight:700; }

/* A switch is 42px and always fits, so a row carrying one must never wrap — the sub-text
   inside the label was widening .t past the 55% floor and shoving the switch onto a line of
   its own, left-aligned under the words. Only rows with a VALUE (a phone number, a plan name)
   need the wrap, and those are links and buttons, never labels. */
label.aset-row{ flex-wrap:nowrap; }
label.aset-row .t{ min-width:0; }

/* A switch that cannot take effect says so and stops responding, rather than accepting a tap
   and quietly changing nothing (round-99h). */
.aset-row.is-overridden .t{ opacity:.55; }
.aset-sw:disabled{ opacity:.4; cursor:not-allowed; }
.aset-note.aset-warn{ color:#8A5A1E; font-weight:600; }

/* the privacy card's own group label, which sits above a dark panel rather than a white one */
.aset-gl-lead{ margin-top:2px; }

/* sign out is an action, not a setting — it gets a button, inside the card it belongs to */
.aset-row.aset-signout{ padding:12px 15px; }
.aset-row.aset-signout .btn{ margin:0; }

/* the one navigation row inside the dark privacy panel — its chevron needs to be visible
   against #2C3B36 rather than the light track colour the white cards use */
.aset-lead-card .aset-row .chev{ color:#7F9C90; }
.aset-lead-card a.aset-row .t{ color:#fff; }

/* ============ Round-100b: the sub-pages inherit the settings look ====================
   Places, Channels, Location sharing, Billing and Top-up are hosted inside the settings
   split, so they should read as the same product as the list beside them. Done as scoped
   styling rather than by rewriting five working pages: their markup keeps its meaning and
   only its appearance is re-pointed at design 08. */

/* cards become groups: white, soft shadow, no border, the list's own radius */
.aset-detail .card{
  background:#fff; border:0; border-radius:14px; padding:16px 15px;
  box-shadow:0 3px 12px rgba(44,59,54,.07);
}
.aset-detail .card + .card{ margin-top:12px; }
.aset-detail .card > b:first-child,
.aset-detail .card > div > b{ font-size:1rem; }

/* a checkbox that is a SETTING becomes the list's switch. Deliberately not every checkbox:
   the channels matrix puts one in each table cell, where a 42px switch would wreck the grid,
   and those stay boxes. */
.aset-detail label > input[type="checkbox"]{
  appearance:none; -webkit-appearance:none; flex:none; margin:0; cursor:pointer;
  /* !important because every one of these carries an inline style="width:auto" to escape a
     global input rule, and an inline style beats a stylesheet — without this the switch
     collapses to a sliver and reads as broken. */
  width:42px !important; height:25px !important;
  border-radius:99px; background:var(--track); position:relative;
  transition:background .18s ease;
}
.aset-detail label > input[type="checkbox"]::after{
  content:""; position:absolute; top:3px; left:3px; width:19px; height:19px; border-radius:50%;
  background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.25); transition:left .18s ease;
}
.aset-detail label > input[type="checkbox"]:checked{ background:var(--accent); }
.aset-detail label > input[type="checkbox"]:checked::after{ left:20px; }
.aset-detail label > input[type="checkbox"]:disabled{ opacity:.4; cursor:not-allowed; }
.aset-detail label > input[type="checkbox"]:focus-visible{ outline:2px solid var(--deep); outline-offset:2px; }
.aset-detail td input[type="checkbox"]{ appearance:auto; -webkit-appearance:auto; width:auto;
  height:auto; background:none; border-radius:0; }
.aset-detail td input[type="checkbox"]::after{ content:none; }

/* the state pills match the list's muted value text rather than shouting */
.aset-detail .pill{ background:var(--mintbg,#E7EDE6); color:var(--deep); font-weight:700;
  border:0; }
.aset-detail .pill.pill-active{ background:var(--accent); color:#fff; }

/* small buttons pick up the list's radius so nothing looks borrowed from another page */
.aset-detail .btn{ border-radius:10px; }

/* per-service radius rows (round-101): label, slider, switch — the Places control in the
   settings list's own clothes */
/* Round-103c: the Places service row — switch, name, "within", slider, distance. */
.aset-row.aset-svc{ flex-wrap:wrap; gap:8px 10px; align-items:center; }
.aset-svc-head{ display:flex; align-items:center; gap:10px; font-weight:700; min-width:150px;
  flex:1 1 auto; margin:0; }
.aset-within{ font-size:.85rem; color:var(--ink-soft); }
.aset-km{ font-size:.9rem; min-width:52px; text-align:right; }
.aset-range{
  /* 120px, the width Places uses inline — the two controls are the same control. */
  flex:0 0 120px; width:120px; accent-color:var(--accent); height:24px; margin:0;
}

/* ---- sub-page header: same bar, same type, and it stays put (round-101b) -------------
   The panes on the settings list have a sticky header with a 1.05rem title; the sub-pages had
   a 1.6rem heading that scrolled away. They are the same kind of screen, so they get the same
   bar — one that is still there when you are deep in a long page and want out. */
.aset-subbar{
  /* Round-103d: 1050, not 6. Leaflet stacks its own panes and controls up to z-index 1000, so a
     place card's map scrolled UNDER the sticky bar and drew straight over the back arrow. Above
     Leaflet, below the site header (1100) — the bar must never cover the nav. */
  position:sticky; top:var(--header-h); z-index:1050;
  background:#fff; border-bottom:1px solid var(--track);   /* owner, 2026-10-06: white */
  margin-bottom:10px;
}
/* Owner, 2026-10-06: the site header already shows a back arrow on these pages (html.pg-child,
   signed in), so the bar's own arrow is a second one. Signed out, the header has none: keep it. */
html.pg-child .aset-subbar .aset-back{ display:none; }
.aset-subbar > .container{ display:flex; align-items:center; gap:12px; padding-top:12px;
  padding-bottom:12px; }
.aset-subtitle{ font-size:1.05rem; font-weight:700; margin:0; line-height:1.25; }
.aset-sublead{ color:var(--ink-soft); font-size:.92rem; margin:0 0 14px; }

@media (min-width:768px){
  /* in the split the list is already beside you, so the arrow goes and the bar stops sticking */
  .aset-subbar{ position:static; background:none; border-bottom:0; }
  .aset-subbar .aset-back{ display:none; }
}

/* ---- search over a conversation (round-101e) ----------------------------------------
   .msg-shell is pinned at z-index 2600 so the chat owns the phone screen — which also put it
   above the search sheet (2000) and the raised header (2100). Opening search from a
   conversation showed the conversation. The shell steps back while the sheet is up, the same
   way Ada's FAB already does on this page; the sheet covers it completely, so nothing is lost
   and nothing else on the page changes. */
@media (max-width: 900px){
  html.search-open .msg-page .msg-shell{ z-index:1; }
}

/* Places' own form card, once it is inside the settings split, should look like every other
   group rather than like a form from a different page (round-101f). */
.aset-detail .form-card{
  background:#fff; border:0; border-radius:14px; padding:16px 15px;
  box-shadow:0 3px 12px rgba(44,59,54,.07);
}

/* A line that belongs to the group title above it rather than to the card below: same left
   edge, same rhythm the account list uses, and it never adds height to the gap before a card. */
.aset-gl-sub{
  margin:-5px 0 7px 4px; font-size:.82rem; color:var(--ink-soft);
}
.aset-gl-sub .pill{ margin-left:6px; }

/* Round-104: on a wide screen the split's columns ended flush against the footer, because the
   section's own padding is collapsed away by the sticky detail column. Give the split its own
   floor rather than relying on the page's. */
@media (min-width:768px){
  .aset-split{ padding-bottom:48px; }
}

/* The default detail is a real panel now, not a centred sentence — it needs to lay out like a
   column, not like an empty state. */
@media (min-width:768px){
  .aset-empty.aset-empty-panel{
    display:block; place-items:initial; min-height:0; padding:0; text-align:left;
    background:none; box-shadow:none; color:inherit; font-size:inherit;
  }
  .aset-empty-panel .container{ max-width:none !important; }
  .aset-empty-panel .aset-gl{ margin-top:0; }
}

/* ---- row icons (round-104b) ---------------------------------------------------------
   Outline icons only, in the theme's accent — never emoji. Emoji carry their own colour and
   their own culture: they read as a different product on each platform, and on the dark panel
   they glow. One stroke colour that follows the row's own text is the whole point. */
.aset-ico{
  flex:none; width:22px; height:22px; display:grid; place-items:center; color:var(--accent);
}
.aset-ico svg{ width:20px; height:20px; }
.aset-lead-card .aset-ico{ color:#9FD3BC; }
.aset-row .t{ display:flex; flex-direction:column; }
.aset-svc-head .aset-ico{ color:var(--accent); }

/* a card heading with its icon, matching the row icons' size and colour */
.aset-cardhead{ display:flex; align-items:center; gap:9px; }
.aset-cardhead .aset-ico{ width:20px; height:20px; }


/* ── round-106: /account/channels ─────────────────────────────────────────────────────────
   The matrix header stacks its icon over the channel name — three columns of identical
   checkboxes need something to track back up to, and side-by-side the header was wider than
   the column it labels, which forced the whole table into a horizontal scroll on a phone. */
/* table.pro carries min-width:520px, which is right for a wide data table and wrong for a
   4-column matrix living in a 286px settings column: it forced a horizontal scroll on the very
   screen the grid exists to be read at a glance on. Fixed layout instead — the class column
   takes what it needs and the three checkbox columns share the rest evenly. */
table.pro.chan-matrix{ min-width:0; table-layout:fixed; }
table.pro.chan-matrix th, table.pro.chan-matrix td{ padding:10px 4px; }
table.pro.chan-matrix th:first-child, table.pro.chan-matrix td:first-child{ padding-left:12px; }
.chan-matrix col.chan-c{ width:22%; }
table.pro.chan-matrix td:first-child{ font-size:.9rem; overflow-wrap:anywhere; }

/* overflow-wrap stays NORMAL here: breaking "WhatsApp" into "WhatsAp / p" is worse than a
   column one pixel tighter than ideal. The size is set so the longest label fits whole. */
.chan-th{ display:flex; flex-direction:column; align-items:center; gap:4px;
  font-size:.64rem; line-height:1.15; letter-spacing:0; overflow-wrap:normal; hyphens:none; }
/* the header band is dark green: the icon takes the header's own white, not the
   accent it uses on a white card. */
.chan-th .aset-ico{ width:18px; height:18px; color:inherit; }
.chan-th .aset-ico svg{ width:18px; height:18px; }

/* A channel card's action buttons keep their own line on a narrow screen rather than
   squeezing the name beside them. */
.aset-detail .card .aset-cardhead{ font-size:1rem; }


/* ── round-107: even rhythm in the standard header's right cluster ────────────────────────
   The row LOOKED unevenly spaced while being, technically, spaced perfectly: space-between
   equalises the gaps between BOXES, and the boxes were not the same size. A .nav-icon is a 42px
   box around a 22px glyph (10px of built-in air each side); .bell and .msg-btn were 28px boxes
   around 20px glyphs (4px); .avatar was 34px of solid image with none at all. So the eye saw
   22px between the icons left of Report and 6px between those on its right, from gaps the
   layout considered identical. Measured on a 375px viewport before: 22/22/25/19/10/6/12.

   Scoped to the ICON-shaped children BY NAME. A `> *` first cut also caught the signed-out
   "Sign in" button — a text pill — and squashed it into a square with its label hanging out;
   the visual suite caught that on mobile /get-app and /prepare. Written out longhand rather
   than with :is()/:where(): nothing else in this codebase uses them, and the app has to render
   in a Chrome-101 WebView on hardware still in the field. A cosmetic rule is not the place to
   be the first to find out.

   Appended at the end of the file deliberately: the round-95l squeeze at max-width:420px sets
   .nav-icon to 36px, but the base .nav-icon rule appears LATER at equal specificity and quietly
   wins, so anything that must hold has to come after both.

   The map header is separate markup and untouched. Round-179 (owner): the stories page's row is
   spread like every other user page now (it was packed right since round-96h). */
.site-header .nav-right > .nav-icon,
.site-header .nav-right > .nav-report,
.site-header .nav-right > .bell,
.site-header .nav-right > .avatar,
.site-header .nav-right > .nav-grid{
  width:42px; height:42px; flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center; padding:0;
}
/* above 900px Report is a labelled pill, not a circle — it keeps its own width */
@media (min-width:901px){
  .site-header .nav-right > .nav-report{ width:auto; height:auto; padding:9px 16px; }
}
/* the glyphs match too: a 20px bell beside a 22px map icon is the same unevenness one step down */
.site-header .nav-right > .nav-icon svg,
.site-header .nav-right > .bell svg,
.site-header .nav-right > .nav-grid svg{ width:22px; height:22px; }
.site-header .nav-right > .nav-report svg{ width:18px; height:18px; }
.site-header .nav-right .grid-btn{ width:100%; height:100%; }
/* round-144b: the phone-only search trigger must not sit beside the inline search box — the
   uniform-box rule above re-showed it on desktop. Higher specificity, placed after. */
.site-header .nav-right > .nav-icon.omni-open{ display:none; }
@media (max-width:1024px){ .site-header .nav-right > .nav-icon.omni-open{ display:inline-flex; } }

@media (max-width:640px){
  .site-header .nav-right > .nav-icon,
  .site-header .nav-right > .nav-report,
  .site-header .nav-right > .bell,
  .site-header .nav-right > .avatar,
  .site-header .nav-right > .nav-grid{ width:38px; height:38px; }
  .site-header .nav-right .avatar img,
  .site-header .nav-right .avatar-fallback{ width:30px; height:30px; }
}
@media (max-width:420px){
  .site-header .nav-right > .nav-icon,
  .site-header .nav-right > .nav-report,
  .site-header .nav-right > .bell,
  .site-header .nav-right > .avatar,
  .site-header .nav-right > .nav-grid{ width:36px; height:36px; }
  .site-header .nav-right > .nav-icon svg,
  .site-header .nav-right > .bell svg,
  .site-header .nav-right > .nav-grid svg{ width:20px; height:20px; }
  .site-header .nav-right > .nav-report svg{ width:17px; height:17px; }
  /* the round-95l scale(.92) shrank Report to 39px against its 42px neighbours; the uniform box
     now carries the sizing, so the transform only reintroduces the mismatch */
  .site-header .nav-right > .nav-report{ transform:none; }
  /* the avatar was the widest ink in the row and sat at the cramped end of it */
  .site-header .nav-right .avatar img,
  .site-header .nav-right .avatar-fallback{ width:28px; height:28px; }
}

/* ── round-111: /u/{id} — the composite profile (design 11) ───────────────────────────────
   The map surface and floating card from direction 03, 07's network row under a divider in the
   same card, 07's about, 10's badge wall, communities, 09's report cards, 06's quiet card.

   The map is a BACKDROP and plots nothing. Its height is fixed rather than viewport-relative:
   this page scrolls, so a vh-sized header would eat the fold on a short phone for a decoration. */
.pf-page{ padding:var(--header-h) 0 60px; }
.pf-mapwrap{ position:absolute; left:0; right:0; top:0; height:calc(var(--header-h) + 210px);
  overflow:hidden; z-index:0; pointer-events:none; }
.pf-mapbg{ position:absolute; inset:0;
  background:linear-gradient(150deg,#EFEAE2 0%,#E4E9E2 42%,#D8E3DD 100%); }
.pf-mapbg .leaflet-container{ background:transparent; }
/* the last stop is FULLY opaque: at .88 the map's bottom edge showed as a hard seam against
   the cream ground, because the wrapper ends there and the tiles simply stop. */
.pf-scrim{ position:absolute; inset:0;
  background:linear-gradient(180deg, rgba(241,235,228,.18) 0%, rgba(241,235,228,.06) 42%,
    rgba(241,235,228,.72) 82%, rgba(241,235,228,1) 100%); }
.pf-wrap{ position:relative; z-index:1; max-width:640px; }

/* the identity card floats on the map, as the sign-in card does on /login */
.pf-idc{ margin-top:138px; background:#fff; border-radius:20px; padding:18px;
  box-shadow:0 14px 38px rgba(44,59,54,.18); }
.pf-id{ display:flex; gap:14px; align-items:center; }
.pf-av{ width:58px; height:58px; border-radius:50%; object-fit:cover; flex:none; }
.pf-av-f{ background:var(--accent, #618D7C); color:#fff; display:grid; place-items:center;
  font-weight:800; font-size:1.4rem; }
.pf-idc h1{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.32rem; line-height:1.15;
  margin:0; overflow-wrap:anywhere; }
.pf-sub{ color:var(--ink-soft, #5C6E66); font-size:.88rem; margin:2px 0 0; }
.pf-acts{ display:flex; gap:8px; margin-top:14px; }
.pf-acts .btn{ flex:1; }
.pf-acts .pf-more{ flex:0 0 auto; padding:9px 14px; }
.pf-menu{ margin-top:8px; background:#fff; border-radius:12px; overflow:hidden;
  box-shadow:0 6px 20px rgba(44,59,54,.16); }
.pf-menu > *{ display:block; width:100%; text-align:left; padding:11px 14px; font-size:.92rem;
  color:var(--ink); border:0; background:none; cursor:pointer; }
.pf-menu > * + *{ border-top:1px solid rgba(173,195,185,.4); }

/* the per-service counter */
.pf-legend{ display:flex; gap:14px; flex-wrap:wrap; margin-top:14px; padding-top:13px;
  border-top:1px solid rgba(173,195,185,.45); }
.pf-legend span{ display:flex; align-items:center; gap:6px; font-size:.78rem;
  color:var(--ink-soft, #5C6E66); }
.pf-sv{ width:9px; height:9px; border-radius:50%; flex:none; display:inline-block; }

/* the divider, then the network row */
.pf-net{ margin-top:14px; padding-top:14px; border-top:1px solid rgba(173,195,185,.45); }
.pf-fr{ display:flex; gap:22px; flex-wrap:wrap; }
.pf-fr b{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.12rem; }
.pf-fr span{ color:var(--ink-soft, #5C6E66); font-size:.85rem; }
.pf-faces{ display:flex; align-items:center; gap:9px; margin-top:14px; padding-top:13px;
  border-top:1px solid rgba(173,195,185,.45); }
.pf-stack{ display:flex; flex:none; }
.pf-av-s{ width:30px; height:30px; font-size:.8rem; box-shadow:0 0 0 2.5px #fff; margin-left:-9px; }
.pf-stack .pf-av-s:first-child{ margin-left:0; }
.pf-mut{ font-size:.78rem; color:var(--ink-soft, #5C6E66); }

.pf-gl{ font-size:.72rem; text-transform:uppercase; letter-spacing:1.2px;
  color:var(--sage2, #7B988C); margin:22px 0 8px 4px; font-weight:700; }
/* white, like every other card on this page. The base .card is the bordered paper panel used
   across the site; next to the badge tiles and the report cards it read as a different kind of
   thing on the same page. */
.pf-card{ background:#fff; border:0; border-radius:16px; padding:18px;
  box-shadow:0 3px 12px rgba(44,59,54,.07); }
.pf-card p{ font-size:.94rem; margin:0; }
.pf-cm{ display:flex; justify-content:space-between; align-items:center; gap:10px; padding:11px 0;
  border-bottom:1px solid rgba(173,195,185,.4); }
.pf-cm:last-child{ border-bottom:0; }

/* 10's badge wall, without its level meter */
.pf-bw{ display:grid; grid-template-columns:repeat(auto-fill,minmax(118px,1fr)); gap:11px; }
.pf-b{ background:#fff; border-radius:15px; padding:15px 11px; text-align:center;
  box-shadow:0 3px 12px rgba(44,59,54,.07); }
.pf-b-ic{ width:46px; height:46px; border-radius:50%; display:grid; place-items:center;
  margin:0 auto 9px; background:var(--mint, #E7EDE6); color:var(--accent, #618D7C); }
.pf-b-ic svg{ width:22px; height:22px; }
.pf-b b{ display:block; font-size:.84rem; line-height:1.25; }

/* 09's report cards */
.pf-fc{ display:block; background:#fff; border-radius:16px; padding:16px; margin-top:11px;
  box-shadow:0 3px 12px rgba(44,59,54,.06); color:var(--ink); }
.pf-fc:hover{ box-shadow:0 6px 20px rgba(44,59,54,.12); text-decoration:none; }
.pf-fh{ display:flex; align-items:center; gap:8px; margin-bottom:8px; flex-wrap:wrap; }
.pf-fh b{ font-size:.72rem; letter-spacing:1px; color:var(--ink-soft, #5C6E66); }
.pf-fh .pf-sub{ font-size:.75rem; margin:0; }
.pf-fc h3{ font-size:1.02rem; margin:0 0 5px; }
.pf-fc .pf-sub{ font-size:.9rem; }
.pf-ff{ margin-top:11px; padding-top:11px; border-top:1px solid rgba(173,195,185,.4); }

/* 06's quiet card */
.pf-hid{ background:#fff; border-radius:14px; padding:15px 17px; display:flex; gap:11px;
  align-items:flex-start; box-shadow:0 3px 12px rgba(44,59,54,.06); }
.pf-hid-ic{ flex:none; color:var(--accent, #618D7C); margin-top:2px; }
.pf-hid-ic svg{ width:18px; height:18px; }
.pf-hid b{ font-size:.92rem; }
.pf-hid .pf-sub{ font-size:.8rem; margin-top:2px; }

@media (max-width:520px){
  .pf-mapwrap{ height:calc(var(--header-h) + 170px); }
  .pf-idc{ margin-top:104px; }
}

/* ── round-116: /dashboard as design 02 (tiles) ───────────────────────────────────────────
   Two tile grids with FIXED column counts, not auto-fit. auto-fit gives whatever number of
   columns happens to fit, which at ~900px meant five of the six readings across and the sixth
   alone on its own row beside half a screen of nothing. Six divides by two and three, so the
   readings are always a full block at every width.

   The action and announcement tiles get their own grid: they carry a sentence rather than a
   figure, so they want half a row each, not a sixth of one. */
.dash-page{ padding:var(--header-h) 0 60px; }
.dash-wrap{ max-width:1040px; }
.dash-h1{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.6rem; line-height:1.15;
  margin:18px 0 0; }
.dash-sub{ color:var(--ink-soft, #5C6E66); font-size:.9rem; margin:2px 0 0; }
.dash-note{ color:var(--ink-soft, #5C6E66); font-size:.85rem; margin:8px 0 0; }

.dash-gl{ font-size:.7rem; text-transform:uppercase; letter-spacing:1.2px;
  color:var(--sage2, #7B988C); font-weight:700; margin:22px 0 8px 4px; }
.dash-glrow{ display:flex; align-items:baseline; gap:9px; flex-wrap:wrap; margin:22px 0 8px 4px; }
.dash-glrow .dash-gl{ margin:0; }

.dash-tiles{ display:grid; grid-template-columns:repeat(2,1fr); gap:12px; }
@media (min-width:620px){ .dash-tiles{ grid-template-columns:repeat(3,1fr); } }
.dash-tiles2{ display:grid; grid-template-columns:1fr; gap:12px; }
@media (min-width:620px){ .dash-tiles2{ grid-template-columns:repeat(2,1fr); } }

.dash-tile{ background:#fff; border-radius:16px; padding:16px;
  box-shadow:0 4px 16px rgba(44,59,54,.07); }
.dash-tile > b{ display:block; font-family:var(--serif, Cambria, Georgia, serif);
  font-size:2.1rem; line-height:1; }
.dash-tile > span{ display:block; font-size:.72rem; text-transform:uppercase; letter-spacing:.9px;
  color:var(--sage2, #7B988C); margin-top:4px; }
.dash-tile .fo{ font-size:.8rem; color:var(--ink-soft, #5C6E66); margin-top:8px; }
.dash-tile.amber > b{ color:#8A6D2F; }
.dash-tile.red > b{ color:var(--st-impact, #C0392B); }
.dash-tile .bar{ height:6px; border-radius:99px; background:rgba(173,195,185,.35);
  margin-top:10px; overflow:hidden; }
.dash-tile .bar i{ display:block; height:100%; border-radius:99px; background:var(--accent, #618D7C); }
.dash-tile.amber .bar i{ background:#E8A33D; }
.dash-tile.red .bar i{ background:var(--st-impact, #C0392B); }

/* an action tile carries a sentence and a button rather than a figure */
.dash-tile.act{ display:flex; gap:12px; align-items:flex-start; }
.dash-tile.act .ic{ width:30px; height:30px; border-radius:50%; flex:none; display:grid;
  place-items:center; font-weight:800; font-size:.85rem;
  background:var(--mint, #E7EDE6); color:var(--deep, #4F8577); }
.dash-tile.act.hot{ background:#FCEFEC; }
.dash-tile.act.hot .ic{ background:#F3D9D3; color:#A2382A; }
.dash-tile.act.warn{ background:#FCF3E4; }
.dash-tile.act.warn .ic{ background:#F0DDBE; color:#8A6D2F; }
.dash-tile.act .bd{ flex:1; min-width:0; }
.dash-tile.act h3{ font-size:.98rem; line-height:1.25; margin-bottom:2px; }
.dash-tile.act p{ font-size:.82rem; color:var(--ink-soft, #5C6E66); margin:0; }
.dash-tile.act .btn{ margin-top:9px; }
.dash-tile.ann h3{ font-size:.98rem; line-height:1.25; margin:0 0 4px; }
.dash-tile.ann p{ font-size:.85rem; color:var(--ink-soft, #5C6E66); margin:0; }

.dash-card{ background:#fff; border-radius:16px; padding:6px 18px;
  box-shadow:0 4px 16px rgba(44,59,54,.07); }
.dash-fact{ font-size:.9rem; color:var(--ink-soft, #5C6E66); margin:12px 0; }
.dash-row{ display:flex; gap:11px; align-items:center; padding:12px 0; color:var(--ink);
  border-bottom:1px solid rgba(173,195,185,.4); flex-wrap:wrap; }
.dash-row:last-child{ border-bottom:0; }
.dash-row:hover{ text-decoration:none; }
/* Round-199aa: the title gets a readable measure before anything else wraps.
   It was `flex:1; min-width:0`, which lets a flex item shrink to nothing — so on a phone the
   pill and the two buttons took their space first and the TITLE wrapped instead of the ROW:
   "Water / rising / near flooding" one word per line, with the status pill floating inside the
   break. Seen on a G6. The row already had flex-wrap; it never got the chance to use it.
   A min-width turns that around — the title keeps its line, and the pill and buttons wrap
   underneath together, which is the break a reader expects. */
.dash-row .t{ display:flex; flex-direction:column; flex:1 1 55%; min-width:13rem; }
/* …except where 13rem is most of the screen: below that the title simply takes the full width
   and everything else follows it, rather than fighting for a line it cannot have. */
@media (max-width:360px){ .dash-row .t{ min-width:0; flex-basis:100%; } }
.dash-row .t .mut{ color:var(--ink-soft, #5C6E66); font-size:.8rem; }
.dash-emo{ font-size:1.15rem; flex:none; }
.dash-sv{ width:10px; height:10px; border-radius:50%; flex:none; background:var(--accent, #618D7C); }
.dash-sv.sv-flood{ background:#5EA9C1; } .dash-sv.sv-fire{ background:#C0392B; }
.dash-sv.sv-crime{ background:#6B5B95; } .dash-sv.sv-waste{ background:#BC8A52; }
.dash-sv.sv-weather{ background:#E8A33D; }

.dash-two{ display:grid; gap:14px; }
@media (min-width:880px){ .dash-two{ grid-template-columns:1fr 1fr; align-items:start; } }
/* one child, one column — an account with no SafeNet subjects should not get half a blank row */
.dash-two.solo{ grid-template-columns:1fr !important; }

/* ── round-117: /communities/{slug} as design 07-rail ─────────────────────────────────────
   Map header, sticky rail with stat tiles, real tabs. Grid children get min-width:0 — the
   mockup's own lesson: a grid item's min-width defaults to auto, and the tab row's unwrappable
   width forced the whole page into a sideways scroll on a phone. */
.cpage2{ padding-top:var(--header-h); }
.cpage2-map{ position:relative; z-index:0; height:230px; overflow:hidden;
  background:linear-gradient(150deg,#EFEAE2 0%,#E4E9E2 42%,#D8E3DD 100%); }
.cpage2-mapbg{ position:absolute; inset:0; }
.cpage2-mapbg .leaflet-container{ background:transparent; }
.cpage2-scrim{ position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg, rgba(241,235,228,.14) 0%, rgba(241,235,228,.05) 40%,
    rgba(241,235,228,.72) 80%, rgba(241,235,228,1) 100%); }

.cpage2-lay{ display:grid; gap:18px; margin-top:14px; padding-bottom:60px; }
/* Round-125: every pane at least viewport-tall, so the pinned search+tabs bar can STAY pinned
   across a tab switch — a short pane would otherwise clamp the scroll and snap the bar loose. */
.cpage2-main{ min-height:calc(var(--app-h, 100vh) - var(--header-h, 64px)); }
.cpage2-lay > *{ min-width:0; }
.cpage2-stick{ margin-top:-92px; position:relative; z-index:2; }
/* Round-125d: on phones the card was eating the boundary — the fence sat mostly behind the
   overlap. A taller map and a shallower overlap keep the geography readable above the card. */
@media (max-width:879px){
  .cpage2-map{ height:270px; }
  .cpage2-stick{ margin-top:-44px; }
}
.cpage2-idc{ box-shadow:0 14px 38px rgba(44,59,54,.18); border:0; border-radius:20px; }
.cpage2-idc h1{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.3rem;
  line-height:1.15; margin:0; }
.cpage2-sub{ color:var(--ink-soft, #5C6E66); font-size:.88rem; margin:2px 0 0; }
.cpage2-type{ color:var(--sage2, #7B988C); font-weight:700; letter-spacing:.5px;
  font-size:.78rem; margin:4px 0 0; }
.cpage2-acts{ display:flex; gap:8px; margin-top:14px; flex-wrap:wrap; }
.cpage2-acts .btn{ flex:1 1 auto; }
/* round-125b: .cpage2-settings retired — the alerts link left the rail card; the setting
   lives in Manage → "Your membership". */

.cpage2-cap{ display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin-top:12px; }
.cpage2-cap div{ background:#fff; border-radius:14px; padding:13px;
  box-shadow:0 3px 12px rgba(44,59,54,.07); }
.cpage2-cap b{ display:block; font-family:var(--serif, Cambria, Georgia, serif);
  font-size:1.4rem; line-height:1; }
.cpage2-cap span{ font-size:.7rem; color:var(--ink-soft, #5C6E66); display:block; margin-top:3px; }

@media (min-width:880px){
  /* Round-125: NO align-items:start — a sticky child can only travel inside its grid item,
     and start-alignment shrinks the rail to its content (186px), leaving the card zero room to
     stick. Default stretch gives it the full row. The visible card is the inner element, so the
     stretched container costs nothing visually. */
  .cpage2-lay{ grid-template-columns:300px 1fr; }
  .cpage2-stick{ position:sticky; top:calc(var(--header-h) + 18px); }
}

/* tabs: sticky under the site header, opaque ground, self-scrolling row */
.cpage2-tabs{ display:flex; gap:4px; border-bottom:1px solid rgba(173,195,185,.5);
  overflow-x:auto; }
.cpage2-tab{ padding:9px 13px; font-weight:700; font-size:.9rem; color:var(--ink-soft, #5C6E66);
  border-bottom:2.5px solid transparent; white-space:nowrap; display:inline-flex;
  align-items:center; gap:6px; }
.cpage2-tab:hover{ text-decoration:none; }
.cpage2-tab.on{ color:var(--ink, #2C3B36); border-bottom-color:var(--accent, #618D7C); }
.cpage2-tab .n{ display:inline-grid; place-items:center; min-width:18px; height:18px;
  border-radius:999px; background:var(--accent, #618D7C); color:#fff; font-size:.68rem;
  font-weight:700; padding:0 5px; }

.cpage2-gl{ font-size:.7rem; text-transform:uppercase; letter-spacing:1.2px;
  color:var(--sage2, #7B988C); font-weight:700; margin:20px 0 8px 4px; }
.cpage2-main > .cpage2-gl:first-of-type{ margin-top:0; }
.cpage2-card{ border:0; border-radius:16px; padding:16px 18px; margin-bottom:2px;
  background:#fff; box-shadow:0 3px 12px rgba(44,59,54,.07); }

.cpage2-fr{ display:flex; gap:11px; align-items:center; padding:10px 0; color:var(--ink);
  border-bottom:1px solid rgba(173,195,185,.4); }
.cpage2-fr:last-child{ border-bottom:0; }
.cpage2-fr:hover{ text-decoration:none; }
.cpage2-fr .t{ display:flex; flex-direction:column; flex:1; min-width:0; }
.cpage2-sv{ width:10px; height:10px; border-radius:50%; flex:none; }
.cpage2-av{ width:32px; height:32px; border-radius:50%; background:var(--sage2, #7B988C);
  color:#fff; display:flex; align-items:center; justify-content:center; font-weight:800;
  font-size:.8rem; }
.cpage2-postrow{ display:block; padding:10px 0; color:var(--ink);
  border-bottom:1px solid rgba(173,195,185,.4); }
.cpage2-postrow:last-child{ border-bottom:0; }
.cpage2-postrow:hover{ text-decoration:none; }
.cpage2-postrow h3{ font-size:.96rem; margin:4px 0 1px; }
.cpage2-more{ display:block; width:100%; text-align:center; padding:11px 0 4px;
  font-size:.86rem; font-weight:700; color:var(--deep, #4F8577); background:none; border:0;
  cursor:pointer; }
.cpage2-end{ text-align:center; font-size:.8rem; color:var(--sage2, #7B988C); margin-top:14px; }
.cpage2-search{ display:flex; gap:8px; margin-bottom:10px; }
.cpage2-search input{ flex:1; min-width:0; }

/* the score card keeps its round-91 look, renamed into this page's namespace */
.cpage2-score{ display:grid; grid-template-columns:auto 1fr; gap:4px 16px; align-items:center;
  background:#fff; border-radius:16px; padding:16px 18px; margin:14px 0;
  box-shadow:0 3px 12px rgba(44,59,54,.07); }
.cpage2-score .n{ font-family:var(--serif, Cambria, Georgia, serif); font-size:2.6rem;
  line-height:1; color:var(--score); grid-row:1 / 3; }
.cpage2-score .band{ font-weight:800; color:var(--score); }
.cpage2-score .lbl{ font-size:.82rem; color:var(--ink-soft, #5C6E66); }
.cpage2-score .why{ grid-column:1 / 3; font-size:.82rem; color:var(--ink-soft, #5C6E66);
  margin:6px 0 0; }

/* the reports timeline — 09's grammar, dots in service colours */
.cpage2-tl{ position:relative; padding-left:24px; }
.cpage2-tl:before{ content:""; position:absolute; left:7px; top:6px; bottom:6px; width:2px;
  background:rgba(173,195,185,.5); }
.cpage2-ev{ position:relative; margin-bottom:12px; }
.cpage2-ev .dot{ position:absolute; left:-22px; top:16px; width:11px; height:11px;
  border-radius:50%; box-shadow:0 0 0 3px var(--cream, #F1EBE4); }
.cpage2-ev .card{ display:block; padding:13px 16px; color:var(--ink); border:0;
  border-radius:14px; box-shadow:0 3px 12px rgba(44,59,54,.06); }
.cpage2-ev .card:hover{ text-decoration:none; box-shadow:0 6px 18px rgba(44,59,54,.12); }
.cpage2-ev .tm{ font-size:.7rem; letter-spacing:.9px; text-transform:uppercase;
  color:var(--sage2, #7B988C); font-weight:700; display:flex; gap:8px; align-items:baseline; }
.cpage2-ev .tm .st{ margin-left:auto; letter-spacing:0; text-transform:none; }
.cpage2-ev h3{ font-size:.98rem; margin:3px 0 1px; }

/* the leave modal — leaving is never one tap */
.cpage2-mback{ position:fixed; inset:0; background:rgba(36,53,48,.5); display:flex;
  align-items:center; justify-content:center; z-index:2900; padding:18px; }
.cpage2-modal{ background:#fff; border-radius:18px; padding:22px; max-width:400px; width:100%;
  box-shadow:0 18px 50px rgba(20,26,24,.35); }
.cpage2-modal h3{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.2rem;
  margin:0 0 8px; }
.cpage2-modal ul{ margin:10px 0 0 18px; font-size:.86rem; color:var(--ink-soft, #5C6E66); }
.cpage2-modal .acts{ display:flex; gap:9px; margin-top:18px; }
.cpage2-modal .acts .btn{ flex:1; }

/* Round-133b: the header-image sheet (.cpage2-bgsheet) was tried and removed the same day —
   the cog links to the backdrop pane and aquaPaneIntoView scrolls it under the pinned bar. */


/* ── round-117 (R2): the Manage tab, /account's row grammar in this page's namespace ── */
.cpage2-mgrp{ background:#fff; border-radius:14px; overflow:hidden;
  box-shadow:0 3px 12px rgba(44,59,54,.07); }
.cpage2-mrow{ display:flex; align-items:center; gap:12px; width:100%; text-align:left;
  padding:13px 16px; border:0; background:none; cursor:pointer; font:inherit;
  color:var(--ink, #2C3B36); border-bottom:1px solid rgba(173,195,185,.4); font-size:.94rem; }
.cpage2-mrow:last-child{ border-bottom:0; }
.cpage2-mrow:hover{ background:rgba(173,195,185,.12); text-decoration:none; }
.cpage2-mrow .t{ flex:1; min-width:0; display:flex; flex-direction:column; }
.cpage2-mrow .t .hint{ font-size:.76rem; color:var(--ink-soft, #5C6E66); }
.cpage2-mrow .ico{ width:20px; height:20px; flex:none; display:inline-grid; place-items:center;
  color:var(--accent, #618D7C); }
.cpage2-mrow .ico svg{ width:18px; height:18px; }
.cpage2-mrow .chev{ color:var(--sage2, #7B988C); }
.cpage2-mrow .chev svg{ width:16px; height:16px; transform:rotate(180deg); }
.cpage2-mrow.danger{ color:var(--st-impact, #C0392B); }
.cpage2-mrow.danger .ico{ color:var(--st-impact, #C0392B); }
.cpage2-subbar{ display:flex; align-items:center; gap:10px; margin-bottom:12px; }
.cpage2-subbar .back{ display:inline-grid; place-items:center; width:34px; height:34px;
  border-radius:50%; color:var(--deep, #4F8577); background:#fff;
  border:1.5px solid var(--track, #ADC3B9); box-shadow:0 1px 4px rgba(44,59,54,.08); }
.cpage2-subbar .back:hover{ background:rgba(97,141,124,.12); }
.cpage2-subbar .back svg{ width:18px; height:18px; }
.cpage2-subbar h2{ font-size:1.05rem; margin:0; }


/* ── round-119 (R4): the official voice ─────────────────────────────────────────────── */
.pill-official{ background:rgba(97,141,124,.16); color:var(--deep, #4F8577); font-weight:800;
  letter-spacing:.4px; text-decoration:none; }
.pill-official .dot{ background:var(--accent, #618D7C); }
a.pill-official:hover{ background:rgba(97,141,124,.26); text-decoration:none; }
.ann-card.ann-official{ border-left:4px solid var(--accent, #618D7C); }
/* the mirror card: a live official alert rendered into the announcements tab */
.cpage2-alert{ display:block; margin-bottom:12px; border-left:4px solid var(--st-impact, #C0392B);
  color:inherit; text-decoration:none; }
.cpage2-alert:hover{ text-decoration:none; box-shadow:0 6px 18px rgba(44,59,54,.12); }


/* ── round-120 → 125c: chatter, restyled to the 07 mockup — soft white bubbles, mint for
   mine, deep-coloured sender lines, the round pill + circular send. ── */
.cpage2-chat{ display:flex; flex-direction:column; padding-top:4px; }
.cpage2-daychip{ text-align:center; margin:8px 0 12px; }
.cpage2-daychip span{ display:inline-block; padding:3px 12px; border-radius:999px; font-size:.72rem;
  background:rgba(173,195,185,.35); color:var(--ink-soft, #5C6E66); }
.cpage2-msg{ position:relative; max-width:86%; align-self:flex-start;
  background:#fff; border-radius:14px; padding:9px 13px; margin:0 0 9px;
  box-shadow:0 2px 8px rgba(44,59,54,.06); }
.cpage2-msg.mine{ align-self:flex-end; margin-left:auto; background:#DDEBE2; }
.cpage2-msg .who{ font-size:.76rem; font-weight:700; color:var(--deep, #4F8577); margin:0 0 2px; }
.cpage2-msg .quote{ font-size:.8rem; margin:0 0 6px; padding:6px 9px; border-left:3px solid
  var(--accent, #618D7C); background:rgba(173,195,185,.18); border-radius:8px;
  color:var(--ink-soft, #5C6E66); overflow-wrap:anywhere; }
.cpage2-msg .quote b{ display:block; color:var(--deep, #4F8577); font-size:.74rem; }
.cpage2-msg .body{ font-size:.92rem; margin:0; overflow-wrap:anywhere; white-space:pre-wrap; }
.cpage2-msg .body.deleted{ font-style:italic; color:var(--sage2, #7B988C); }
.cpage2-msg .when{ display:block; text-align:right; font-size:.66rem; color:var(--sage2, #7B988C); margin-top:3px; }
.cpage2-msg .acts{ position:absolute; top:4px; left:calc(100% + 4px); display:none; gap:2px; }
.cpage2-msg.mine .acts{ left:auto; right:calc(100% + 4px); }
.cpage2-msg:hover .acts, .cpage2-msg:focus-within .acts{ display:flex; }
.cpage2-msg .acts button{ border:0; background:rgba(255,255,255,.9); border-radius:8px;
  cursor:pointer; font-size:.8rem; padding:3px 6px; box-shadow:0 1px 3px rgba(44,59,54,.15); }
/* 07's compose row: round pill + the circular deep send (!importants counter the global
   eight-:not() input rule) */
.cpage2-chatcompose{ display:flex; gap:9px; margin-top:10px; align-items:center;
  /* round-126b: pinned to the viewport bottom while in the room, the messenger's grammar —
     sticky, so it never covers the footer and needs no page-level layout surgery */
  position:sticky; bottom:12px; z-index:5; }
.cpage2-chatcompose input{ flex:1; min-width:0; background:#fff !important; border:0 !important;
  border-radius:999px !important; padding:11px 16px !important; font:inherit; font-size:16px;
  color:var(--ink, #2C3B36); box-shadow:0 3px 12px rgba(44,59,54,.10); outline:none; }
.cpage2-chatcompose .send{ width:42px; height:42px; flex:none; border:0; border-radius:50%;
  background:var(--deep, #4F8577); color:#fff; display:grid; place-items:center; cursor:pointer; }
.cpage2-chatcompose .send svg{ width:18px; height:18px; }
.cpage2-chatcompose .send:active{ transform:scale(.94); }
@media (max-width:640px){
  /* no hover on touch — the row of actions rides along under the bubble instead */
  .cpage2-msg .acts, .cpage2-msg.mine .acts{ position:static; display:flex; margin-top:2px;
    justify-content:flex-end; }
}


/* ── round-121 (R6) / round-122: in-community search — the 07 pill, above the tabs, both
   riding one sticky bar under the top nav; results overlay like the omni search. ── */
/* Round-128b: search + tabs stick TOGETHER at every width (the round-128 tabs-only phone
   variant lasted one round — user preference reversed). */
/* Round-194b: + --phead-h. This bar and .page-head were both sticking to var(--header-h), so on
   a page that has both (SafeNet) they claimed the same strip and the page head — z-index 400
   against this bar's 30 — simply covered it. The tabs looked like they were not sticky at all.
   --phead-h is the title row's measured height (site.js) and is absent when a page has no title
   row, which is why the fallback is 0. */
.cpage2-topbar{ position:sticky; top:calc(var(--header-h) + var(--phead-h, 0px)); z-index:30;
  background:var(--cream, #F1EBE4); padding:10px 0 0; margin-bottom:14px; }
.cpage2-search2{ position:relative; display:flex; align-items:center; gap:9px; background:#fff;
  border-radius:12px; border:1.5px solid var(--track, #ADC3B9); padding:0 12px;
  margin-bottom:10px; }
.cpage2-search2:focus-within{ border-color:var(--accent, #618D7C); }
.cpage2-search2 .ico{ display:inline-grid; place-items:center; color:var(--sage2, #7B988C);
  flex:none; }
.cpage2-search2 .ico svg{ width:17px; height:17px; }
/* !important, reluctantly: the global input rule at the top of this file stacks eight :not()
   selectors — specificity (0,8,1) — and the Chrome-101 baseline rules out :where() to duck it.
   The pill wrapper carries the chrome; the input inside it must be bare. */
.cpage2-search2 input{ flex:1; min-width:0; border:0 !important; outline:none; font:inherit;
  font-size:.9rem; padding:10px 0 !important; background:none !important;
  border-radius:0 !important; box-shadow:none !important; color:var(--ink, #2C3B36); }
.cpage2-search2 .clr{ border:0; background:none; cursor:pointer; font-weight:800; flex:none;
  color:var(--sage2, #7B988C); padding:4px; }
.cpage2-hits{ position:absolute; left:0; right:0; top:calc(100% + 6px); z-index:40;
  max-height:min(calc(var(--app-h, 100vh) * 0.6), 420px); overflow-y:auto; padding:10px 14px;
  box-shadow:0 10px 30px rgba(44,59,54,.18); }
.cpage2-hits .hit{ display:block; padding:7px 4px; font-size:.9rem; color:var(--ink, #2C3B36);
  border-bottom:1px solid rgba(173,195,185,.35); text-decoration:none; overflow-wrap:anywhere; }
.cpage2-hits .hit:last-child{ border-bottom:0; }
.cpage2-hits .hit:hover{ color:var(--deep, #4F8577); }


/* ── round-122: virtual-community backdrop + the cog ─────────────────────────────────── */
.cpage2-bg{ position:absolute; inset:0; background-image:var(--bgd);
  background-size:cover; background-position:center; }
@media (max-width:1023px){ .cpage2-bg{ background-image:var(--bgt); } }
@media (max-width:639px){ .cpage2-bg{ background-image:var(--bgm); } }
.cpage2-cog{ position:absolute; top:12px; right:14px; z-index:3; display:inline-grid;
  place-items:center; width:36px; height:36px; border-radius:50%;
  background:rgba(255,255,255,.88); color:var(--deep, #4F8577); cursor:pointer; padding:0;
  border:1.5px solid var(--track, #ADC3B9); box-shadow:0 2px 8px rgba(44,59,54,.18); }
.cpage2-cog:hover{ background:#fff; text-decoration:none; }
.cpage2-cog svg{ width:18px; height:18px; }


/* ── round-122b: the community search as a phone sheet — the omni sheet's rules, this page's
   elements. Same html.search-open lock, same --vvt pan offset, same vh-then---vvh sizing (the
   dvh line is DROPPED as a parse error on Chrome 101, which is exactly the plan). ── */
.cpage2-search2 .cclose{ display:none; }
@media (max-width:900px){
  html.csearch-open .site-header, html.csearch-open #signupBar{ visibility:hidden; }
}
.cpage2-search2 input::-webkit-search-cancel-button{ -webkit-appearance:none; appearance:none; }
@media (max-width: 900px){
  /* The sticky topbar's own z-index:30 makes it a stacking context, which would IMPRISON the
     fixed sheet below the header's z2100 no matter what z the sheet claims. While the sheet is
     open the wrapper steps out of the way; html.search-open is set by the same code path. */
  html.search-open .cpage2-topbar{ position:static; z-index:auto; }
  .cpage2-search2.sheet{
    /* 2300: above the site header (2100) and the signup bar — this sheet lives OUTSIDE the
       header, so unlike the omni sheet it must out-stack the chrome instead of replacing it. */
    position:fixed; inset:0; top:var(--vvt, 0px); z-index:2300;
    display:grid;
    grid-template-columns:auto auto minmax(0,1fr) auto;   /* back | icon | input | clear */
    grid-template-rows:auto minmax(0,1fr);                /* bar  | scrollable results */
    align-items:center; gap:8px;
    margin:0; border:0; border-radius:0;
    padding:calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 10px) 12px 0;
    background:var(--paper, #FBFBF8);
    height:var(--app-h, 100vh);
    height:var(--vvh, var(--app-h, 100dvh));
  }
  .cpage2-search2.sheet .cclose{
    display:inline-flex; align-items:center; justify-content:center;
    grid-column:1; grid-row:1; width:36px; height:36px; border:0; background:none;
    cursor:pointer; color:var(--deep, #4F8577); border-radius:50%;
  }
  .cpage2-search2.sheet .cclose svg{ width:20px; height:20px; }
  /* the omni sheet's exact bar: back | pill input | clear — no inline magnifier, the input
     itself is the pill (the !importants counter the pill-mode strip-down, which also carries
     !important against the global eight-:not() input rule) */
  .cpage2-search2.sheet .ico{ display:none; }
  .cpage2-search2.sheet input{
    grid-column:3; grid-row:1; height:44px; margin:0; padding:0 12px !important;
    border:1.5px solid var(--track, #ADC3B9) !important; border-radius:999px !important;
    background:#FBFBF8 !important;
    font-size:16px !important;   /* THE iOS zoom threshold — do not lower this */
  }
  .cpage2-search2.sheet input:focus{ outline:none; border-color:var(--accent, #618D7C) !important; }
  .cpage2-search2.sheet .clr{
    grid-column:4; grid-row:1; display:inline-flex; align-items:center; justify-content:center;
    width:var(--close-size, 44px); height:var(--close-size, 44px); padding:0; border-radius:50%;
    color:var(--ink-soft, #5C6E66); font-size:1.1rem; font-weight:400; line-height:1;
  }
  .cpage2-search2.sheet .cpage2-hits{ background:var(--paper, #FBFBF8); }
  .cpage2-search2.sheet .cpage2-hits{
    grid-column:1 / -1; grid-row:2; align-self:stretch; min-height:0;
    position:static; margin:0 -12px; max-height:none; overflow-y:auto;
    border-radius:0; box-shadow:none;
  }
}


/* ── round-123: no scrollbar rails on phones — Android draws a persistent gutter where iOS
   already overlays an auto-hiding one; below the desktop breakpoint the rail is noise. Scoped
   to small screens so desktop keeps its affordance. ── */
@media (max-width: 900px){
  html{ scrollbar-width:none; }                     /* firefox */
  ::-webkit-scrollbar{ width:0; height:0; }          /* chrome/webview */
}


/* ── round-125's condensing bar RETIRED (round-132, user call): the pinned bar keeps BOTH the
   search pill and the tabs at every width — the same bar whether resting or stuck. Phones
   already worked this way (round-128c); desktop now matches. ── */


/* ── round-126: the back arrow in the logo slot ─────────────────────────────────────── */
.nav-back{ display:none; }
html.pg-child .site-header .brand{ display:none; }
html.pg-child .site-header .nav-back{
  display:inline-flex; align-items:center; justify-content:center; flex:none;
  width:48px; height:48px; padding:0; border:0; border-radius:50%;
  background:none; color:var(--deep, #4F8577); cursor:pointer;
}
.nav-back svg{ width:31px; height:31px; }   /* round-126b: +40% per request */
html.pg-child .site-header .nav-back:hover{ background:rgba(97,141,124,.12); }


/* ── round-127: announcements as 07 — teaser, lock row, post cards ───────────────────── */
.cpage2-compose{ position:sticky; top:calc(var(--header-h) + 52px); z-index:10;
  display:flex; gap:11px; align-items:center; background:#fff; border-radius:16px;
  padding:13px 16px; margin-bottom:10px; cursor:pointer;
  box-shadow:0 6px 20px rgba(44,59,54,.13); }
.cpage2-compose .fake{ flex:1; min-width:0; color:var(--sage2, #7B988C); font-size:.9rem; }
.cpage2-lockrow{ background:#fff; border-radius:12px; padding:10px 14px; text-align:center;
  font-size:.8rem; color:var(--ink-soft, #5C6E66); margin-bottom:10px;
  box-shadow:0 2px 8px rgba(44,59,54,.06); }
.cpage2-lockrow a{ font-weight:700; }
.cpage2-post{ background:#fff; border-radius:16px; padding:18px; margin-bottom:12px;
  box-shadow:0 4px 16px rgba(44,59,54,.07); }
.cpage2-post.pin{ box-shadow:0 6px 22px rgba(44,59,54,.13);
  border-top:3px solid var(--accent, #618D7C); }
.cpage2-post.ann-official{ border-left:4px solid var(--accent, #618D7C); }
.cpage2-post .pinlab{ font-size:.68rem; text-transform:uppercase; letter-spacing:1.1px;
  color:var(--accent, #618D7C); font-weight:700; margin-bottom:5px; }
.cpage2-post h3{ font-size:1.08rem; margin-bottom:5px; }
.cpage2-post .pf{ display:flex; align-items:center; gap:8px; margin-top:12px; padding-top:11px;
  border-top:1px solid rgba(173,195,185,.4); flex-wrap:wrap; }


/* ── round-128c: both search sheets' back arrows sized like the nav back arrow (31px glyph
   in a 48px target) — one back-arrow size across the app. ── */
@media (max-width:900px){
  /* round-175: sizes now come from the shared close-button block at the end of the file */
}


/* ── round-129: the communities directory — 10's shelves front, 03's mosaic see-all, one
   shared card anatomy (hero fence/backdrop, type chip, avatar stack, Open + Join). ── */
.cdir-h1{ font-family:var(--serif, Cambria, Georgia, serif); font-size:2rem; margin-bottom:12px; }
.cdir-search{ max-width:640px; }
.cdir-shelf{ margin-top:26px; }
.cdir-shelf .head{ display:flex; align-items:baseline; gap:12px; margin-bottom:11px; }
.cdir-shelf h2{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.25rem; margin:0; }
.cdir-shelf .all{ margin-left:auto; font-size:.8rem; font-weight:700; white-space:nowrap; }
.cdir-shelf .scroll{ display:flex; gap:13px; overflow-x:auto; padding-bottom:8px;
  scroll-snap-type:x mandatory; -webkit-overflow-scrolling:touch; }
.cdir-shelf .scroll > *{ flex:0 0 250px; scroll-snap-align:start; }

.cdir-card{ background:#fff; border-radius:18px; overflow:hidden;
  box-shadow:0 4px 16px rgba(44,59,54,.08); display:flex; flex-direction:column; }
.cdir-hero{ height:104px; position:relative; display:grid; place-items:center; flex:none; }
.cdir-hero.fence{ background:linear-gradient(135deg,#E4EDE6,#D8E5DD); }
.cdir-hero.fence svg{ width:64%; height:76%; }
.cdir-hero.pic{ background-size:cover; background-position:center; background-color:#fff; }
.cdir-hero .type{ position:absolute; left:10px; top:10px; background:rgba(255,255,255,.92);
  border-radius:999px; padding:3px 11px; font-size:.66rem; font-weight:800; letter-spacing:.6px;
  text-transform:uppercase; color:var(--ink, #2C3B36); }
.cdir-hero .unread{ position:absolute; right:10px; top:10px; background:var(--deep, #4F8577);
  color:#fff; border-radius:999px; min-width:22px; height:22px; display:grid; place-items:center;
  font-size:.72rem; font-weight:800; padding:0 6px; }
.cdir-body{ padding:12px 15px 14px; display:flex; flex-direction:column; flex:1; }
.cdir-body h3{ font-size:.98rem; margin:0; }
.cdir-body .mut{ font-size:.76rem; color:var(--ink-soft, #5C6E66); }
.cdir-body .clamp{ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; margin:2px 0 0; }
.cdir-body .meta{ display:flex; align-items:center; gap:8px; margin-top:8px; }
.cdir-body .avs{ display:flex; }
.cdir-body .avs span{ width:24px; height:24px; border-radius:50%; background:var(--sage2, #7B988C);
  color:#fff; display:grid; place-items:center; font-size:.66rem; font-weight:800;
  border:2px solid #fff; margin-left:-7px; overflow:hidden; }
.cdir-body .avs span:first-child{ margin-left:0; }
.cdir-body .avs img{ width:100%; height:100%; object-fit:cover; }
.cdir-body .acts{ display:flex; gap:8px; margin-top:auto; padding-top:11px; }
.cdir-body .acts .open{ flex:1; text-align:center; border:1.5px solid var(--track, #ADC3B9);
  border-radius:999px; padding:7px; font-size:.8rem; font-weight:700;
  color:var(--ink-soft, #5C6E66); }
.cdir-body .acts .join{ flex:1; text-align:center; border:0; background:var(--deep, #4F8577);
  color:#fff; border-radius:999px; padding:7px; font:inherit; font-size:.8rem; font-weight:700;
  cursor:pointer; }
.cdir-body .acts a.join{ display:block; }
.cdir-body .acts .join.in{ background:var(--mint, #E7EDE6); color:var(--deep, #4F8577); }
.cdir-body .acts .open:hover, .cdir-body .acts .join:hover{ text-decoration:none; }
.cdir-new{ border:2px dashed var(--track, #ADC3B9); background:none; box-shadow:none;
  display:grid; place-items:center; text-align:center; color:var(--ink-soft, #5C6E66);
  padding:20px 14px; font:inherit; cursor:pointer; min-height:180px; }
.cdir-new b{ color:var(--deep, #4F8577); }
.cdir-new:hover{ text-decoration:none; }
.cdir-foot{ text-align:center; margin-top:26px; font-size:.9rem; }
.cdir-foot a{ font-weight:700; }

/* the see-all mosaic (03) */
.cdir-mas{ columns:3; column-gap:14px; margin-top:20px; }
@media (max-width:900px){ .cdir-mas{ columns:2; } }
@media (max-width:560px){ .cdir-mas{ columns:1; } }
.cdir-mitem{ break-inside:avoid; margin-bottom:14px; }


/* ── Round-134: SafeNet as 11-tabs — Today / People (Places + Setup in R3) ─────────────────────
   The tab chrome is .cpage2-topbar/.cpage2-tabs, reused verbatim. Band colours are defined ONCE
   here as tokens; C# and JS speak in accent keys ("info|warn|danger|emergency"), never hex. */
:root{
  --sn-info:#4F8577; --sn-warn:#E8A33D; --sn-danger:#C0392B; --sn-emergency:#8E1E12;
}
.sn2-h1{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.85rem; }
.sn2-status{ display:inline-flex; gap:8px; align-items:center; background:#fff; border-radius:999px;
  padding:6px 14px; font-size:.8rem; font-weight:700; box-shadow:0 3px 10px rgba(44,59,54,.08);
  margin-top:6px; }
.sn2-status .d{ width:9px; height:9px; border-radius:50%; background:var(--sn-info); flex:none; }
.sn2-status .d.warn{ background:var(--sn-warn); }
.sn2-status .d.danger, .sn2-status .d.emergency{ background:var(--sn-danger); }
.sn2-bar{ margin-top:12px; }
.sn2-lede{ color:var(--ink-soft, #5C6E66); font-size:.92rem; max-width:560px; margin:14px 0 16px; }
.cpage2-tab .n.warn{ background:var(--sn-warn); color:#fff; }

/* the need card — 07-quiet grammar: white card, band left border, plain-language title */
.sn2-need{ background:#fff; border-radius:16px; padding:14px 18px; margin-bottom:12px;
  box-shadow:0 4px 14px rgba(44,59,54,.09); border-left:6px solid var(--sn-warn); }
.sn2-need.info{ border-color:var(--sn-info); }
.sn2-need.danger{ border-color:var(--sn-danger); }
.sn2-need.emergency{ border-color:var(--sn-emergency); }
.sn2-need h3{ font-size:1rem; margin:0; }
.sn2-need p{ font-size:.85rem; color:var(--ink-soft, #5C6E66); margin:3px 0 10px; }
.sn2-need .acts{ display:flex; gap:8px; flex-wrap:wrap; }

/* the cast strip — presence ring is the message; dashed = asset, tick = shared with me */
.sn2-cast{ display:flex; gap:14px; margin:6px 0 14px; overflow-x:auto; padding-bottom:6px; }
.sn2-c{ flex:0 0 auto; text-align:center; width:76px; color:var(--ink, #2C3B36); min-width:0; }
.sn2-c:hover{ text-decoration:none; }
.sn2-c .av{ width:54px; height:54px; border-radius:50%; background:#fff; margin:0 auto;
  display:flex; align-items:center; justify-content:center; font-size:1.4rem; overflow:hidden;
  border:3px solid var(--track, #ADC3B9); box-shadow:0 3px 10px rgba(44,59,54,.1); }
.sn2-c .av img{ width:100%; height:100%; object-fit:cover; }
.sn2-c.snf-live .av{ border-color:#2E9E5B; }
.sn2-c.snf-recent .av{ border-color:var(--sn-warn); }
.sn2-c.asset .av{ border-style:dashed; }
.sn2-c.add .av{ border-style:dotted; font-size:1.15rem; }
.sn2-c b{ font-size:.76rem; display:block; margin-top:4px; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; }
.sn2-c small{ font-size:.64rem; color:var(--ink-soft, #5C6E66); display:block; line-height:1.2;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

/* the day spine — 04's grammar, ported from the mockup */
.sn2-day{ position:relative; padding-left:86px; margin-top:8px; }
.sn2-day::before{ content:""; position:absolute; left:64px; top:6px; bottom:6px; width:2px;
  background:var(--track, #ADC3B9); }
.sn2-day .hr{ position:relative; margin-bottom:9px; }
.sn2-day .hr .t{ position:absolute; left:-86px; width:52px; text-align:right; font-size:.72rem;
  font-weight:800; color:var(--ink-soft, #5C6E66); top:14px; }
.sn2-day .node{ position:absolute; left:-27px; top:16px; width:12px; height:12px; border-radius:50%;
  background:var(--sn-info); border:3px solid var(--cream, #F1EBE4); }
.sn2-day .hr.warn .node{ background:var(--sn-warn); }
.sn2-day .hr.danger .node, .sn2-day .hr.emergency .node{ background:var(--sn-danger);
  box-shadow:0 0 0 5px rgba(192,57,43,.14); }
.sn2-day .ev{ background:#fff; border-radius:14px; padding:11px 15px;
  box-shadow:0 3px 10px rgba(44,59,54,.07); display:flex; gap:12px; align-items:center; }
.sn2-day .ev .who{ width:34px; height:34px; border-radius:50%; background:var(--mint, #E7EDE6);
  display:flex; align-items:center; justify-content:center; font-size:.95rem; flex:0 0 auto; }
.sn2-day .ev .tx{ min-width:0; }
.sn2-day .ev b{ font-size:.9rem; display:block; }
.sn2-day .ev small{ color:var(--ink-soft, #5C6E66); font-size:.76rem; display:block; }
.sn2-day .ev .risk{ margin-left:auto; font-size:.64rem; font-weight:800;
  color:var(--ink-soft, #5C6E66); background:var(--mint, #E7EDE6); border-radius:999px;
  padding:3px 9px; flex:0 0 auto; white-space:nowrap; }
.sn2-day .hr.warn .risk{ background:#FBEED7; color:#8a5a12; }
.sn2-day .hr.danger .risk, .sn2-day .hr.emergency .risk{ background:#F6DEDA; color:var(--sn-danger); }
.sn2-day .gap{ color:var(--ink-soft, #5C6E66); font-size:.76rem; font-style:italic;
  padding:8px 0 12px; }
.sn2-day .now{ position:relative; background:var(--deep, #4F8577); color:#fff; border-radius:14px;
  padding:11px 15px; font-size:.86rem; margin-top:2px; }
.sn2-day .now::before{ content:""; position:absolute; left:-27px; top:14px; width:12px; height:12px;
  border-radius:50%; background:var(--deep, #4F8577); box-shadow:0 0 0 5px rgba(79,133,119,.25); }
@media (max-width: 560px){
  .sn2-day{ padding-left:64px; }
  .sn2-day::before{ left:46px; }
  .sn2-day .hr .t{ left:-64px; width:38px; font-size:.64rem; }
  .sn2-day .node, .sn2-day .now::before{ left:-23px; }
}

/* ── Round-134 R2: the People cards (03's grammar) ── */
.sn2-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); gap:16px;
  margin-top:6px; }
.sn2-grid > *{ min-width:0; }
.sn2-card{ background:#fff; border-radius:18px; overflow:hidden;
  box-shadow:0 4px 16px rgba(44,59,54,.08); }
/* the mini map strip carries the presence colour as its bottom border */
.sn2-strip{ height:110px; position:relative; border-bottom:3px solid var(--track, #ADC3B9);
  background:linear-gradient(115deg, #EAF0E9, #DAE6DD); z-index:0; }   /* contains Leaflet panes */
.sn2-strip.snf-live{ border-color:#2E9E5B; }
.sn2-strip.snf-recent{ border-color:var(--sn-warn); }
.sn2-strip.empty{ display:flex; align-items:center; justify-content:center; font-size:1.8rem; }
.sn2-card .b{ padding:13px 16px 15px; }
.sn2-card .name b{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.08rem; }
.sn2-card .name small{ color:var(--ink-soft, #5C6E66); font-size:.74rem; margin-left:6px; }
.sn2-card .pres{ margin-top:3px; font-size:.82rem; color:var(--ink-soft, #5C6E66); }
.sn2-gnote{ margin-top:10px; background:var(--mint, #E7EDE6); border-radius:10px;
  padding:7px 11px; font-size:.78rem; }
.sn2-gnote.warn{ background:#FBEED7; }
.sn2-acts{ display:flex; gap:7px; margin-top:12px; flex-wrap:wrap; }
.sn2-acts a, .sn2-acts button{ border:1.5px solid var(--track, #ADC3B9); background:none;
  border-radius:999px; padding:6px 13px; font-family:var(--font, Calibri, sans-serif);
  font-size:.78rem; font-weight:700; color:var(--ink-soft, #5C6E66); cursor:pointer; }
.sn2-acts .pri{ background:var(--deep, #4F8577); border-color:var(--deep, #4F8577); color:#fff; }
.sn2-acts a:hover{ text-decoration:none; }
.sn2-fold{ border-top:1px solid var(--track, #ADC3B9); padding:14px 16px 16px; }
.sn2-newc{ border:2px dashed var(--track, #ADC3B9); background:none; border-radius:18px;
  display:flex; align-items:center; justify-content:center; text-align:center;
  color:var(--ink-soft, #5C6E66); padding:30px 18px; font-family:var(--font, Calibri, sans-serif);
  font-size:.9rem; cursor:pointer; }
.sn2-newc b{ color:var(--deep, #4F8577); }
/* subject marks drawn on zone heroes / the setup preview (aquaPlaceMaps.setMarks) */
.sn-mark{ width:30px; height:30px; border-radius:50%; background:#fff;
  border:2.5px solid var(--deep, #4F8577); display:flex; align-items:center; justify-content:center;
  font-size:.9rem; box-shadow:0 3px 8px rgba(44,59,54,.2); }
.sn-mark.stale{ border-color:var(--track, #ADC3B9); opacity:.75; }

/* ── Round-134 R3: Places (08) + Setup (09) ── */
.sn2-strip.zone{ height:120px; }
.sn2-winchip{ display:inline-block; margin-top:4px; background:var(--mint, #E7EDE6);
  border-radius:999px; padding:2px 11px; font-size:.66rem; font-weight:800;
  color:var(--ink-soft, #5C6E66); letter-spacing:.5px; }
.occ-in{ color:#2E9E5B; font-weight:800; }
.sn2-zlog{ margin-top:9px; border-top:1px dashed var(--track, #ADC3B9); padding-top:8px;
  font-size:.78rem; color:var(--ink-soft, #5C6E66); }
.sn2-zlog span{ display:block; }
.sn2-zlog b{ color:var(--ink, #2C3B36); }
.sn2-zedit{ max-width:560px; }
/* Round-142m (user report): the place-search results were unstyled buttons — a long
   Nominatim label ran as one line over the box edge. A list card now: one row per hit,
   wrapping, the region muted, the row a real target. */
.sn2-zresults{ margin-top:6px; border:1.5px solid var(--track, #ADC3B9); border-radius:12px;
  background:#fff; overflow:hidden; box-shadow:0 6px 18px rgba(44,59,54,.10); }
.sn2-zresults button{ display:block; width:100%; text-align:left; border:0; background:none;
  padding:10px 12px; cursor:pointer; font-family:var(--font, Calibri, sans-serif); font-size:.9rem;
  line-height:1.35; color:var(--ink, #2C3B36); white-space:normal; word-break:break-word;
  border-bottom:1px solid var(--track, #ADC3B9); }
.sn2-zresults button:last-child{ border-bottom:0; }
.sn2-zresults button:hover, .sn2-zresults button:focus-visible{ background:var(--mintbg, #E7EDE6); outline:none; }
.sn2-zresults button b{ font-weight:700; }
.sn2-zresults button small, .sn2-zresults button .rg{ color:var(--ink-soft, #5C6E66); font-size:.8rem; }
.sn2-mapgo{ text-decoration:none; display:block; }
/* Round-142o: the drawn-area editor — the switch, the corner bar, the crosshair map */
.sn2-shapeseg{ max-width:320px; margin-bottom:4px; }
.sn2-drawbar{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:0 0 10px; }
.sn2-drawn{ font-size:.86rem; font-weight:700; color:var(--deep, #4F8577); margin-right:auto; }
.sn2-zpick.drawing{ box-shadow:0 0 0 3px rgba(79,133,119,.25); }
.sn-ctoggle{ display:flex; align-items:center; gap:8px; font-size:.84rem; font-weight:700; color:var(--ink, #2C3B36);
  padding:8px 10px; margin:0 0 6px; border-radius:10px; background:var(--mintbg, #E7EDE6); cursor:pointer; }
.sn-ctoggle input{ width:auto; margin:0; accent-color:var(--deep, #4F8577); }
.sn-ctoggle small{ color:var(--ink-soft, #5C6E66); font-weight:600; }
.sn2-mapgo b{ color:var(--deep, #4F8577); }
.sn2-zpick{ height:240px; border-radius:12px; overflow:hidden;
  border:1.5px solid var(--track, #ADC3B9); position:relative; z-index:0; }
/* the setup rail: steps left, the live preview right (sticky below header + tab bar) */
.sn2-cols{ display:grid; grid-template-columns:1.05fr .95fr; gap:22px; align-items:start; }
@media (max-width: 820px){ .sn2-cols{ grid-template-columns:1fr; } }
.sn2-cols > *{ min-width:0; }
.sn2-step{ position:relative; background:#fff; border-radius:16px; padding:16px 18px 16px 58px;
  margin-bottom:12px; box-shadow:0 4px 14px rgba(44,59,54,.07); }
.sn2-step .n{ position:absolute; left:16px; top:16px; width:28px; height:28px; border-radius:50%;
  background:var(--deep, #4F8577); color:#fff; display:flex; align-items:center;
  justify-content:center; font-weight:800; font-size:.85rem; }
.sn2-step.done .n{ background:#2E9E5B; }
.sn2-step.locked{ opacity:.65; }
.sn2-step.locked .n{ background:var(--track, #ADC3B9); }
.sn2-step h3{ font-size:1rem; margin:0; }
.sn2-step p{ font-size:.84rem; color:var(--ink-soft, #5C6E66); margin:3px 0 0; }
.sn2-step .state{ margin-top:9px; font-size:.8rem; }
.sn2-step .state .pill{ background:var(--mint, #E7EDE6); border-radius:999px; padding:3px 11px;
  font-weight:700; margin:0 6px 4px 0; display:inline-block; }
.sn2-step button{ margin-top:9px; border:0; background:var(--deep, #4F8577); color:#fff;
  border-radius:999px; padding:7px 15px; font-family:var(--font, Calibri, sans-serif);
  font-size:.79rem; font-weight:700; cursor:pointer; }
.sn2-step button.o{ background:none; border:1.5px solid var(--track, #ADC3B9);
  color:var(--ink-soft, #5C6E66); }
/* The DONE tag shares a flex row with the title rather than floating at the card corner —
   absolutely positioned, it collided with wrapped titles on phone-width cards, and localized
   strings run longer than "DONE". */
.sn2-step .hd{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.sn2-step .hd h3{ min-width:0; }
.sn2-step .tick{ flex:0 0 auto; color:#2E9E5B; font-weight:800; font-size:.78rem; }
.sn2-preview{ background:#fff; border-radius:18px; overflow:hidden;
  box-shadow:0 6px 20px rgba(44,59,54,.1); position:sticky;
  top:calc(var(--header-h, 64px) + 64px); }
.sn2-preview .pv-map{ height:190px; position:relative; z-index:0; }
.sn2-preview .pv-b{ padding:14px 18px 16px; }
.sn2-preview .pv-b h3{ font-family:var(--serif, Cambria, Georgia, serif); font-size:1.1rem; }
.sn2-preview .pv-row{ display:flex; justify-content:space-between; align-items:center;
  font-size:.84rem; padding:8px 0; border-bottom:1px dashed var(--track, #ADC3B9); }
.sn2-preview .pv-row:last-of-type{ border:0; }
.sn2-preview .pv-row .ok{ color:#2E9E5B; font-weight:800; }
.sn2-preview .pv-row .todo{ color:var(--ink-soft, #5C6E66); }
.sn2-preview .hintline{ margin-top:10px; background:var(--mint, #E7EDE6); border-radius:10px;
  padding:8px 12px; font-size:.78rem; }

/* ── Round-134 R4: the live-map SafeNet console (02-console grammar, light theme) ── */
/* The minimized button: permanently the 44px chip — it never expands in place, it opens the
   side panel instead. .map-card.min supplies the circle; this pins it there. */
.sn-console-btn{ width:44px !important; height:44px; padding:10px !important; cursor:pointer;
  border:0; display:flex; align-items:center; justify-content:center; }
.sn-console-btn .card-ico{ margin:0; }
.sn-console-btn.warn{ box-shadow:0 0 0 3px rgba(232,163,61,.55); }
/* console content inside the shared #stationPanel body */
.sn-ck{ font-size:.68rem; font-weight:800; letter-spacing:1.2px; text-transform:uppercase;
  color:var(--ink-soft, #5C6E66); margin:14px 2px 8px; }
.sn-ck:first-child{ margin-top:2px; }
/* Round-139g: rows wear the person panel's TILE grammar (design 06-metric-tiles) */
.sn-crow{ display:flex; gap:12px; align-items:center; padding:12px 14px; border-radius:16px;
  cursor:pointer; background:#F6F8F6; margin-bottom:10px; }
.sn-crow:hover{ background:var(--mint, #E7EDE6); }
.sn-crow.danger{ border-left:4px solid var(--sn-danger); }
.sn-crow .av{ width:40px; height:40px; border-radius:50%; background:var(--mint, #E7EDE6);
  display:flex; align-items:center; justify-content:center; font-size:1.1rem; overflow:hidden;
  border:2.5px solid var(--track, #ADC3B9); flex:0 0 auto; }
.sn-crow .av img{ width:100%; height:100%; object-fit:cover; }
.sn-crow .av.asset{ border-style:dashed; }
.sn-crow .av.p-live{ border-color:#2E9E5B; }
.sn-crow .av.p-recent{ border-color:var(--sn-warn); }
.sn-crow .tx{ min-width:0; flex:1; }
.sn-crow .tx b{ font-size:.9rem; display:block; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; }
.sn-crow .tx small{ font-size:.72rem; color:var(--ink-soft, #5C6E66); display:block;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sn-crow .bat{ font-size:.7rem; color:var(--ink-soft, #5C6E66); flex:0 0 auto; }
.sn-crow .bat.low{ color:var(--sn-warn); font-weight:800; }
.sn-crow .go{ border:0; background:none; font-size:1.15rem; color:var(--deep, #4F8577);
  cursor:pointer; padding:4px 8px; flex:0 0 auto; }
.sn-cev{ display:flex; gap:10px; align-items:center; background:#fff; border-radius:12px;
  border-left:4px solid var(--sn-info); padding:9px 10px; margin-bottom:8px; cursor:pointer;
  box-shadow:0 2px 8px rgba(44,59,54,.06); }
.sn-cev.warn{ border-color:var(--sn-warn); }
.sn-cev.danger, .sn-cev.emergency{ border-color:var(--sn-danger); }
.sn-cev .who{ width:32px; height:32px; border-radius:50%; background:var(--mint, #E7EDE6);
  display:flex; align-items:center; justify-content:center; font-size:.95rem; flex:0 0 auto; }
.sn-cev .tx{ min-width:0; flex:1; }
.sn-cev .tx b{ font-size:.84rem; display:block; font-weight:700; }
.sn-cev .tx small{ font-size:.7rem; color:var(--ink-soft, #5C6E66); }
.sn-cev .x{ border:0; background:none; color:var(--ink-soft, #5C6E66); cursor:pointer;
  font-size:.9rem; padding:6px 8px; flex:0 0 auto; }
.sn-cev .x:hover{ color:var(--sn-danger); }
.sn-cquiet{ border:1px dashed var(--track, #ADC3B9); border-radius:12px; padding:12px;
  text-align:center; color:var(--ink-soft, #5C6E66); font-size:.8rem; }
.sn-cfoot{ margin-top:14px; }
.sn-cback{ border:0; background:none; color:var(--deep, #4F8577); font-family:inherit;
  font-size:.86rem; font-weight:800; cursor:pointer; padding:0 0 10px; display:block; }

/* ── Round-134 R5: guardian need cards in /activity — the 07-quiet grammar on ntf rows ── */
.ntf-ev.sn-need .c{ border-left:6px solid var(--c, var(--sn-warn)); }

/* ---- Round-136c: identity + the guardians room ---- */
/* The live strip is a LINK into the live map centred on the person — the overlay outranks
   Leaflet's own panes/controls (they top out at 1000) so one tap anywhere works. */
/* Round-141d: z-index:0 makes the strip its OWN stacking context, so the big avatar (1040) and
   the map overlay (1050 — needed to beat Leaflet's 1000 INSIDE the card) stay trapped below the
   page's sticky tab bar (.cpage2-topbar z30) instead of scrolling over it. */
.sn2-stripwrap{ position:relative; z-index:0; }
.sn2-stripwrap .sn2-go{ position:absolute; inset:0; z-index:1050; }
/* the small round face on card name lines — the linked account's real sign-in picture */
.sn2-idav{ width:22px; height:22px; border-radius:50%; object-fit:cover;
  vertical-align:-5px; margin-right:2px; }
/* "Watching me": one row per guardian — identity, what they see, until when, controls */
.sn2-glist{ display:flex; flex-direction:column; gap:10px; max-width:640px; }
.sn2-grow{ display:flex; align-items:center; gap:12px; }
.sn2-grow.paused{ opacity:.62; }
.sn2-grow .gav img, .sn2-grow .gav .fb{ width:44px; height:44px; border-radius:50%;
  object-fit:cover; display:flex; align-items:center; justify-content:center;
  background:var(--mint, #E7EDE6); font-weight:800; color:var(--deep, #4F8577); }
.sn2-grow .gtx{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.sn2-grow .gtx small{ color:var(--ink-soft, #5C6E66); font-size:.82rem; }
.sn2-grow .gacts{ display:flex; gap:6px; flex:0 0 auto; }

/* ---- Round-137: one card grammar for people, assets and guardians ---- */
/* strip background when there is no live map: the community default wallpaper */
.sn2-strip.wall{ background:url("/images/cbg-default.jpg") center/cover no-repeat; }
/* the ONE large identity riding the strip (2x the retired name-line face) */
.sn2-bigav{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:72px; height:72px; border-radius:50%; overflow:hidden; z-index:1040;
  pointer-events:none; display:flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.92); border:3px solid #fff;
  box-shadow:0 4px 14px rgba(44,59,54,.28); font-size:2rem; }
.sn2-bigav img{ width:100%; height:100%; object-fit:cover; }
/* pending invitation: a theme-coloured outline person, not an identity nobody approved yet */
.sn2-bigav.pend svg{ width:38px; height:38px; color:var(--deep, #4F8577); stroke:currentColor; }
/* Round-139: a real account without a sign-in picture shows its INITIALS, not an emoji */
.sn2-c .av .ini, .sn2-bigav .ini, .sn-crow .av .ini, .sn-pin .ini{
  font-weight:700; color:var(--deep, #4F8577); font-size:.82em; letter-spacing:.03em; }
.sp-inihero{ display:flex; align-items:center; justify-content:center; min-height:120px;
  background:var(--mint, #E7EDE6); position:relative; }
.sp-inihero .ini{ font-size:2.6rem; font-weight:700; color:var(--deep, #4F8577);
  letter-spacing:.05em; }
/* Round-139: the SafeNet confirm modal — the live map gate's grammar (dimmed blurred backdrop,
   centred accent-edged panel) on WHITE, because it sits over a white page, not a map. */
.sn2-cfm-backdrop{ position:fixed; inset:0; z-index:2590;
  background:rgba(44,59,54,.38); backdrop-filter:blur(2px);
  animation:gateFade .18s ease-out; }
.sn2-cfm{ position:fixed; top:calc(var(--header-h, 64px) + 60px); left:50%;
  transform:translateX(-50%); z-index:2600; width:min(380px, calc(100vw - 28px));
  padding:18px 20px; background:#fff; border-radius:14px;
  border-left:3px solid var(--accent, #618D7C); box-shadow:0 12px 36px rgba(44,59,54,.28); }
.sn2-cfm h3{ margin:0 0 6px; font-size:1.05rem; color:var(--ink, #2C3B36); }
.sn2-cfm p{ margin:0; font-size:.9rem; color:var(--ink-soft, #5C6E66); line-height:1.45; }
.sn2-cfm .acts{ display:flex; gap:10px; justify-content:flex-end; margin-top:16px; }
.sn2-cfm .acts button{ border-radius:999px; padding:8px 18px; font-weight:600; font-size:.88rem;
  cursor:pointer; font-family:inherit; }
.sn2-cfm .acts .o{ background:#fff; border:1.5px solid var(--track, #ADC3B9);
  color:var(--ink, #2C3B36); }
.sn2-cfm .acts .danger{ background:#B3261E; border:1.5px solid #B3261E; color:#fff; }
.sn2-cfm .acts .danger:disabled{ opacity:.6; cursor:default; }
[dir="rtl"] .sn2-cfm{ border-left:none; border-right:3px solid var(--accent, #618D7C); }
/* Round-139d: the consent card (design safenet-consent/01-classic-card) — avatar pair joined
   by a shield badge, one icon row per offered scope with its own switch. Icons are the shared
   outline set riding currentColor, so the whole card themes from --deep. */
.snjc{ text-align:center; }
.snjc-kick{ font-size:.72rem; letter-spacing:.18em; font-weight:700; color:var(--deep, #4F8577); }
.snjc-pair{ display:flex; align-items:center; justify-content:center; margin:24px 0 0; }
.snjc-av{ width:84px; height:84px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  font-weight:700; font-size:1.5rem; color:#fff; border:4px solid #fff; overflow:hidden;
  box-shadow:0 6px 18px rgba(44,59,54,.2); }
.snjc-av img{ width:100%; height:100%; object-fit:cover; }
.snjc-av.g{ background:linear-gradient(135deg,#4F8577,#2C5B4E); z-index:2; position:relative; }
.snjc-av.me{ background:linear-gradient(135deg,#ADC3B9,#7FA396); margin-left:-16px; }
[dir="rtl"] .snjc-av.me{ margin-left:0; margin-right:-16px; }
.snjc-link{ width:34px; height:34px; border-radius:50%; background:var(--mint, #E7EDE6);
  display:flex; align-items:center; justify-content:center; margin:-16px auto 20px; position:relative;
  z-index:3; border:3px solid #fff; color:var(--deep, #4F8577); }
.snjc-link svg{ width:17px; height:17px; }
.snjc-head{ font-size:1.35rem; line-height:1.3; margin:0 0 8px; }
.snjc-sub{ color:var(--ink-soft, #5C6E66); font-size:.94rem; margin:0 0 22px; }
.snjc-scopes{ text-align:left; border:1.5px solid var(--mint, #E7EDE6); border-radius:16px;
  padding:6px 0; margin-bottom:10px; }
[dir="rtl"] .snjc-scopes{ text-align:right; }
.snjc-scope{ display:flex; align-items:center; gap:12px; padding:11px 16px; width:100%;
  background:none; border:none; cursor:pointer; font-family:inherit; text-align:inherit; color:inherit; }
.snjc-scope + .snjc-scope{ border-top:1px solid #F0F4F0; }
.snjc-scope .ic{ width:34px; height:34px; border-radius:10px; background:var(--mint, #E7EDE6);
  display:flex; align-items:center; justify-content:center; color:var(--deep, #4F8577); flex:0 0 auto; }
.snjc-scope .ic svg{ width:18px; height:18px; }
.snjc-scope .tx{ min-width:0; }
.snjc-scope .tx b{ display:block; font-size:.9rem; }
.snjc-scope .tx small{ color:var(--ink-soft, #5C6E66); font-size:.76rem; }
.snjc-scope .tog{ margin-left:auto; width:42px; height:24px; border-radius:999px;
  background:var(--deep, #4F8577); position:relative; flex:0 0 auto; transition:background .15s; }
[dir="rtl"] .snjc-scope .tog{ margin-left:0; margin-right:auto; }
.snjc-scope .tog::after{ content:""; position:absolute; top:3px; right:3px; width:18px; height:18px;
  border-radius:50%; background:#fff; transition:left .15s, right .15s; }
.snjc-scope .tog.off{ background:#D7E0DA; }
.snjc-scope .tog.off::after{ right:auto; left:3px; }
.snjc-until{ font-size:.8rem; color:var(--ink-soft, #5C6E66); margin:14px 0 22px; }
.snjc-acts{ display:flex; gap:12px; }
.snjc-btn{ flex:1; border:none; border-radius:999px; padding:15px; font-size:1rem; font-weight:700;
  cursor:pointer; font-family:inherit; text-decoration:none; display:flex; align-items:center;
  justify-content:center; }
.snjc-btn:hover{ text-decoration:none; }
.snjc-btn.ok{ background:var(--deep, #4F8577); color:#fff; box-shadow:0 8px 20px rgba(79,133,119,.35); }
.snjc-btn.no{ background:#fff; border:1.5px solid var(--track, #ADC3B9); color:var(--ink, #2C3B36); }
.snjc-btn[disabled]{ opacity:.6; cursor:default; }
.snjc-fine{ font-size:.74rem; color:var(--ink-soft, #5C6E66); margin-top:16px; }
/* Round-139d: the approved card (design safenet-approved/02-linked-avatars, on white) — the
   two real identities joined by a dashed cord, LINK ACTIVE badge, granted-scope tiles. */
.snja{ text-align:center; background:#fff; }
.snja-pair{ display:flex; align-items:center; justify-content:center; margin-top:6px; }
.snja-av{ width:92px; height:92px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  font-weight:800; font-size:1.5rem; color:#fff; border:5px solid #fff; overflow:hidden;
  box-shadow:0 8px 22px rgba(44,59,54,.2); }
.snja-av img{ width:100%; height:100%; object-fit:cover; }
.snja-av.a1{ background:linear-gradient(135deg,#4F8577,#2C5B4E); position:relative; z-index:2; }
.snja-av.a2{ background:linear-gradient(135deg,#E0B252,#C98B3A); margin-left:-18px; }
[dir="rtl"] .snja-av.a2{ margin-left:0; margin-right:-18px; }
.snja-cord{ width:120px; height:4px; margin:-48px auto 44px; position:relative; z-index:0;
  background:repeating-linear-gradient(90deg, var(--deep, #4F8577) 0 8px, transparent 8px 14px); }
.snja-badge{ display:inline-flex; align-items:center; gap:8px; background:#EDF4F0; color:#2E7D5B;
  font-weight:800; font-size:.72rem; letter-spacing:.12em; text-transform:uppercase;
  border-radius:999px; padding:8px 18px; margin-bottom:12px; }
.snja-badge .dot{ width:9px; height:9px; border-radius:50%; background:#2E9E5B; animation:snjaPulse 1.6s infinite; }
@keyframes snjaPulse{ 50%{ box-shadow:0 0 0 6px rgba(46,158,91,.18) } }
.snja-head{ font-size:1.5rem; margin:0 0 8px; }
.snja-sub{ color:var(--ink-soft, #5C6E66); font-size:.94rem; margin:0 0 24px; line-height:1.55; }
.snja-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(86px, 1fr)); gap:10px; margin-bottom:22px; }
.snja-grid .g{ background:#F6F8F6; border-radius:14px; padding:14px 8px; }
.snja-grid .ic{ display:flex; justify-content:center; color:var(--deep, #4F8577); }
.snja-grid .ic svg{ width:22px; height:22px; }
.snja-grid small{ display:block; font-size:.66rem; color:var(--ink-soft, #5C6E66); font-weight:700;
  letter-spacing:.06em; text-transform:uppercase; margin-top:7px; }
.snja-share{ background:#F6F8F6; border-radius:14px; padding:14px 16px; margin-bottom:20px; }
.snja-acts{ display:flex; gap:12px; }
.snja-acts .snjc-btn{ border-radius:14px; font-size:.95rem; }
/* Round-139e: the live-map person panel wears design safenet-panel/06-metric-tiles with
   05-timeline-feed's event rail. Tiles on the panel's white body; nodes coloured per event
   type in the JS. */
.snp-tiles{ display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:12px; }
.snp-tile{ background:#F6F8F6; border-radius:16px; padding:14px 16px; min-width:0; }
.snp-tile.wide{ grid-column:1/-1; display:flex; align-items:center; gap:14px; }
.snp-tile small{ display:block; color:#8AA096; font-size:.66rem; letter-spacing:.12em;
  font-weight:800; text-transform:uppercase; margin-bottom:4px; }
.snp-tile b{ font-size:.95rem; line-height:1.3; display:block; }
.snp-tile .sub{ display:block; color:var(--ink-soft, #5C6E66); font-size:.76rem; margin-top:3px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.snp-av{ width:56px; height:56px; border-radius:18px; overflow:hidden; flex:0 0 auto;
  background:linear-gradient(135deg,#4F8577,#2C5B4E); color:#fff; font-weight:800; font-size:1.1rem;
  display:flex; align-items:center; justify-content:center; }
.snp-av img{ width:100%; height:100%; object-fit:cover; }
.snp-av .ini{ color:#fff; font-size:1rem; }
.snp-wtx{ min-width:0; flex:1; }
.snp-live{ display:inline-flex; align-items:center; gap:6px; font-size:.64rem; font-weight:800;
  letter-spacing:.1em; text-transform:uppercase; border-radius:999px; padding:6px 11px;
  flex:0 0 auto; background:#E9EFEA; color:#5C6E66; }
.snp-live i{ width:8px; height:8px; border-radius:50%; background:#9AA59E; }
.snp-live.p-live{ background:#E5F3EA; color:#2E7D5B; }
.snp-live.p-live i{ background:#2E9E5B; animation:snjaPulse 1.6s infinite; }
.snp-live.p-recent{ background:#FAF2DF; color:#9A6B1F; }
.snp-live.p-recent i{ background:#E0B252; }
.snp-live.p-paused{ background:#EFEFEF; color:#777; }
.snp-ring{ width:52px; height:52px; border-radius:50%; float:right; margin:-2px 0 0 8px;
  display:flex; align-items:center; justify-content:center; }
[dir="rtl"] .snp-ring{ float:left; margin:-2px 8px 0 0; }
.snp-ring i{ width:40px; height:40px; border-radius:50%; background:#F6F8F6; display:flex;
  align-items:center; justify-content:center; font-style:normal; font-weight:800; font-size:.72rem; }
.snp-ring.none{ background:#E2E9E4; }
.snp-share{ background:#fff; border:1.5px solid #EDF1ED; border-radius:16px; padding:12px 16px 10px; margin-bottom:12px; }
.snp-share small{ display:block; color:#8AA096; font-size:.66rem; letter-spacing:.12em;
  font-weight:800; text-transform:uppercase; margin-bottom:8px; }
.snp-share .row{ display:flex; flex-wrap:wrap; gap:6px; }
.snp-chip{ background:#EDF4F0; color:#3A6B5C; font-size:.72rem; font-weight:700;
  border-radius:8px; padding:5px 10px; }
.snp-evs{ background:#fff; border:1.5px solid #EDF1ED; border-radius:16px; padding:14px 16px 6px 22px; }
.snp-evs .tl{ position:relative; padding-left:8px; }
.snp-evs .tl::before{ content:""; position:absolute; left:7px; top:8px; bottom:6px; width:2px; background:#E2E9E4; }
.snp-evs .day{ font-size:.64rem; letter-spacing:.16em; color:#8AA096; font-weight:800;
  text-transform:uppercase; margin:0 0 10px -8px; background:#fff; width:fit-content;
  padding-right:10px; position:relative; z-index:1; }
.snp-evs .tev{ position:relative; padding:0 0 13px 24px; }
.snp-evs .tev .k{ position:absolute; left:-8px; top:1px; width:16px; height:16px; border-radius:50%;
  border:3px solid #fff; box-shadow:0 0 0 1.5px #DCE4DE; z-index:1; }
.snp-evs .tev b{ font-size:.84rem; display:block; line-height:1.35; }
.snp-evs .tev small{ color:#8AA096; font-size:.72rem; }
[dir="rtl"] .snp-evs{ padding:14px 22px 6px 16px; }
[dir="rtl"] .snp-evs .tl{ padding-left:0; padding-right:8px; }
[dir="rtl"] .snp-evs .tl::before{ left:auto; right:7px; }
[dir="rtl"] .snp-evs .day{ margin:0 -8px 10px 0; padding-right:0; padding-left:10px; }
[dir="rtl"] .snp-evs .tev{ padding:0 24px 13px 0; }
[dir="rtl"] .snp-evs .tev .k{ left:auto; right:-8px; }
/* Round-139f/g: the SafeNet surfaces (person panel AND console — map-engine adds .snp in
   _openSafeNetPanel/_openSafeNetConsole; the shell strips it for every other panel).
   WHITE HEAD at every width (user call, round-139g), and on phones a bottom SHEET (design
   10-mobile-dock): grab handle, rounded top, ~68% tall — the map and the pin stay visible
   above it. Incidents and stations keep round-97's full-screen takeover. */
.station-panel.snp .sp-head{ background:#fff; color:var(--ink, #2C3B36); }
.station-panel.snp .sp-head h2{ color:var(--ink, #2C3B36); }
.station-panel.snp .sp-head .sub{ color:var(--ink-soft, #5C6E66); }
.station-panel.snp .sp-close{ background:#F1F5F1; color:var(--ink, #2C3B36); }
/* Round-139g: the console feed reuses the panel's timeline; entries there carry a dismiss ✕ */
.snp-evs .tev{ cursor:pointer; }
.snp-evs .tev .x{ position:absolute; right:0; top:-2px; border:0; background:none;
  color:var(--ink-soft, #5C6E66); cursor:pointer; font-size:.85rem; padding:4px 6px; }
.snp-evs .tev .x:hover{ color:var(--sn-danger, #C0392B); }
/* Round-140b: the manage fold's section grammar (user calls) — every section opens on a
   DASHED divider (same rule as its row separators), and the collapsibles swap the browser's
   caret for a narrow chevron that turns when open. */
.sn2-mdiv{ border-top:1px dashed var(--track, #ADC3B9); margin:16px 0 14px; }
.sn2-manage h4{ margin:0 0 8px; }
.sn2-manage details > summary{ list-style:none; display:flex; align-items:center; gap:8px; cursor:pointer; }
.sn2-manage details > summary::-webkit-details-marker{ display:none; }
.sn2-manage details > summary::before{ content:"›"; display:inline-block; width:12px;
  font-size:1.1rem; font-weight:700; color:var(--deep, #4F8577); line-height:1;
  transition:transform .15s; flex:0 0 auto; }
.sn2-manage details[open] > summary::before{ transform:rotate(90deg); }

/* Round-141g: the device pairing code. It is read off THIS screen and typed into another, so it
   is set large, wide-tracked and monospaced — the three things that stop a 4 from becoming a 9.
   (The alphabet already rules out 0/O and 1/I/L; typography has to carry the rest.) */
.sn2-pair-code{ display:flex; align-items:center; justify-content:space-between; gap:10px;
  flex-wrap:wrap; margin-top:10px; padding:12px 14px; border-radius:12px;
  border:1px dashed var(--track, #ADC3B9); background:var(--wash, #F3F4F1); }
.sn2-pair-code .c{ font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size:1.5rem; font-weight:700; letter-spacing:.22em; color:var(--deep, #4F8577);
  user-select:all; }
.sn2-pair-code small{ font-size:.8rem; }

/* The code INPUT on /safenet/pair mirrors that display — but the placeholder must not inherit
   the uppercasing and tracking, or the example ("e.g. 7HJK2MQX") renders as spaced-out shouting
   and reads like a value already typed. */
.snp-code{ letter-spacing:.18em; text-transform:uppercase; font-size:1.15rem;
  font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.snp-code::placeholder{ letter-spacing:normal; text-transform:none; font-size:.95rem;
  font-family:inherit; }
[dir="rtl"] .sn2-manage details > summary::before{ content:"‹"; }
[dir="rtl"] .sn2-manage details[open] > summary::before{ transform:rotate(-90deg); }
#snConsole .snp-evs .tev{ padding-right:30px; }  /* room for the ✕ (no :has — old G6 WebView) */
[dir="rtl"] .snp-evs .tev .x{ right:auto; left:0; }
[dir="rtl"] #snConsole .snp-evs .tev{ padding-right:24px; padding-left:30px; }
@media (max-width:720px){
  .station-panel.snp{
    top:auto; height:auto;
    max-height:calc(var(--vvh, var(--app-h, 100dvh)) * .68);
    border-radius:26px 26px 0 0;
    box-shadow:0 -14px 40px rgba(44,59,54,.28);
  }
  .station-panel.snp .sp-head{
    border-radius:26px 26px 0 0; padding:20px 22px 6px;
  }
  .station-panel.snp .sp-head::before{
    content:""; position:absolute; top:8px; left:50%; transform:translateX(-50%);
    width:44px; height:5px; border-radius:99px; background:#D9E2DC;
  }
  .station-panel.snp .sp-close{ top:16px; }
  .station-panel.snp #spBody{
    padding-bottom:calc(16px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  }
}
.sn2-card.pending .b .name b{ color:var(--ink-soft, #5C6E66); }
/* Round-141f: an ENDED card (share revoked or declined) reads as dormant, not broken — muted
   name, quieter wallpaper, no presence dot. Its history is intact and one tap invites them
   back onto this same card. */
.sn2-card.ended .b .name b{ color:var(--ink-soft, #5C6E66); }
.sn2-card.ended .sn2-strip.wall{ filter:saturate(.55) opacity(.85); }
.sn2-card.ended .pres{ color:var(--ink-soft, #5C6E66); font-size:.8rem; }
/* guardian cards: same grammar, shield tint, no map link */
.sn2-card.guardian .sn2-strip.wall{ filter:saturate(.85); }
.sn2-card.gpaused{ opacity:.65; }
.sn2-card .sn2-acts button.danger{ color:#C0392B; border-color:#E2BFB9; }
/* setup: themed outline pin replaces the emoji (user request #18) */
.sn2-pinic svg{ width:15px; height:15px; vertical-align:-2px; color:var(--deep, #4F8577); }
.sn2-step .state .pill img{ width:18px; height:18px; border-radius:50%; object-fit:cover;
  vertical-align:-4px; margin-right:3px; }

/* ---- Round-138: invite modes, honest scope toggles, card action icons ---- */
/* the email|batch segmented switch on the invite form */
.sn2-seg{ display:flex; gap:0; border:1.5px solid var(--track, #ADC3B9); border-radius:999px;
  overflow:hidden; margin-bottom:12px; }
.sn2-seg button{ flex:1; border:0; background:none; padding:8px 12px; cursor:pointer;
  font-family:var(--font, Calibri, sans-serif); font-weight:700; font-size:.85rem;
  color:var(--ink-soft, #5C6E66); }
.sn2-seg button.on{ background:var(--deep, #4F8577); color:#fff; }
/* scope toggles: the ON state is unmistakable (filled + check), OFF is a quiet outline */
.sn2-scopetog{ border:1.5px solid var(--track, #ADC3B9); background:#fff;
  color:var(--ink-soft, #5C6E66); border-radius:999px; padding:6px 12px; cursor:pointer;
  font-family:var(--font, Calibri, sans-serif); font-weight:700; font-size:.8rem; }
.sn2-scopetog.on{ background:var(--deep, #4F8577); border-color:var(--deep, #4F8577); color:#fff; }
.sn2-scopetog.on::before{ content:"\2713\00a0"; }
/* card action buttons carry svgs now (share) - without a size they render invisible */
.sn2-acts svg{ width:15px; height:15px; vertical-align:-3px; }
.sn2-invform .btn svg{ width:15px; height:15px; vertical-align:-3px; margin-right:4px; }

/* Round-142g: asset pairing — the type switch that decides the add form, the icon picker,
   the pair code beside its QR, the tracker status with its account chip (owner side), the
   device card on Check this device, and the "you are the tracker" guardian card. */
.sn2-kindseg button{ font-size:.92rem; padding:10px 12px; }
.sn2-emojipick{ display:flex; flex-wrap:wrap; gap:6px; }
.sn2-emojipick button{ width:42px; height:42px; border-radius:12px; border:1.5px solid var(--track, #ADC3B9);
  background:#fff; font-size:1.35rem; line-height:1; cursor:pointer; padding:0; }
.sn2-emojipick button.on{ border-color:var(--deep, #4F8577); background:var(--mintbg, #E7EDE6); box-shadow:0 0 0 2px var(--deep, #4F8577) inset; }
.sn2-pairbox{ display:flex; align-items:center; gap:14px; flex-wrap:wrap; margin-top:10px; }
.sn2-pairbox .sn2-pair-code{ flex:1; min-width:200px; margin-top:0; }
.sn2-qr{ flex:none; width:132px; height:132px; padding:4px; background:#fff; border-radius:12px;
  border:1px solid var(--track, #ADC3B9); box-sizing:border-box; }
.sn2-qr svg{ width:100%; height:100%; display:block; }
.sn2-kindseg button svg{ width:16px; height:16px; vertical-align:-3px; margin-right:4px; }
/* Round-142i: an asset's identity is its outline icon in the theme's deep green — on the card's
   big avatar, the cast strip, the picker, the setup pill and the device card alike */
.sn2-bigav .oic, .sn2-c .av .oic{ display:flex; align-items:center; justify-content:center; width:100%; height:100%; color:var(--deep, #4F8577); }
.sn2-bigav .oic svg{ width:44px; height:44px; }
.sn2-c .av .oic svg{ width:30px; height:30px; }
.sn2-step .state .pill .oic{ display:inline-flex; vertical-align:-3px; margin-right:2px; color:var(--deep, #4F8577); }
.sn2-step .state .pill .oic svg{ width:15px; height:15px; }
.sn2-emojipick button svg{ width:22px; height:22px; color:var(--deep, #4F8577); vertical-align:middle; }
.sn2-devstat-emoji svg{ width:40px; height:40px; color:var(--deep, #4F8577); }
/* Round-142h: the setup rail — a sized avatar in the step-1 pills, sized icons in the step
   buttons, and a grey OPTIONAL tick on the steps that are not required */
.sn2-step .state .pill img, .sn2-step .state .pill .ini{ width:18px; height:18px; border-radius:50%;
  object-fit:cover; vertical-align:-4px; display:inline-flex; align-items:center; justify-content:center;
  font-size:.62rem; margin-right:2px; }
.sn2-step .state .pill .ini{ background:var(--deep, #4F8577); color:#fff; font-weight:700; }
.sn2-step button svg{ width:15px; height:15px; vertical-align:-3px; }
.sn2-step .tick.opt{ color:var(--ink-soft, #5C6E66); font-weight:700; opacity:.85; }
.sn2-acct-av.none svg{ width:18px; height:18px; color:var(--ink, #2C3B36); }
.sn2-tracker-dev .sn2-pinic svg{ width:15px; height:15px; vertical-align:-3px; }
.sn2-tracker{ margin:4px 0 10px; padding:12px 14px; border-radius:12px; background:var(--wash, #F3F4F1); border:1px solid var(--track, #ADC3B9); }
.sn2-tracker-head{ display:flex; justify-content:space-between; align-items:center; gap:8px; flex-wrap:wrap; }
.sn2-tracker-dev{ display:flex; flex-direction:column; gap:2px; font-size:.92rem; }
.sn2-tracker-dev small{ color:var(--ink-soft, #5C6E66); font-size:.78rem; }
.sn2-tracker-meta{ font-size:.82rem; color:var(--ink-soft, #5C6E66); margin-top:6px; }
.sn2-acct{ display:flex; align-items:center; gap:10px; margin-top:10px; }
.sn2-acct > span:last-child{ display:flex; flex-direction:column; line-height:1.25; }
.sn2-acct > span:last-child small{ color:var(--ink-soft, #5C6E66); font-size:.76rem; }
.sn2-acct-av{ width:36px; height:36px; border-radius:50%; overflow:hidden; flex:none;
  display:flex; align-items:center; justify-content:center; background:var(--deep, #4F8577); color:#fff; font-weight:700; }
.sn2-acct-av img{ width:100%; height:100%; object-fit:cover; }
.sn2-acct-av.none{ background:var(--track, #ADC3B9); font-size:1rem; }
.sn2-devstat{ display:flex; gap:14px; align-items:flex-start; }
.sn2-devstat-emoji{ font-size:2.4rem; line-height:1; flex:none; }
.sn2-devstat-body{ flex:1; min-width:0; }
.sn2-devstat-body > b{ font-size:1.05rem; }
.sn2-card.guardian.tracker .sn2-strip.wall{ filter:saturate(.7) hue-rotate(20deg); }

/* Round-142: the review queue's pager — plain links, an enhanced navigation per page. */
.arev-pager{ display:flex; align-items:center; justify-content:center; gap:14px; margin:18px 0 6px; flex-wrap:wrap; }

/* Round-142f (owner's call): on PHONES the stories feed and the activity centre go back to
   white — page, sticky filter strip, sticky header block and day stickers alike; the cream is
   for larger screens. Above the app's phone breakpoint the round-142f cream rules stand. */
@media (max-width:900px){
  .stories-page, .stories-filters{ background:#fff; }
  /* round-144c: SEPARATE rules — a selector list containing :has() is dropped whole by a
     browser without :has() (the G6's WebView), which is why Android stayed cream */
  html.pg-ntf, html.pg-ntf body{ background:#fff; }
  body:has(.ntf-wrap){ background:#fff; }
  html:has(.ntf-wrap){ background:#fff; }
  .ntf-top, .ntf-day{ background:#fff; }
}   /* at the END of the file: these must out-order the cream rules above */

/* Round-142r (user call): the emergency plan on WHITE — page, body and html alike, at every
   width; its cards keep their own white and separate by shadow. At the END of the file so it
   out-orders the cream rules above. */
.plan-page{ background:#fff; }
html:has(.plan-page), html:has(.plan-page) body{ background:#fff; }
.plan-page .plan-bar2, .plan-page .plan-item, .plan-page .plan-card{ box-shadow:0 2px 10px rgba(44,59,54,.08); border:1px solid #EEF2EE; }


/* ═══════════ Round-143: Local resources — the finale design (design/local-resources/11-finale) ═══════════
   Sticky tabs above a Leaflet hero; the reader's own face as "you are here"; the kind pills, the
   radius slider and the Add button along the map's bottom edge; the "within X of you" cards with a
   narrow Review dropdown; a horizontal shelf of "around the places you saved". Named res2-* so the
   round-82 popup/pin rules above keep serving the live map untouched. */
.res2-topbar{ position:sticky; top:var(--header-h); z-index:1201; background:#fff; border-bottom:1px solid #E4EBE5; }
.res2-tabs{ display:flex; gap:4px; padding:0 18px; overflow-x:auto; scrollbar-width:none; }
.res2-tabs::-webkit-scrollbar{ display:none; }
.res2-tab{ flex:none; padding:14px 14px 12px; font-weight:700; font-size:.9rem; color:var(--ink-soft, #5C6E66);
  border-bottom:3px solid transparent; display:inline-flex; align-items:center; gap:7px; white-space:nowrap; }
.res2-tab:hover{ text-decoration:none; }
.res2-tab svg{ width:17px; height:17px; }
.res2-tab.on{ color:var(--deep, #4F8577); border-bottom-color:var(--deep, #4F8577); }
.res2-tab.add{ margin-left:auto; color:var(--tan, #BC8A52); } .res2-tab.add.on{ border-bottom-color:var(--tan, #BC8A52); }
.res2-tab .sm{ display:none; }
@media (max-width:760px){
  .res2-tabs{ padding:0 8px; } .res2-tab{ padding:12px 10px 10px; font-size:.84rem; }
  .res2-tab .lg{ display:none; } .res2-tab .sm{ display:inline; }
}
.res2-hero{ position:relative; height:calc(var(--app-h, 100vh) * 0.58); min-height:400px; overflow:hidden; background:var(--track, #ADC3B9); }
@media (max-width:760px){ .res2-hero{ height:calc(var(--app-h, 100vh) * 0.52); min-height:380px; } }
.res2-map{ position:absolute; inset:0; }
.res2-hero .leaflet-container{ font-family:var(--font); }
.res2-bar{ position:absolute; left:12px; right:12px; bottom:12px; display:flex; gap:10px; align-items:center; z-index:1001; min-height:52px; }
.res2-chips{ flex:1; min-width:0; display:flex; gap:8px; overflow-x:auto; scrollbar-width:none; padding:4px 2px; }
.res2-chips::-webkit-scrollbar{ display:none; }
.res2-chip{ flex:none; border:1.5px solid var(--track, #ADC3B9); background:#fff; border-radius:999px; padding:7px 13px 7px 10px;
  font-size:.8rem; font-weight:700; color:var(--ink, #2C3B36); display:inline-flex; align-items:center; gap:6px;
  box-shadow:0 2px 8px rgba(44,59,54,.08); cursor:pointer; font-family:inherit; }
.res2-chip svg{ width:16px; height:16px; color:var(--deep, #4F8577); }
.res2-chip.on{ background:var(--deep, #4F8577); border-color:var(--deep, #4F8577); color:#fff; } .res2-chip.on svg{ color:#fff; }
.res2-radius{ flex:none; background:#fff; border-radius:999px; padding:7px 12px; display:flex; align-items:center; gap:9px;
  box-shadow:0 2px 8px rgba(44,59,54,.12); font-size:.78rem; font-weight:700; }
.res2-radius svg{ width:16px; height:16px; color:var(--deep, #4F8577); }
.res2-radius input{ width:120px; accent-color:var(--deep, #4F8577); margin:0; }
.res2-radius output{ min-width:44px; text-align:right; color:var(--deep, #4F8577); font-weight:800; }
/* round-144b: the .res2-addbtn beside the slider is gone (owner's call) — the Add tab is the one door */
@media (max-width:760px){
  .res2-bar{ flex-wrap:wrap; min-height:104px; align-content:flex-end; }
  .res2-chips{ flex:1 1 100%; order:1; }
  .res2-radius{ order:2; flex:1; } .res2-radius input{ width:100%; flex:1; }

}
/* the pins */
.res-mk{ width:34px; height:34px; border-radius:50%; background:#fff; border:2.5px solid var(--c, #4F8577); color:var(--c, #4F8577);
  display:flex; align-items:center; justify-content:center; box-shadow:0 3px 10px rgba(44,59,54,.2); }
.res-mk svg{ width:17px; height:17px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.res-mk.gone{ border-style:dashed; }
.res-me{ box-shadow:0 0 0 4px rgba(79,133,119,.35), 0 4px 14px rgba(44,59,54,.3); }
.res-me .ini{ font-weight:700; color:var(--deep, #4F8577); font-size:.9rem; letter-spacing:.03em; }
.res-me svg{ width:22px; height:22px; color:var(--deep, #4F8577); }
.res-ring-lbl{ background:var(--deep, #4F8577); color:#fff; font-size:.68rem; font-weight:800; border-radius:999px; padding:2px 8px;
  white-space:nowrap; transform:translate(-50%,-140%); width:auto !important; height:auto !important; }
/* the sections */
.res2-wrap{ max-width:1180px; margin:0 auto; padding:24px 24px 40px; }
@media (max-width:760px){ .res2-wrap{ padding:18px 14px 32px; } }
.res2-sec{ scroll-margin-top:calc(var(--header-h) + 56px); }
.res2-sech{ display:flex; justify-content:space-between; align-items:baseline; gap:12px; margin:0 0 12px; }
.res2-sech h2{ font-size:1.25rem; margin:0; } .res2-sech h2 b{ color:var(--deep, #4F8577); }
.res2-sech small{ color:var(--ink-soft, #5C6E66); font-weight:600; font-size:.8rem; white-space:nowrap; }
.res2-list{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
@media (max-width:760px){ .res2-list{ grid-template-columns:1fr; } }
.res2-card{ display:flex; gap:12px; align-items:flex-start; background:#fff; border-radius:14px; padding:12px 14px; min-height:88px;
  transition:box-shadow .3s; }
.res2-card.flash{ box-shadow:0 0 0 3px var(--deep, #4F8577); }
.res2-card .ic{ width:40px; height:40px; border-radius:12px; background:var(--mint, #E7EDE6); color:var(--deep, #4F8577);
  display:flex; align-items:center; justify-content:center; flex:none; }
.res2-card .ic svg{ width:20px; height:20px; }
.res2-card .tx{ flex:1; min-width:0; }
.res2-card .k{ display:block; font-size:.7rem; letter-spacing:.8px; text-transform:uppercase; color:var(--tan, #BC8A52); font-weight:800; }
.res2-card b.n{ display:block; font-size:.93rem; }
.res2-card .acc{ display:block; font-size:.74rem; color:var(--ink-soft, #5C6E66); }
.res2-card .fr{ display:block; font-size:.76rem; color:#2E9E5B; }
.res2-card.ageing .fr, .res2-card.stale .fr, .res2-card.disputed .fr{ color:#B8730E; }
.res2-card.gone .fr{ color:#C0392B; }
.res2-card.stale .ic, .res2-card.ageing .ic, .res2-card.disputed .ic{ background:#FBEFD9; color:#8A5A10; }
.res2-card.gone .ic{ background:#F8E1DD; color:#8E2A1D; } .res2-card.gone b.n{ text-decoration:line-through; }
.res2-card .dist{ text-align:right; font-weight:800; font-size:.9rem; white-space:nowrap; flex:none; }
.res2-card .dist small{ display:block; color:var(--ink-soft, #5C6E66); font-weight:600; font-size:.72rem; }
.res2-card.skel-r .tx{ display:flex; flex-direction:column; gap:8px; }
/* the review control: narrow, under the walking time, a dropdown of the two honest answers */
.res-rev{ position:relative; margin-top:6px; }
.res-rev summary{ list-style:none; display:inline-flex; align-items:center; gap:3px; border:1.5px solid var(--track, #ADC3B9);
  border-radius:999px; padding:3px 8px 3px 10px; font-size:.7rem; font-weight:700; color:var(--deep, #4F8577); background:#fff; cursor:pointer; }
.res-rev summary::-webkit-details-marker{ display:none; } .res-rev summary::marker{ content:""; }
.res-rev summary svg{ width:12px; height:12px; transition:transform .15s; }
.res-rev[open] summary svg{ transform:rotate(180deg); }
.res2-card.needs .res-rev summary{ border-color:var(--tan, #BC8A52); color:var(--tan, #BC8A52); }
.res-rev .menu{ position:absolute; right:0; top:calc(100% + 4px); background:#fff; border-radius:12px;
  box-shadow:0 8px 24px rgba(44,59,54,.18); padding:6px; min-width:150px; z-index:5; text-align:left; }
.res-rev .menu button{ display:flex; align-items:center; gap:8px; width:100%; border:0; background:none; border-radius:8px;
  padding:8px 10px; font-size:.8rem; font-weight:700; color:var(--ink, #2C3B36); cursor:pointer; font-family:inherit; }
.res-rev .menu button svg{ width:15px; height:15px; color:#2E9E5B; } .res-rev .menu button.no svg{ color:#C0392B; }
.res-rev .menu button:hover{ background:var(--mint, #E7EDE6); }
.res-rev .done{ display:inline-block; font-size:.7rem; font-weight:700; color:var(--deep, #4F8577); margin-top:6px; }
.res-rev .done.warn{ color:#8A5A10; }
@media (prefers-reduced-motion:reduce){ .res-rev summary svg, .res2-card{ transition:none; } }
/* around the places you saved — the communities shelf's snap row */
.res2-shelfwrap{ position:relative; }
.res2-shelf{ display:flex; gap:14px; overflow-x:auto; scroll-snap-type:x mandatory; padding:2px 2px 10px; scrollbar-width:thin; -webkit-overflow-scrolling:touch; }
.res2-shelf > *{ flex:0 0 340px; scroll-snap-align:start; }
@media (max-width:760px){ .res2-shelf > *{ flex-basis:86%; } }
.res2-ar{ background:#fff; border-radius:18px; overflow:hidden; }
.res2-strip{ height:110px; position:relative; background:var(--track, #ADC3B9); }
.res2-ar .b{ padding:14px; }
.res2-ar h3{ font-size:1.02rem; display:flex; justify-content:space-between; gap:8px; margin:0; align-items:baseline; }
.res2-ar h3 small{ color:var(--ink-soft, #5C6E66); font-weight:600; font-size:.78rem; text-align:right; }
.res2-ar h3 small b{ color:var(--tan, #BC8A52); }
.res2-ar .res2-card{ background:var(--paper, #F3F4F1); margin-top:8px; padding:9px 10px; min-height:0; }
.res2-ar .res2-card .ic{ width:32px; height:32px; } .res2-ar .res2-card b.n{ font-size:.84rem; }
.res2-ar .res-rev{ display:none; }
.res2-ar .empty{ font-size:.8rem; color:var(--ink-soft, #5C6E66); margin-top:8px; }
.res-home{ width:30px; height:30px; border-radius:50%; background:var(--tan, #BC8A52); color:#fff; display:flex; align-items:center;
  justify-content:center; box-shadow:0 3px 10px rgba(44,59,54,.25); }
.res-home svg{ width:15px; height:15px; fill:none; stroke:currentColor; stroke-width:2; }
.res2-note{ font-size:.78rem; color:var(--ink-soft, #5C6E66); margin:10px 0 0; }
.res2-attrib{ font-size:.72rem; color:var(--ink-soft, #5C6E66); margin-top:10px; } .res2-attrib a{ text-decoration:underline; }
.res2-signin{ background:#fff; border-radius:18px; padding:18px; display:flex; flex-direction:column; gap:10px; justify-content:center; }
/* the add page (design 05) */
.res2-add{ max-width:1100px; margin:0 auto; padding:26px 24px 40px; display:grid; grid-template-columns:460px 1fr; gap:26px; align-items:start; }
@media (max-width:860px){ .res2-add{ grid-template-columns:1fr; padding:18px 14px 32px; } }
.res2-form{ background:#fff; border-radius:20px; padding:22px; }
.res2-form h1{ font-size:1.4rem; margin:4px 0; } .res2-form .lede{ color:var(--ink-soft, #5C6E66); font-size:.88rem; margin-bottom:14px; }
.res2-kg{ display:grid; grid-template-columns:repeat(4,1fr); gap:8px; margin-bottom:6px; }
.res2-kg button{ border:1.5px solid var(--track, #ADC3B9); background:#fff; border-radius:12px; padding:10px 4px; font-size:.72rem;
  font-weight:700; display:flex; flex-direction:column; align-items:center; gap:6px; color:var(--ink, #2C3B36); cursor:pointer; font-family:inherit; }
.res2-kg button.on{ border-color:var(--deep, #4F8577); background:var(--mint, #E7EDE6); }
.res2-kg svg{ width:20px; height:20px; color:var(--deep, #4F8577); }
.res2-more{ background:none; border:0; color:var(--deep, #4F8577); font-weight:700; font-size:.8rem; cursor:pointer; padding:4px 0 10px; font-family:inherit; }
.res2-form label.f{ display:block; font-size:.82rem; font-weight:700; margin:12px 0 4px; }
.res2-form input.f, .res2-form textarea.f{ width:100%; border:1.5px solid var(--track, #ADC3B9); border-radius:10px; padding:10px 12px; font:inherit; font-size:.9rem; }
.res2-form .hint{ color:var(--ink-soft, #5C6E66); font-size:.76rem; margin-top:4px; }
.res2-pick{ height:240px; border-radius:12px; overflow:hidden; border:1.5px solid var(--track, #ADC3B9); margin-top:6px; background:var(--track, #ADC3B9); }
.res2-photo{ border:1.5px dashed var(--track, #ADC3B9); border-radius:12px; padding:14px; text-align:center; color:var(--ink-soft, #5C6E66);
  font-size:.84rem; margin-top:12px; display:flex; align-items:center; justify-content:center; gap:8px; cursor:pointer; position:relative; }
.res2-photo svg{ width:18px; height:18px; color:var(--deep, #4F8577); }
.res2-photo input{ position:absolute; inset:0; opacity:0; cursor:pointer; width:100%; height:100%; }
.res2-photo.has{ border-style:solid; border-color:var(--deep, #4F8577); color:var(--deep, #4F8577); font-weight:700; }
.res2-side .res2-strip{ height:330px; border-radius:18px; overflow:hidden; margin-bottom:14px; }
.res2-side h2{ font-size:1rem; margin:0 0 8px; } .res2-side .res2-list{ grid-template-columns:1fr; gap:8px; } .res2-side .res-rev{ display:none; }
.res2-back{ display:flex; width:max-content; align-items:center; gap:6px; color:var(--ink-soft, #5C6E66); font-size:.82rem; font-weight:700; margin-bottom:10px; }
.res2-back svg{ width:14px; height:14px; transform:rotate(90deg); }
/* round-144b: phones have the header's own back arrow */
@media (max-width:760px){ .res2-back{ display:none !important; } }

/* round-143b: the shelf strip is a door to the place's page; See all under the cards */
.res2-stripwrap{ position:relative; }
.res2-strip-link{ position:absolute; inset:0; z-index:1001; cursor:pointer; }
.res2-seeall{ display:inline-block; margin-top:10px; font-size:.8rem; font-weight:700; color:var(--deep, #4F8577); }
.res2-seeall:hover{ text-decoration:underline; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Round-144: the volunteer network — the finale (design/volunteer/11-finale-*.html).
   One palette, six pages: the pitch, the join wizard, the duty dashboard, a callout, the
   settings list and the hours grid. Every rule is vol2-prefixed; the round-82 .vol-* block is
   gone with the page it styled.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.vol2{ --deep:#4F8577; --ink:#2C3B36; --soft:#5C6E66; --mint:#E7EDE6; --track:#ADC3B9; --paper:#F3F4F1;
  --tan:#BC8A52; --red:#C0392B; --amber:#D9922B; --green:#2E9E5B; color:var(--ink); }
.vol2 .kicker{ font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--tan); font-weight:800; }
.vol2 .hint{ color:var(--soft); font-size:.78rem; }
.vol2 svg.oi{ width:20px; height:20px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.vol2-btn{ border:0; border-radius:999px; padding:10px 18px; font-weight:700; font-size:.9rem; display:inline-flex; align-items:center; justify-content:center; gap:8px; cursor:pointer; font-family:inherit; text-decoration:none; }
.vol2-btn.g{ background:var(--deep); color:#fff; } .vol2-btn.o{ background:#fff; border:1.5px solid var(--ink); color:var(--ink); }
.vol2-btn.t{ background:var(--tan); color:#fff; } .vol2-btn.r{ background:var(--red); color:#fff; }
.vol2-btn svg{ width:16px; height:16px; } .vol2-btn:hover{ text-decoration:none; filter:brightness(.97); } .vol2-btn:disabled{ opacity:.55; cursor:default; }
.vol2-pill{ display:inline-flex; align-items:center; gap:5px; border-radius:999px; padding:3px 10px; font-size:.72rem; font-weight:800; background:var(--mint); color:var(--ink); white-space:nowrap; }
.vol2-pill svg{ width:12px; height:12px; }
.vol2-pill.ok{ background:#E3F3E8; color:#1E6B3E; } .vol2-pill.warn{ background:#FBEFD9; color:#8A5A10; } .vol2-pill.bad{ background:#F8E1DD; color:#8E2A1D; } .vol2-pill.tan{ background:#F3E6D4; color:#7A4E1F; }
.vol2-sk{ border:1.5px solid var(--track); background:#fff; border-radius:999px; padding:7px 12px; font-size:.82rem; font-weight:600; display:inline-flex; align-items:center; gap:6px; color:var(--ink); margin:0 6px 6px 0; cursor:pointer; font-family:inherit; }
.vol2-sk.on{ background:var(--deep); border-color:var(--deep); color:#fff; } .vol2-sk svg{ width:16px; height:16px; }
.vol2-sk.ro{ cursor:default; }
.vol2-tog{ display:inline-flex; align-items:center; gap:10px; font-weight:800; cursor:pointer; background:none; border:0; font-family:inherit; color:inherit; padding:0; }
.vol2-tog i{ width:54px; height:30px; border-radius:15px; background:var(--green); position:relative; display:inline-block; }
.vol2-tog i::after{ content:""; position:absolute; right:3px; top:3px; width:24px; height:24px; border-radius:50%; background:#fff; }
.vol2-tog.off i{ background:var(--track); } .vol2-tog.off i::after{ right:auto; left:3px; }
.vol2-tog.sm i{ width:44px; height:24px; } .vol2-tog.sm i::after{ width:18px; height:18px; }
.vol2-badges{ display:flex; gap:6px; flex-wrap:wrap; }
.vol2-note{ margin-top:10px; font-size:.84rem; color:var(--deep); font-weight:700; }
.vol2-err{ margin-top:10px; font-size:.84rem; color:var(--red); font-weight:700; }
/* the audience picker, multi-select */
.vol2-aud{ display:grid; grid-template-columns:1fr 1fr; gap:6px; }
.vol2-aud button{ border:1.5px solid var(--track); background:#fff; border-radius:10px; padding:9px 10px; font-size:.78rem; font-weight:700; display:flex; align-items:center; gap:8px; text-align:left; color:var(--ink); cursor:pointer; font-family:inherit; }
.vol2-aud button i{ width:16px; height:16px; border-radius:5px; border:1.5px solid var(--track); display:inline-flex; align-items:center; justify-content:center; flex:none; }
.vol2-aud button i svg{ width:11px; height:11px; opacity:0; }
.vol2-aud button.on{ border-color:var(--deep); background:var(--mint); } .vol2-aud button.on i{ background:var(--deep); border-color:var(--deep); color:#fff; } .vol2-aud button.on i svg{ opacity:1; }
.vol2-aud button.lock{ cursor:default; opacity:.9; }
@media (max-width:560px){ .vol2-aud{ grid-template-columns:1fr; } }
/* the shift grid: 7 days × 12 two-hour blocks */
.vol2-grid{ background:#fff; border-radius:18px; padding:16px; display:grid; grid-template-columns:56px repeat(7,1fr); gap:4px; user-select:none; touch-action:none; }
.vol2-grid .h{ font-size:.72rem; font-weight:800; color:var(--soft); text-align:center; padding:6px 0; }
.vol2-grid .t{ font-size:.7rem; color:var(--soft); padding-top:6px; text-align:right; padding-right:6px; }
.vol2-c{ height:22px; border-radius:6px; background:var(--paper); cursor:pointer; } .vol2-c.on{ background:var(--deep); } .vol2-c.now{ outline:2px solid var(--tan); }
.vol2-leg{ display:flex; gap:16px; margin-top:12px; font-size:.8rem; color:var(--soft); align-items:center; flex-wrap:wrap; }
.vol2-leg i{ display:inline-block; width:14px; height:14px; border-radius:4px; background:var(--deep); vertical-align:-2px; margin-right:4px; }
.vol2-bar{ display:flex; gap:8px; margin-top:14px; flex-wrap:wrap; align-items:center; }
@media (max-width:560px){ .vol2-grid{ padding:10px; gap:3px; grid-template-columns:44px repeat(7,1fr); } .vol2-c{ height:18px; } }
/* uploads and small forms */
.vol2-upl{ border:1.5px dashed var(--track); border-radius:12px; padding:14px; text-align:center; color:var(--soft); font-size:.84rem; display:flex; align-items:center; justify-content:center; gap:8px; cursor:pointer; background:#fff; position:relative; overflow:hidden; }
.vol2-upl svg{ width:18px; height:18px; color:var(--deep); flex:none; } .vol2-upl.has{ border-style:solid; border-color:var(--deep); color:var(--deep); font-weight:700; }
.vol2-upl input[type=file]{ position:absolute; inset:0; opacity:0; cursor:pointer; width:100%; height:100%; }
.vol2-f label{ display:block; font-weight:700; font-size:.82rem; margin:12px 0 5px; }
.vol2-f input, .vol2-f select{ width:100%; border:1.5px solid var(--track); border-radius:10px; padding:9px 12px; font:inherit; background:#fff; color:var(--ink); }
.vol2-f .row2{ display:grid; grid-template-columns:1fr 1fr; gap:8px; } @media (max-width:560px){ .vol2-f .row2{ grid-template-columns:1fr; } }
/* the in-page back link exists for large screens only — on a phone the header's own back arrow does the job */
.vol2-back{ display:inline-flex; align-items:center; gap:6px; color:var(--soft); font-weight:700; font-size:.84rem; margin-bottom:10px; } .vol2-back svg{ width:14px; height:14px; }
@media (max-width:760px){ .vol2-back{ display:none !important; } }
/* map markers (Leaflet divIcons) */
.vol2-mk{ width:16px; height:16px; border-radius:50%; background:var(--deep, #4F8577); border:3px solid #fff; box-shadow:0 2px 6px rgba(44,59,54,.3); }
.vol2-mk.red{ width:24px; height:24px; background:#C0392B; }
.vol2-base{ position:relative; } .vol2-base-lbl{ position:absolute; left:50%; top:-22px; transform:translateX(-50%); background:#BC8A52; color:#fff; font-size:.66rem; font-weight:800; border-radius:999px; padding:2px 8px; white-space:nowrap; pointer-events:none; }

/* ── the pitch ── */
.vol2-pitch-hero{ background:var(--ink); color:#fff; padding:52px 0; }
.vol2-pitch-hero .in{ max-width:900px; margin:0 auto; padding:0 24px; display:grid; grid-template-columns:1.2fr 1fr; gap:30px; align-items:center; }
@media (max-width:760px){ .vol2-pitch-hero{ padding:34px 0; } .vol2-pitch-hero .in{ grid-template-columns:1fr; } }
.vol2-pitch-hero h1{ font-family:Georgia,serif; font-weight:600; font-size:2.4rem; line-height:1.1; margin:6px 0 12px; color:#fff; }
@media (max-width:560px){ .vol2-pitch-hero h1{ font-size:1.8rem; } }
.vol2-pitch-hero p{ color:#C7D3CC; font-size:1rem; max-width:44ch; margin:0; }
.vol2-pitch-hero .n{ background:#3A4A44; border-radius:18px; padding:18px; } .vol2-pitch-hero .n b{ display:block; font-size:2.2rem; } .vol2-pitch-hero .n small{ color:#C7D3CC; }
.vol2-pitch-hero .n .sp{ display:block; height:16px; }
.vol2-pitch{ max-width:900px; margin:26px auto; padding:0 24px; display:grid; grid-template-columns:1fr 1fr; gap:24px; }
@media (max-width:760px){ .vol2-pitch{ grid-template-columns:1fr; padding:0 14px; } }
.vol2-why h2{ font-family:Georgia,serif; font-size:1.3rem; margin:0 0 10px; } .vol2-why p{ line-height:1.6; color:var(--soft); margin:0 0 10px; }
.vol2-join{ background:#fff; border-radius:20px; padding:22px; display:flex; flex-direction:column; gap:12px; align-self:start; }
.vol2-join h2{ font-size:1.05rem; margin:0; } .vol2-join p{ color:var(--soft); font-size:.88rem; line-height:1.5; margin:0; }
.vol2-join .steps{ display:grid; gap:8px; } .vol2-join .steps span{ display:flex; align-items:center; gap:10px; font-size:.84rem; font-weight:600; }
.vol2-join .steps span b{ width:24px; height:24px; border-radius:50%; background:var(--mint); color:var(--deep); display:inline-flex; align-items:center; justify-content:center; font-size:.72rem; flex:none; }
.vol2-join .vol2-btn{ padding:14px; font-size:1rem; }
.vol2-join .bad-list{ display:flex; gap:6px; flex-wrap:wrap; align-items:center; }

/* ── the duty dashboard ──
   the hero, the tab bar and the callouts stack in column 1; the side panel spans column 2.
   Every piece is a DIRECT child of .vol2-wrap, so the sticky tab bar's containing block is the
   whole page whichever panel is showing (nested in a column, it stopped sticking once that
   column emptied). */
.vol2-wrap{ max-width:1100px; margin:28px auto; padding:0 24px; display:grid; grid-template-columns:1fr 360px; grid-template-rows:auto auto 1fr; gap:0 22px; align-items:start; }
.vol2-wrap > *{ min-width:0; }
.vol2-wrap > .vol2-hero{ grid-column:1; grid-row:1; } .vol2-wrap > .vol2-mtabs{ grid-column:1; grid-row:2; } .vol2-wrap > #panelCallouts{ grid-column:1; grid-row:3; }
.vol2-wrap > .vol2-side{ grid-column:2; grid-row:1 / span 3; }
@media (max-width:860px){ .vol2-wrap{ grid-template-columns:1fr; grid-template-rows:none; margin:16px auto; padding:0 14px; }
  .vol2-wrap > .vol2-hero, .vol2-wrap > .vol2-mtabs, .vol2-wrap > #panelCallouts, .vol2-wrap > .vol2-side{ grid-column:1; grid-row:auto; } }
.vol2-hero{ background:var(--ink); color:#fff; border-radius:22px; padding:26px 28px; display:grid; grid-template-columns:1fr auto; gap:20px; align-items:center; margin-bottom:16px; }
@media (max-width:560px){ .vol2-hero{ grid-template-columns:1fr; padding:20px; } }
.vol2-hero h1{ font-size:1.6rem; margin:0 0 4px; color:#fff; } .vol2-hero p{ color:#C7D3CC; font-size:.9rem; max-width:44ch; margin:0; }
.vol2-hero .stat{ display:flex; gap:22px; margin-top:14px; flex-wrap:wrap; } .vol2-hero .stat b{ display:block; font-size:1.5rem; } .vol2-hero .stat small{ color:#C7D3CC; font-size:.74rem; }
.vol2-hero .score{ background:#3A4A44; border-radius:14px; padding:10px 12px; display:inline-flex; align-items:center; gap:10px; margin-top:12px; font-size:.84rem; }
.vol2-hero .score b{ font-size:1.2rem; } .vol2-hero .score svg{ width:18px; height:18px; color:#E3C9A5; }
.vol2-hero .vol2-tog{ color:#fff; }
.vol2 h2.vol2-h2{ font-size:1rem; margin:0 0 10px; }
.vol2-side{ background:#fff; border-radius:18px; padding:16px; align-self:start; }
.vol2-side .who{ display:flex; align-items:center; gap:12px; margin-bottom:12px; }
.vol2-side .who .av{ width:52px; height:52px; border-radius:50%; overflow:hidden; flex:none; display:inline-flex; } .vol2-side .who .av img, .vol2-side .who .av .avatar{ width:100%; height:100%; display:block; object-fit:cover; }
.vol2-side .who b{ display:block; } .vol2-side .who small{ color:var(--soft); }
.vol2-callout{ background:#fff; border-radius:18px; padding:16px 18px; border-left:6px solid var(--red); }
.vol2-callout + .vol2-callout{ margin-top:10px; }
.vol2-callout .k{ font-size:.72rem; letter-spacing:1px; text-transform:uppercase; color:var(--red); font-weight:800; }
.vol2-callout h3{ font-size:1.1rem; margin:4px 0; } .vol2-callout p{ color:var(--soft); font-size:.86rem; margin:0; }
.vol2-callout .acts{ display:flex; gap:8px; margin-top:12px; flex-wrap:wrap; align-items:center; } .vol2-callout .acts .vol2-btn{ padding:9px 16px; font-size:.86rem; }
.vol2-callout .acts .view{ margin-left:auto; color:var(--deep); font-weight:700; font-size:.84rem; display:inline-flex; align-items:center; gap:4px; }
.vol2-callout .acts .view svg{ width:14px; height:14px; transform:rotate(-90deg); }
.vol2-callout.going{ border-left-color:var(--amber); } .vol2-callout.going .k{ color:var(--amber); }
.vol2-callout.closed{ border-left-color:var(--track); opacity:.85; } .vol2-callout.closed .k{ color:var(--soft); }
.vol2-empty{ background:#fff; border-radius:18px; padding:22px; color:var(--soft); font-size:.9rem; }
/* the settings summary inside the side card — the same row grammar as the settings list */
.vol2-srow{ display:flex; align-items:center; gap:10px; padding:10px 0; border-bottom:1px solid #EEF2EE; color:inherit; font-size:.86rem; text-decoration:none; }
.vol2-srow:hover{ text-decoration:none; color:inherit; }
.vol2-srow .ic{ color:var(--deep); flex:none; } .vol2-srow .ic svg{ width:18px; height:18px; } .vol2-srow .tx{ flex:1; min-width:0; }
.vol2-srow b{ display:block; font-size:.88rem; } .vol2-srow small{ color:var(--soft); font-size:.76rem; }
.vol2-srow .v{ color:var(--soft); font-size:.8rem; text-align:right; } .vol2-srow .chev{ color:var(--track); }
/* small screens: callouts and settings are TABS under the hero card, and the bar sticks */
.vol2-mtabs{ display:none; position:sticky; top:0; z-index:20; background:var(--paper); padding:6px 0 10px; margin:-6px 0 8px; }
.vol2-mtabs nav{ display:flex; background:#fff; border-radius:999px; padding:4px; box-shadow:0 2px 8px rgba(44,59,54,.08); }
.vol2-mtabs button{ flex:1; border:0; background:none; border-radius:999px; padding:10px; font-weight:700; font-size:.88rem; color:var(--soft); display:inline-flex; align-items:center; justify-content:center; gap:6px; cursor:pointer; font-family:inherit; }
.vol2-mtabs button svg{ width:16px; height:16px; } .vol2-mtabs button.on{ background:var(--deep); color:#fff; }
.vol2-mtabs button .n{ background:var(--red); color:#fff; border-radius:999px; min-width:18px; height:18px; font-size:.68rem; display:inline-grid; place-items:center; padding:0 5px; }
.vol2-mtabs button.on .n{ background:#fff; color:var(--red); }
@media (max-width:860px){
  .vol2-mtabs{ display:block; top:var(--header-h, 0px); }
  .vol2-panel{ display:none; } .vol2-panel.on{ display:block; }
  .vol2-side{ padding:14px; }
}

/* ── a callout ── */
.vol2-cwrap{ max-width:1100px; margin:22px auto; padding:0 24px; display:grid; grid-template-columns:1fr 420px; gap:20px; }
.vol2-cwrap > *{ min-width:0; }
@media (max-width:860px){ .vol2-cwrap{ grid-template-columns:1fr; padding:0 14px; margin:12px auto; } }
.vol2-cback{ margin:14px 24px 0; }
.vol2-cmap{ border-radius:22px; min-height:560px; overflow:hidden; background:#E4EBE5; } @media (max-width:860px){ .vol2-cmap{ min-height:280px; } }
.vol2-ask{ background:#fff; border-radius:22px; padding:22px; box-shadow:0 8px 24px rgba(44,59,54,.1); display:flex; flex-direction:column; gap:12px; }
.vol2-ask .k{ color:var(--red); font-weight:800; letter-spacing:1px; text-transform:uppercase; font-size:.74rem; display:flex; align-items:center; gap:8px; }
.vol2-ask .k i{ width:10px; height:10px; border-radius:50%; background:var(--red); animation:vol2p 1s infinite; } @keyframes vol2p{ 50%{ opacity:.3; } }
.vol2-ask .k.calm{ color:var(--soft); } .vol2-ask .k.calm i{ animation:none; background:var(--track); }
.vol2-ask h1{ font-size:1.5rem; line-height:1.2; margin:0; } .vol2-ask p{ color:var(--soft); font-size:.9rem; margin:0; }
.vol2-ask .facts{ display:grid; grid-template-columns:1fr 1fr; gap:8px; } .vol2-ask .facts div{ background:var(--paper); border-radius:12px; padding:10px; font-size:.8rem; color:var(--soft); }
.vol2-ask .facts b{ display:block; color:var(--ink); font-size:1rem; }
.vol2-ask .who{ display:flex; gap:6px; align-items:center; font-size:.82rem; color:var(--soft); }
.vol2-ask .who i{ width:26px; height:26px; border-radius:50%; background:var(--deep); display:inline-block; border:2px solid #fff; margin-left:-8px; overflow:hidden; flex:none; }
.vol2-ask .who i:first-child{ margin-left:0; } .vol2-ask .who i img, .vol2-ask .who i .avatar{ width:100%; height:100%; display:block; object-fit:cover; }
.vol2-ask .big{ display:grid; gap:8px; margin-top:auto; } .vol2-ask .big .vol2-btn{ padding:14px; font-size:1rem; }
.vol2-ask .tl{ border-top:1px solid #EEF2EE; padding-top:10px; font-size:.8rem; color:var(--soft); display:grid; gap:6px; } .vol2-ask .tl span b{ color:var(--ink); }
@media (prefers-reduced-motion:reduce){ .vol2-ask .k i{ animation:none; } }

/* ── the join wizard ── */
.vol2-jwrap{ max-width:720px; margin:36px auto; padding:0 20px; }
@media (max-width:760px){ .vol2-jwrap{ margin:18px auto; padding:0 12px; } }
.vol2-steps{ display:flex; gap:8px; margin-bottom:22px; }
.vol2-steps button{ flex:1; text-align:center; font-size:.74rem; font-weight:700; color:var(--soft); padding:10px 0 0; border:0; border-top:4px solid var(--mint); background:none; cursor:pointer; font-family:inherit; }
.vol2-steps button.on{ color:var(--deep); border-top-color:var(--deep); } .vol2-steps button.done{ border-top-color:var(--green); }
@media (max-width:560px){ .vol2-steps button{ font-size:.62rem; } }
.vol2-card{ background:#fff; border-radius:22px; padding:26px; } .vol2-card h1{ font-size:1.4rem; margin:0 0 6px; } .vol2-card > p.lede{ color:var(--soft); margin:0 0 16px; font-size:.9rem; }
@media (max-width:560px){ .vol2-card{ padding:18px 14px; } }
.vol2-acts{ display:flex; justify-content:space-between; margin-top:18px; gap:8px; flex-wrap:wrap; } .vol2-acts .grp{ display:flex; gap:8px; }
.vol2-sub{ font-weight:800; font-size:.82rem; margin:18px 0 6px; display:flex; align-items:center; gap:8px; flex-wrap:wrap; } .vol2-sub small{ font-weight:600; color:var(--soft); }
.vol2-far{ display:flex; align-items:center; gap:14px; } .vol2-far svg{ width:22px; height:22px; color:var(--deep); flex:none; } .vol2-far input{ flex:1; accent-color:var(--deep); min-width:0; } .vol2-far output{ font-weight:800; color:var(--deep); min-width:56px; text-align:right; }
.vol2-farmap{ height:260px; border-radius:16px; overflow:hidden; margin-top:12px; background:#E4EBE5; }
.vol2-farhint{ display:flex; gap:8px; align-items:center; margin-top:10px; flex-wrap:wrap; position:relative; }
.vol2-farhint .vol2-btn{ padding:7px 12px; font-size:.8rem; }
.vol2-farhint input{ flex:1; min-width:180px; border:1.5px solid var(--track); border-radius:999px; padding:8px 12px; font:inherit; font-size:.84rem; }
.vol2-places{ list-style:none; margin:6px 0 0; padding:0; background:#fff; border:1px solid #E4EBE5; border-radius:12px; overflow:hidden; }
.vol2-places button{ display:block; width:100%; text-align:left; border:0; background:none; padding:9px 12px; font:inherit; font-size:.84rem; cursor:pointer; color:var(--ink); border-bottom:1px solid #EEF2EE; }
.vol2-places button:last-child{ border-bottom:0; } .vol2-places button:hover{ background:#FAFBF9; } .vol2-places button small{ color:var(--soft); display:block; font-size:.74rem; }
.vol2-idopt{ display:grid; grid-template-columns:repeat(4,1fr); gap:8px; }
.vol2-idopt button{ border:1.5px solid var(--track); background:#fff; border-radius:12px; padding:12px 6px; font-size:.78rem; font-weight:700; display:flex; flex-direction:column; align-items:center; gap:6px; color:var(--ink); cursor:pointer; font-family:inherit; }
.vol2-idopt button.on{ border-color:var(--deep); background:var(--mint); } .vol2-idopt svg{ color:var(--deep); width:22px; height:22px; }
@media (max-width:560px){ .vol2-idopt{ grid-template-columns:1fr 1fr; } }
.vol2-whybox{ background:var(--paper); border-radius:12px; padding:12px 14px; font-size:.82rem; color:var(--soft); line-height:1.5; margin-top:12px; }
.vol2-review{ display:grid; gap:8px; }
.vol2-review .r{ display:flex; justify-content:space-between; gap:12px; background:var(--paper); border-radius:12px; padding:10px 12px; font-size:.86rem; }
.vol2-review .r b{ color:var(--ink); } .vol2-review .r span{ color:var(--soft); text-align:right; } .vol2-review .r a, .vol2-review .r button{ color:var(--deep); font-weight:700; background:none; border:0; font:inherit; font-weight:700; cursor:pointer; padding:0; margin-left:6px; }

/* ── the settings list (the account-settings grammar, in this palette) ── */
.vol2-swrap{ max-width:640px; margin:30px auto; padding:0 20px; } .vol2-swrap h1{ font-size:1.6rem; margin:4px 0 12px; }
@media (max-width:560px){ .vol2-swrap{ margin:16px auto; padding:0 12px; } }
.vol2-gl{ font-size:.7rem; letter-spacing:1.2px; text-transform:uppercase; color:var(--tan); font-weight:800; margin:18px 0 6px 4px; }
.vol2-scard{ background:#fff; border-radius:16px; overflow:hidden; }
.vol2-row{ display:flex; align-items:center; gap:12px; padding:14px 16px; border-bottom:1px solid #EEF2EE; color:inherit; text-decoration:none; width:100%; background:none; border-left:0; border-right:0; border-top:0; font:inherit; text-align:left; cursor:pointer; }
.vol2-row:last-child{ border-bottom:0; } .vol2-row:hover{ text-decoration:none; color:inherit; } a.vol2-row:hover, button.vol2-row:hover{ background:#FAFBF9; }
.vol2-row.static{ cursor:default; } .vol2-row.static:hover{ background:none; }
.vol2-row .ic{ color:var(--deep); flex:none; } .vol2-row .ic svg{ width:20px; height:20px; } .vol2-row .tx{ flex:1; min-width:0; }
.vol2-row b{ display:block; font-size:.92rem; } .vol2-row small{ color:var(--soft); font-size:.78rem; }
.vol2-row .v{ color:var(--soft); font-size:.84rem; text-align:right; } .vol2-row .chev{ color:var(--track); }
.vol2-pane{ background:#fff; border-radius:16px; padding:18px; }
.vol2-ph{ display:flex; align-items:center; gap:10px; margin-bottom:12px; } .vol2-ph h2{ font-size:1.05rem; margin:0; }
.vol2-pback{ border:0; background:var(--mint); color:var(--deep); border-radius:999px; width:34px; height:34px; display:inline-grid; place-items:center; cursor:pointer; flex:none; } .vol2-pback svg{ width:16px; height:16px; }
.vol2-leave{ margin:16px 4px 0; } .vol2-leave a, .vol2-leave button{ color:var(--red); font-weight:700; background:none; border:0; font:inherit; font-weight:700; cursor:pointer; padding:0; }
.vol2-hist{ list-style:none; margin:10px 0 0; padding:0; } .vol2-hist li{ display:flex; justify-content:space-between; gap:10px; padding:8px 0; border-bottom:1px solid #EEF2EE; font-size:.84rem; } .vol2-hist li b{ color:var(--deep); }

/* ── the hours page ── */
.vol2-hwrap{ max-width:1000px; margin:28px auto; padding:0 24px; } .vol2-hwrap h1{ font-size:1.6rem; margin:4px 0 4px; } .vol2-hwrap .lede{ color:var(--soft); margin:0 0 16px; }
@media (max-width:560px){ .vol2-hwrap{ margin:14px auto; padding:0 10px; } }

/* ── the reviewer pages (admin + verified agencies) ── */
.vol2-q{ display:grid; gap:12px; }
.vol2-qcard{ background:#fff; border:1px solid #E4EBE5; border-radius:16px; padding:14px 16px; display:grid; grid-template-columns:auto 1fr; gap:14px; align-items:start; }
@media (max-width:640px){ .vol2-qcard{ grid-template-columns:1fr; } }
.vol2-qcard .av{ width:44px; height:44px; border-radius:50%; overflow:hidden; display:inline-flex; } .vol2-qcard .av img, .vol2-qcard .av .avatar{ width:100%; height:100%; object-fit:cover; display:block; }
.vol2-qcard .head{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; } .vol2-qcard .head b{ font-size:.95rem; }
.vol2-qcard .meta{ color:var(--soft, #5C6E66); font-size:.8rem; margin-top:2px; }
.vol2-qimgs{ display:flex; gap:8px; margin-top:10px; flex-wrap:wrap; }
.vol2-qimgs a{ display:block; width:120px; height:84px; border-radius:10px; overflow:hidden; background:#EEF2EE; border:1px solid #E4EBE5; } .vol2-qimgs img{ width:100%; height:100%; object-fit:cover; display:block; }
.vol2-qacts{ display:flex; gap:8px; margin-top:10px; flex-wrap:wrap; align-items:center; } .vol2-qacts input{ flex:1; min-width:160px; border:1.5px solid #ADC3B9; border-radius:999px; padding:7px 12px; font:inherit; font-size:.84rem; }
.vol2-qacts .vol2-btn{ padding:8px 14px; font-size:.84rem; }
.vol2-qskills{ display:flex; gap:6px; flex-wrap:wrap; margin-top:8px; }
.vol2-dir{ width:100%; border-collapse:collapse; font-size:.86rem; } .vol2-dir th{ text-align:left; font-size:.72rem; text-transform:uppercase; letter-spacing:.8px; color:#5C6E66; padding:8px 8px; border-bottom:1px solid #E4EBE5; }
.vol2-dir td{ padding:9px 8px; border-bottom:1px solid #EEF2EE; vertical-align:middle; } .vol2-dir .who{ display:flex; align-items:center; gap:8px; } .vol2-dir .who .av{ width:30px; height:30px; border-radius:50%; overflow:hidden; display:inline-flex; flex:none; } .vol2-dir .who .av img, .vol2-dir .who .av .avatar{ width:100%; height:100%; object-fit:cover; display:block; }

/* Round-145b (owner's call): the volunteer pages sit on the finale design's PAPER (#F3F4F1),
   not the site's cream — html, body and the page alike, at every width, so nothing cream
   shows through under the white cards. At the END of the file to out-order the cream rules. */
html.pg-vol, html.pg-vol body, .page.vol2{ background:#F3F4F1; }

/* Round-145c (owner's call): clicking an uncertainty ring (or any vector on the map) drew the
   browser's focus rectangle around the circle's bounding box — Leaflet makes paths focusable.
   Suppressed for pointer focus only; keyboard focus keeps its ring. */
.leaflet-container path.leaflet-interactive:focus:not(:focus-visible){ outline:none; }

/* Round-145d: the incident panel's hero while its media count is still unknown — a reserved,
   shimmering box the photo lands in, so the text under it never moves. */
@keyframes edHeroShimmer{ 0%{ background-position:100% 0; } 100%{ background-position:0 0; } }
.ed-hero.ed-hero-wait{ background:linear-gradient(90deg, #DCE4DE 25%, #EAF0EC 37%, #DCE4DE 63%); background-size:400% 100%; animation:edHeroShimmer 1.4s ease infinite; }
@media (prefers-reduced-motion:reduce){ .ed-hero.ed-hero-wait{ animation:none; } }

/* Round-145d (owner's call): on the phone map Ada is the first button of the fixed column; the
   floating FAB (and its JS placement) is off there. Desktop keeps the floating button. */
.rs-ada{ display:none; }
@media (max-width:1000px){
  /* round-147: two rules, not one list — a WebView without :has() support drops a whole
     rule whose selector list contains it (144c trap); with the list, the G6 showed the
     FLOATING Ada pill over the fixed column. Same for the hide rule below. */
  html.pg-map .rs-ada{
    display:inline-flex; align-items:center; justify-content:center; width:44px; height:44px; border-radius:50%;
    border:0; background:var(--deep, #4F8577); color:#fff;
    /* round-145f (owner's call): the SAME face as the floating Ada pill — regular weight, italic,
       the site font — not the bold that made the word look wider */
    font-size:.9rem; font-weight:400; font-style:italic; letter-spacing:.01em; font-family:var(--font, inherit);   /* .9rem — owner's call */
    box-shadow:none; cursor:pointer; flex:0 0 auto;   /* round-145f (owner's call): no shadow on the map */
  }
  body:has(#map) .rs-ada{
    display:inline-flex; align-items:center; justify-content:center; width:44px; height:44px; border-radius:50%;
    border:0; background:var(--deep, #4F8577); color:#fff;
    /* round-145f (owner's call): the SAME face as the floating Ada pill — regular weight, italic,
       the site font — not the bold that made the word look wider */
    font-size:.9rem; font-weight:400; font-style:italic; letter-spacing:.01em; font-family:var(--font, inherit);   /* .9rem — owner's call */
    box-shadow:none; cursor:pointer; flex:0 0 auto;   /* round-145f (owner's call): no shadow on the map */
  }
  html.pg-map .asst-fab{ display:none !important; }
  body:has(#map) .asst-fab{ display:none !important; }
}

/* Round-145h (owner's call): the feedback page — no hero, a compact gold-kicker heading, a wide
   form on desktop, gold stars. The same gold on the SafeNet kicker. */
:root{ --gold:#D6A544; }
.fb-head{ padding:26px 0 4px; }
.fb-head .kicker{ color:var(--gold); margin-bottom:6px; }
.fb-head h1{ font-size:clamp(1.45rem,2.6vw,1.9rem); line-height:1.2; margin:0 0 6px; }
.fb-head .lead{ color:var(--ink-soft); margin:0; font-size:.95rem; max-width:64ch; }
.fb-section{ padding-top:18px; }
.fb-card{ max-width:760px; margin:0 auto; border:0; }
/* the heading column is the card's column: same 760px, same centre, so the kicker, title and
   lead start where the card starts on wide screens (owner's snapshot) */
.fb-head .container{ max-width:800px; }
.chip.star{ color:var(--gold); letter-spacing:1px; }
.chip.star.sel{ background:var(--accent); border-color:var(--accent); color:#fff; }
main.sn2 .kicker{ color:var(--gold); }

/* Round-145i: the feedback page's composer (+ for screenshots / PDFs) and the admin thumbnails */
.fb-composer textarea{ border:0; box-shadow:none; background:transparent; }
.fb-composer .composer-limits{ font-size:.78rem; color:var(--ink-soft); }
.afb-atts{ display:flex; gap:8px; flex-wrap:wrap; margin:4px 0 6px; }
.afb-att{ display:inline-flex; align-items:center; gap:6px; border:1px solid var(--track); border-radius:10px; overflow:hidden; background:#fff; text-decoration:none; color:var(--ink); font-size:.8rem; }
.afb-att img{ width:64px; height:64px; object-fit:cover; display:block; }
.afb-att span{ padding:8px 10px; }

/* Round-145j (owner's call): the feedback page on white; the card keeps its own paper tint so it
   still reads as a card. Under-box texts (limits / sign-in hint / toast / validation) share one margin. */
html.pg-feedback, html.pg-feedback body, .page.fb-page{ background:#fff; }
.fb-under{ margin:8px 0 0; }
.fb-page #upToast{ margin:8px 0 0; }

/* Round-146c: chips that are radio inputs (the static feedback form) — same look as .chip.sel
   when checked; the input itself is present but invisible, so keyboard and screen readers keep
   a real control. */
.chip.chip-radio{ position:relative; }
.chip.chip-radio input{ position:absolute; opacity:0; width:0; height:0; margin:0; pointer-events:none; }
.chip.chip-radio:has(input:checked){ background:var(--accent); border-color:var(--accent); color:var(--paper); }
.chip.chip-radio:has(input:focus-visible){ outline:2px solid var(--deep); outline-offset:2px; }
.chip.star.chip-radio:has(input:checked){ background:var(--accent); border-color:var(--accent); color:#fff; }

/* Round-147b (owner's pick, design/get-app/04): /get-app — one card over the map ground. */
html.pg-getapp, html.pg-getapp body, .page.ga-page{ background:#F3F4F1; }
/* Round 224 (owner: the page scrolled sideways on a phone): the live map behind the card is wider
   than the screen, and iOS WebKit can let a transformed map layer escape overflow:hidden. Paint
   containment clips it for certain, and the page itself never pans sideways. */
html.pg-getapp, html.pg-getapp body{ overflow-x:clip; }
.ga-stage{ contain:paint; }
.ga-stage{ position:relative; overflow:hidden; min-height:calc(var(--app-h, 100vh) - var(--header-h, 64px)); display:grid; place-items:center; padding:40px 24px;
  background: linear-gradient(rgba(191,209,196,.55) 1.5px,transparent 1.5px) 0 0/44px 44px, linear-gradient(90deg,rgba(191,209,196,.55) 1.5px,transparent 1.5px) 0 0/44px 44px, linear-gradient(120deg,#E9EEE7,#DCE6DF 60%,#CFE0D6); }
.ga-mk{ position:absolute; width:12px; height:12px; border-radius:50%; background:var(--deep, #4F8577); border:2px solid #fff; box-shadow:0 2px 6px rgba(44,59,54,.3); }
.ga-mk.av{ width:34px; height:34px; background:#fff; color:var(--deep, #4F8577); font-weight:800; font-size:.72rem; display:grid; place-items:center; border:2px solid var(--deep, #4F8577); }
.ga-card{ position:relative; z-index:1; background:#fff; border-radius:28px; padding:34px; max-width:860px; width:100%; display:grid; grid-template-columns:1fr auto; gap:34px; align-items:center; box-shadow:0 26px 70px rgba(44,59,54,.28); }
/* round-147c: the card exactly as design 04 draws it — the design kicker is tan, .72rem, 1.4px */
.ga-kicker{ display:inline-block; font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--tan, #BC8A52); font-weight:800; margin-bottom:0; }
.ga-map{ position:absolute; inset:0; z-index:0; pointer-events:none; background:transparent; }
.ga-map .leaflet-control-attribution{ font-size:.62rem; opacity:.75; }
.ga-card h1{ font-family:Georgia,serif; font-weight:600; font-size:2.2rem; line-height:1.1; margin:6px 0 10px; }
.ga-lead{ color:var(--ink-soft, #5C6E66); line-height:1.55; max-width:44ch; margin:0; }
.ga-or{ margin:22px 0 10px; font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--ink-soft, #5C6E66); font-weight:800; display:flex; align-items:center; gap:10px; }
.ga-or::after{ content:""; flex:1; height:1px; background:#E4EBE5; }
.ga-badges{ display:flex; gap:10px; flex-wrap:wrap; }
.ga-store{ display:inline-flex; align-items:center; gap:10px; background:var(--ink, #2C3B36); color:#fff; border-radius:12px; padding:10px 16px 10px 12px; text-decoration:none; min-width:190px; }
.ga-store:hover{ background:#3A4A44; text-decoration:none; color:#fff; }
.ga-store i{ width:28px; height:28px; border-radius:7px; display:inline-flex; align-items:center; justify-content:center; flex:none; font-style:normal; font-size:1.05rem; background:#fff; color:var(--ink, #2C3B36); }
.ga-store i svg{ width:18px; height:18px; }
.ga-store small{ display:block; font-size:.62rem; letter-spacing:.6px; text-transform:uppercase; opacity:.8; } .ga-store b{ display:block; font-size:.98rem; line-height:1.1; }
.ga-store.apk{ background:var(--mint, #E7EDE6); color:var(--ink, #2C3B36); } .ga-store.apk:hover{ background:#DCE6DF; color:var(--ink, #2C3B36); } .ga-store.apk i{ background:var(--deep, #4F8577); color:#fff; }
.ga-tiny{ margin:14px 0 0; font-size:.78rem; color:var(--ink-soft, #5C6E66); }
.ga-tiny a{ color:var(--deep, #4F8577); font-weight:700; }
.ga-qr{ display:grid; justify-items:center; gap:10px; }
.ga-qr img{ width:220px; height:220px; padding:16px; border-radius:22px; background:var(--mint, #E7EDE6); display:block; }
.ga-qr b{ font-size:.86rem; } .ga-req{ font-size:.78rem; color:var(--ink-soft, #5C6E66); text-align:center; }
/* on a phone the QR is pointless: the badges lead */
.ga-phone{ display:none; }
@media (max-width:700px){ .ga-stage{ padding:24px 16px 40px; align-items:start; } .ga-card{ grid-template-columns:1fr; padding:24px; } .ga-qr, .ga-or{ display:none; } .ga-card h1{ font-size:1.8rem; }
  .ga-desk{ display:none; } .ga-phone{ display:inline; } }



/* ===== Round-148 (owner's pick, design/learn/11-finale): the Learning centre =====
   A scored checklist on the paper ground: the score card (ring + verdict), the progress strip
   with the three milestone pills, three groups of module cards (tile · text · one verb), the
   lesson reader with its "Do it" call and quiz, the guides grid. Same tokens as the volunteer
   and local-resources finales. Every :has() rule stands alone (old WebViews drop a whole list). */
html.pg-learn, html.pg-learn body, .page.lc-page{ background:#F3F4F1; }
.lc-wrap{ max-width:820px; margin:0 auto; padding:40px 24px 70px; }
.lc-wrap.lc-narrow{ max-width:780px; }
.lc-page .kicker{ font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--tan, #BC8A52); font-weight:800; }
.lc-page h1{ font-family:Georgia,"Times New Roman",serif; font-weight:600; font-size:2.2rem; margin:6px 0 6px; color:var(--ink, #2C3B36); line-height:1.15; }
.lc-page .lead{ color:var(--ink-soft, #5C6E66); line-height:1.5; margin:0 0 20px; max-width:60ch; font-size:1rem; }
.lc-page .pill{ display:inline-flex; align-items:center; gap:5px; border-radius:999px; padding:3px 10px; font-size:.72rem; font-weight:800; background:var(--mint, #E7EDE6); color:var(--ink, #2C3B36); white-space:nowrap; }
.lc-page .pill.ok{ background:#E3F3E8; color:#1E6B3E; } .lc-page .pill.warn{ background:#FBEFD9; color:#8A5A10; }
.lc-page .pill.badge{ background:#F3E6D4; color:#7A4E1F; }
.lc-page .btn{ border:0; border-radius:999px; padding:11px 20px; font-weight:700; font-size:.92rem; display:inline-flex; align-items:center; justify-content:center; gap:8px; line-height:1.2; text-decoration:none; cursor:pointer; }
.lc-page .btn.g{ background:var(--deep, #4F8577); color:#fff; } .lc-page .btn.o{ background:#fff; border:1.5px solid var(--ink, #2C3B36); color:var(--ink, #2C3B36); }
.lc-page .svc{ display:inline-flex; align-items:center; gap:6px; font-weight:700; font-size:.8rem; color:var(--ink, #2C3B36); } .lc-page .svc i{ width:10px; height:10px; border-radius:50%; display:inline-block; }
/* the score card */
.lc-score{ background:#fff; border-radius:22px; padding:20px 22px; display:grid; grid-template-columns:auto 1fr; gap:18px; align-items:center; margin-bottom:8px; }
.lc-ring{ width:92px; height:92px; border-radius:50%; background:conic-gradient(var(--deep, #4F8577) 0 calc(var(--pct, 0) * 1%), #E4EBE5 calc(var(--pct, 0) * 1%) 100%); display:grid; place-items:center; }
.lc-ring b{ width:70px; height:70px; border-radius:50%; background:#fff; display:grid; place-items:center; font-size:1.3rem; color:var(--ink, #2C3B36); }
.lc-score h2{ font-size:1.1rem; margin:0 0 4px; color:var(--ink, #2C3B36); } .lc-score p{ color:var(--ink-soft, #5C6E66); font-size:.88rem; line-height:1.5; margin:0; }
/* the progress strip */
.lc-prog{ background:#fff; border-radius:18px; padding:12px 18px; display:flex; align-items:center; gap:14px; margin-bottom:22px; flex-wrap:wrap; }
.lc-prog .bar{ flex:1; min-width:140px; height:8px; border-radius:4px; background:#E4EBE5; overflow:hidden; } .lc-prog .bar i{ display:block; height:100%; background:var(--deep, #4F8577); transition:width .4s ease; }
.lc-prog b{ font-size:.86rem; white-space:nowrap; color:var(--ink, #2C3B36); }
/* groups and module cards */
.lc-grp{ margin-top:22px; }
.lc-grp .k{ font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--tan, #BC8A52); font-weight:800; margin-bottom:10px; display:flex; align-items:center; gap:10px; }
.lc-grp .k span{ color:var(--ink-soft, #5C6E66); letter-spacing:0; text-transform:none; font-weight:600; font-size:.78rem; }
.lc-mods{ display:grid; gap:10px; }
.lc-mod{ background:#fff; border-radius:18px; padding:16px 18px; display:grid; grid-template-columns:52px 1fr auto; gap:14px; align-items:center; text-decoration:none; color:var(--ink, #2C3B36); border:1.5px solid transparent; }
.lc-mod:hover{ border-color:var(--deep, #4F8577); }
.lc-mod .n{ width:52px; height:52px; border-radius:14px; display:grid; place-items:center; font-weight:800; font-size:1.05rem; background:var(--mint, #E7EDE6); color:var(--deep, #4F8577); }
.lc-mod.done .n{ background:var(--deep, #4F8577); color:#fff; } .lc-mod.next .n{ background:#FBEFD9; color:#8A5A10; }
.lc-mod h3{ font-size:1rem; margin:0; color:var(--ink, #2C3B36); } .lc-mod p{ color:var(--ink-soft, #5C6E66); font-size:.84rem; line-height:1.45; margin:2px 0 0; }
.lc-mod .m{ display:flex; gap:8px; margin-top:8px; flex-wrap:wrap; align-items:center; }
.lc-mod .go{ white-space:nowrap; } .lc-mod.done .go{ background:#fff; border:1.5px solid var(--track, #ADC3B9); color:var(--ink-soft, #5C6E66); }
.lc-cta{ margin-top:26px; display:flex; gap:10px; flex-wrap:wrap; }
.lc-nudge{ margin-top:18px; background:#fff; border-radius:18px; padding:14px 18px; display:flex; gap:14px; align-items:center; justify-content:space-between; flex-wrap:wrap; }
.lc-nudge p{ margin:0; color:var(--ink-soft, #5C6E66); font-size:.88rem; line-height:1.5; max-width:52ch; }
.lc-guides{ display:grid; grid-template-columns:repeat(auto-fill, minmax(230px, 1fr)); gap:10px; }
.lc-guide{ background:#fff; border-radius:18px; padding:16px; display:grid; gap:6px; text-decoration:none; color:var(--ink, #2C3B36); border:1.5px solid transparent; align-content:start; }
.lc-guide:hover{ border-color:var(--deep, #4F8577); }
.lc-guide .k{ font-size:.7rem; letter-spacing:1.2px; text-transform:uppercase; font-weight:800; color:var(--tan, #BC8A52); margin:0; }
.lc-guide h3{ font-size:.98rem; margin:0; } .lc-guide p{ color:var(--ink-soft, #5C6E66); font-size:.82rem; line-height:1.5; margin:0; }
/* the lesson reader */
.lc-back{ display:block; width:max-content; font-weight:700; font-size:.86rem; color:var(--deep, #4F8577); text-decoration:none; margin-bottom:16px; }
.lc-min{ color:var(--ink-soft, #5C6E66); font-size:.82rem; font-weight:700; margin:-8px 0 16px; }
.lc-preview{ background:#FBEFD9; color:#8A5A10; border-radius:12px; padding:8px 12px; }
.lc-card{ background:#fff; border-radius:22px; padding:26px 28px; }
.lc-card.md-body h2{ font-size:1.15rem; margin:22px 0 8px; } .lc-card.md-body h2:first-child{ margin-top:0; }
.lc-card.md-body table{ width:100%; border-collapse:collapse; font-size:.86rem; margin:10px 0; } .lc-card.md-body th, .lc-card.md-body td{ text-align:left; padding:7px 8px; border-bottom:1px solid #E4EBE5; vertical-align:top; }
.lc-card.md-body ol, .lc-card.md-body ul{ line-height:1.55; }
.lc-do{ margin-top:14px; background:var(--mint, #E7EDE6); border-radius:18px; padding:16px 20px; display:flex; gap:14px; align-items:center; justify-content:space-between; flex-wrap:wrap; }
.lc-do b{ font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--deep, #4F8577); } .lc-do p{ margin:4px 0 0; color:var(--ink, #2C3B36); font-size:.9rem; line-height:1.45; max-width:52ch; }
.lc-quiz{ margin-top:18px; }
.lc-qhead h2{ font-size:1.2rem; margin:0 0 4px; } .lc-qhead p{ color:var(--ink-soft, #5C6E66); font-size:.86rem; margin:0 0 16px; }
.lc-q{ border:0; padding:0; margin:0 0 18px; }
.lc-q legend{ font-weight:700; font-size:.96rem; margin-bottom:10px; display:flex; gap:10px; align-items:flex-start; line-height:1.4; padding:0; }
.lc-q legend .n{ flex:none; width:26px; height:26px; border-radius:8px; background:var(--mint, #E7EDE6); color:var(--deep, #4F8577); display:grid; place-items:center; font-size:.8rem; font-weight:800; }
.lc-opts{ display:flex; flex-wrap:wrap; gap:8px; }
.lc-opts .chip{ border:1.5px solid var(--track, #ADC3B9); background:#fff; border-radius:999px; padding:8px 14px; font-size:.86rem; font-weight:700; display:inline-flex; align-items:center; gap:7px; color:var(--ink, #2C3B36); cursor:pointer; line-height:1.3; }
.lc-opts .chip.chip-radio.sel{ background:var(--accent, #618D7C); border-color:var(--accent, #618D7C); color:#fff; }   /* the JS twin of :has(input:checked) for old WebViews */
.lc-result{ display:grid; grid-template-columns:52px 1fr; gap:14px; align-items:start; }
.lc-result .n{ width:52px; height:52px; border-radius:14px; display:grid; place-items:center; font-weight:800; font-size:1.2rem; background:var(--deep, #4F8577); color:#fff; }
.lc-result.bad .n{ background:#FBEFD9; color:#8A5A10; }
.lc-result h2{ font-size:1.15rem; margin:4px 0 6px; } .lc-result p{ color:var(--ink-soft, #5C6E66); font-size:.9rem; line-height:1.5; margin:0 0 6px; }
.lc-result .hint{ font-size:.8rem; }
.lc-explain{ margin:8px 0 0 18px; padding:0; font-size:.88rem; line-height:1.5; color:var(--ink, #2C3B36); } .lc-explain li{ margin-bottom:6px; } .lc-explain span{ color:var(--ink-soft, #5C6E66); }
.lc-acts{ grid-column:1 / -1; display:flex; gap:10px; flex-wrap:wrap; margin-top:6px; }
@media (max-width:560px){
  .lc-wrap{ padding:26px 16px 60px; } .lc-page h1{ font-size:1.7rem; }
  .lc-mod{ grid-template-columns:44px 1fr; } .lc-mod .go{ grid-column:2; justify-self:start; } .lc-mod .n{ width:44px; height:44px; }
  .lc-score{ grid-template-columns:1fr; justify-items:start; } .lc-card{ padding:20px 18px; }
}


/* ===== Round-149: the live-map card at the top of the stories feed (first-visit landing) =====
   One inert Leaflet map (aquaGetAppMap draws #gaMap) beside three lines and a button; the whole
   card is the link. On a phone the map sits above the text. */
.st-mapcard{ display:grid; grid-template-columns:260px 1fr; gap:0; background:#fff; border-radius:22px; overflow:hidden; margin:0 0 16px; text-decoration:none; color:var(--ink, #2C3B36); border:1.5px solid transparent; box-shadow:0 8px 24px rgba(44,59,54,.08); }
.st-mapcard:hover{ border-color:var(--deep, #4F8577); }
.st-mapcard, .st-mapcard:hover, .st-mapcard:focus, .st-mapcard:visited, .st-mapcard *{ text-decoration:none; }   /* a card, not a link: no underline on hover */
.st-mapcard-map{ position:relative; z-index:0; isolation:isolate; min-height:170px; background:#E9EEE7; pointer-events:none; }   /* round-159: Leaflet's z-index 400/1000 stay INSIDE the card */
.st-mapcard-map .leaflet-control-attribution{ font-size:.58rem; opacity:.7; }
.st-mapcard-text{ padding:18px 22px; display:grid; gap:6px; align-content:center; justify-items:start; }
.st-mapcard-text .kicker{ font-size:.7rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--tan, #BC8A52); font-weight:800; }
.st-mapcard-text b{ font-family:Georgia,"Times New Roman",serif; font-weight:600; font-size:1.25rem; line-height:1.2; }
.st-mapcard-text p{ margin:0; color:var(--ink-soft, #5C6E66); font-size:.88rem; line-height:1.45; max-width:48ch; }
.st-mapcard-text .btn{ margin-top:6px; }
@media (max-width:640px){ .st-mapcard{ grid-template-columns:1fr; } .st-mapcard-map{ min-height:150px; } .st-mapcard-text{ padding:14px 16px; } }
/* Round-158: the card's ✕ — a sibling of the link, floated over its corner */
.st-mapcard-wrap{ position:relative; margin:0 0 16px; }
.st-mapcard-wrap .st-mapcard{ margin:0; }
.st-mapcard-x{
  position:absolute; top:10px; right:10px; z-index:2; width:32px; height:32px; border-radius:50%; border:0;
  background:rgba(255,255,255,.94); color:var(--ink-soft, #5C6E66); font-size:.9rem; font-weight:800;
  display:grid; place-items:center; cursor:pointer; box-shadow:0 2px 8px rgba(44,59,54,.18);
}
.st-mapcard-x:hover{ color:var(--ink, #2C3B36); background:#fff; }
.st-mapcard-x .lbl{ display:none; }
/* Round-159 (owner's call): on phones the ✕ becomes a labelled pill at the RIGHT of "Open the live
   map" — the map is on top there, so a corner ✕ over tiles was invisible against them. Still a
   sibling of the link, positioned over the text block's last row. */
@media (max-width:640px){
  .st-mapcard-x{ top:auto; bottom:14px; right:14px; width:auto; height:36px; border-radius:999px; padding:0 13px;
    display:inline-flex; align-items:center; gap:6px; white-space:nowrap;
    font-size:.8rem; font-weight:800; background:var(--mint, #E7EDE6); box-shadow:none; }
  .st-mapcard-x .lbl{ display:inline; }
  /* the pill sits to the right of the green button's own row; the text above keeps its width */
  .st-mapcard-text{ padding-bottom:16px; }
}


/* ===== Round-150 (owner's pick, design/map-bar/11-finale): the phone dock + the time sheet =====
   ≤720px only. The .time-row becomes one white pill: the mint state chip (.tb-state, injected by
   site.js) and the red report button with its words. The band strip is hidden in the row and
   shown inside .tb-sheet, restyled as a segmented control; the ⚙ popover mounts under it. */
.map-report-sm .lbl{ display:none; }
.tb-state{ display:none; }   /* desktop keeps the bands and the gear; the chip is a phone control */
@media (max-width:720px){
  /* round-152 (owner): the dock wears a hairline of the theme colour, as the finale did */
  .time-row{ background:#fff; border:1px solid var(--line, #ADC3B9); border-radius:999px; padding:6px; gap:6px; align-items:stretch; box-shadow:0 12px 30px rgba(44,59,54,.22); }
  .time-row .base-switch{ display:none; }
  .tb-state{ display:inline-flex; flex:0 0 auto; min-width:108px; border:0; border-radius:999px; background:var(--mint, #E7EDE6); color:var(--deep, #4F8577);
    font-family:var(--font, inherit); font-weight:800; font-size:.84rem; display:inline-flex; align-items:center; justify-content:center; gap:6px; padding:0 12px; cursor:pointer; }
  .tb-state .tb-car{ font-size:.58rem; opacity:.8; transition:transform .18s ease; } .tb-state.open .tb-car{ transform:rotate(180deg); }
  .tb-dot{ width:8px; height:8px; border-radius:50%; background:var(--green, #2E9E5B); box-shadow:0 0 0 3px rgba(46,158,91,.25); animation:tbPulse 2.2s ease-in-out infinite; }
  .tb-ic{ width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
  .tb-flag{ width:7px; height:7px; border-radius:50%; background:var(--tan, #BC8A52); }
  .map-report-sm{ flex:1; width:auto; height:46px; border-radius:999px; gap:8px; font-weight:800; font-size:.92rem; box-shadow:none; padding:0 14px; white-space:nowrap; }
  .map-report-sm .lbl{ display:inline; }
}
@media (prefers-reduced-motion: reduce){ .tb-dot{ animation:none; } }
@keyframes tbPulse{ 0%,100%{ box-shadow:0 0 0 3px rgba(46,158,91,.25); } 50%{ box-shadow:0 0 0 6px rgba(46,158,91,.1); } }
.tb-backdrop{ position:fixed; inset:0; z-index:3090; background:rgba(20,26,23,.32); }
.tb-sheet{ position:fixed; left:0; right:0; bottom:0; z-index:3100; background:#F6F8F6; border-radius:26px 26px 0 0; box-shadow:0 -14px 40px rgba(44,59,54,.2);
  max-height:min(calc(var(--app-h, 100vh) * 0.64), calc(var(--vvh, var(--app-h, 100dvh)) - 72px)); display:grid; grid-template-rows:auto minmax(0,1fr) auto;
  padding-bottom:var(--safe-bottom, env(safe-area-inset-bottom, 0px)); animation:tbIn .22s cubic-bezier(.2,.8,.25,1); }
@keyframes tbIn{ from{ transform:translateY(100%); } to{ transform:none; } }
.tb-head{ position:relative; padding:18px 18px 4px; display:flex; align-items:center; justify-content:space-between; touch-action:none; cursor:grab; }
.tb-head::before{ content:""; position:absolute; left:50%; top:7px; width:42px; height:5px; border-radius:99px; background:#D9E2DC; transform:translateX(-50%); }
.tb-head h4{ margin:0; font-size:.72rem; letter-spacing:1.3px; text-transform:uppercase; color:var(--tan, #BC8A52); font-weight:800; }
.tb-body{ overflow:auto; padding:6px 18px 4px; display:grid; gap:14px; }
/* id selectors so the ≤640px `#timeSwitch, .stories-time{ max-width; margin-right }` rule and the
   inline arrow padding from the engines cannot clip the sixth band inside the sheet */
.tb-sheet #timeSwitch, .tb-sheet #storyTime, .tb-sheet .base-switch{ display:grid; grid-template-columns:repeat(6,1fr); gap:4px; background:#E4EBE5; border-radius:14px;
  padding:4px !important; border:0; box-shadow:none; overflow:visible; position:static; transform:none; max-width:none !important; margin:0 !important; width:100%; }
.tb-sheet .base-switch button{ border-radius:11px; background:transparent; color:var(--ink-soft, #5C6E66); padding:10px 2px; font-size:.74rem; border-left:0; white-space:nowrap; min-width:0; }
.tb-sheet .base-switch button.on{ background:#fff; color:var(--deep, #4F8577); box-shadow:0 2px 6px rgba(44,59,54,.12); }
.tb-sheet .base-switch button.locked{ opacity:.55; }
.tb-sheet .base-switch [data-pin-filters], .tb-sheet .base-switch .time-arrow{ display:none; }
.tb-sheet .pin-filters{ position:static; transform:none; box-shadow:none; border:0; background:transparent; padding:0; min-width:0; max-width:none; gap:9px; }
.tb-sheet .pin-filters h4{ color:var(--tan, #BC8A52); letter-spacing:1.3px; margin-top:4px; }
/* round-150b (owner): switches, not checkboxes — text left, the toggle on the right, like the finale */
.tb-sheet .pin-filters > label{ display:flex; justify-content:space-between; align-items:center; gap:12px; padding:11px 0; border-top:1px solid #E4EBE5; font-size:.92rem; font-weight:600; }
.tb-sheet .pin-filters > label:first-of-type{ border-top:0; }
.tb-sheet .pin-filters > label input[type=checkbox]{ appearance:none; -webkit-appearance:none; order:2; margin:0 0 0 auto; width:44px; height:26px; border-radius:999px;
  background:#D9E2DC; position:relative; flex:none; cursor:pointer; transition:background .18s ease; border:0; }
.tb-sheet .pin-filters > label input[type=checkbox]::after{ content:""; position:absolute; top:3px; left:3px; width:20px; height:20px; border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.2); transition:left .18s ease; }
.tb-sheet .pin-filters > label input[type=checkbox]:checked{ background:var(--deep, #4F8577); }
.tb-sheet .pin-filters > label input[type=checkbox]:checked::after{ left:21px; }
.tb-sheet .pin-filters > label input[type=checkbox]:focus-visible{ outline:2px solid var(--deep, #4F8577); outline-offset:2px; }
/* the exact window as two boxes side by side ("From · …" → "To · …"), no hint text under them */
.tb-sheet .pin-filters .tf-range{ display:grid; grid-template-columns:1fr 1fr; gap:8px; border-top:1px solid #E4EBE5; padding-top:10px; }
.tb-sheet .pin-filters .tf-range h4, .tb-sheet .pin-filters .tf-range [data-range-clear]{ grid-column:1 / -1; }
.tb-sheet .pin-filters .tf-lab{ display:flex; flex-direction:column; align-items:stretch; gap:2px; background:#fff; border:1.5px solid var(--track, #ADC3B9); border-radius:12px; padding:7px 10px; font-size:.72rem; font-weight:800; color:var(--ink-soft, #5C6E66); min-width:0; }
.tb-sheet .pin-filters .tf-lab input{ border:0 !important; background:transparent !important; padding:0 !important; border-radius:0 !important; box-shadow:none !important;
  font-size:.84rem; font-weight:600; color:var(--ink, #2C3B36); width:100%; min-width:0; height:auto; }   /* !important: a page-level input rule (9px 12px, 1px border) outranked this */
.tb-sheet .pin-filters .tf-lab input:focus{ outline:none; }
.tb-sheet .pin-filters .tf-cap, .tb-sheet .pin-filters > small{ display:none; }
.tb-sheet .pin-filters .tf-tag{ border-top:1px solid #E4EBE5; padding-top:10px; }
/* the report link must never underline — it is a button (hover/focus after the sheet closes did) */
.map-report-sm, .map-report-sm:hover, .map-report-sm:focus, .map-report-sm:active, .map-report-sm:visited{ text-decoration:none; }
.tb-foot{ padding:8px 18px 12px; }
.tb-done{ width:100%; border:0; border-radius:999px; padding:12px; background:var(--deep, #4F8577); color:#fff; font-weight:800; font-size:.92rem; font-family:var(--font, inherit); cursor:pointer; }

/* ---- round-151: media conversion — processing / failed tiles ---- */
.gal-item.processing, .gal-audio.processing{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px;
  background:var(--ph, var(--mintbg, #EAF1EC)); color:var(--ink-soft, #5B6B64); cursor:default;
}
.gal-item.processing::after{ content:""; position:absolute; inset:0; background:rgba(255,255,255,.55); }
.gal-item.processing > *{ position:relative; z-index:1; }
.gal-audio.processing{ flex-direction:row; padding:8px 12px; border-radius:12px; }
.gal-spin{
  width:22px; height:22px; border-radius:50%; border:2.5px solid rgba(79,133,119,.25); border-top-color:var(--deep, #4F8577);
  animation:galspin .9s linear infinite; flex:0 0 auto;
}
@keyframes galspin{ to{ transform:rotate(360deg); } }
.gal-proc{ font-size:.72rem; font-weight:700; letter-spacing:.02em; text-transform:uppercase; }
.media-failed{ display:flex; gap:6px; flex-wrap:wrap; margin-top:6px; }
.media-failed-tile{ font-size:.74rem; color:#8A2E22; background:#FBECE9; border:1px dashed #E5B5AC; border-radius:8px; padding:4px 8px; }
.up-tile.processing{ background:var(--mintbg, #EAF1EC); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; }
.up-tile.processing img[data-thumb]{ display:none; }
.up-tile.ready img[data-thumb]{ display:block; width:100%; height:100%; object-fit:cover; }
.up-tile.ready .gal-spin, .up-tile.ready .up-proc{ display:none; }
.up-tile.failed{ opacity:.7; }
.up-proc{ font-size:.66rem; font-weight:700; text-transform:uppercase; letter-spacing:.02em; color:var(--deep, #4F8577); position:absolute; left:6px; bottom:22px; }
.ed-hero-proc{ position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px; background:var(--mintbg, #EAF1EC); color:var(--ink-soft, #5B6B64); font-size:.78rem; }
@media (prefers-reduced-motion: reduce){ .gal-spin{ animation:none; border-top-color:rgba(79,133,119,.25); } }

/* ---- round-152: /learn/how-it-works — design how-it-works/07-chapters ----
   The long version, read as chapters: a sticky contents rail on the left, one column of
   prose on the right with a serif number per chapter, a drop cap, pull quotes and figure
   cards. The ground is the learning centre's paper (html.pg-learn), so the two pages under
   /learn read as one place. Everything here is static markup; only the rail's highlight is
   scripted (aquaChapters), and it degrades to a plain list of anchors without it. */
.hiw-wrap{
  max-width:1080px; margin:0 auto; padding:34px 22px 80px;
  display:grid; grid-template-columns:210px 1fr; gap:46px; align-items:start;
}
.hiw-toc{ position:sticky; top:calc(var(--header-h, 64px) + 14px); align-self:start; }
.hiw-toc-k{ font-size:.7rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--tan, #BC8A52); font-weight:800; margin-bottom:10px; }
.hiw-toc a{
  display:flex; gap:9px; align-items:baseline; padding:7px 0 7px 12px; color:var(--ink-soft, #5C6E66);
  font-size:.86rem; line-height:1.3; border-left:2px solid var(--track-soft, #E4EBE5); text-decoration:none;
}
.hiw-toc a span{ font-weight:800; font-size:.74rem; opacity:.7; min-width:9px; }
.hiw-toc a:hover{ color:var(--ink, #2C3B36); }
.hiw-toc a.on{ color:var(--ink, #2C3B36); font-weight:700; border-left-color:var(--deep, #4F8577); }
.hiw-mins{ margin-top:14px; font-size:.75rem; color:var(--ink-soft, #5C6E66); }
.hiw-toc-cta{ margin-top:12px; }

.hiw-art{ min-width:0; }
.hiw-art h1{
  font-family:Georgia, "Times New Roman", serif; font-weight:600; font-size:clamp(1.8rem, 4.4vw, 2.7rem);
  line-height:1.06; margin:8px 0 14px; color:var(--ink, #2C3B36);
}
.hiw-art > .lead{ font-size:1.08rem; color:var(--ink-soft, #5C6E66); line-height:1.6; max-width:58ch; margin:0 0 38px; }

.hiw-ch{ margin-bottom:44px; max-width:64ch; scroll-margin-top:calc(var(--header-h, 64px) + 20px); }
.hiw-num{ font-family:Georgia, "Times New Roman", serif; font-size:3rem; line-height:1; color:var(--track, #ADC3B9); }
.hiw-ch h2{ font-family:Georgia, "Times New Roman", serif; font-weight:600; font-size:1.62rem; margin:2px 0 12px; color:var(--ink, #2C3B36); }
.hiw-ch p{ font-size:1rem; line-height:1.72; color:#3d4c46; margin:0 0 12px; }
/* the drop cap belongs to a chapter's OPENING paragraph only — and never in a right-to-left
   script, where a floated initial is simply wrong */
.hiw-ch > p:first-of-type::first-letter{
  font-family:Georgia, "Times New Roman", serif; font-size:2.6rem; float:left; line-height:1;
  padding:4px 8px 0 0; color:var(--deep, #4F8577);
}
html[dir="rtl"] .hiw-ch > p:first-of-type::first-letter{ float:none; font-size:inherit; padding:0; color:inherit; font-family:inherit; }

.hiw-pull{
  border-left:4px solid var(--tan, #BC8A52); padding:6px 18px; margin:18px 0;
  font-family:Georgia, "Times New Roman", serif; font-size:1.2rem; line-height:1.4; color:var(--ink, #2C3B36);
}
html[dir="rtl"] .hiw-pull{ border-left:0; border-right:4px solid var(--tan, #BC8A52); }
.hiw-fig{
  background:#fff; border-radius:18px; padding:15px 18px; margin:16px 0;
  display:grid; grid-template-columns:auto 1fr; gap:14px; align-items:center;
  font-size:.86rem; color:var(--ink-soft, #5C6E66); line-height:1.5;
}
.hiw-fig b{ font-family:Georgia, "Times New Roman", serif; font-size:1.7rem; color:var(--deep, #4F8577); white-space:nowrap; }
.hiw-go{ display:inline-block; color:var(--deep, #4F8577); font-weight:700; font-size:.92rem; text-decoration:none; }
.hiw-go:hover{ text-decoration:underline; }

/* the six services, the four layers, the school programme — one row grammar for all three */
.hiw-svcs{ display:grid; gap:10px; margin:16px 0 18px; }
.hiw-svcs > div{ background:#fff; border-radius:14px; padding:12px 15px; display:grid; gap:3px; }
.hiw-svcs b{ font-size:.92rem; color:var(--ink, #2C3B36); }
.hiw-svcs span{ font-size:.85rem; color:var(--ink-soft, #5C6E66); line-height:1.5; }
.hiw-layers{ display:grid; gap:10px; margin:16px 0 18px; }
.hiw-layers > div{ display:grid; gap:3px; border-left:2px solid var(--track-soft, #E4EBE5); padding:2px 0 2px 14px; }
.hiw-layers b{ font-size:.88rem; }
.hiw-layers span{ font-size:.85rem; color:var(--ink-soft, #5C6E66); line-height:1.5; }
html[dir="rtl"] .hiw-layers > div{ border-left:0; border-right:2px solid var(--track-soft, #E4EBE5); padding:2px 14px 2px 0; }

.hiw-arch{ margin:18px 0; }
.hiw-arch .arch-box{ min-width:0; width:100%; max-width:330px; font-size:.9rem; padding:11px 18px; }
.hiw-arch .arch-box small{ display:block; font-weight:600; font-size:.72rem; color:var(--ink-soft, #5C6E66); margin-top:2px; }

.hiw-day{ background:#fff; border-radius:18px; padding:20px 20px 8px 30px; margin:18px 0; }
.hiw-day-k{ font-family:Georgia, "Times New Roman", serif; font-size:1.05rem; margin:0 0 14px -4px; color:var(--ink, #2C3B36); }
.hiw-day .tl-item::before{ border-color:#fff; }
.hiw-day .tl-item .t{ font-size:.95rem; }
.hiw-day .tl-item p{ font-size:.88rem; margin-bottom:0; }
.hiw-day-stats{ display:grid; grid-template-columns:1fr 1fr; gap:12px; border-top:1px solid var(--track-soft, #E4EBE5); padding:14px 0 12px; margin-left:-18px; }
.hiw-day-stats b{ display:block; font-family:Georgia, "Times New Roman", serif; font-size:1.3rem; color:var(--deep, #4F8577); }
.hiw-day-stats span{ font-size:.78rem; color:var(--ink-soft, #5C6E66); line-height:1.45; }

.hiw-table{ background:#fff; border-radius:18px; padding:6px 14px; margin:16px 0; overflow-x:auto; }
.hiw-faq details.faq{ background:#fff; border-color:var(--track-soft, #E4EBE5); }

.hiw-end{
  background:#fff; border-radius:22px; padding:20px 24px; display:flex; gap:16px; flex-wrap:wrap;
  align-items:center; justify-content:space-between; max-width:64ch;
}
.hiw-end b{ font-family:Georgia, "Times New Roman", serif; font-size:1.18rem; font-weight:600; }
.hiw-end-cta{ display:flex; gap:8px; flex-wrap:wrap; }

@media (max-width:860px){
  .hiw-wrap{ grid-template-columns:1fr; gap:14px; padding:16px 0 60px; }
  /* the rail becomes a jump strip ABOVE the article — one line that scrolls sideways, the way
     the design lays it out on a phone. The read time and the map button go: the first is a
     nicety and the second is repeated by the end card, and three rows before the title would
     bury it. The strip bleeds to the screen edges so a swipe has somewhere to go. */
  .hiw-toc{
    position:static; order:1; display:flex; gap:0; overflow-x:auto; scrollbar-width:none;
    padding:2px 16px 10px; border-bottom:1px solid var(--track-soft, #E4EBE5);
  }
  .hiw-toc::-webkit-scrollbar{ display:none; }
  .hiw-toc-k, .hiw-mins, .hiw-toc-cta{ display:none; }
  .hiw-toc a{
    flex:0 0 auto; white-space:nowrap; border-left:0; border-bottom:2px solid transparent;
    padding:6px 12px 8px; margin:0; font-size:.82rem;
  }
  .hiw-toc a.on{ border-left:0; border-bottom-color:var(--deep, #4F8577); }
  html[dir="rtl"] .hiw-toc a{ border-right:0; }
  .hiw-art{ order:2; padding:0 16px; }
  .hiw-ch{ margin-bottom:34px; }
  .hiw-num{ font-size:2.4rem; }
  .hiw-day{ padding:16px 16px 6px 26px; }
  .hiw-day-stats{ grid-template-columns:1fr; margin-left:-14px; }
  .hiw-end{ flex-direction:column; align-items:flex-start; }
}

/* ---- round-152 (owner): audio where it was drawn as a broken picture ----
   The viewer gets a card with a real player (a voice note has no frame to show), and the map's
   details panel grows an audio row under the hero instead of feeding an .m4a to an <img>. */
.lb-audio{
  display:flex; flex-direction:column; align-items:center; gap:12px; padding:26px 22px;
  background:#FBFBF8; border-radius:20px; min-width:min(420px, 84vw); max-width:92vw;
  box-shadow:0 18px 50px rgba(20,28,25,.28);
}
.lb-audio .lb-audio-ico{ display:inline-flex; }
.lb-audio .lb-audio-ico svg{ width:34px; height:34px; color:var(--deep, #4F8577); stroke:currentColor; }
.lb-audio audio{ width:min(380px, 76vw); height:40px; }
.lb-audio small{ color:var(--ink-soft, #5C6E66); font-size:.78rem; }
/* round-152b (owner): the voice notes sit directly under the media, square — the hero above
   them has no radius either, so a rounded card floated where the panel is otherwise flush.
   The negative margins cancel .ed-head's padding (14px 18px 10px), so the strip runs the full
   width of the panel and butts against the picture. */
.ed-audio{ display:flex; flex-direction:column; gap:1px; margin:-14px -18px 12px; }
.ed-audio .gal-audio{ background:var(--mintbg, #EAF1EC); border-radius:0; }
.ed-audio[hidden]{ display:none !important; }

/* ---- round-154: "seen by" — the one counter in the row that is not a control ----
   Same shape and spacing as its neighbours so the row reads as one set, but it does not
   depress, hover or take the keyboard: nobody presses a view. */
.story-views, .lb-views{ cursor:default; pointer-events:none; }
.story-views:hover, .lb-views:hover{ background:inherit; }
.story-actions .story-views{ display:inline-flex; align-items:center; gap:5px; }
@media (prefers-reduced-motion: no-preference){ .story-views [data-n]{ transition:none; } }

/* ── Round-155: the report acknowledgement, design 06 "quiet" ─────────────────────────────
   One sentence set large, two lines of fact, one button. It fills the wizard's full screen
   (the site header stays hidden until Done), and on the classic form it simply sits in the
   page. An OUTLINE check, never a filled tick: the screen says RECEIVED, not well done. */
.ack2{
  flex:1; display:flex; flex-direction:column; justify-content:center; gap:22px;
  padding:0 10px; min-height:calc(var(--app-h, 100vh) * 0.58);
}
.ack2-ring{
  width:74px; height:74px; border-radius:50%; border:2.5px solid var(--green, #1FA97A);
  display:grid; place-items:center; flex:none;
}
.ack2-ring svg{
  width:34px; height:34px; stroke:var(--green, #1FA97A); stroke-width:2.5;
  fill:none; stroke-linecap:round; stroke-linejoin:round;
}
.ack2-say{
  font-family:Georgia, 'Times New Roman', serif; font-weight:600;
  font-size:clamp(1.6rem, 6vw, 2rem); line-height:1.16; margin:0; color:var(--ink, #2C3B36);
}
.ack2-meta{ color:var(--ink-soft, #5C6E66); font-size:.88rem; line-height:1.6; max-width:46ch; }
.ack2-foot{ flex:none; padding:12px 10px calc(16px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))); display:grid; }
.ack2-foot .btn{ width:100%; }

/* Round-166: the CLASSIC panel's ✕ (stations, partners — anything without .ed/.snp) on a phone.
   The head pads its top by the status-bar inset (style.css), so the pills start below the clock,
   but the ✕ was still absolute at top:14px — under the battery icon on the owner's iPhone. Its
   centre now sits on the pills' centre: head padding (20px + inset) plus half a pill (~13px)
   minus half the 51px thumb-sized button. Placed last on purpose: source order decides here. */
@media (max-width:720px){
  .station-panel:not(.ed):not(.snp) .sp-close{
    top:calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 20px + 13px - 25.5px);
    display:flex; align-items:center; justify-content:center;
  }
}

/* ============================================================
   Round-168: Ada blocks — the structured half of an answer (AdaBlocks.razor). Namespaced
   .ada-* so the site's hero .stat (tan numbers on a dark panel) is left alone; tokens only.
   ============================================================ */
.msg-bubble .ada-headline{ margin:2px 0 10px; font-weight:700; line-height:1.4; color:var(--ink, #2C3B36); }
.msg-bubble .ada-headline.ada-tone-up::before{ content:"▲ "; color:var(--st-impact, #C0392B); }
.msg-bubble .ada-headline.ada-tone-down::before{ content:"▼ "; color:var(--st-active, #1FA97A); }
.msg-bubble .ada-stats{ display:grid; grid-template-columns:repeat(auto-fit, minmax(96px, 1fr)); gap:8px; margin:6px 0 10px; }
.msg-bubble .ada-stat{ background:#fff; border:1px solid var(--track, #ADC3B9); border-radius:12px; padding:10px 10px 8px; display:flex; flex-direction:column; gap:2px; min-width:0; }
.msg-bubble .ada-stat .v{ font-size:1.35rem; font-weight:800; line-height:1.1; color:var(--ink, #2C3B36); }
.msg-bubble .ada-stat .v small{ font-size:.7rem; font-weight:600; margin-left:2px; color:var(--ink-soft, #5C6E66); }
.msg-bubble .ada-stat .k{ font-size:.74rem; color:var(--ink-soft, #5C6E66); }
.msg-bubble .ada-stat a{ text-decoration:none; }
.msg-bubble .ada-delta{ font-size:.74rem; font-weight:700; }
.msg-bubble .ada-delta-bad{ color:var(--st-impact, #C0392B); }
.msg-bubble .ada-delta-good{ color:var(--st-active, #1FA97A); }
.msg-bubble .ada-delta-flat{ color:var(--ink-soft, #5C6E66); }
.msg-bubble .ada-bars, .msg-bubble .ada-chart{ margin:6px 0 10px; }
.msg-bubble .ada-bars figcaption, .msg-bubble .ada-chart figcaption, .msg-bubble .ada-sub{
  font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--sage2, #8FA99B); margin:0 0 6px; font-weight:700; }
.msg-bubble .ada-table{ margin:6px 0 8px; }
.msg-bubble .ada-table th.num, .msg-bubble .ada-table td.num{ text-align:right; }
.msg-bubble .ada-table .tbl-scroll{ margin:0; }
.msg-bubble .ada-chip-x{ margin-top:8px; }
.msg-bubble .ada-list ul{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:6px; }
.msg-bubble .ada-list li{ display:flex; flex-wrap:wrap; gap:6px 8px; align-items:center; padding:8px 10px; background:#fff; border:1px solid var(--track, #ADC3B9); border-radius:10px; }
.msg-bubble .ada-list .pill{ font-size:.66rem; padding:2px 8px; }
.msg-bubble .ada-list .ada-sub2{ flex-basis:100%; font-size:.85rem; color:var(--ink-soft, #5C6E66); }
.msg-bubble .ada-steps ol{ margin:0; padding-left:20px; display:flex; flex-direction:column; gap:6px; }
.msg-bubble .ada-cite{ font-size:.7rem; }
.msg-bubble .ada-actions{ margin:8px 0 2px; gap:6px; }
.msg-bubble .ada-actions .chip{ font-size:.8rem; }
.msg-bubble .ada-actions .chip svg{ width:14px; height:14px; margin-right:4px; vertical-align:-2px; }
.msg-bubble .ada-gate{ margin:8px 0; display:flex; gap:10px; align-items:center; justify-content:space-between; flex-wrap:wrap; }
.msg-bubble .ada-suggest{ margin:8px 0 0; gap:6px; }
/* Round 224: Ask, while Ada answers — three dots in the theme's colours on a light ground, so the
   button reads as busy (not broken). The reduced-motion net stills them. */
.compose-send.ada-busy:not(#_){
  background:var(--mint, #E7EDE6); border-color:var(--mint, #E7EDE6); opacity:1; cursor:default;
  display:inline-flex; align-items:center; justify-content:center; min-width:64px;
}
/* the owner's pick, after Claude Code's own: three dots in a triangle ORBITING the centre — they
   travel round together, 120 degrees apart, each swelling at the top of the circle and shrinking at
   the bottom. (A first pass pulsed fixed dots inside a turning triangle; it read as dots swapping
   places.) One keyframe set; the delays put the three a third of a turn apart. */
.ada-dots{ position:relative; display:inline-block; width:24px; height:24px; }
.ada-dots i{
  position:absolute; left:50%; top:50%; width:7px; height:7px; margin:-3.5px 0 0 -3.5px; border-radius:50%;
  animation:adaOrbit 1.5s linear infinite;
}
.ada-dots i:nth-child(1){ background:var(--deep, #4F8577); }
.ada-dots i:nth-child(2){ background:var(--accent, #618D7C); animation-delay:-.5s; }
.ada-dots i:nth-child(3){ background:var(--tan, #BC8A52); animation-delay:-1s; }
@keyframes adaOrbit{
  0%   { transform:rotate(0deg)   translateY(-8px) scale(1.15); opacity:1; }
  25%  { transform:rotate(90deg)  translateY(-8px) scale(.85);  opacity:.8; }
  50%  { transform:rotate(180deg) translateY(-8px) scale(.55);  opacity:.55; }
  75%  { transform:rotate(270deg) translateY(-8px) scale(.85);  opacity:.8; }
  100% { transform:rotate(360deg) translateY(-8px) scale(1.15); opacity:1; }
}
@media (prefers-reduced-motion: reduce){
  .ada-dots i{ animation:none; opacity:1; }
  .ada-dots i:nth-child(1){ transform:rotate(0deg) translateY(-8px); }
  .ada-dots i:nth-child(2){ transform:rotate(120deg) translateY(-8px); }
  .ada-dots i:nth-child(3){ transform:rotate(240deg) translateY(-8px); }
}
/* Round 223: an answer the server could not finish — what arrived, then a quiet note and the way on */
.msg-bubble .ada-cut{ margin:8px 0 0; padding-top:8px; border-top:1px dashed #E7E2DA; }
.msg-bubble .ada-cut .hint{ margin:0 0 6px; font-size:.8rem; }
.msg-bubble .ada-cut .chip-row{ gap:6px; }
.msg-bubble .ada-suggest .chip{ font-size:.78rem; }

/* Round-169: the admin model tab's labelled fields and switch rows */
.field-wrap{ display:flex; flex-direction:column; gap:4px; }
.field-wrap .lbl{ font-size:.8rem; color:var(--ink-soft,#5A6B63); font-weight:600; }
.switch-row{ display:inline-flex; align-items:center; gap:8px; font-size:.92rem; cursor:pointer; }
.switch-row input{ width:18px; height:18px; accent-color:var(--green,#1FA97A); }
.msg-bubble.ada-streaming p:last-child{ margin-bottom:0; }


/* ============ Round-175: ONE close / back button ============
   The owner's call after the G6 walkthrough: every dismissal is the size of the map rail's
   buttons (Ada, Layers, Weather, SafeNet — 44px) with a 20px outline glyph, on every width.
   Before this they were 32, 34, 36, 40, 44, 48 and 51px depending on which round added them.
   The glyph is the OutlineIcons "x" (window.X_SVG in JS-built markup), never the ✕ character,
   whose size and weight vary by font. Variants keep their COLOURS only (white on a photo,
   mint on a white head); nothing below may be overridden for size elsewhere.
   Placed last: equal-specificity rules above lose on source order. */
:root{ --close-size:44px; --close-glyph:20px; }
.sp-close, .asst-close, .wz2-close, .lb-close, .lb-side-close,
.omni-sheet .omni-close, .cpage2-search2.sheet .cclose,
.station-panel .sp-close, .asst-drawer .asst-close, .lightbox.comments-only .lb-side-close{
  width:var(--close-size); height:var(--close-size); min-width:var(--close-size); flex:0 0 var(--close-size);
  padding:0; line-height:1; font-size:1rem;
  display:inline-flex; align-items:center; justify-content:center; border-radius:50%;
}
.sp-close svg, .asst-close svg, .wz2-close svg, .lb-close svg, .lb-side-close svg,
.omni-sheet .omni-close svg, .cpage2-search2.sheet .cclose svg, .st-mapcard-x svg, .cmt-reply-x svg{
  width:var(--close-glyph); height:var(--close-glyph); flex:0 0 auto;
}
/* the story map card's corner ✕ (desktop) and its labelled phone pill share the height */
.st-mapcard-x{ width:var(--close-size); height:var(--close-size); }
@media (max-width:640px){ .st-mapcard-x{ width:auto; height:var(--close-size); } }
/* the reply banner's × is inline, but still a thumb target — the banner row grows to fit */
.cmt-reply-x{
  width:var(--close-size); height:var(--close-size); margin:-10px -8px -10px 0; padding:0;
  display:inline-flex; align-items:center; justify-content:center; border-radius:50%;
}
.cmt-reply-x:hover{ background:rgba(97,141,124,.14); }
/* the map cards' collapse caret: a 44px hit area that does not grow the card head */
.map-card .card-caret{
  width:var(--close-size); height:var(--close-size); margin:-12px -12px -12px auto;
  align-items:center; justify-content:center; border-radius:50%;
}
.map-card .card-caret:active{ background:rgba(243,244,241,.14); }
.map-card .card-caret svg{ width:var(--close-glyph); height:var(--close-glyph); }
/* The full-surface closes the list above missed: the composer sheet's cancel and Ada's enlarge modal. */
.cs-cancel, .ada-modal-close{
  width:var(--close-size); height:var(--close-size); min-width:var(--close-size); padding:0;
  display:inline-flex; align-items:center; justify-content:center; border-radius:50%; line-height:1;
}
.cs-cancel svg, .ada-modal-close svg{ width:var(--close-glyph); height:var(--close-glyph); }
/* Inline dismissals (banners, search clear, reply chips, feed entries): the SAME 44px target and
   20px glyph, but no fill — a filled circle on a one-line banner would shout. Negative margins
   keep the banner's own height unchanged. */
.x-dismiss{
  width:var(--close-size); height:var(--close-size); min-width:var(--close-size); padding:0;
  display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
  border:0; border-radius:50%; background:none; color:inherit; cursor:pointer; line-height:1;
  margin-top:-10px; margin-bottom:-10px;
}
.x-dismiss:hover{ background:rgba(97,141,124,.12); }
.x-dismiss svg{ width:var(--close-glyph); height:var(--close-glyph); }
.snp-evs .tev .x.x-dismiss{ top:-12px; margin:0; }

/* Round-175: report wizard — the review card, the call button, the restored-draft note */
.rpt-sum{ background:#fff; border:1px solid rgba(44,59,54,.12); border-radius:16px; padding:14px 16px; margin:0 0 14px; }
.rpt-sum-k{ font-size:.72rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:#618D7C; margin-bottom:6px; }
.rpt-sum-h{ display:flex; align-items:center; gap:8px; font-size:1rem; color:#2C3B36; }
.rpt-sum ul{ list-style:none; margin:8px 0 0; padding:0; display:grid; gap:6px; font-size:.9rem; color:#4a5a55; }
.rpt-sum li{ display:flex; align-items:center; gap:6px; min-width:0; }
.rpt-sum li svg{ width:16px; height:16px; flex:0 0 auto; }
.rpt-sum .rpt-sum-txt{ font-style:italic; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:block; }
.rpt-call{ display:inline-flex; align-items:center; gap:6px; margin-top:8px; }
.rpt-call svg{ width:16px; height:16px; }
.wz2-restored{ margin:0 0 6px; font-size:.82rem; color:#618D7C; font-weight:600; }
.submit-status{ min-height:1.4em; }
.chip.sel svg{ width:14px; height:14px; vertical-align:-2px; }
/* Round-175: crawled headlines are kept whole (300 chars) — cards show three lines of them */
.story-title, .ed-hl{ display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; overflow-wrap:anywhere; }
/* Round-175: no "Save image" / download callout on media */
audio, video, .gal img, .lightbox img, .ed-hero img, .story-media img, .up-tile img{ -webkit-touch-callout:none; }
.gal img, .lightbox img, .ed-hero img, .story-media img{ -webkit-user-select:none; user-select:none; -webkit-user-drag:none; }

/* ============ Round-176: messages that feel instant ============ */
.msg-head-row{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.msg-new{ width:var(--close-size); height:var(--close-size); border-radius:50%; border:0; display:inline-flex; align-items:center; justify-content:center;
  background:var(--accent, #618D7C); color:#fff; cursor:pointer; flex:0 0 auto; }
.msg-new svg{ width:var(--close-glyph); height:var(--close-glyph); }
.msg-tick{ margin-left:6px; font-weight:700; letter-spacing:-.18em; opacity:.75; }
.msg-tick.read{ color:#2F7DB8; opacity:1; }
.msg-bubble.mine .msg-tick.read{ color:#CFE6FF; }
.msg-tick.pending svg{ width:12px; height:12px; vertical-align:-2px; }
.msg-bubble.sending{ opacity:.72; }
.msg-bubble.failed{ outline:2px solid #C0392B; outline-offset:-2px; }
.msg-fail{ color:#C0392B !important; font-weight:600; }
.msg-bubble.mine .msg-fail{ color:#FFE3DF !important; }
.msg-retry{ border:0; background:none; color:inherit; font:inherit; text-decoration:underline; cursor:pointer; padding:6px 2px; }
.msg-pick-backdrop{ position:fixed; inset:0; z-index:2600; border:0; background:rgba(28,40,36,.42); backdrop-filter:blur(2px); cursor:default; }
.msg-pick{ position:fixed; z-index:2601; left:50%; top:50%; transform:translate(-50%,-50%); width:min(440px, calc(100vw - 32px));
  max-height:min(560px, calc(var(--vvh, var(--app-h, 100dvh)) - 48px)); display:flex; flex-direction:column; background:#fff; border-radius:18px;
  box-shadow:0 18px 50px rgba(28,40,36,.3); padding:14px 16px 10px; animation:msgPickIn .18s ease-out; }
@keyframes msgPickIn{ from{ opacity:0; transform:translate(-50%,-46%); } }
@media (prefers-reduced-motion: reduce){ .msg-pick{ animation:none; } }
.msg-pick-head{ display:flex; align-items:center; justify-content:space-between; }
.msg-pick-head h2{ margin:0; font-size:1.1rem; }
.msg-pick-q{ margin:10px 0 8px; width:100%; font-size:16px; padding:10px 12px; border-radius:12px; border:1px solid var(--track, #ADC3B9); }
.msg-pick-list{ overflow-y:auto; overscroll-behavior:contain; min-height:80px; }
.msg-pick-row{ display:flex; align-items:center; gap:10px; padding:8px 4px; border-radius:10px; color:inherit; text-decoration:none; }
.msg-pick-row:hover, .msg-pick-row:focus-visible{ background:#F1F4F1; }
/* Round-176: comments post at once; the reply chip is one line */
.cmt.cmt-pending.sending{ opacity:.65; }
.cmt.cmt-pending.failed .cmt-pend-state{ color:#C0392B; font-weight:600; }
.cmt-replying > span{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ============ Round-176: the report panel is ONE scroller ============
   The G6 walkthrough: the hero, headline, description and reactions were fixed rows and only
   the comment pane scrolled — on a 720px phone that left a strip about 150px tall for the
   conversation. Now everything above the composer scrolls away together, the tabs stick under
   the status bar once they reach it, and the composer stays pinned (sticky, pushed to the
   bottom by margin-top:auto when the thread is short). Sticky offsets respect #spBody's
   padding-top (the safe-area inset), never a margin — see mobile-web-ui-traps. */
.station-panel.ed #spBody{
  display:flex; flex-direction:column; overflow-y:auto; overflow-x:hidden;
  overscroll-behavior:contain; -webkit-overflow-scrolling:touch;
}
.station-panel.ed #spBody > *{ flex-shrink:0; }
.station-panel.ed .ed-tabs{ position:sticky; top:0; z-index:3; background:var(--paper, #F3F4F1); padding-right:64px; }
.station-panel.ed .ed-panes{ overflow:visible; flex:1 0 auto; }
.station-panel.ed .ed-compose{ position:sticky; bottom:12px; z-index:3; margin-top:auto !important;
  /* a paper "shadow" below the card hides comments scrolling through the gap under it */
  box-shadow:0 8px 26px rgba(44,59,54,.14), 0 40px 0 12px var(--paper, #F3F4F1); }
html.in-app .station-panel.ed .ed-compose{ bottom:calc(12px + var(--safe-bottom, 0px)); }
.station-panel.ed .ed-desc.open .js-cmt-body{ max-height:none; overflow:visible; }

/* Round-176: Ada's tables become stacked rows when the chat is narrow (phones, the drawer) */
.ada-table{ container-type:inline-size; }
.ada-rows{ display:none; }
@container (max-width: 520px){
  .ada-table .tbl-scroll{ display:none; }
  /* Round 222: the enlarge button wraps the wide table — hidden with it, or it floats over the row cards */
  .ada-table .ada-media{ display:none !important; }
  .ada-rows{ display:grid; gap:6px; margin:6px 0; }
  .ada-row{ display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 14px; background:#FBFBF8; border:1px solid #E7E2DA; border-radius:10px; padding:8px 10px; }
  .ada-row-h{ flex:1 0 100%; font-size:.92rem; }
  /* Round 222: a row that opens its area reads as a link — theme colour and a chevron */
  a.ada-row-h{ color:var(--deep, #4F8577); text-decoration:none; }
  a.ada-row-h b::after{ content:" ›"; font-weight:800; }
  .ada-row-c{ display:inline-flex; align-items:baseline; gap:5px; font-size:.86rem; }
  .ada-row-c small{ color:var(--ink-soft, #55635C); font-size:.74rem; }
}
.ada-bars svg, .ada-chart svg{ width:100%; height:auto; }

/* Round-176: the whole story card opens the story; Ada's button hides while scrolling down */
.story-card .story-body{ cursor:pointer; }
.asst-fab{ transition:transform .15s, box-shadow .15s, opacity .2s ease, translate .2s ease; }
html.fab-hide .asst-fab:not(.asst-fab-map){ opacity:0; translate:0 18px; pointer-events:none; }
@media (prefers-reduced-motion: reduce){ .asst-fab{ transition:none; } }

.ada-chip.ada-chip-clear{ margin:0; background:#fff; opacity:1; border-style:solid; }

/* Round-176: the account page opens on who you are; Follow carries its icon */
.aset-me{ display:flex; align-items:center; gap:12px; padding:12px 14px; margin:0 0 16px; background:#fff; border-radius:16px;
  box-shadow:0 2px 12px rgba(44,59,54,.08); color:inherit; text-decoration:none; }
.aset-me-av{ width:48px; height:48px; border-radius:50%; object-fit:cover; flex:0 0 48px; }
.aset-me-fb{ display:inline-flex; align-items:center; justify-content:center; background:var(--mintbg, #EAF1EC); color:var(--deep, #4F8577); font-weight:800; font-size:1.2rem; }
.aset-me-t{ display:flex; flex-direction:column; min-width:0; flex:1; }
.aset-me-t b{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.aset-me-t small{ color:var(--ink-soft, #55635C); }
.aset-me-go{ font-size:.82rem; color:var(--accent, #618D7C); font-weight:700; white-space:nowrap; }
.pf-follow{ display:inline-flex; align-items:center; gap:6px; }
.pf-follow svg{ width:16px; height:16px; flex:0 0 auto; }

/* ============ Round-177: grid menu groups; the phone header is six targets ============ */
.grid-menu .grid-gh{ grid-column:1/-1; font-size:.68rem; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  color:#A8700F; padding:10px 6px 2px; }   /* owner, 2026-10-06: the group dividers in amber/gold */
.grid-menu .grid-gh:first-child{ padding-top:2px; }
.grid-menu a.pressed{ background:rgba(97,141,124,.22); transform:scale(.97); }
.grid-menu a.grid-account{ grid-column:1/-1; flex-direction:row; justify-content:flex-start; gap:10px; margin-top:8px;
  padding:12px 12px; background:#fff; border:1px solid var(--track, #ADC3B9); font-size:.86rem; }
.grid-menu a.grid-account .grid-account-go{ margin-left:auto; display:inline-flex; }
.grid-menu a.grid-account .grid-account-go svg{ width:18px; height:18px; transform:rotate(180deg); }
.nav-grid.open .grid-menu{ animation:gridIn .16s ease-out; transform-origin:top right; }
@keyframes gridIn{ from{ opacity:0; transform:translateY(-6px) scale(.98); } }
@media (prefers-reduced-motion: reduce){ .nav-grid.open .grid-menu{ animation:none; } }
@media (max-width:720px){
  /* back/brand · search · bell · messages · avatar · grid — Map, Stories and Report live in
     the grid menu and the map's own dock */
  /* :not(#_) lifts this above the round-96 spread rules ((0,4,0)) without !important.
     Round-179 (owner): Stories stays in the bar; Messages moves to the menu instead (the grid
     button carries its unread count). The stories page keeps its own bar unchanged. */
  /* Round-195: :not(.omni-open) — the magnifier became an <a href="/search"> when search became
     a page, and this round-177 rule hides header ANCHORS by href. It had never matched it before
     because it was a <button>, so pointing it at a page made the search target disappear. */
  .site-header:not(#_) .nav-right > a.nav-icon:not([href="/stories"]):not(.omni-open), .site-header:not(#_) .nav-right > .nav-report{ display:none; }
  .site-header:not(#_):not(.on-stories) .nav-right .msg-btn{ display:none; }
  .site-header:not(.on-stories) .grid-btn .bell-badge:not([hidden]){ display:inline-flex; }
}
/* Round-177: no jumps — a diagnostics re-run dims the old rows instead of removing them;
   the dashboard's note line keeps its height once the location is known */
.diag-card.running{ opacity:.55; transition:opacity .2s ease; }
.dash-note{ min-height:1.5em; }

/* ============ Round-177: motion — quiz result, progress ring, tab switches ============ */
@property --pct{ syntax:"<number>"; inherits:true; initial-value:0; }
.lc-ring, .diag-ring{ transition:--pct .7s cubic-bezier(.2,.7,.2,1); }
.lc-result{ animation:lcResultIn .28s ease-out both; }
.lc-result.ok .n{ animation:lcBadgePop .5s cubic-bezier(.2,1.4,.4,1) .1s both; }
@keyframes lcResultIn{ from{ opacity:0; transform:translateY(8px); } }
@keyframes lcBadgePop{ from{ transform:scale(.4); opacity:0; } }
.ed-pane.on, .aset-pane, [role="tabpanel"]:not([hidden]){ animation:paneFade .15s ease-out; }
@keyframes paneFade{ from{ opacity:0; } }
@media (prefers-reduced-motion: reduce){
  .lc-ring, .diag-ring{ transition:none; }
  .lc-result, .lc-result.ok .n, .ed-pane.on, .aset-pane, [role="tabpanel"]{ animation:none; }
}

/* ============ Round-179 ============ */
.grid-btn{ position:relative; }
.grid-btn .bell-badge{ display:none; position:absolute; top:-2px; right:-2px; }
.grid-menu a.grid-msgs{ position:relative; }
.grid-menu a.grid-msgs .bell-badge{ position:absolute; top:6px; right:18px; }
.grid-menu a.grid-msgs .bell-badge[hidden]{ display:none; }

/* Map cards open BESIDE the button column on phones (owner): the Sensor network, Layers and
   Weather cards used to grow in place, which pushed every button below them down the screen
   and shifted the whole centred column. Now the button keeps its 44px circle (ringed while
   open) and the card's body floats to its left, pinned to the top of the column. */
@media (max-width:720px){
  .right-stack{ overflow:visible; }
  .right-stack .map-card.collapsible:not(.min){
    width:44px; max-height:44px; padding:12px; border-radius:50%; overflow:visible;
    position:static; box-shadow:0 0 0 3px var(--tan-light, #E3C9A5);
  }
  .right-stack .map-card.collapsible:not(.min) .card-head h2,
  .right-stack .map-card.collapsible:not(.min) .card-head h3,
  .right-stack .map-card.collapsible:not(.min) .card-caret{ display:none; }
  .right-stack .map-card.collapsible:not(.min) .card-head{ gap:0; }
  .right-stack .map-card.collapsible:not(.min) .card-ico svg{ width:20px; height:20px; }
  .right-stack .map-card.collapsible:not(.min) .card-body{
    position:absolute; top:0; right:calc(100% + 4px); margin:0; z-index:2;
    width:min(280px, calc(100vw - 88px));
    max-height:calc(var(--vvh, var(--app-h, 100vh)) * .46); overflow-y:auto; overscroll-behavior:contain;
    background:var(--panel-grad); color:var(--paper);
    border:1px solid rgba(243,244,241,.3); border-radius:16px; padding:12px 14px;
    box-shadow:0 10px 28px rgba(28,40,36,.35);
    opacity:1; animation:mapCardIn .18s ease-out;
  }
  .right-stack .map-card.collapsible:not(.min) .card-body::before{
    content:attr(data-title); display:block; margin:0 0 8px;
    font-size:.72rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; color:var(--tan-light, #E3C9A5);
  }
  @keyframes mapCardIn{ from{ opacity:0; transform:translateX(8px); } }
}
@media (max-width:720px) and (prefers-reduced-motion: reduce){
  .right-stack .map-card.collapsible:not(.min) .card-body{ animation:none; }
}

/* ============ Round-180: header spacing (owner's snapshot) ============
   Phones. The icon cluster's wrapper steps out of the layout (display:contents) so the search
   pill / brand pin and every icon share ONE row with equal gaps — before, the gap between the
   first item and the cluster was a margin, never the same as the gaps inside it. */
@media (max-width:720px){
  .map-header{ justify-content:space-between; gap:0; }
  .map-header .nav-right{ display:contents; }
  /* the search pill: logo · search, fully round, 42px tall like every button; the gap inside it
     is the SAME as the gaps between the buttons. With space-between that gap works out as
     (row width − everything fixed) / 6, so the pill uses the same sum:
     10+10 bar padding + 8+3 pill padding + 2 border + 26 logo + 34 search + 5×42 buttons = 303 */
  .map-header .msearch:not(.omni-sheet){
    flex:0 0 auto; gap:clamp(4px, calc((100vw - 303px) / 6), 24px);
    padding:3px 3px 3px 8px; border-radius:999px;
  }
  .map-header #omniGo{ display:none; }
  .map-header .msearch:not(.omni-sheet) .msearch-page{ display:inline-flex; }
  .map-header .msearch:not(.omni-sheet) .msearch-btn{
    width:34px; height:34px; padding:0; border-radius:50%;
    display:inline-flex; align-items:center; justify-content:center;
    background:#E4ECE6;
  }
  .map-header .avatar{ width:42px; height:42px; flex:0 0 auto; pointer-events:auto;
    display:inline-flex; align-items:center; justify-content:center; }
  .map-header .avatar img, .map-header .avatar-fallback{
    width:42px; height:42px; border:1px solid var(--track, #ADC3B9);
    box-shadow:0 4px 14px rgba(44,59,54,.18);
  }
  .map-header .nav-right > *{ pointer-events:auto; }

  .site-header{ justify-content:space-between; }
  .site-header .nav-right{ display:contents; }
  .site-header .brand, .site-header .nav-back{ flex:0 0 auto; margin:0; }
}

/* Round-180 (owner): ONE back button on phones — the size of the top bar's (48px box, 31px
   chevron): the messages thread, the search sheets, the comments viewer and the account panes.
   Placed last so it outranks the round-175 close-size rule for the search sheets' arrows. */
@media (max-width:720px){
  .msg-back, .omni-sheet .omni-close, .cpage2-search2.sheet .cclose, .lb-back, .aset-back{
    width:48px; height:48px; min-width:48px; flex:0 0 48px; padding:0; margin:0;
    display:inline-flex; align-items:center; justify-content:center;
    border:0; border-radius:50%; background:none; box-shadow:none; color:var(--deep, #4F8577);
  }
  .msg-back svg, .omni-sheet .omni-close svg, .cpage2-search2.sheet .cclose svg, .lb-back svg, .aset-back svg{
    width:31px; height:31px;
  }
  .msg-back:active, .omni-sheet .omni-close:active, .cpage2-search2.sheet .cclose:active,
  .lb-back:active, .aset-back:active{ background:rgba(97,141,124,.12); }
  .msg-shell:not(.thread-open) .msg-back{ display:none; }
}

/* Round-181 (iOS): while the keyboard is up iOS pans the page, and the sheet follows the pan
   through --vvt, which script updates only after the keyboard has gone — so for a moment the
   sheet sat out of place and the page (and its colours) showed around it. A still paper layer
   under the sheet (the header is lifted to 2100 while searching) covers whatever the sheet
   does not, whatever the timing. Only the sheet's own geometry was moving; this never does. */
@media (max-width:1024px){
  html.search-open body::after{
    content:""; position:fixed; left:0; right:0; top:-50vh; bottom:-50vh;
    background:var(--paper, #F3F4F1); z-index:2050; pointer-events:none;
  }
}

/* ============ Round-182 (phones) ============ */
/* Stories/map: the bottom bar (time chip + Report) flashed through while the keyboard closed
   inside search — it is not needed under the search sheet at all. */
@media (max-width:1024px){
  html.search-open .time-row{ visibility:hidden; }
}
/* iOS: the keyboard resizes the web view, so Ada's bottom-anchored button jumped up and down
   with it. It now steps out while the keys are up (Android's keyboard already covers it) and
   fades back in once the view has settled. */
@media (max-width:900px){
  .asst-fab:not(.asst-fab-map){ transition:transform .15s, box-shadow .15s, opacity .2s ease .35s, translate .2s ease, visibility 0s linear .35s; }
  html.kb-open .asst-fab:not(.asst-fab-map){ opacity:0; visibility:hidden; pointer-events:none; transition:none; }
}
/* Round-183: the report composer's sign-in line for signed-out reporters */
.composer-signin{ margin:6px 0 0; font-size:.86rem; }
.composer-signin a{ font-weight:700; color:var(--deep, #4F8577); }

/* Round-183: signed-out Activity */
.ntf-signin{ margin:12px 0 16px; padding:14px 16px; }
.ntf-anon-list{ display:grid; gap:8px; }
.ntf-anon-row{ display:flex; gap:12px; align-items:flex-start; background:#fff; border-radius:14px; padding:12px 14px;
  color:inherit; text-decoration:none; box-shadow:0 2px 10px rgba(44,59,54,.06); }
.ntf-anon-row:hover{ text-decoration:none; background:#FBFBF8; }
.ntf-anon-sev{ flex:0 0 auto; font-size:.66rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; border-radius:999px; padding:3px 9px; color:#fff; margin-top:2px; }
.ntf-anon-sev.em{ background:#C0392B; } .ntf-anon-sev.wa{ background:#D98E04; }
.ntf-anon-t{ display:flex; flex-direction:column; min-width:0; }
.ntf-anon-t b{ overflow:hidden; text-overflow:ellipsis; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }
.ntf-anon-t small{ color:var(--ink-soft, #55635C); margin-top:2px; }
[data-anon-near].err{ border-color:#C0392B; }

/* Round-184 (owner): the page you are on is marked by a light circle behind its icon — the same
   tint as the open grid button — never by a different (filled) icon or colour. Not on the live
   map, whose bar has its own button style. */
.site-header .nav-right > .on[aria-current="page"]{
  background:rgba(97,141,124,.14); border-radius:50%; color:var(--deep, #4F8577);
}
.site-header .nav-right > .nav-svg.on{ color:var(--deep, #4F8577); }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Round-186 (branch nav-06, NOT on production) — the phone's bottom navigation.
   Design: design/nav-layout/06-finale.html. Everything here is inside a ≤720 px query, so the
   desktop layout is byte-identical to before; and every rule that HIDES part of the old header
   is written as a hide, never an edit, so reverting the branch restores the old bar exactly.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
.navf{ display:none; }

/* Round-188c: the phone header's single gap, borrowed from the live map's search pill so the
   two bars measure the same. See the note where it is used. */
:root{ --hdr-gap: clamp(4px, calc((100vw - 303px) / 6), 24px); }

/* Round-195: the search PAGE. The look is the main search's, because that is the one readers
   already know — a bar of [back · magnifier · input · clear] over a body of grouped hits. What
   is new is that it is a page: one state, a URL you can share, a back button, and no way to be
   half-open. The sheet it replaces cost two live bugs in a fortnight by being a state on an
   element instead of a place. */
html.pg-srch .site-header{ display:none; }
/* Round-217b (found on the Android emulator, signed out): the sign-up banner is fixed above
   everything and the search bar sits at the top of the screen — it was drawn right over the box.
   Search is a task page like /report and /login, which have hidden it since rounds 142/146. */
html.pg-srch #signupBar{ display:none !important; }
html.pg-srch body, html.pg-srch{ background:#fff; }
.srch{ padding:0; }
.srch-bar{
  /* Round-195c: the inset belongs on the TOP. It was written
     `padding:8px 12px calc(8px + env(safe-area-inset-top))` — a three-value shorthand, so that
     calc landed on the BOTTOM and the bar ran under the iPhone's status bar: the clock sat on
     the back arrow and the placeholder ran under the battery. */
  position:sticky; top:0; z-index:40;
  display:flex; align-items:center; gap:6px;
  /* Round-195e: --safe-top FIRST, env() only as the fallback. This is the house rule at
     style.css:704 and I ignored it: Android's WebView never wires the system bars into env(), so
     env(safe-area-inset-top) is 0 there and the bar ran under the clock and the battery. The app
     fills --safe-top from the native side, which is the only number Android has. */
  padding:calc(10px + var(--safe-top, env(safe-area-inset-top, 0px))) 12px 10px;
  /* Round-195d: no rule under the bar. It drew a line across an empty page — and the page below
     it is the same white, so the line separated nothing from nothing. */
  background:#fff;
}
.srch-back{
  display:inline-flex; align-items:center; justify-content:center; flex:0 0 44px;
  width:44px; height:44px; color:var(--deep, #4F8577); border-radius:50%;
}
.srch-back svg{ width:28px; height:28px; }
.srch-back:hover{ background:rgba(97,141,124,.12); text-decoration:none; }

/* The field is a pill, the shape this app uses for every search box it has. Loose on the bar it
   read as three unrelated marks — an arrow, a magnifier and some grey text — rather than as
   something to type in. */
.srch-field{
  flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:9px; height:44px;
  background:var(--paper, #F3F4F1); border:1.5px solid var(--track, #ADC3B9);
  /* Round-195d (owner's call): rounded, not a stadium. A pill is the app's shape for a CHIP —
     something you pick — and this is a field you fill. */
  border-radius:14px; padding:0 6px 0 13px;
}
.srch-field:focus-within{ border-color:var(--accent, #618D7C); background:#fff; }
.srch-ic{ display:inline-grid; place-items:center; color:var(--sage2, #7B988C); flex:0 0 auto; }
.srch-ic svg{ width:18px; height:18px; }

/* appearance:none matters twice on iOS: Safari draws its OWN magnifier inside type="search"
   (which is why the bar showed two), and its own clear ×. 16px is a THRESHOLD, not a taste —
   iOS zooms the viewport for any focused input below it, and a zoomed search page pushes its
   own clear button off the right edge. */
.srch-field input:not(#_){
  -webkit-appearance:none; appearance:none;
  flex:1 1 auto; min-width:0;
  border:0 !important; outline:none; background:none !important; box-shadow:none !important;
  font:inherit; font-size:16px; padding:0 !important; color:var(--ink, #2C3B36);
  border-radius:0 !important;
}
.srch-field input::-webkit-search-cancel-button,
.srch-field input::-webkit-search-decoration,
.srch-field input::-webkit-search-results-button{ -webkit-appearance:none; appearance:none; display:none; }
.srch-clr{
  border:0; background:none; cursor:pointer; color:var(--sage2, #7B988C); flex:0 0 auto;
  width:34px; height:34px; display:grid; place-items:center; border-radius:50%; padding:0;
}
.srch-clr svg{ width:18px; height:18px; }
.srch-body{ padding:14px 12px calc(24px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))); }
.srch-hint{ color:var(--ink-soft, #55635C); font-size:.92rem; padding:6px 4px; }
.srch-body .omni-col{ margin-bottom:8px; }

/* The communities pill is a link now; it keeps the pill's shape and reads like the input it
   replaces, so nothing about the page it sits on had to change. */
.srch-link{ text-decoration:none; }
.srch-link:hover{ text-decoration:none; border-color:var(--accent, #618D7C); }
.srch-link-ph{ color:var(--ink-soft, #7B988C); font-size:.9rem; }
/* A link has no input to give it height, so the /communities pill collapsed to its text (24px)
   beside the community page's real field (46px). Phones keep their 34px rule (.cdir-search
   under the phone query), which outranks this one. */
.cpage2-search2.srch-link{ box-sizing:border-box; min-height:46px; }

/* Round-194b: the ＋ that asks which. A page with one add keeps a plain ＋; a page with two or
   more gets this — one tool, a short menu, and the action on its own page. The scrim is a real
   button so a tap anywhere else closes the menu and a screen reader can reach the escape. */
.page-tool-menu{ position:relative; flex:0 0 auto; display:inline-flex; }
.ptm-scrim{
  position:fixed; inset:0; z-index:600; border:0; background:transparent; cursor:default; padding:0;
}
.ptm-list{
  position:absolute; top:calc(100% + 8px); right:0; z-index:601; min-width:232px;
  background:#fff; border:1px solid var(--track, #ADC3B9); border-radius:14px; padding:6px;
  box-shadow:0 14px 36px rgba(44,59,54,.22);
}
.ptm-list a{
  display:flex; align-items:center; gap:10px; padding:11px 12px; border-radius:10px;
  color:var(--ink, #2C3B36); font-weight:700; font-size:.92rem; text-decoration:none;
}
.ptm-list a:hover, .ptm-list a:focus{ background:var(--mint, #E7EDE6); text-decoration:none; }
.ptm-list a svg{ width:19px; height:19px; flex:0 0 auto; color:var(--deep, #4F8577); }
@media (prefers-reduced-motion: no-preference){
  .ptm-list{ animation:gridIn .16s ease-out; transform-origin:top right; }
}

/* Round-192: starting a community, on its own page. A single column that stops at a readable
   measure, and the two actions on one row — Cancel quiet, Create filled — so the primary one is
   never the thing the navigation happens to be sitting on. */
.cnew .cnew-card{ max-width:560px; }
.cnew-actions{ display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top:4px; }
.cnew-actions .btn{ flex:0 0 auto; }
@media (max-width:720px){
  /* On a phone the pair goes full width and the primary sits FIRST — the thumb lands on Create,
     not on Cancel, and neither is under anything. */
  .cnew-actions:not(#_){ flex-direction:column-reverse; align-items:stretch; gap:8px; }
  .cnew-actions .btn:not(#_){ width:100%; text-align:center; }
}

/* ── The page's own title row — EVERY width ───────────────────────────────────────────────────
   The app header carries the APP: search, account, the drawer. This row carries the PAGE: its
   name on the left, its tools at the far right. At most one filled mark per page; when a page
   grows more tools than that, the rest belong behind a ⋯ rather than spread across the row.

   Outside the phone query on purpose. The markup ships at every width — a desktop reader sees
   the same heading and the same ＋ — so styling it only under 720px left the button as a 4×19px
   grey sliver on a laptop, which is what it did until this was split out. The only page-head
   rules that belong to phones are the sticky and the ground it paints; they stay below.

   One page header, everywhere. Activity is the one that reads best, so its metrics are the
   metrics: a tan kicker at .72rem/800 on 2 px of tracking, and a heading that scales with the
   screen instead of being fixed. Every page that grew its own title styling now defers. */
/* Round-188b: the tool is centred on the TITLE's line, not hung off the bottom of the block.
   With flex-end it aligned to whatever the last line happened to be, so a two-line heading or
   a page with no heading at all (SafeNet, whose kicker IS the title) put it somewhere else each
   time. A two-row grid fixes it to the row the title occupies: kicker on row 1, heading on
   row 2, tool on row 2 centred.

   `.page-head-t{ display:contents }` is what lets the kicker and the heading — which are its
   children, not the row's — become grid items themselves. Chrome has had display:contents since
   65, so the 101 baseline is fine. Where a page has NO heading the second row collapses and the
   tool centres on the kicker, which is then the title in fact. */
.page-head{
  display:grid; grid-template-columns:minmax(0,1fr) auto; column-gap:10px;
  align-items:center;
}
.page-head-t{ display:contents; }
.page-head .kicker, .page-head .ntf-kicker, .page-head .cdir-kicker{ grid-column:1; grid-row:1; }
.page-head h1, .page-head .cdir-h1{ grid-column:1; grid-row:2; }
/* Row 1 — the first line of the title, which on every page that has a .page-head is the
   kicker. "Centred on the first text" is the ask, and it is also what survives a heading that
   wraps: the tool does not wander down the block as the title grows. If a .page-head is ever
   written WITHOUT a kicker, move its heading to row 1 rather than leaving row 1 empty. */
.page-head .page-tool{ grid-column:2; grid-row:1; align-self:center; }
.page-head .kicker, .page-head .ntf-kicker{
  display:block; font-size:.72rem; font-weight:800; letter-spacing:2px; text-transform:uppercase;
  color:var(--tan, #BC8A52);
}
.page-head h1, .page-head .cdir-h1{
  margin:0; font-size:clamp(1.4rem, 3vw, 1.7rem); font-weight:700; color:var(--ink, #2C3B36);
}
.page-tool{
  flex:0 0 auto; width:38px; height:38px; border:0; border-radius:50%; cursor:pointer;
  display:grid; place-items:center; text-decoration:none;
  background:var(--mint, #E7EDE6); color:var(--deep, #4F8577);
}
.page-tool:hover{ text-decoration:none; }
.page-tool.add{ background:var(--deep, #4F8577); color:#fff; box-shadow:0 3px 10px rgba(79,133,119,.35); }
.page-tool.add:hover{ background:var(--deep-2, #3E6B5F); }
.page-tool svg{ width:20px; height:20px; stroke-width:2.4; }

@media (max-width: 720px){
  /* the bar: five places, floating clear of the edge so the map runs under it */
  /* align-items:start, not center: "Report an issue" wraps to two lines where the other four
     labels take one, and centring each cell independently would then sit the report icon a few
     pixels above its neighbours — a row of icons that is almost level reads as a mistake. From
     the top they all start at the same y and the labels hang below, with room for a third line
     in the languages that need one. */
  /* Round-205u: ONE number for the gap under the bar, so the bar and the room the page leaves
     for it cannot drift apart. It used to be written twice — bottom:10px+inset here and
     padding-bottom:92px+inset on the page — and moving one without the other either floats the
     bar over the last card or leaves a band of dead white under the feed.

     In the APP it is two thirds of that. The web keeps the full gap: a browser has its own
     chrome down there (an address bar that comes and goes), and the room that needs is not the
     room a home indicator needs. The indicator itself is about five pixels tall and sits eight
     from the edge, so two thirds of the inset still clears it — this trims the dead space, not
     the gesture area. */
  :root{ --navf-gap: calc(10px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))); }
  html.in-app{ --navf-gap: calc((10px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))) * 2 / 3); }

  .navf:not(#_){
    display:grid; grid-template-columns:repeat(5,1fr); align-items:start;
    position:fixed; left:10px; right:10px; z-index:1200;
    bottom:var(--navf-gap);
    height:72px; padding:11px 4px 0; border-radius:999px; background:rgba(255,255,255,.97);
    box-shadow:0 14px 36px rgba(44,59,54,.30), 0 2px 6px rgba(44,59,54,.10);
  }
  .navf-i{
    display:grid; justify-items:center; gap:2px; position:relative;
    text-decoration:none; border:0; background:none; font:inherit; padding:0; cursor:pointer;
    color:var(--ink-soft, #5C6E66); font-size:.62rem; font-weight:700; line-height:1.1;
  }
  /* `a:hover{ text-decoration:underline }` is global, and on a touch screen a tap leaves the
     hover state stuck — so every tab kept a line under its label until something else was
     touched. The label is not a link in a sentence; it is a tab's name. */
  .navf-i:hover, .navf-i:active, .navf-i:focus, .navf-i:focus-visible{ text-decoration:none; }
  .navf-l{ text-align:center; padding:0 2px; }
  .navf-ic{ width:44px; height:26px; border-radius:999px; display:grid; place-items:center; }
  .navf-ic svg{ width:21px; height:21px; }
  .navf-i.on{ color:var(--deep); }
  .navf-i.on .navf-ic{ background:rgba(97,141,124,.16); }
  /* Report is a tab like the others — the app's warning triangle, in red. No disc, no plus. */
  .navf-i.rep{ color:var(--red); }
  .navf-i.rep.on .navf-ic{ background:rgba(192,57,43,.12); }
  .navf-i .bell-badge{
    /* right:auto is the whole bug. The base rule pins the badge to right:0; this one adds a
       left, and an absolutely positioned box with BOTH edges named and width:auto stretches to
       fill the gap between them — 28px across a 69px tab for a single digit. It was never the
       padding: the badge was being pulled into an ellipse by its own offsets. */
    position:absolute; top:-2px; left:calc(50% + 7px); right:auto;
    background:var(--red); color:#fff;
    font-size:.58rem; font-weight:800; border:2px solid #fff;
    /* round, per the base rule — the ring is inside the box, so the circle stays a circle */
    box-sizing:border-box; min-width:18px; height:18px; padding:0 3px; border-radius:999px;
    display:inline-flex; align-items:center; justify-content:center; line-height:1;
  }

  /* Give the bar its room. Pages scroll under it; the map's own furniture lifts with it. */
  /* 82px is the bar and its breathing room; the rest is the gap beneath it, which is now a
     variable — so shrinking the gap in the app shrinks this by exactly as much. The sum is
     unchanged on the web. */
  body:not(.on-map) main.page{ padding-bottom:calc(82px + var(--navf-gap)); }

  /* The header keeps what is ABOUT THIS PAGE and loses what the bar now carries. Matched by
     HREF, because the same destination wears different classes in the map header and the inner
     one. Hides only — the elements stay in the DOM so their JS (badges, the stories count) goes
     on working, and reverting the branch restores them untouched. */
  /* BOTH headers: the live map uses .map-header, every other page .site-header. Missing that is
     why the map kept a full set of icons on the first pass. */
  .site-header:not(#_) .nav-right > a[href="/"],
  .site-header:not(#_) .nav-right > a[href="/stories"],
  .site-header:not(#_) .nav-right > a[href="/report"],
  .site-header:not(#_) .nav-right > a[href="/activity"],
  .map-header:not(#_) .nav-right > a[href="/"],
  .map-header:not(#_) .nav-right > a[href="/stories"],
  .map-header:not(#_) .nav-right > a[href="/report"],
  .map-header:not(#_) .nav-right > a[href="/activity"]{ display:none; }
  /* Round-186 (owner's call): the launcher is back in the header, last in the row — after Sign in
     or the avatar. The bar carries the five places a thumb reaches for; everything else lives in
     a drawer, and a drawer belongs at the edge rather than in the best seat on the screen. */
  .site-header:not(#_) .nav-right > .nav-grid,
  .map-header:not(#_) .nav-right > .nav-grid{ order:100; }
  /* The launcher used to carry 2px of its own, which made the last gap in the row wider than
     the others — the sort of difference nobody can name and everybody sees. Gone from both
     headers now that both rows are spaced by --hdr-gap. */

  /* the map's legal strip sat under the old dock; it now sits under the bar */
  /* stacked above the bar, under the band chip — the privacy link has to stay reachable */
  .map-legal:not(#_){ bottom:calc(90px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))); }
  /* The drawer is a DROPDOWN again (owner's call). It made sense as a slide-up while its button
     lived in the bar; the launcher is back at the top right, and a panel that opens from the
     corner you pressed is the one nobody has to think about. The base rule already draws it —
     all that is needed here is room at its foot so the floating bar does not cover the last row,
     and the bar kept above it so it stays tappable. */
  .grid-menu:not(#_){ padding-bottom:calc(96px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))); }
  html.grid-open .navf:not(#_){ z-index:2750; }

  /* ── The header, as the finale draws it (owner's call) ──────────────────────────────────────
     Logo (or, one level down, the back arrow that replaces it) hard LEFT; everything that
     belongs to the person — messages, their account or Sign in, and the launcher — hard RIGHT.

     This replaces round-180's `display:contents` + space-between, which spread every target
     evenly across the row. That was the right answer when the row held six unlabelled targets
     and the question was how to fit them; the bar took four of them away, and an evenly spread
     row now reads as things scattered rather than as two groups with a job each.

     Site search stays in the right-hand group, ahead of the three. It is not in the finale's
     header because the mockup drew search as a PAGE tool (search communities, search stories);
     the real site has one search across everything, and dropping it here would put a phone back
     where round-84 found it — no site search anywhere except the live map. */
  /* ONE gap for the whole row, and it is the live map's. Round-180 set the map's search pill an
     internal gap of clamp(4px, (100vw - 303px)/6, 24px) — 12px on a 375 screen — so the logo and
     the magnifier sit at a measured distance rather than a guessed one. The same expression here
     gives the same number at every width, which is what "the same spacing" has to mean when the
     two headers hold different things.
     The map's own right-hand icons are NOT that number: .map-header is justify-content:
     space-between, so whatever is left over after the pill is split between them (~47px at 375).
     That is the map spreading four items across a bar with nothing else in it, not a spacing
     decision worth copying onto a row whose controls are deliberately grouped at one end. */
  /* Round-188e (owner): the map's bar is arranged like every other page's — the search pill
     (logo + magnifier) at the left, the person's three at the right, one gap throughout.
     Round-180 had it justify-content:space-between with .nav-right{display:contents}, which
     spread all four items across a bar that has nothing in the middle: ~47px between icons at
     375, against 12px inside the pill beside them. Two spacings in one bar, and neither chosen.
     pointer-events stays OFF on the group and ON on its children — the map has to remain
     draggable through the space between the icons, which display:contents gave for free and a
     real box would otherwise take away. */
  .map-header:not(#_){ justify-content:flex-start; gap:var(--hdr-gap); }
  .map-header:not(#_) .nav-right{
    display:flex; align-items:center; gap:var(--hdr-gap);
    margin-left:auto; flex:0 0 auto; pointer-events:none;
  }
  .map-header:not(#_) .nav-right > *{ pointer-events:auto; }

  .site-header:not(#_){ justify-content:flex-start; gap:var(--hdr-gap); }
  .site-header:not(#_) .nav-right{
    display:flex; align-items:center; gap:var(--hdr-gap); margin-left:auto; flex:0 0 auto;
  }
  .site-header:not(#_) .brand, .site-header:not(#_) .nav-back{ flex:0 0 auto; margin:0; }

  /* Search sits beside the logo, as it does on the live map — where the brand pin and the
     magnifier are one object at the left end of the bar. It is the only control up here that
     acts on what is IN FRONT of you rather than on your account, so it belongs with the mark
     that means "this app", not in the cluster that means "you". order:-1 rather than moving
     the markup: it stays inside .nav-right, so every rule and script that addresses that row
     still finds it. */
  .site-header:not(#_) .nav-right{ margin-left:0; flex:1 1 auto; min-width:0; }
  .site-header:not(#_) .nav-right > .nav-icon.omni-open{ order:-1; margin-right:auto; }

  /* Round-190 (owner): the same 42px targets the live map's bar uses. The inner header had been
     squeezed to 36px at 420 and below by round-95l, because at 360dp a row of SIX unlabelled
     icons plus the Report pill overflowed and clipped the launcher off the right edge. The bar
     took four of them away: brand + search + messages + avatar + launcher now measure about
     262px of a 360px row, so the squeeze is paying for a crowding that no longer exists — and
     it left the two headers with different sized icons on adjacent screens. */
  .site-header:not(#_) .nav-right > .nav-icon,
  .site-header:not(#_) .nav-right > .bell,
  .site-header:not(#_) .nav-right > .avatar,
  .site-header:not(#_) .nav-right > .nav-grid{ width:42px; height:42px; }
  .site-header:not(#_) .nav-right > .nav-icon svg,
  .site-header:not(#_) .nav-right > .bell svg{ width:22px; height:22px; }
  .site-header:not(#_) .nav-right > .avatar img,
  .site-header:not(#_) .nav-right > .avatar .avatar-fallback{ width:42px; height:42px; }

  /* Messages returns to every inner page. Round-177 hid it everywhere but Stories because the
     header was already six targets wide; it is now three plus search, and a message someone
     sent you is not something to go looking for in a drawer. The live map keeps its own copy
     (.map-header, which that rule never touched). */
  .site-header:not(#_) .nav-right:not(#_) .msg-btn{ display:inline-flex; }

  /* No rule under the header, and no shadow either. The strip, the header and the page are all
     the same white, so a hairline only draws a line across nothing — and a drop shadow under a
     bar that is the same colour as what it sits on reads as a smudge. What separates the header
     from the page is the page's own heading. */
  .site-header:not(#_){ border-bottom:0; box-shadow:none; }

  /* ── The page's own title row ───────────────────────────────────────────────────────────────
     The app header carries the APP: search, account, the drawer. This row carries the PAGE: its
     name on the left, its tools at the far right. It sticks under the header so the tools stay
     within reach however far the page has been scrolled — the other half of "less scrolling".
     At most one filled mark per page; when a page grows more tools than that, the rest belong
     behind a ⋯ rather than spread across the row. */
  /* Phones add the two things a phone needs: the row sticks under the header so the tools stay
     reachable however far the page has scrolled, and it paints the page's own ground so the
     list slides under it rather than through it. The rest of the row is defined for every
     width, above this query. */
  /* --header-h, not a hand-rolled 64 + signup. The variable is the ONE place that knows how
     tall the chrome above this row really is, and it already carries two cases the arithmetic
     missed: the sign-up bar (64 + --signup-h) and, INSIDE THE APP, the notch
     (`html.in-app:not(.signup-bar-on){ --header-h: calc(64px + var(--safe-top)) }`, style.css).
     That second one is the reported bug: on a notched phone in the app the header stood
     --safe-top taller than this row's offset, so the kicker and the top of the heading slid
     under it on every scroll. A browser never showed it, because --safe-top is 0 there. */
  .page-head{
    position:sticky; top:var(--header-h, 64px); z-index:400;
    background:#fff; padding:10px 0 8px;
  }

  /* ── White, not cream and not paper (owner's call) ──────────────────────────────────────────
     The user pages were cream (#F1EBE4), the colour of the marketing surfaces; round-186 moved
     them to paper (#F3F4F1) to stop them arguing with the cards. White finishes the thought —
     the header is white, the bar is white, and a ground that matches them puts every card, chip
     and pill on this page in relief instead of a shade apart from the chrome around it.
     Each of these is a surface the page paints, matched by the same selector that painted it
     cream — including the pre-:has() twins, since :has() does nothing on the Chrome 101
     baseline. */
  body:not(#_), html:not(#_){ background:#fff; }
  html.pg-ntf:not(#_), html.pg-ntf body:not(#_),
  html.pg-msg body:not(#_), .stories-page:not(#_){ background:#fff; }
  .ntf-top:not(#_), .cpage2-topbar:not(#_){ background:#fff; }
  /* ── Communities: the search rides with the title row ──────────────────────────────────────
     It is not decoration at the top of a list, it is how this page is used — so it stays on
     screen with the heading instead of scrolling away after four tiles. --phead-h is the title
     row's measured height (site.js), so a heading that wraps pushes the search down with it
     rather than under it. The paper ring fills the pill's rounded corners, which would
     otherwise show the list sliding past underneath.
     Its height is the live map's service chip height: the same hand, the same control size. */
  /* Round-194c: :not(.sheet) is load-bearing, and its absence was a live bug. Focusing this
     pill promotes the SAME element to a full-screen sheet by adding .sheet — which the round-129
     machinery styles as position:fixed, inset:0, display:grid. This rule carries ID specificity
     (the :not(#_) lifter) and sits later in the file, so it beat every one of those: the sheet
     stayed a 34px sticky pill while html.csearch-open hid the page chrome around it, and the
     Communities search opened a BLANK WHITE SCREEN on phones. Shipped in 188c, found on the G6.
     A state class must always be excluded from a rule that describes the resting state. */
  .cdir-search:not(.sheet):not(#_){
    position:sticky; top:calc(var(--header-h, 64px) + var(--phead-h, 66px));
    z-index:399; margin-bottom:14px;
    min-height:34px; padding:0 12px;   /* .pill-svc measures 34px on a phone */
    box-shadow:0 0 0 6px #fff;
  }
  .cdir-search:not(.sheet) input:not(#_){ padding:0 !important; font-size:.86rem; }

  /* ── Signing in owns the screen ────────────────────────────────────────────────────────────
     One way out and nothing else: the header goes transparent over the page's own artwork, the
     brand, the search, the launcher and the sign-in button all go, and what is left is a back
     button on a white rounded square — squared off, not a circle, so it reads as a control
     rather than as another avatar.
     html.pg-login is set by the server (App.razor) — no :has(), which does nothing on the
     Chrome 101 baseline this app still supports. */
  html.pg-login .site-header{ background:none; border:0; box-shadow:none; }
  html.pg-login .site-header .brand,
  html.pg-login .site-header .omni,
  html.pg-login .site-header .nav-right{ display:none; }
  html.pg-login .site-header .nav-back{
    display:inline-flex; align-items:center; justify-content:center;
    width:44px; height:44px; padding:0; border:0; border-radius:14px;
    background:#fff; color:var(--deep, #4F8577); cursor:pointer;
    box-shadow:0 3px 12px rgba(44,59,54,.16);
  }
  html.pg-login .site-header .nav-back svg{ width:26px; height:26px; }

  /* Sign in is the LAST thing in the row and the smallest it can be while still reading as the
     one filled control: a compact pill hugging the right edge, not a button competing with the
     page's own action. margin-left:auto pushes it past whatever the page put before it. */
  .site-header .nav-right > .btn.btn-blue,
  .map-header .nav-right > .btn.btn-blue{
    margin-left:auto; margin-right:0; order:99;
    padding:8px 15px; font-size:.84rem; font-weight:700; border-radius:999px;
    box-shadow:0 3px 10px rgba(79,133,119,.30);
  }

  /* 3 · The band chip stays where it has always been — the bottom of the page, on Stories as on
     the map (owner's call). It was tried at the top right of the title row; that corner belongs
     to the page's OWN tools (＋, ⋯), and a band control is not one of them. Stories therefore
     takes the same bottom-left chip the map gets below, with no page-specific override. */

  /* Communities reads like the other pages now: a tan kicker over the heading. */
  .cdir-kicker{ display:block; color:var(--tan, #BC8A52); font-size:.72rem; font-weight:800;
    letter-spacing:2px; text-transform:uppercase; margin-bottom:2px; }
  .cdir-h1:not(#_){ margin-top:0; }

  /* The Stories head — the same kicker-over-heading the live pages already use on /activity.
     Phones only: on desktop the page keeps the shape it has today. */
  .stories-filters:not(#_){ align-items:stretch; gap:8px; padding-top:8px; }
  .st-head{ padding:0 2px; }
  .st-head .kicker{ display:block; color:var(--tan, #BC8A52); font-size:.72rem; font-weight:800;
    letter-spacing:2px; text-transform:uppercase; }
  .st-head h1{ font-size:1.3rem; font-weight:700; color:var(--ink, #2C3B36); margin:0; line-height:1.15; }
  .st-head .kicker{ font-size:.66rem; letter-spacing:1.8px; }

  /* The map's own dock sat where the bar now is. Report has moved into the bar, so the red
     shortcut goes; the time chip lifts above the bar rather than hiding, because the band it
     names ("Live", "7 days") is what the pins on screen mean. */
  /* The band chip, as design 06 draws it: a white pill at the bottom LEFT, not a full-width bar
     and not centred. Round-150d had made .time-row a wide white pill because it was a dock of
     two things — this chip and the red report button; report lives in the bar now, so the
     wrapper is gone and the chip is the whole control. */
  .time-row:not(#_){
    left:12px; right:auto; transform:none; width:auto;
    background:none; border:0; box-shadow:none; padding:0;
    /* clear of the legal line below it, which is anchored to the corner */
    bottom:calc(112px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  }
  .time-row .map-report-sm{ display:none !important; }
  /* Round 222 (owner, iOS): Ada's map-layer chips sat 32px off the bottom — under the bar, so a
     "Map the change" tap seemed to open something hidden behind it. Bottom RIGHT, level with the
     band chip (the left column is band chip, then zoom), clear of the legal line under it. */
  #adaLayers:not(#_){
    left:auto; right:12px; justify-content:flex-end;
    bottom:calc(112px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
    max-width:calc(100vw - 170px);
  }

  /* The zoom pair comes back to phones (owner's call), stacked straight above the band chip.
     Round-179 had retired it on the reasoning that pinch and double-tap do the job — they do,
     for anyone holding the phone in two hands. One-handed, at arm's length, over a map of a
     whole country, a pair of buttons in the corner is the difference between looking and
     giving up.
     .map-actions is the container map-engine appends the relocated Leaflet control to, and
     since round-94 it holds nothing else — so showing it on a phone shows exactly the zoom
     pair. It cannot be unhidden from the control itself: display:none on the parent takes the
     subtree with it.
     Bottom: the chip sits at 112 and stands 34 tall, so 156 clears it by ten. */
  .map-actions:not(#_){
    display:flex; flex-direction:column; align-items:flex-start; justify-content:flex-start;
    top:auto; right:auto; left:12px;
    bottom:calc(156px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  }
  .map-actions .leaflet-control-zoom:not(#_){
    border:0; border-radius:12px; overflow:hidden;
    box-shadow:0 8px 22px rgba(44,59,54,.20);
  }
  .map-actions .leaflet-control-zoom a:not(#_){
    width:38px; height:38px; line-height:38px; font-size:20px; border:0;
  }
  .map-actions .leaflet-control-zoom a + a:not(#_){
    border-top:1px solid rgba(243,244,241,.30);
  }
  .tb-state:not(#_){
    background:#fff; color:var(--deep, #4F8577);
    box-shadow:0 8px 22px rgba(44,59,54,.20); min-width:0; padding:9px 14px;
  }

  /* Privacy and Terms keep production's corner — bottom LEFT — and sit BEHIND the bar (z 840
     against the bar's 1200). They are an obligation rather than something anyone goes looking
     for; what the Google OAuth verification needs is a reachable privacy link, not a prominent
     one. Left in the document, clickable wherever the bar does not cover them. */
  .map-legal:not(#_){
    left:0; right:auto; bottom:0; z-index:840;
    border-radius:0 5px 0 0;
  }
}

/* round-186 (branch nav-06): the Stories head is part of the phone layout only */
@media (min-width: 721px){ .st-head{ display:none; } }
/* Owner, 2026-10-07: no "Near you / Trending" head on phones either, so the scope select and the
   service chips sit at the top and the cards get the room. The h1 stays for screen readers. */
.st-head{ position:absolute !important; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; border:0; }

/* ===========================================================================
   Round-198 (offline Phase 0): /offline — the document the service worker
   serves for any navigation it cannot reach the network for.

   Sized from the SMALL viewport unit and padded by the safe-area variable
   FIRST (var(--safe-top, env(...))), not env() alone: Android's WebView never
   wires the system bars into env(), so an env()-only rule reads 0 there and
   the card slides under the status bar. Same rule as the search bar — see the
   note on .srch-bar.
   =========================================================================== */
.offl{
  /* 100vh first: svh is Chrome 108 / Safari 15.4, and a browser at this app's floor drops the
     declaration it cannot parse — leaving no min-height at all and the card pinned to the top of
     a short box rather than centred in the screen. */
  min-height:var(--app-h, 100vh);
  min-height:var(--app-h, 100svh);
  display:grid;
  place-items:center;
  padding:calc(24px + var(--safe-top, env(safe-area-inset-top, 0px))) 16px
          calc(24px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  background:var(--paper, #F3F4F1);
}
.offl-card{
  width:min(420px, 100%);
  text-align:center;
  display:grid;
  gap:14px;
  justify-items:center;
}
.offl-mark{ color:var(--accent, #618D7C); opacity:.75; line-height:0; }
/* Round-199q: the shell's secondary action — reporting, the one thing that still works with no
   network. Set apart from "Try again" by a rule rather than by weight: it is a different kind of
   answer (do the thing anyway) rather than a weaker version of the same one (get back online). */
.offl-alt{
  display:grid; justify-items:center; gap:10px;
  margin-top:6px; padding-top:16px;
  border-top:1px solid var(--track, #ADC3B9);
  width:100%;
}
.offl-alt .offl-body{ font-size:.88rem; }

/* Round-199ae: the emergency number this phone saved while it still had signal.
   Same separated-block treatment as .offl-alt — it is a third kind of answer (ring somebody),
   not a variation on the other two. Filled by js/emergency.js, so it starts empty and hidden;
   the rules below describe what lands in it. */
.offl-emg{
  display:grid; justify-items:center; gap:8px;
  margin-top:6px; padding-top:16px;
  border-top:1px solid var(--track, #ADC3B9);
  width:100%;
}
.emg-lead{ margin:0; font-size:.9rem; line-height:1.45; }
/* Wraps rather than scrolls: a country with three services on a narrow phone must show all
   three, and a number a reader has to swipe sideways to find is a number they do not find. */
.emg-row{ display:flex; flex-wrap:wrap; gap:8px; justify-content:center; }
.emg-call{ white-space:nowrap; }
/* The label that makes this safe to act on: which country the number is for, and when it was
   saved. Quiet enough not to compete with the number, never small enough to miss — the reader
   this protects is one who has crossed a border since. */
.emg-note{
  margin:2px 0 0; font-size:.78rem; line-height:1.45;
  color:var(--ink-soft, #5C6E66); text-align:center;
}
/* Inside the report form's red alert box the note sits on a tinted panel, where --ink-soft is
   too pale to read against the wash. */
.sp-alertbox .emg-note{ color:inherit; opacity:.8; text-align:left; }
.sp-alertbox .emg-lead, .sp-alertbox .emg-row{ text-align:left; justify-content:flex-start; }

/* Round-199w: the line under a dashboard report row that explains an outcome the pill cannot.
   Indented to the row's text column so it reads as belonging to that report rather than to the
   list, and quiet — it is context, not a second status. */
.dash-why{
  margin:-4px 0 10px; padding-left:30px;
  font-size:.8rem; line-height:1.45; color:var(--ink-soft, #5C6E66);
}
/* Round-199l: the short report form the worker serves for report with no network.
   It borrows .offl's frame — same paper, same safe-area padding — but NOT its centring: this
   one has a form in it, and a column of fields centred in a 1280px window reads as a fragment
   of a page that failed to load rather than as the whole thing. Narrow and top-aligned, the
   way the wizard's own panel is. */
.offl-rep{
  min-height:var(--app-h, 100vh);
  min-height:var(--app-h, 100svh);
  padding:calc(24px + var(--safe-top, env(safe-area-inset-top, 0px))) 16px
          calc(32px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  background:var(--paper, #F3F4F1);
}
.offl-rep > .container{ width:min(560px, 100%); margin-inline:auto; padding-inline:0; }
.offl-rep .offl-title{ margin-bottom:6px; }
.offl-rep .offl-body{ margin-bottom:18px; }
.offl-rep .form-card{ display:grid; gap:18px; }
/* The chips wrap on a narrow phone; without this they run off the card's right edge. */
.offl-rep .chip-row{ display:flex; flex-wrap:wrap; gap:8px; }
.offl-rep textarea{ width:100%; resize:vertical; min-height:96px; }
.offl-rep [data-ro-send]{ justify-self:start; }

.offl-title{ margin:0; font-size:1.35rem; font-weight:700; color:var(--ink, #2C3B36); }
.offl-body{
  margin:0;
  font-size:.95rem;
  line-height:1.55;
  color:var(--ink-soft, #5C6E66);
  /* The sentence is long and the phone is narrow; a ragged right beats a river
     of stretched spaces at 320px. */
  text-wrap:pretty;
}
.offl-retry{ margin-top:4px; min-width:150px; }
.offl-back{ margin:0; font-size:.85rem; color:var(--accent, #618D7C); font-weight:600; }

/* ---------------- Round-206: the offline toast (window.aquaToast in site.js) ----------------
   One dark card floating over the page, the same ink-on-paper pair as the app's own banner so a
   reader who has seen either recognises the other. Above the bottom bar by measurement (site.js
   sets `bottom` from the bar's rect); this rule is the resting place where there is no bar.
   No close control and no gesture: it is there while the fact it states is true. */
.aqua-toast{
  position:fixed; left:12px; right:12px; z-index:2700;
  bottom:calc(12px + var(--safe-bottom, env(safe-area-inset-bottom, 0px)));
  margin:0 auto; max-width:440px; box-sizing:border-box;
  padding:10px 14px; border-radius:14px;
  background:#2C3B36; color:#F3F4F1;
  box-shadow:0 5px 14px rgba(0,0,0,.22);
  display:grid; gap:8px; justify-items:center;
  font-size:13px; line-height:1.35;
  /* A sentence over somebody's reading must not take their taps: only the two links do. */
  pointer-events:none;
}
.aqua-toast.at-top{
  bottom:auto;
  top:calc(var(--header-h, 0px) + 10px);
}
.aqua-toast-row{ display:flex; align-items:center; gap:9px; min-width:0; }
.aqua-toast-i{ flex:none; }
.aqua-toast-t{ min-width:0; text-align:start; }
.aqua-toast-acts{ display:flex; flex-wrap:wrap; justify-content:center; gap:8px; }
.aqua-toast-acts[hidden]{ display:none; }
.aqua-toast-a{
  pointer-events:auto;
  display:inline-flex; align-items:center; min-height:32px; padding:0 12px; border-radius:9px;
  background:#F3F4F1; color:#2C3B36; font-weight:600; font-size:13px; text-decoration:none;
}
.aqua-toast-a[hidden]{ display:none; }
.aqua-toast-a.aqua-toast-call{ background:transparent; color:#F3F4F1; box-shadow:inset 0 0 0 1.5px rgba(243,244,241,.55); }
/* The keyboard owns the bottom of the screen while it is up; the toast returns with the page. */
html.kb-open .aqua-toast:not(.at-top){ display:none; }
/* A full-screen viewer or sheet is its own surface; the toast waits underneath it. */
html.lb-open .aqua-toast, html.composer-open .aqua-toast, html.grid-open .aqua-toast, html.csearch-open .aqua-toast{ display:none; }
/* The report form makes room for a toast at its top (--toast-room, set by site.js or by the app
   that draws the toast): see html:has(.rpt-full) .rpt-full. */
.aqua-toast.at-top{ top:calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 8px); }
/* A changed sentence is announced by a single small swell — nothing moves across the page. */
@keyframes aquaToastNudge{ 0%{ transform:scale(1); } 40%{ transform:scale(1.035); } 100%{ transform:scale(1); } }
.aqua-toast.nudge{ animation:aquaToastNudge var(--dur-3, 260ms) ease-out; }
/* Wide screens: a card in the corner, not a bar across the reading column. */
@media (min-width:1024px){
  .aqua-toast{ left:24px; right:auto; margin:0; max-width:380px; justify-items:start; bottom:24px; }
  .aqua-toast-acts{ justify-content:flex-start; }
}

/* ---------------- Round-207 (effect 3): the "new stories" pill ----------------
   Under the header, centred, above the feed. It is the only thing that tells a reader who has
   scrolled that there is more — nothing is put in above them until they ask. */
.story-pill{
  position:fixed; z-index:890; left:50%; transform:translateX(-50%);
  top:calc(var(--header-h, 64px) + 10px);
  min-height:36px; padding:0 16px; border:0; border-radius:999px; cursor:pointer;
  background:#2C3B36; color:#F3F4F1; font:600 .84rem/1 inherit; font-family:inherit;
  box-shadow:0 5px 14px rgba(0,0,0,.22); white-space:nowrap;
  animation:storyPillIn var(--dur-2, 180ms) var(--ease-out, ease-out) both;
}
@keyframes storyPillIn{ from{ opacity:0 } }
.story-pill:focus-visible{ outline:2px solid #F3F4F1; outline-offset:2px; }
html.lb-open .story-pill, html.composer-open .story-pill, html.grid-open .story-pill, html.kb-open .story-pill{ display:none; }

/* ---------------- Round-207: the web bars under an app that draws its own ----------------
   html.chrome-nav / html.chrome-top are emitted by the server for an app build that says it
   draws that bar (App.razor). The web bar is made INVISIBLE, not removed:
     - it keeps reserving exactly the space it always did, so nothing on the page is laid out
       differently and the pinned dimensions stay true;
     - window.aquaChrome reads it to tell the app what to draw, and forwards the app's taps to it;
     - every existing rule that takes a bar away (the report form, /search, a child page) still
       does, with display:none, and that is how the app learns there is no bar here.
   opacity, not visibility: a hidden-by-visibility element is skipped by elementFromPoint, which
   is how aquaChrome notices that a sheet or a viewer has covered the bar's place.

   The live map is the exception the owner asked for: there the app's header steps aside and the
   website's own header is what the reader sees, on phones and on wide screens alike. */
html.chrome-nav .navf{ opacity:0; }
/* Round-211b: NOT opacity on the header itself — the menu's panel lives inside it, and an invisible
   header made an invisible menu (the app's menu button opened it; nobody could see it). The header
   loses its own buttons and edge; the panel keeps full visibility.
   Round-212b: it KEEPS its ground. The app paints its bar in the colour the page reports for the
   header, and a transparent ground reported "no colour": the app's bar let the feed show through
   as it scrolled under the fixed header (seen on the tablet emulator, beige page through the bar). */
html.chrome-top:not(.pg-map) .site-header:not(.adm-header){ box-shadow:none !important;
  border-color:transparent !important; backdrop-filter:none !important; -webkit-backdrop-filter:none !important; }
html.chrome-top:not(.pg-map) .site-header:not(.adm-header) > :not(.nav-right),
html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .nav-right > :not(.nav-grid),
html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .nav-grid > :not(.grid-menu){ opacity:0; }
/* Round-212 (phase 5, wide): the app's wide bar has its own arrangement — mark, destinations as text
   tabs, a search FIELD, Report, account, menu — and the page lays its invisible header out to match,
   so every control the app forwards a touch to sits where the app draws it:
     - the inline search box goes; the search LINK (which opens /search, as on a phone, and carries
       the keyboard on iOS) takes a field's width — the app draws the field over it and lets the
       touch through;
     - the destination icons go (the app's tabs stand for them; their badges are still read from
       the bottom bar, which stays in the page);
     - the brand shrinks to its mark, which is the room the tabs need on a tablet held upright.
   Same breakpoint as aquaChrome's "wide" (site.js), so the two can never disagree. */
@media (min-width: 721px){
  html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .omni{ display:none !important; }
  html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .brand > span{ display:none !important; }
  html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .nav-right > a.nav-icon:not(.omni-open),
  html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .nav-right > a.bell{ display:none !important; }
  html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .nav-right > a.omni-open{ display:flex !important;
    width:clamp(150px, 20vw, 240px); height:38px; border-radius:999px; }
}
/* Round-212f (owner): the menu panel hangs 8px below its BUTTON, which sits centred in the 64px
   header — so its top edge began 3px inside the header, and the app's header, drawn over the page,
   covered that border. Under the app's header it hangs 8px below the HEADER instead: the button's
   11px of header beneath it, plus the same 8px. */
html.chrome-top:not(.pg-map) .site-header:not(.adm-header) .grid-menu{ top:calc(100% + 19px); }
/* Round-217 (owner): with the app's bottom bar, the menu ENDS ABOVE the bar and scrolls inside —
   the app's bar is drawn over the page, so a menu reaching behind it had its last rows covered.
   Its top is the header's foot + 8px (round 212f); its foot is the bar's top − 8px. No room is
   kept at its foot any more for a bar floating over its last row: nothing floats over it now. */
@media (max-width: 720px){
  html.chrome-nav .grid-menu:not(#_){
    padding-bottom:12px;
    /* --header-h is the header's foot from the top of the screen whichever way it is reached:
       64px + the status bar, or 64px + the sign-up banner (which already holds the status bar). */
    max-height:calc(var(--app-h, 100dvh) - 72px - var(--navf-gap, 10px) - 8px - (var(--header-h, 64px) + 8px));
  }
}
/* Round-211: the ADMIN header stays the page's own for now — it carries the scope switcher and
   tools the app's header does not draw yet, and an invisible control nobody can see is a lost
   one. It is still kept sharp on a resume (sharpTop). */

/* Round-209: a picture that could not load is drawn as a quiet icon in the site's colours
   (site.js replaces its source); these keep it filling its box the way the photograph would. */
img.img-broken{ object-fit:cover; background:#E7EFEA; }

/* Round-219: "This report is no longer available" — a link to a report that is gone. Under the map's
   own header and chips, above the map, and gone by itself. */
.map-gone{ position:fixed; left:50%; transform:translateX(-50%); top:calc(var(--safe-top, 0px) + var(--signup-h, 0px) + 128px);
  z-index:1300; max-width:calc(100vw - 32px); background:#2C3B36; color:#F3F4F1; padding:10px 16px; border-radius:12px;
  font-weight:600; font-size:.9rem; line-height:1.35; box-shadow:0 6px 18px rgba(0,0,0,.22); text-align:center; }

/* Round-220: a post reached by its link (#ann-N, from the community's own search) stopped with its
   title under the fixed header. Every anchor target on a page with the header stops below it. */
article.cpage2-post, [id^="ann-"]{ scroll-margin-top:calc(var(--header-h, 64px) + 12px); }

/* Round 224 (owner): Check this device's debug readouts stay out of sight until five taps on the
   page title (site.js aquaDiagDebug). The inline display:flex would otherwise beat [hidden]. */
#diagDebug[hidden]{ display:none !important; }

/* Owner, 2026-10-05: on a pending report the pink "held from auto-publish" note was a full capsule,
   so two lines of text read as a blob. Soft corners on that note only; the other pills stay round. */
.arev-card .pill.arev-held{ border-radius:.5rem; }

/* Owner, 2026-10-06: an OPEN card's button turns white on a phone, where the 44px button and its
   panel are separate (the panel floats beside it). The panel itself keeps the map's accent green
   (owner, same day: "change the panel background back to accent"); on a desktop the card is the
   panel, so it stays accent too. */
@media (max-width:720px){
  .right-stack .map-card.collapsible:not(.min){
    background:#fff; color:var(--ink, #2C3B36); border-color:var(--line, #E2DCD3);
    box-shadow:0 0 0 3px var(--tan-light, #E3C9A5), 0 6px 16px rgba(28,40,36,.18);
  }
  .right-stack .map-card.collapsible:not(.min) > .card-head .card-ico{ color:var(--ink, #2C3B36); }
  .right-stack .map-card.collapsible:not(.min) .card-body{ color:var(--paper); }
}
/* …and an open panel sits above the map's zoom buttons and time band, which used to cover its last
   rows on a phone (browsers without :has keep the old stacking). */
.right-stack:has(.map-card.collapsible:not(.min)){ z-index:1100; }

/* Owner, 2026-10-06: "disable underlining text across the app's buttons and links". One rule, last
   in the cascade and !important, so it also outranks the eighteen older underline rules and inline
   styles. Keyboard users keep the focus ring, which is how a focused link is found. */
a, a:link, a:visited, a:hover, a:focus, a:active,
button, [role="button"], .btn, .link, .chip{ text-decoration:none !important; }

/* Owner, 2026-10-06: the Local resources layer's radius and type chips, under its row in Layers
   (on the accent-green panel) */
.res-ctl{ display:grid; gap:6px; margin:2px 0 6px 8px; }
.res-ctl > div{ display:flex; flex-wrap:wrap; gap:4px; }
.res-ctl button{
  font:inherit; font-size:.74rem; line-height:1; padding:5px 8px; border-radius:999px; cursor:pointer;
  border:1px solid rgba(243,244,241,.35); background:transparent; color:var(--paper, #F3F4F1);
}
.res-ctl button.on{ background:var(--paper, #F3F4F1); border-color:var(--paper, #F3F4F1); color:var(--ink, #2C3B36); }


/* Owner, 2026-10-06: "Report this" is a quiet link, not a button — small, muted, no box */
.flag-link{ background:none; border:0; padding:0; font:inherit; font-size:.74rem; color:var(--ink-soft, #5C6E66);
  opacity:.75; cursor:pointer; white-space:nowrap; }
.flag-link:hover, .flag-link:focus-visible{ opacity:1; color:var(--ink, #2C3B36); }
.flag-row{ display:flex; justify-content:flex-end; margin:2px 4px 0; }
.story-actions .flag-link{ margin-left:auto; margin-right:8px; }
.flag-sheet .flag-reasons{ display:grid; gap:6px; margin:10px 0; font-size:.9rem; }
.flag-sheet .flag-reasons label{ display:flex; gap:8px; align-items:center; }
.flag-sheet textarea{ width:100%; margin-bottom:6px; }
.flag-sheet .flag-msg{ min-height:1.2em; font-size:.85rem; }

/* Owner, 2026-10-06: the Report link sits at the bottom right of the report text (map panel) and
   at the far right of the "via [source]" line (story cards) */
.ed-desc .flag-row{ margin:6px 0 0; }
.story-src-row{ display:flex; align-items:baseline; gap:10px; margin-top:6px; }
.story-src-row .story-src{ margin-top:0; }
.story-src-row .flag-more{ margin-left:auto; }
/* the ⋯ that holds "⚑ Report" (owner, 2026-10-06: not so visible) */
.flag-more{ position:relative; display:inline-flex; vertical-align:middle; }
.flag-more-btn{ background:none; border:0; padding:0 4px; min-width:24px; height:20px; line-height:1; font-size:1.05rem;
  color:var(--ink-soft, #5C6E66); opacity:.7; cursor:pointer; border-radius:6px; }
.flag-more-btn:hover, .flag-more-btn:focus-visible, .flag-more.open .flag-more-btn{ opacity:1; background:var(--track, rgba(0,0,0,.06)); }
.flag-menu{ position:absolute; right:0; top:calc(100% + 4px); z-index:40; background:var(--card, #fff);
  border:1px solid var(--line, rgba(0,0,0,.12)); border-radius:8px; box-shadow:0 6px 18px rgba(0,0,0,.14); padding:4px; }
.flag-menu[hidden]{ display:none; }
.flag-menu .flag-link{ display:block; padding:6px 12px; font-size:.82rem; opacity:1; border-radius:6px; color:var(--ink, #2C3B36); }
.flag-menu .flag-link:hover, .flag-menu .flag-link:focus-visible{ background:var(--track, rgba(0,0,0,.06)); }

/* Owner, 2026-10-06: /communities/area/{osm} — an OSM area anywhere in the world, before anyone
   has started its community. One card: the outline, what it is, one Join. */
.carea-card{ background:#fff; border-radius:20px; overflow:hidden; box-shadow:0 6px 28px rgba(44,59,54,.09); }
.carea-map{ display:block; width:100%; height:180px; background:#E9EFE8; padding:14px; }
.carea-map polygon{ fill:rgba(79,133,119,.22); stroke:#4F8577; stroke-width:.8; stroke-linejoin:round; }
.carea-body{ padding:18px 20px 22px; }
.carea-kick{ font-size:.72rem; letter-spacing:1.2px; text-transform:uppercase; color:var(--accent); font-weight:700; }
.carea-h{ font-family:Cambria, Georgia, serif; font-size:1.7rem; line-height:1.15; margin:4px 0 2px; }
.carea-where{ color:var(--ink-soft); margin:0 0 12px; }
.carea-lead{ margin:0 0 10px; }
.carea-approx{ font-size:.85rem; color:var(--ink-soft); background:#F6F3EE; border-radius:12px; padding:10px 12px; margin:0 0 12px; }
.carea-join{ display:block; width:100%; text-align:center; margin-top:8px; padding:14px; border-radius:999px;
  background:#4F8577; color:#fff !important; font-weight:700; border:0; text-decoration:none !important; }

/* Owner, 2026-10-06: registration = design 03, three steps (design/register/03-three-steps.html). */
.reg3{ background:var(--bg, #F1EBE4); min-height:100%; }
.reg3-wrap{ max-width:540px; margin:0 auto; padding:26px 16px 30px; }
.reg3-steps{ display:flex; gap:6px; margin-bottom:8px; }
.reg3-steps i{ flex:1; height:5px; border-radius:99px; background:rgba(173,195,185,.5); }
.reg3-steps i.on{ background:#4F8577; }
.reg3-stepn{ font-size:.74rem; letter-spacing:1.2px; text-transform:uppercase; color:#7B988C; font-weight:700; margin-bottom:14px; }
.reg3-step h1{ font-family:Cambria, Georgia, serif; font-size:1.8rem; line-height:1.15; margin:0; }
.reg3-lead{ color:var(--ink-soft); margin:6px 0 18px; }
.reg3-loc, .reg3-field{ display:flex; gap:10px; align-items:center; background:#fff; border-radius:16px; padding:0 16px;
  box-shadow:0 3px 14px rgba(44,59,54,.09); }
.reg3-loc input, .reg3-field input{ flex:1; border:0; outline:none; font:inherit; font-size:16px; padding:15px 0; background:none; min-width:0; }
.reg3-gps{ display:flex; gap:8px; align-items:center; justify-content:center; width:100%; font:inherit; font-size:.86rem;
  color:#4F8577; font-weight:700; padding:12px; background:none; border:0; cursor:pointer; }
.reg3-note{ font-size:.86rem; color:var(--ink-soft); margin:4px 0 10px; }
.reg3-grp{ font-size:.72rem; letter-spacing:1.3px; text-transform:uppercase; color:#7B988C; font-weight:700; margin:16px 0 8px; }
.reg3-pick{ display:flex; gap:12px; align-items:center; width:100%; text-align:left; font:inherit; color:inherit;
  background:#fff; border-radius:16px; padding:12px 14px; margin-bottom:8px; box-shadow:0 3px 12px rgba(44,59,54,.07);
  border:2px solid transparent; cursor:pointer; }
.reg3-pick.sel{ border-color:#4F8577; background:#E7EDE6; }
.reg3-ic{ width:44px; height:44px; border-radius:12px; display:grid; place-items:center; font-size:1.3rem; background:#E7EDE6; flex:none; }
.reg3-txt{ flex:1; min-width:0; }
.reg3-txt b{ display:block; font-size:.98rem; }
.reg3-txt small{ display:block; color:var(--ink-soft); font-size:.78rem; }
.reg3-tag{ font-size:.68rem; font-weight:700; color:#A0661C; background:#FFF4E3; border-radius:99px; padding:2px 8px; margin-left:6px; }
.reg3-tick{ width:24px; height:24px; border-radius:50%; border:2px solid #ADC3B9; display:grid; place-items:center; font-size:.72rem; color:#fff; flex:none; }
.reg3-pick.sel .reg3-tick{ background:#4F8577; border-color:#4F8577; }
.reg3-cc{ font-weight:700; border-right:1px solid #E3E8E2; padding-right:10px; white-space:nowrap; }
.reg3-hint{ font-size:.8rem; color:#7B988C; margin-top:8px; }
.reg3-opt{ display:flex; gap:12px; align-items:center; background:#fff; border-radius:16px; padding:14px 16px; margin-top:12px;
  box-shadow:0 3px 12px rgba(44,59,54,.07); }
.reg3-opt p{ flex:1; margin:0; }
.reg3-opt small{ display:block; color:var(--ink-soft); font-size:.8rem; }
.reg3-sw{ width:46px; height:28px; border-radius:99px; background:#4F8577; position:relative; flex:none; cursor:pointer; border:0; padding:0; }
.reg3-sw::after{ content:""; position:absolute; top:3px; left:21px; width:22px; height:22px; border-radius:50%; background:#fff; }
.reg3-sw.off{ background:#ADC3B9; }
.reg3-sw.off::after{ left:3px; }
.reg3-summary{ background:#fff; border-radius:16px; padding:14px 16px; margin-top:16px; font-size:.9rem; box-shadow:0 3px 12px rgba(44,59,54,.07); }
.reg3-cta{ position:sticky; bottom:12px; display:flex; gap:10px; align-items:center; margin-top:24px; }
.reg3-go{ flex:1; background:#4F8577; color:#fff; border:0; border-radius:999px; padding:15px; font:inherit; font-size:1rem;
  font-weight:700; cursor:pointer; box-shadow:0 8px 24px rgba(44,59,54,.25); }
.reg3-go:disabled{ opacity:.45; box-shadow:none; cursor:default; }
.reg3-bk{ border:0; background:#fff; width:50px; height:50px; border-radius:50%; font-size:1.2rem; cursor:pointer;
  box-shadow:0 3px 12px rgba(44,59,54,.1); color:var(--ink); flex:none; }
.reg3-who{ text-align:center; font-size:.78rem; color:#7B988C; margin-top:14px; }

/* Owner, 2026-10-08 (iPhone): two magnifiers in the community search — iOS WebKit draws its own
   glass inside every type="search" field, beside the icon our search boxes already show. Every
   search field here brings its own icon, so the native one (and its decoration) goes everywhere. */
input[type="search"]{ -webkit-appearance:none; appearance:none; }
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-results-decoration,
input[type="search"]::-webkit-search-results-button{ -webkit-appearance:none; appearance:none; display:none; }

/* Owner, 2026-10-09: the Terms / Community Guidelines / Privacy notice at sign-in and registration. */
.ls-legal{ margin:14px 2px 0; font-size:.78rem; line-height:1.45; color:var(--ink-soft); text-align:center; }
.ls-legal a, .reg3-legal a{ color:var(--accent, #618D7C); font-weight:700; text-decoration:underline; }
.reg3-legal{ margin:12px 4px 0; font-size:.8rem; line-height:1.45; color:var(--ink-soft); }

/* ── Messaging plan (2026-10-09): platform announcements — card, tooltip, banner, modal, What's new.
   Never alert colours or alert wording: news must not look like danger. Kind sets one accent:
   product = the brand green, tip = tan, service change = amber. Everything is [data-ann], drawn at
   runtime by announce.js and stripped from saved offline copies. ── */
.ann{ --ann-c:var(--accent, #618D7C); --ann-bg:#EEF4F1; font-family:var(--font); color:var(--ink, #2C3B36); }
.ann-k-tip{ --ann-c:#A87638; --ann-bg:#F8F0E6; }
.ann-k-service{ --ann-c:#B7791F; --ann-bg:#FBF2E2; }
.ann-kind{ display:inline-block; font-size:.68rem; font-weight:800; letter-spacing:.07em; text-transform:uppercase;
  color:var(--ann-c); background:var(--ann-bg); border-radius:999px; padding:2px 8px; }
.ann-t{ margin:6px 0 2px; font-size:1rem; line-height:1.3; font-weight:800; color:var(--ink, #2C3B36); }
.ann-b{ margin:2px 0 0; font-size:.9rem; line-height:1.45; color:var(--ink-soft, #55635C); }
.ann-go{ display:inline-flex; align-items:center; margin-top:10px; border-radius:999px; padding:7px 15px; font-weight:800;
  font-size:.85rem; background:var(--ann-c); color:#fff !important; text-decoration:none !important; }
.ann-go:hover{ filter:brightness(1.06); }
.ann-x{ border:0; background:none; cursor:pointer; color:var(--ink-soft, #55635C); width:34px; height:34px; border-radius:10px;
  display:grid; place-items:center; flex:none; }
.ann-x:hover{ background:rgba(44,59,54,.07); }
.ann-out{ opacity:0; transform:scale(.98); transition:opacity .18s, transform .18s; }
.ann-slot:empty{ display:none; }

/* card — the first item of a feed */
.ann-slot{ margin:0 0 12px; }
.ann-card{ position:relative; display:flex; gap:12px; align-items:flex-start; background:#fff; border-radius:18px;
  padding:14px 10px 14px 16px; border:1px solid var(--track, #DCE5E0); border-inline-start:4px solid var(--ann-c);
  box-shadow:0 4px 16px rgba(44,59,54,.06); }
.ann-card .ann-main{ flex:1; min-width:0; }
.ann-card .ann-img{ width:72px; height:72px; object-fit:cover; border-radius:12px; flex:none; }

/* banner — a thin strip under the header */
.ann-banner-slot:empty{ display:none; }
/* Floats just under the fixed header (over the top of the page) rather than in the flow: the
   header is fixed and pages take their offset from --header-h, which the app pins — growing it
   for a once-a-visit strip would move every sticky element on the page. */
.ann-banner-slot{ position:fixed; top:var(--header-h, 64px); left:0; right:0; z-index:1050; }
.ann-banner-slot .ann-banner{ box-shadow:0 6px 18px rgba(31,42,38,.12); }
.ann-banner{ display:flex; align-items:center; gap:10px; padding:8px 8px 8px 14px; background:var(--ann-bg);
  border-bottom:1px solid rgba(44,59,54,.08); font-size:.88rem; }
.ann-banner .ann-line{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ann-banner .ann-b{ display:inline; font-size:inherit; }
.ann-banner .ann-go{ margin:0; padding:5px 12px; font-size:.8rem; white-space:nowrap; }
html.kb-open .ann-banner-slot, html.pg-map .ann-banner-slot{ display:none; }
@media (max-width:720px){
  .ann-banner{ flex-wrap:wrap; }
  .ann-banner .ann-line{ white-space:normal; flex-basis:calc(100% - 90px); }
}

/* tooltip — a bubble pointing at the new thing */
.ann-tip{ position:fixed; z-index:2600; width:min(300px, calc(100vw - 20px)); background:#fff; border-radius:16px;
  padding:12px 14px; box-shadow:0 14px 40px rgba(31,42,38,.22); border:1px solid var(--track, #DCE5E0); }
.ann-tip .ann-arrow{ position:absolute; top:-7px; inset-inline-start:calc(var(--ann-ax, 30px) - 7px); width:14px; height:14px;
  background:#fff; border-left:1px solid var(--track, #DCE5E0); border-top:1px solid var(--track, #DCE5E0); transform:rotate(45deg); }
[dir="rtl"].ann-tip .ann-arrow, .ann-tip[dir="rtl"] .ann-arrow{ inset-inline-start:auto; left:calc(var(--ann-ax, 30px) - 7px); }
.ann-tip.ann-above .ann-arrow{ top:auto; bottom:-7px; transform:rotate(225deg); }
.ann-tip .ann-t{ display:block; font-size:.95rem; }
.ann-row{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-top:10px; }
.ann-row .ann-go{ margin-top:0; }
.ann-ok, .ann-later{ border:1px solid var(--track, #DCE5E0); background:#fff; border-radius:999px; padding:7px 14px; font-weight:800;
  font-size:.85rem; color:var(--ink, #2C3B36); cursor:pointer; font-family:var(--font); }
.ann-lit{ position:relative; z-index:1; box-shadow:0 0 0 3px var(--ann-bg, #EEF4F1), 0 0 0 5px rgba(97,141,124,.55); border-radius:12px; }

/* modal — rare; the server allows one a month */
.ann-ov{ position:fixed; inset:0; z-index:3000; background:rgba(31,42,38,.45); display:grid; place-items:center; padding:20px; }
.ann-modal{ width:min(440px, 100%); background:#fff; border-radius:22px; padding:22px 22px 18px; box-shadow:0 30px 80px rgba(0,0,0,.25);
  max-height:calc(var(--app-h, 100vh) - 40px); overflow:auto; }
.ann-modal .ann-img{ width:100%; max-height:200px; object-fit:cover; border-radius:14px; margin-bottom:12px; }
.ann-modal .ann-t{ font-family:Cambria, Georgia, serif; font-size:1.35rem; }
.ann-modal .ann-row{ margin-top:16px; }
.ann-modal .ann-go{ flex:1; justify-content:center; padding:11px 16px; }
@media (max-width:720px){
  .ann-ov{ align-items:end; padding:0; }
  .ann-modal{ width:100%; border-radius:22px 22px 0 0; padding-bottom:calc(18px + var(--safe-bottom, env(safe-area-inset-bottom, 0px))); }
}

/* What's new — the list under Alerts */
.ann-news{ display:flex; flex-direction:column; gap:12px; margin:8px 0 30px; }
.ann-news-it{ background:#fff; border-radius:16px; padding:14px 16px; border:1px solid var(--track, #DCE5E0);
  border-inline-start:4px solid var(--ann-c, #618D7C); --ann-c:var(--accent, #618D7C); --ann-bg:#EEF4F1; }
.ann-news-it.ann-k-tip{ --ann-c:#A87638; --ann-bg:#F8F0E6; }
.ann-news-it.ann-k-service{ --ann-c:#B7791F; --ann-bg:#FBF2E2; }
.ann-news-it h3{ margin:6px 0 4px; font-size:1rem; }
.ann-news-it p{ margin:0; color:var(--ink-soft, #55635C); line-height:1.45; white-space:pre-line; }
.ann-news-meta{ font-size:.78rem; color:var(--ink-soft, #55635C); }

@media (prefers-reduced-motion: no-preference){
  .ann-card, .ann-banner{ animation:ann-in .28s ease-out; }
  .ann-tip{ animation:ann-in .22s ease-out; }
  .ann-modal{ animation:ann-up .26s ease-out; }
}
@keyframes ann-in{ from{ opacity:0; transform:translateY(-4px); } }
@keyframes ann-up{ from{ opacity:0; transform:translateY(16px); } }
/* the admin editor (/admin/announcements) */
.ann-ed{ display:grid; grid-template-columns:minmax(0,1fr) 380px; gap:22px; align-items:start; }
.ann-ed .card{ margin-bottom:14px; }
.ann-ed-h{ font-size:.82rem; letter-spacing:.07em; text-transform:uppercase; color:var(--ink-soft, #55635C); margin:0 0 10px; }
.ann-ed-prev{ position:sticky; top:calc(var(--header-h, 64px) + 16px); background:var(--paper, #F3F4F1); border-radius:18px; padding:14px; }
.ann-prev-l{ font-size:.75rem; font-weight:800; color:var(--ink-soft, #55635C); margin:12px 0 6px; }
.ann-preview .ann-banner{ border-radius:12px; }
.ann-langs{ display:flex; gap:6px; flex-wrap:wrap; margin:6px 0 12px; }
.ann-lang{ border:1px solid var(--track, #DCE5E0); background:#fff; border-radius:9px; padding:4px 9px; font-weight:800; font-size:.8rem; cursor:pointer; color:var(--ink, #2C3B36); }
.ann-lang.on{ background:var(--deep, #204A41); color:#fff; border-color:var(--deep, #204A41); }
.ann-lang.empty{ opacity:.55; }
.ann-lang.rev{ box-shadow:inset 0 -3px 0 #D68910; }
.ann-check{ display:flex; gap:10px; align-items:flex-start; padding:8px 0; cursor:pointer; }
.ann-check span{ display:flex; flex-direction:column; }
.ann-check small{ color:var(--ink-soft, #55635C); }
.ann-sub{ display:flex; gap:8px; flex-wrap:wrap; margin:0 0 8px 28px; }
.ann-pill{ display:inline-flex; gap:6px; align-items:center; border:1px solid var(--track, #DCE5E0); border-radius:999px; padding:4px 11px; background:#fff; font-size:.85rem; cursor:pointer; }
.ann-grid2{ display:grid; grid-template-columns:1fr 1fr; gap:0 14px; }
@media (max-width:980px){ .ann-ed{ grid-template-columns:1fr; } .ann-ed-prev{ position:static; } }
@media (max-width:560px){ .ann-grid2{ grid-template-columns:1fr; } }
