/* raincheck serving page (ticket 13 / spec L). No framework, no build step. */
:root {
  color-scheme: dark;
  --bg: #0b0d10;
  --panel: rgba(16, 19, 24, .94);
  --line: #2a2f38;
  --ink: #e8ebef;
  --dim: #9aa4b2;
  --accent: #2f6ea6;        /* 4.5:1 with white at 12px; #4d90c8 was 3.4:1 */
  --grey: #3a4049;          /* the "no publishable value" fill, mirrored in the legend */
  /* the MEASURED clearance for #provenance: app.js writes it from the strip's own
     offsetHeight on every resize. This is only the value before the first measurement -
     a hard-coded clearance is what put a toggle underneath the strip in the prototype,
     where a real click never reached it. */
  --prov: 72px;
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--ink);
  font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
#map { position: absolute; inset: 0; }

.skip { position: absolute; z-index: 5; left: -9999px; top: 12px; background: var(--accent);
  color: #fff; padding: 12px 16px; border-radius: 8px; }
.skip:focus { left: 12px; }

.panel { position: absolute; z-index: 2; background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: 12px 14px; backdrop-filter: blur(6px); font-size: 13px; }
/* one left column that owns its own scrolling: the panels shrink to fit between the
   header and the credit strip rather than sliding under it */
#left { position: absolute; z-index: 2; top: 12px; left: 12px; bottom: var(--prov); width: 340px;
  display: flex; flex-direction: column; gap: 8px; }
#insight { position: static; overflow-y: auto; flex: 1 1 auto; min-height: 120px; }
#legend { position: static; overflow-y: auto; flex: 0 1 auto; max-height: 46%; padding: 8px 14px; }
/* the right column is the same shape as #left: the panels shrink against each other and
   own their own scrolling, so the record card ticket 07 adds can share the column rather
   than float over the freshness rows it was opened from */
#right { position: absolute; z-index: 2; top: 12px; right: 12px; bottom: var(--prov); width: 300px;
  display: flex; flex-direction: column; gap: 8px; }
#layers { position: static; overflow-y: auto; flex: 1 1 auto; min-height: 120px; }
/* frontend 07: the record card is a flex SIBLING in the column - relative only to anchor
   its close button, never fixed or floated, so it shares the column with the layer panel
   and can never cover the freshness rows or the credit strip. It owns its scroll. */
#card { position: relative; overflow-y: auto; flex: 0 1 auto; min-height: 0; max-height: 46%; }
#card-close { position: absolute; top: 8px; right: 10px; background: #171b21;
  border: 1px solid #313845; border-radius: 7px; color: var(--dim); font: inherit;
  cursor: pointer; padding: 0 7px; min-height: 24px; }
#card-close:hover { border-color: #4a5464; }
#card-close:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }
#card-h { padding-right: 34px; overflow-wrap: anywhere; }
#card-h:focus { outline: none; }
#card-id { overflow-wrap: anywhere; }

/* frontend5 01 MUST 3: a card that opens off-screen or into a busy column is easy to miss
   entirely, so a click that opens it gets a brief flash - background only, so nothing
   reflows and no layout moves. insight.js's showCard() adds/removes the class per open (a
   second click while the card is already open re-triggers it); h.focus() and
   scrollIntoView() stay the mechanism that actually MOVES the reader's attention, this is
   only the "look here" cue for a sighted reader whose focus is already elsewhere. */
@keyframes card-flash { from { background: rgba(142, 203, 255, .3); } to { background: transparent; } }
#card.flash { animation: card-flash 600ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  #card.flash { animation: none; }
}

/* frontend3 02: the credit strip - fixed at the viewport bottom at EVERY width (the OSMF
   map-adjacent credit on a phone), one line at 1280, wrapping below 900px. Never
   nowrap/ellipsis: clipping the ODbL sentence would collapse the credit the strip exists
   to keep visible. Everything else the old slab held is in the #info dialog.
   frontend5 01 MUST 5: tighter padding and a smaller font shrink it further - every pinned
   licence string stays, in full, the same always-mounted element; only the chrome around
   them shrinks. --prov is read off the strip's own MEASURED offsetHeight (app.js), so a
   smaller strip narrows the columns' own clearance with no arithmetic to touch. */
#provenance { position: fixed; z-index: 3; bottom: 0; left: 0; right: 0; margin: 0;
  border-radius: 0; border-width: 1px 0 0; padding: 2px 10px; font-size: 11px;
  display: flex; flex-wrap: wrap; align-items: center; gap: 1px 12px; }
#provenance p { margin: 0; }
#geo-attribution:empty { display: none; }
#info-btn { margin-left: auto; min-width: 44px; min-height: 44px; background: #171b21;
  color: #cfd6df; border: 1px solid #313845; border-radius: 50%; cursor: pointer;
  font: inherit; font-size: 16px; }
#info-btn:hover { border-color: #4a5464; }
#info-btn:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }

/* the info dialog: a native <dialog>, so the focus trap, Esc and the backdrop are the
   platform's. On a phone it styles as a full-width bottom sheet (media query below). */
#info { background: var(--panel); color: var(--ink); border: 1px solid var(--line);
  border-radius: 12px; padding: 16px 18px; max-width: 560px; width: calc(100% - 48px);
  max-height: 80vh; font-size: 13px; }
#info::backdrop { background: rgba(4, 6, 9, .6); }
#info-close { position: absolute; top: 10px; right: 12px; background: #171b21;
  border: 1px solid #313845; border-radius: 7px; color: var(--dim); font: inherit;
  cursor: pointer; min-width: 44px; min-height: 44px; }
#info-close:hover { border-color: #4a5464; }
#info-close:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }
#info h2 { padding-right: 52px; }

h1 { font-size: 17px; font-weight: 650; margin: 0 0 6px; letter-spacing: -.01em; }
h2 { font-size: 13px; margin: 0 0 6px; }
h3 { font-size: 12px; margin: 0 0 4px; }
p { margin: 0 0 8px; }
code { font-size: 12px; color: var(--dim); }
.note { color: var(--dim); font-size: 12px; margin: 0 0 6px; }
.lbl { color: var(--dim); font-size: 11px; text-transform: uppercase;
  letter-spacing: .06em; margin: 10px 0 4px; font-weight: 600; }

.btns { display: flex; flex-wrap: wrap; gap: 8px; }
.btns button { background: #171b21; color: #cfd6df; border: 1px solid #313845; border-radius: 7px;
  padding: 5px 9px; cursor: pointer; font: inherit; font-size: 12px; min-height: 36px; }
.btns button:hover { border-color: #4a5464; }
.btns button[aria-pressed="true"] { background: var(--accent); color: #fff; border-color: #6fb0e4; }
.btns button:focus-visible, #livetoggle:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }

#headline { margin-top: 12px; }
.big { font-size: 24px; font-weight: 650; letter-spacing: -.02em; line-height: 1.15; }
.band { font-size: 13px; color: var(--ink); }
.row { padding: 8px 0; border-top: 1px solid var(--line); }
.row:first-child { border-top: 0; }
.warn { color: #ffcf87; }

/* the live row group dims itself when the pipeline stops writing (ticket 14); the dots dim
   with it, in app.js - a bright panel over grey dots would still read as live */
#live { border-top: 1px solid var(--line); margin-top: 8px; padding-top: 4px; }
#live.stale { border-color: #6a5a33; }
#live.stale #live-h::after { content: " - STALE"; color: #ffcf87; }

.swatches { display: flex; height: 10px; width: 100%; border-radius: 4px; overflow: hidden; margin: 6px 0 2px; }
.swatches span { flex: 1; }
.swatches.one { width: 44px; }
.swatches.one .grey { background: var(--grey); }
.ticks { display: flex; justify-content: space-between; color: var(--dim); font-size: 11px; margin-bottom: 8px; }

#curve { margin: 12px 0 0; }
#curve-svg { width: 100%; height: auto; display: block; }

#tip { display: none; position: absolute; z-index: 3; pointer-events: none; max-width: 260px;
  background: rgba(10, 12, 15, .97); border: 1px solid #313845; border-radius: 8px;
  padding: 8px 10px; font-size: 12px; }
#tip b { display: block; margin-bottom: 2px; }

/* the layer panel (frontend 05, rebuilt by frontend3 02): a row is a CONTROL - name,
   box, chip - with everything else one tap away behind its chevron. A GATED row is
   DARK, never missing: the box is disabled, the chip keeps its own hue and the reason
   prints in the row's detail. */
.lyr { border-top: 1px solid var(--line); padding: 8px 0; }
.lyr:first-of-type { border-top: 0; }
.lrow { display: flex; align-items: center; gap: 7px; }
.lrow label { display: flex; gap: 7px; align-items: center; cursor: pointer; flex: 1;
  min-height: 44px; }
.lyr .nm { flex: 1; }
.lyr .note { margin: 4px 0 0; }
.lyr.gated { opacity: .78; }
.lyr.gated label, .lyr input:disabled { cursor: not-allowed; }
.chev { flex: 0 0 auto; width: 35px; min-height: 44px; background: none; border: 0;
  color: var(--dim); font: inherit; font-size: 16px; cursor: pointer; }
.chev[aria-expanded="true"] { transform: rotate(90deg); }
.chev:hover { color: var(--ink); }
.chev:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }
.det { margin: 2px 0 0 20px; }
.src { color: var(--dim); font-size: 11px; display: flex; gap: 6px; align-items: baseline;
  justify-content: space-between; margin-top: 3px; }
.src b { font-weight: 500; color: #b9c3d0; }
.src .why { opacity: .78; font-style: normal; }
.st { font-size: 11px; font-weight: 700; letter-spacing: .06em; }
.st-FRESH { color: #6fd18c; }
.st-STALE { color: #ffcf87; }
.st-OFF   { color: var(--dim); }
.st-GATED { color: #d2a24c; }          /* dark, not absent - the one hue for a shut gate */
.st-AGE   { color: #7f93ad; }          /* age known, no budget frozen -> no verdict */
.st-LOADING { color: #8ecbff; animation: chip-pulse 1s ease-in-out infinite; }
@keyframes chip-pulse { 50% { opacity: 0.4; } }
.lyr input:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }

/* frontend5 01 MUST 2: the ground rows' own disclosure - the same native <details>/
   <summary> treatment #analyst already uses below, a second use of the platform primitive
   rather than a second interaction pattern. Default CLOSED (no `open` unless the reader's
   own click put it there); the rows inside keep the ordinary .lyr styling untouched. */
#ground-layers { border-top: 1px solid var(--line); padding-top: 4px; margin-bottom: 2px; }
#ground-layers summary { cursor: pointer; color: var(--dim); font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; min-height: 44px; display: flex;
  align-items: center; }
#ground-layers summary:hover { color: var(--ink); }
#ground-layers summary:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }

/* the NavigationControl's stated home: clear of the right card and the credit strip -
   the default bottom-right corner is covered twice at 1280 (spec sec. 2.1). */
.maplibregl-ctrl-bottom-right { right: 316px; bottom: calc(var(--prov) + 8px); }

@media (max-width: 900px) {
  #left, #right { position: static; width: auto; }
  /* the legend renders FIRST under the map strip: the fill is ON by default at 375,
     so its key belongs directly under the map it explains */
  #left { display: flex; flex-direction: column; }
  #legend { order: -1; }
  #right { display: none; }
  body.drawer-open #right { display: block; }
  /* the dock joins the page flow under the map strip; the mode bar stays floating on it */
  #hour-dock { position: static; transform: none; width: auto; margin: 12px; }
  #insight, #layers, #card, #legend { position: static; width: auto; max-height: none; margin: 12px; }
  #map { position: relative; height: 60vh; }
  body { overflow-y: auto; padding-bottom: var(--prov); }
  /* the 40vh cap exists to keep the analyst summary on-card at 1280; at 375 it would
     only manufacture a nested scroller inside an already-scrolling page */
  #recent { max-height: none; }
  #info { margin: auto 0 0; width: 100%; max-width: none; max-height: 85vh;
    border-radius: 12px 12px 0 0; }
  .maplibregl-ctrl-bottom-right { right: 8px; bottom: 8px; }
}

/* frontend2 03: the flood-zone scenario radio and its legend, both inside the layer's own
   row so they appear with the layer and vanish with it. The swatch is the same hue the
   paint uses (layers.js ZONE_LEGEND), never a second copy of a colour. */
.opts { margin: 3px 0 2px 20px; display: flex; flex-wrap: wrap; gap: 2px 10px; }
.opts label { display: flex; align-items: center; gap: 4px; }
.zlegend { margin: 2px 0 2px 20px; }
.zl { display: flex; align-items: baseline; gap: 5px; font-size: 11px; color: #9aa4b2;
      line-height: 1.35; }
.zl b { color: #cfd6df; font-weight: 600; }
.zl .sw { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 2px;
          border: 1px solid #0b0d10; }

/* frontend2 05: the two audiences (D7). The rider surface is everything visible by
   default; the analyst prose sits behind this ONE disclosure, default closed. The
   summary is a real <summary>, so the keyboard and screen-reader semantics are the
   platform's - it only gets the page's focus ring and label styling. */
#analyst { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 8px; }
#analyst summary { cursor: pointer; color: var(--dim); font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; }
#analyst summary:hover { color: var(--ink); }
#analyst summary:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }
#analyst[open] summary { margin-bottom: 8px; }

/* the rider's recent-flooding rows: date left, labelled-asset counts right; hover or
   focus rings the event's Cells on the map, and the row shows it took the pointer.
   Capped so the analyst disclosure is never scrolled out of its own card at 1280.
   18vh, not the spec's 40vh: the cap exists FOR the summary's visibility (spec sec. 2.1's
   own purpose), and the REAL payload's label + caveat strings are ~45px taller than the
   prototype's - measured at 1280x800, 40vh left the summary ~80px below the card fold. */
#recent { margin-top: 12px; max-height: 18vh; overflow-y: auto; }
/* Flood history's column is the title and this list, so the record gets the room the
   storm answer would have used - the full window scrolls without fighting the chips */
body[data-mode="history"] #recent { max-height: 52vh; }
#recent .chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0; }
#recent .chips button { font: inherit; font-size: 11px; background: #171b21; color: #cfd6df;
  border: 1px solid #313845; border-radius: 999px; padding: 1px 8px; cursor: pointer; }
#recent .chips button[aria-pressed="true"] { background: #24405e; border-color: #8ecbff; }
#recent .chips button:focus-visible { outline: 2px solid #8ecbff; outline-offset: 1px; }
#rec-zones button b { font-weight: 600; color: #8ecbff; }
#rec-time { display: flex; align-items: center; gap: 8px; margin: 4px 0 6px; }
#rec-time input { flex: 1; min-width: 0; accent-color: #8ecbff; }
#rec-time-lbl { white-space: nowrap; }
.rec { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 8px;
  align-items: baseline; cursor: pointer;
  font-size: 12px; color: var(--dim); border-top: 1px solid var(--line); padding: 4px 0; }
.rec.pinned { background: rgba(142, 203, 255, 0.08); border-left: 2px solid #8ecbff;
  padding-left: 6px; }
.rec .zl { flex-basis: 100%; font-style: normal; font-size: 11px; opacity: 0.75; }
/* the analyst prose, folded: every sentence intact, one platform disclosure deep */
details.why > summary { font-size: 11px; color: var(--dim); cursor: pointer;
  letter-spacing: .02em; margin: 2px 0; }
details.why[open] > summary { color: #8ecbff; }
.rec b { color: #b9c3d0; font-weight: 500; white-space: nowrap; }
.rec span { text-align: right; }
.rec:hover { color: var(--ink); background: rgba(142, 203, 255, .06); }
.rec:focus-visible { outline: 2px solid #8ecbff; outline-offset: 1px; color: var(--ink); }

/* the one-sentence rider description under each layer row */
.lyr .sub { margin: 2px 0 0; }

/* ------------------------------------------------- frontend5 03: modes, slider, drawer */
/* The mode bar: the only always-on controls. Three named questions; each is a layer SET
   (app.js setMode), so the map never shows two stories at once. */
#modes { top: 12px; left: 50%; transform: translateX(-50%); z-index: 3;
  display: flex; gap: 4px; padding: 4px; border-radius: 12px; }
#modes button { background: none; color: #cfd6df; border: 1px solid transparent;
  border-radius: 9px; padding: 0 16px; min-height: 44px; cursor: pointer; font: inherit;
  font-size: 13px; }
#modes button:hover { border-color: #4a5464; }
#modes button[aria-pressed="true"] { background: var(--accent); color: #fff; font-weight: 600; }
#modes button:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }

/* The layers drawer: the full per-layer controls, freshness chips and gate prose survive
   whole in #right - hidden until asked for, never deleted. */
#layers-btn { position: absolute; z-index: 3; top: 12px; right: 12px; min-height: 44px;
  padding: 0 14px; background: var(--panel); color: #cfd6df; border: 1px solid var(--line);
  border-radius: 10px; cursor: pointer; font: inherit; font-size: 13px;
  backdrop-filter: blur(6px); }
#layers-btn:hover { border-color: #4a5464; }
#layers-btn:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }
#layers-btn[aria-expanded="true"] { background: var(--accent); color: #fff; }
#right { display: none; top: 64px; }
body.drawer-open #right { display: flex; }

/* The storm-hour slider, docked over the map: one storm night, hour by hour. Hidden with
   the dock whenever the view has no hours (insight.js) and in any mode but storms. */
#hour-dock { left: 50%; transform: translateX(-50%); bottom: calc(var(--prov) + 10px);
  width: min(620px, calc(100% - 24px)); padding: 10px 18px; border-radius: 12px; }
#hours input[type="range"] { width: 100%; margin: 4px 0 2px; accent-color: var(--accent); }
#hours .hticks { display: flex; justify-content: space-between; color: var(--dim);
  font-size: 11px; }
#hours output { display: block; text-align: center; font-size: 12px; color: var(--ink);
  margin-top: 2px; }
body:not([data-mode="storms"]) #hour-dock { display: none; }
/* the recent-flooding rows are the Flood history mode's list */
body:not([data-mode="history"]) #recent { display: none; }
/* the storm answer and its picker belong to Storm replay; the flood modes keep the title */
body:not([data-mode="storms"]) #answer, body:not([data-mode="storms"]) #views,
body:not([data-mode="storms"]) #views-h { display: none; }
/* the fleet status line is Live now's answer card; the other modes explain themselves */
body:not([data-mode="live"]) #livemeta { display: none; }
/* no Cell ramp is on screen in Flood history (cells off, routes off): the marker meanings
   live on their drawer rows, so the ratio legend would only mislead here */
body[data-mode="history"] #legend { display: none; }
/* same lie in Live now: MODE_SET.live turns every fill off but applyRamp()'s `ramped`
   stays true (view is never reset - byte-pinned, test_page.py), so the stale ratio
   legend would sit beside a fleet that isn't coloured by it. Ceiling: a reader who left
   the routes layer on loses its legend here too - the trade history mode already made. */
body[data-mode="live"] #legend { display: none; }

/* the storm/window picker: one native select in the answer card */
#views select { width: 100%; min-height: 36px; background: #171b21; color: #cfd6df;
  border: 1px solid #313845; border-radius: 7px; font: inherit; font-size: 12px;
  padding: 0 8px; }
#views select:focus-visible { outline: 2px solid #8ecbff; outline-offset: 2px; }
