:root{
  /* THE BROWSER'S OWN WIDGETS NEED TELLING TOO.
     Without this the user agent renders buttons, text inputs, scrollbars and
     focus rings for a light page whatever the tokens below say — so on a dark
     page the search box and the track button came out as white rectangles,
     the button with white text inside it. Found by rendering the page in dark
     mode; nothing in the stylesheet was wrong, the stylesheet simply was not
     the whole story. */
  color-scheme:light dark;
  --navy:#1E2A78; --navy-deep:#141C52; --gold:#F5C400;
  --ink:#171A21; --ink-soft:#5A6270; --line:#DFE3EA; --bg:#FFFFFF; --panel:#F6F7FA;
  --green-ink:#2F7C2A; --green-bg:#E4F2E1;
  --orange-ink:#B85A10; --orange-bg:#FCEBD8;
  --red-ink:#96201B; --red-bg:#FBE0DE;
  --unknown-ink:#5A6270; --unknown-bg:#E8EAED;
  /* Shell tokens. The rail is navy in both themes because it is the
     municipal masthead colour, so its ink is declared rather than derived. */
  --rail-ink:#C4CBEB; --rail-cap:#8E9AD6; --rail-line:rgba(255,255,255,.18);
  /* Interactive ink. Navy on white is the brand; on the dark surface it is
     nearly invisible, and the two controls that are not <a> elements — the
     legend's disclosure and the dock's back button — do not inherit the
     link override below. */
  --link:#1E2A78;
  /* The storm ramp's own ink, for the one headline that is about a cyclone.
     Green/orange/red are PAGASA bands and a chance of approach is not one. */
  --cyc-ink:#43349b; --cyc-bg:#F1EFFA;
  --shadow:0 2px 12px rgba(20,28,82,.14); --scrim:rgba(20,28,82,.32);
  --ui:"Public Sans",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  /* ── STRUCTURAL TOKENS ──────────────────────────────────────────────────
   *
   * Added for the look, and NO NEW HUE with them. Green, orange and red are
   * PAGASA bands, violet is storm strength, blue is a chance of approach, the
   * DHSUD ramp is hazard and risk, slate is wind speed, navy and gold are the
   * municipal masthead. Every one of those means something, and a decorative
   * colour introduced next to them would be a sixth vocabulary a reader has
   * to learn is meaningless. So this refresh is carried entirely by RADIUS,
   * ELEVATION, SPACING and MOTION.
   *
   * A RADIUS SCALE, because there was not one. The stylesheet had radii of
   * 2, 3, 4, 5, 6, 7, 8 and 10 pixels, chosen one rule at a time — which is
   * most of what made a carefully built page look unfinished. Corners that
   * disagree by a pixel read as carelessness even to someone who could not
   * say why.
   */
  --r-xs:4px; --r-sm:6px; --r:9px; --r-lg:12px; --r-xl:16px; --r-pill:999px;

  /* ELEVATION IN THREE STEPS, not one shadow used everywhere. The distance a
   * thing sits off the page should say what kind of thing it is: e1 for a
   * resting control, e2 for a card that owns its content, e3 for something
   * floating over the map. Two layers each — a tight contact shadow and a
   * wide soft one — because a single blur reads as a grey halo. */
  --e1:0 1px 2px rgba(20,28,82,.06),0 1px 3px rgba(20,28,82,.09);
  --e2:0 1px 2px rgba(20,28,82,.05),0 4px 14px rgba(20,28,82,.09);
  --e3:0 4px 8px rgba(20,28,82,.07),0 12px 32px rgba(20,28,82,.16);

  /* A softer rule for dividers INSIDE a card. --line is the boundary of a
   * thing; at full strength between every row of one it turns a list into a
   * grid of boxes. */
  --line-soft:#EBEEF3;

  /* MOTION. One duration and one curve, so nothing on the page eases
   * differently from anything else. Short enough to feel like a response
   * rather than an animation — a warning page must never feel like it is
   * thinking. Switched off wholesale under prefers-reduced-motion below. */
  --t:140ms cubic-bezier(.2,.6,.3,1);

  /* THE FOCUS RING, which this page did not have. The search box carried a
   * bare `outline:none` — invisible keyboard focus on a page that an
   * emergency operator may well be driving from the keyboard while looking
   * at something else. Two rings: a halo in the page colour so the ring
   * reads against any surface it lands on, then the ring itself. */
  --focus:#1E2A78;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ink:#EEF0F4; --ink-soft:#A6AEBD; --line:#2C3242; --bg:#12141B; --panel:#1A1E28;
    --green-bg:#1B2E1A; --orange-bg:#33230F; --red-bg:#341615; --unknown-bg:#23262E;
    --green-ink:#8FD98A; --orange-ink:#F0A65C; --red-ink:#F09189; --unknown-ink:#A6AEBD;
    --shadow:0 2px 12px rgba(0,0,0,.5); --scrim:rgba(0,0,0,.55); --link:#9DB2FF;
    --cyc-ink:#9c8ee4; --cyc-bg:#221E38;
    /* Shadows are darker and the contact layer is stronger: on a dark
       surface a shadow tinted with the brand navy is invisible, so depth has
       to come from black. */
    --e1:0 1px 2px rgba(0,0,0,.4),0 1px 3px rgba(0,0,0,.3);
    --e2:0 1px 2px rgba(0,0,0,.4),0 4px 14px rgba(0,0,0,.4);
    --e3:0 4px 8px rgba(0,0,0,.4),0 12px 32px rgba(0,0,0,.55);
    --line-soft:#232837;
    --focus:#9DB2FF;
  }
}
/*
 * ONE PLACE THAT TURNS THE MOTION OFF.
 *
 * Every transition on this page is timed with --t, so a reader who has asked
 * their system for less motion gets a page that still does everything and
 * animates none of it — rather than each rule having to remember. The
 * satellite cross-fade has its own opt-out beside its own rule, because that
 * one is a picture changing rather than a control responding.
 */
@media (prefers-reduced-motion:reduce){:root{--t:0s}}
*{box-sizing:border-box}
/*
 * THE `hidden` ATTRIBUTE HAS TO WIN, AND WITHOUT THIS IT DOES NOT.
 *
 * The user agent's rule is `[hidden]{display:none}` — one attribute selector,
 * in the UA sheet. Any author rule of equal specificity beats it, so
 * `.flyout{display:flex}` quietly un-hid the layer panel, the table view and
 * the disclaimer dialog: the first render of this shell had all three open at
 * once on top of each other. Every one of them is toggled with `el.hidden` in
 * the script, which is the right API; this is the line that makes it mean
 * something.
 */
[hidden]{display:none!important}
body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.55 var(--ui);
  /* Without this the same weight renders noticeably heavier on macOS than on
     Windows, and the page looked like two different designs depending on who
     opened it. */
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
/* Headings tighten as they get larger, which is what stops a big heading
   reading as a wide one. Body text keeps its natural spacing. */
h1{font-size:1.5rem;margin:.1rem 0 0;letter-spacing:-.018em}
h2{font-size:1rem;letter-spacing:-.006em;margin:1.4rem 0 .4rem}
h3,h4{letter-spacing:-.004em}
/*
 * FIGURES DO NOT JITTER.
 *
 * Proportional digits are different widths, so a reading that ticks from 7 to
 * 11 mm/h shifts every column beside it. On a page that refreshes itself in
 * front of an operator that is movement with no meaning, and movement on a
 * warning page should always mean something.
 */
.num-live,.wnum,td.num,.tval,.mono,table{font-variant-numeric:tabular-nums}
a{color:var(--navy)}

/*
 * FOCUS THAT CAN BE SEEN, ON EVERY CONTROL.
 *
 * The search box had `outline:none` and nothing in its place, so keyboard
 * focus was invisible — on a page an operator may be driving from the
 * keyboard while watching the map rather than the controls.
 *
 * `:focus-visible`, not `:focus`, so a mouse click does not leave a ring
 * behind on everything it touches. The inner halo is painted in the page
 * colour so the ring stays legible whether the control sits on white, on the
 * navy rail or on top of the map.
 */
:where(a,button,input,select,summary,[tabindex]):focus-visible{
  outline:2px solid var(--focus);outline-offset:2px;border-radius:var(--r-xs)}
.rail :focus-visible,.topbar :focus-visible{outline-color:#fff}

/*
 * Thin scrollbars on the regions that scroll. The page itself never does —
 * each region scrolls inside itself — so a full-width platform scrollbar in
 * the middle of the layout reads as a seam rather than as a control.
 */
*{scrollbar-width:thin;scrollbar-color:var(--line) transparent}
*::-webkit-scrollbar{width:10px;height:10px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:var(--line);border-radius:var(--r-pill);
  border:3px solid transparent;background-clip:content-box}
*::-webkit-scrollbar-thumb:hover{background:var(--ink-soft);background-clip:content-box;
  border:3px solid transparent}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) a{color:#9DB2FF}}

/* ═══════════════════════════════════════════════════════════ the app shell
 *
 * Four regions that never move: a top bar, a rail, the stage, the dock. The
 * page does not scroll — each region scrolls inside itself — so the map is
 * never something a reader has to go and find.
 */
html,body{height:100%}
body{overflow:hidden}
.app{height:100%;display:flex;flex-direction:column;min-height:0}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.ic{flex:0 0 auto}

/* ---------- top bar ---------- */
.topbar{flex:0 0 auto;display:flex;align-items:center;gap:.85rem;padding:.65rem 1.1rem;
  background:var(--navy-deep);color:#fff;border-bottom:3px solid var(--gold)}
.topbar .seal{width:36px;height:36px;object-fit:contain;flex:0 0 auto}
.ident{min-width:0}
.eyebrow{margin:0;font-size:.63rem;text-transform:uppercase;letter-spacing:.12em;color:var(--gold)}
.topbar h1{font-size:.95rem;font-weight:600;margin:0;letter-spacing:.01em}
/* THE LIVE PILL IS THE OLD STATUS BAR. Same ids, same staleness rules; it has
   moved to where a reader looks first instead of a strip they scroll past. */
.livepill{margin-left:auto;display:flex;align-items:center;gap:.5rem;font-size:.78rem;
  font-weight:600;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.22);
  border-radius:var(--r-pill);padding:.3rem .75rem;max-width:20rem;min-width:0}
.livepill span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.livepill .dot{width:.55rem;height:.55rem;border-radius:50%;background:#A6AEBD;flex:0 0 auto}
body[data-state="fresh"] .livepill .dot{background:#8FD98A}
body[data-state="stale"] .livepill{background:var(--orange-bg);color:var(--orange-ink);
  border-color:transparent}
body[data-state="stale"] .livepill .dot{background:var(--orange-ink)}
body[data-state="dark"] .livepill{background:var(--red-bg);color:var(--red-ink);
  border-color:transparent}
body[data-state="dark"] .livepill .dot{background:var(--red-ink)}
.pagasa{color:#fff;font-size:.78rem;font-weight:600;text-decoration:none;white-space:nowrap;
  border:1px solid rgba(255,255,255,.45);border-radius:var(--r-pill);padding:.3rem .7rem}
.pagasa:hover{background:rgba(255,255,255,.12)}
.helpbtn{font:600 .78rem var(--ui);color:#C8CEE6;background-color:transparent;cursor:pointer;
  -webkit-appearance:none;appearance:none;border:0;padding:.3rem .2rem;white-space:nowrap}
.helpbtn:hover{color:#fff;text-decoration:underline}
.lgshort{display:none}
/* THE NAVY STAYS. Softening this to the page's quiet surface was tried and
   backed out: how much the "not a warning authority" line stands out is a
   legal decision about an LGU's liability, not a styling one, and a refresh
   brief that says "smoothen the look" is not authority to make a standing
   disclaimer quieter. */
.claim{margin:0;flex:0 0 auto;background:var(--navy);color:#EDEFF7;font-size:.72rem;
  padding:.3rem .9rem;text-align:center}

/* ---------- the three columns ---------- */
.shell{flex:1 1 auto;display:flex;min-height:0}

/* ---------- rail ---------- */
.rail{flex:0 0 auto;width:74px;background:var(--navy);display:flex;flex-direction:column;
  padding:.5rem 0;overflow-y:auto;overflow-x:hidden}
.railcap{margin:.2rem 0 .35rem;text-align:center;font-size:.55rem;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--rail-cap)}
/* THE FADE THAT MAKES THE LOOP WATCHABLE.
   Two satellite tile layers live in this pane at once; the incoming frame is
   loaded at opacity 0 and only crossed over once every tile in view has
   arrived. Leaflet sets opacity on each layer's own container, so a transition
   here is the whole cross-fade — 260ms, which must match SAT_FADE_MS in
   site.js, where the outgoing layer is removed after it. */
/* ---------- Leaflet's own controls ----------
 *
 * These were never styled, and they are the largest remaining tell that this
 * is a map library sitting inside a page rather than part of the page: a
 * chunky translucent-black border, 26px tap targets and a hard-coded white
 * background.
 *
 * THE WHITE IS A BUG, NOT A PREFERENCE. Leaflet's stylesheet sets
 * `background:#fff` and `color:black` on the zoom buttons outright, so on a
 * dark page the map carried two white squares with black glyphs — the same
 * class of fault as the search box and the track button before them, and for
 * the same reason: the stylesheet is not the whole story when someone else's
 * CSS is also on the page.
 *
 * SPECIFICITY, NOT `!important`. Our sheet is linked BEFORE Leaflet's so that
 * a slow CDN cannot leave the page unstyled, which means Leaflet wins every
 * tie. Prefixing with `.map` — the container these controls live in — clears
 * the tie honestly and leaves the rules overridable later.
 */
.map .leaflet-bar{border:0;border-radius:var(--r);box-shadow:var(--e2);overflow:hidden}
.map .leaflet-bar a{width:30px;height:30px;line-height:30px;background:var(--bg);
  color:var(--ink);border-bottom:1px solid var(--line);
  transition:background-color var(--t),color var(--t)}
.map .leaflet-bar a:last-child{border-bottom:0}
.map .leaflet-bar a:hover{background:var(--panel);color:var(--ink)}
.map .leaflet-bar a.leaflet-disabled{background:var(--panel);color:var(--ink-soft)}
/* The container's own backdrop, seen wherever tiles have not arrived — during
   the first paint, at the edges of a pan, and for the whole session if the
   tile host is blocked. Leaflet's default is a light grey that reads as a
   pale hole punched in a dark page.
   TWO CLASSES, because `.leaflet-container` is also one class and its sheet
   loads after ours — a single `.map` tied on specificity and lost on order.
   The computed value stayed rgb(221,221,221) and only measuring showed it. */
.map.leaflet-container{background:var(--panel)}
/* The scale bar is drawn as a bordered box with a hard-coded white fill and a
   black rule, so in dark mode it was a white tab stuck to the corner. */
.map .leaflet-control-scale-line{background:var(--bg);color:var(--ink);
  border-color:var(--line);border-radius:var(--r-xs);padding:.05rem .35rem;
  font-size:.65rem;line-height:1.5}
/* THREE CLASSES. Leaflet scopes this one as `.leaflet-container
   .leaflet-control-attribution`, so a two-class selector only TIED and lost on
   order — the bar stayed rgba(255,255,255,.8) and sat on a dark map as a white
   strip. Each of these overrides had to be measured rather than assumed: the
   zoom buttons needed two classes, this needs three. */
.map.leaflet-container .leaflet-control-attribution{background:var(--bg);
  color:var(--ink-soft);border-radius:var(--r-sm) 0 0 0;padding:.15rem .45rem;
  font-size:.65rem}
.map.leaflet-container .leaflet-control-attribution a{color:var(--link)}
/* Leaflet draws its own popup chrome in white with a white tip. Both need
   telling, and the tip is a rotated square rather than a border, so it is a
   separate element with its own background. */
.map .leaflet-popup-content-wrapper,.map .leaflet-popup-tip{background:var(--bg);
  color:var(--ink);box-shadow:var(--e3)}
.map .leaflet-popup-content-wrapper{border-radius:var(--r-lg)}

.leaflet-satellite-pane .leaflet-layer{transition:opacity 260ms linear}
/* A reader who prefers less motion gets the frame swap without the fade. The
   loop still works; it simply cuts. */
@media (prefers-reduced-motion:reduce){
  .leaflet-satellite-pane .leaflet-layer{transition:none}
}

.railcap2{margin-top:.5rem;padding-top:.6rem;border-top:1px solid var(--rail-line)}

/* A COMING-SOON ENTRY HAS TO LOOK UNAVAILABLE AND NOT BROKEN.
   Dimming alone says "you cannot have this" without saying why, and a reader
   assumes a fault or a permission they lack. The word carries the reason; the
   dimming only stops it being mistaken for a working control. It is a real
   `disabled` button, so pointer, keyboard and screen reader all agree. */
.railbtn.soon{opacity:.45;cursor:default}
.railbtn.soon:hover{background-color:transparent}
.soontag{display:block;font-size:.5rem;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--rail-cap);margin-top:1px}
.metrics,.railadd,.railrisk,.railfoot{display:flex;flex-direction:column;gap:2px;padding:0 6px}
.railfoot{margin-top:auto;padding-top:.5rem}
/* One control, whether it is a metric tab or a layer group. They look the same
   because they do the same thing to the map. */
.metrics button,.railbtn{display:flex;flex-direction:column;align-items:center;gap:3px;
  font:600 .63rem var(--ui);color:var(--rail-ink);background-color:transparent;cursor:pointer;
  -webkit-appearance:none;appearance:none;border:0;border-radius:var(--r);padding:.5rem .1rem;
  position:relative;text-align:center;line-height:1.15;
  transition:background-color var(--t),color var(--t)}
.metrics button:hover,.railbtn:hover{background-color:rgba(255,255,255,.12);color:#fff}
.metrics button[aria-selected="true"],.railbtn[aria-pressed="true"]{
  background-color:var(--bg);color:var(--navy)}
/* THE SELECTED TAB GETS A BAR AS WELL AS A FILL.
   The fill alone is the whole affordance, and on the rail it is a light shape
   on a dark ground — which is also what `:hover` produces, so a reader
   arriving with a mouse cannot tell the tab they are pointing at from the one
   that is on. The bar belongs to the selected state only. */
.metrics button[aria-selected="true"]::after,.railbtn[aria-pressed="true"]::after{
  content:"";position:absolute;left:0;top:9px;bottom:9px;width:3px;
  border-radius:0 var(--r-pill) var(--r-pill) 0;background:var(--gold)}
.railbtn .rdot{position:absolute;top:5px;right:9px;width:7px;height:7px;border-radius:50%;
  background:#8878da}

/* ---------- flyout ---------- */
.flyout{flex:0 0 auto;width:308px;border-right:1px solid var(--line);background:var(--bg);
  display:flex;flex-direction:column;min-height:0}
.flyhead{flex:0 0 auto;display:flex;align-items:center;gap:.5rem;padding:.7rem .9rem;
  border-bottom:1px solid var(--line)}
.flyhead h2{margin:0;font-size:.95rem;flex:1 1 auto}
.flyclose{background-color:transparent;-webkit-appearance:none;appearance:none;border:0;
  color:var(--ink-soft);cursor:pointer;padding:.2rem;display:flex;border-radius:var(--r-sm)}
.flyclose:hover{background-color:var(--panel);color:var(--ink)}
.flybody{flex:1 1 auto;overflow-y:auto;padding:.7rem .9rem 1.2rem}

/* ---------- stage ---------- */
.stage{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;min-height:0}
.mapwrap{flex:1 1 auto;position:relative;min-height:0}
.map{position:absolute;inset:0;height:100%;width:100%}
/* Search floats on the map rather than sitting above a table somewhere else:
   finding your barangay is a map question. */
.mapsearch{position:absolute;top:12px;left:12px;z-index:500;display:flex;align-items:center;
  gap:.5rem;background:var(--bg);border:1px solid var(--line);border-radius:var(--r-pill);
  padding:.45rem .9rem;box-shadow:var(--e2);color:var(--ink-soft);max-width:min(20rem,60%);
  transition:box-shadow var(--t),border-color var(--t)}
/* The ring goes on the PILL, not on the input inside it. The input has no
   border of its own — it is a bare field inside a rounded container — so a
   ring drawn on the input appeared as a rectangle floating inside a pill. */
.mapsearch:focus-within{border-color:var(--focus);outline:2px solid var(--focus);
  outline-offset:2px}
.mapsearch input{font:inherit;font-size:.85rem;color:var(--ink);background:transparent;border:0;
  min-width:0;width:9.5rem}
/* Deliberate: the ring is drawn by :focus-within on the pill above, so the
   input's own outline would be a second ring inside the first. */
.mapsearch input:focus-visible{outline:none}
.mapsearch .count{font-size:.7rem;color:var(--ink-soft);white-space:nowrap}
.legend{position:absolute;left:12px;bottom:12px;z-index:500;width:14.5rem;max-height:52%;
  overflow-y:auto;background:var(--bg);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:.7rem .85rem;box-shadow:var(--e3)}
/* Under the search pill, not over Leaflet's attribution — which is where it
   landed first, and the two overlapped into one unreadable line. */
.mapnote{position:absolute;left:12px;top:58px;z-index:500;margin:0;max-width:min(22rem,52%);
  font-size:.72rem;color:var(--ink-soft);background:var(--bg);border-radius:var(--r-sm);
  padding:.25rem .5rem;box-shadow:var(--shadow)}
.mapnote:empty{display:none}
/* The legend's provenance, folded. */
.prov{margin:.5rem 0 0;font-size:.72rem}
.prov summary{cursor:pointer;color:var(--link);font-weight:600;list-style:none}
.prov summary::-webkit-details-marker{display:none}
.prov summary::before{content:"\002B";margin-right:.35rem;font-weight:700}
.prov[open] summary::before{content:"\2212"}
.prov .src{margin:.4rem 0 0}

/* ---------- the table, as a view ---------- */
.tableview{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;background:var(--bg)}
.filterbar{flex:0 0 auto;display:flex;align-items:baseline;gap:.8rem;padding:.85rem 1.1rem;
  border-bottom:1px solid var(--line)}
.tableview .tw{flex:1 1 auto;overflow:auto;padding:0 .9rem 1rem}

/* ---------- dock ---------- */
.dock{flex:0 0 auto;width:360px;border-left:1px solid var(--line);background:var(--bg);
  display:flex;flex-direction:column;min-height:0}
/*
 * THE ANSWER, IN WORDS, BEFORE ANY NUMBER.
 *
 * The band colour is on the whole card rather than a chip, because this is the
 * one place on the page where the colour IS the message.
 */
.headline{flex:0 0 auto;padding:1.05rem 1.15rem;border-bottom:1px solid var(--line);
  background:var(--panel)}
.headline[data-band="green"]{background:var(--green-bg)}
.headline[data-band="orange"]{background:var(--orange-bg)}
.headline[data-band="red"]{background:var(--red-bg)}
/*
 * THE CYCLONE HEADLINE IS VIOLET, NOT ORANGE.
 *
 * A chance that a storm passes within 300 km is not a PAGASA band, and this
 * page spends green/orange/red on nothing else. The first build painted "2
 * storms could pass near here" on the orange panel, which is the page issuing
 * a warning level it has no authority to issue — the exact mistake the storm
 * ramp exists to avoid.
 */
.headline[data-band="cyclone"]{background:var(--cyc-bg)}
.headline[data-band="cyclone"] .hltitle{color:var(--cyc-ink)}
.hltitle{display:flex;align-items:center;gap:.55rem;font-size:1.12rem;font-weight:700;
  letter-spacing:.01em;margin:0}
.hltitle .dot{width:.65rem;height:.65rem;border-radius:50%;background:currentColor;flex:0 0 auto}
.headline[data-band="green"] .hltitle{color:var(--green-ink)}
.headline[data-band="orange"] .hltitle{color:var(--orange-ink)}
.headline[data-band="red"] .hltitle{color:var(--red-ink)}
.headline[data-band="unknown"] .hltitle{color:var(--unknown-ink)}
.hlsay{margin:.45rem 0 0;font-size:.88rem;line-height:1.45}
.hlwhen{margin:.5rem 0 0;font-size:.72rem;color:var(--ink-soft);display:flex;gap:.4rem;
  align-items:center}

.docklist{flex:1 1 auto;overflow-y:auto;padding:.6rem .7rem 1.1rem}
.dockcap{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem;
  padding:.4rem .45rem .35rem;font-size:.68rem;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-soft)}
.brow{display:flex;align-items:center;gap:.65rem;width:100%;text-align:left;padding:.55rem .45rem;
  border:0;border-radius:var(--r);background-color:transparent;cursor:pointer;font:inherit;
  color:inherit;-webkit-appearance:none;appearance:none}
.brow{transition:background-color var(--t),box-shadow var(--t)}
.brow:hover{background-color:var(--panel)}
.brow[aria-current="true"]{background-color:var(--panel);box-shadow:inset 0 0 0 1px var(--line)}
.brow .bname{flex:1 1 auto;min-width:0;font-size:.9rem;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.brow .bsub{display:block;font-size:.7rem;font-weight:400;color:var(--ink-soft);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.brow .bval{font:600 .95rem var(--mono);white-space:nowrap}
.brow .bval em{font-style:normal;font-size:.68rem;color:var(--ink-soft)}

.detail{flex:1 1 auto;overflow-y:auto;padding:0 0 1rem;min-height:0}
.dockfoot{flex:0 0 auto;display:flex;align-items:center;gap:.55rem;width:100%;text-align:left;
  padding:.7rem .9rem;border:0;border-top:1px solid var(--line);background-color:transparent;
  color:var(--ink);font:600 .8rem var(--ui);cursor:pointer;
  -webkit-appearance:none;appearance:none}
.dockfoot:hover{background-color:var(--panel)}
.dockfoot span{flex:1 1 auto}

/* ---------- the long disclaimer, on request ---------- */
.helpwrap{position:fixed;inset:0;z-index:900;background:var(--scrim);display:flex;
  align-items:flex-start;justify-content:center;padding:3vh 1rem;overflow-y:auto}
.helpwrap .foot{background:var(--bg);border:1px solid var(--line);border-radius:var(--r-lg);
  max-width:46rem;width:100%;padding:1.1rem 1.3rem 1.4rem;box-shadow:var(--shadow)}
.foothead{display:flex;align-items:center;gap:.8rem}
.foothead h2{flex:1 1 auto;margin:0}

/* ═══════════════════════════════════════════════════ small screens
 *
 * Where most people will actually read this, in bad weather, one-handed. The
 * rail becomes a tab bar at the bottom — a thumb reaches the bottom of a phone
 * and not the top — the dock becomes a sheet over the map, and the flyout
 * covers the screen while it is open.
 */
@media (max-width:820px){
  /* The masthead has to hold a name, a state and two links in 390px. The long
     forms of the two links are swapped for short ones, the live pill drops to
     its dot — the dock's headline repeats what it said, in full — and the
     province line goes, because the page is on that municipality's own
     domain and nobody opening it is unsure which one they are looking at. */
  .topbar{gap:.5rem;padding:.45rem .6rem}
  .topbar .seal{width:30px;height:30px}
  .eyebrow{display:none}
  .topbar h1{font-size:.9rem}
  .livepill{padding:.3rem;max-width:none}
  .livepill span:last-child{display:none}
  .lgfull{display:none}
  .claim{font-size:.66rem;padding:.28rem .6rem}

  .shell{flex-direction:column;position:relative}
  .stage{order:1;flex:1 1 auto;min-height:34vh}
  /*
   * THE STRIP READS SENTENCE FIRST, ACTIONS SECOND.
   *
   * In the desktop's source order the actions sit beside a heading; on a phone
   * that heading is hidden and the two buttons and the bulletin link took the
   * whole first line, pushing the sentence — the reason the strip exists — off
   * the bottom of a clipped box.
   */
  /*
   * THE METER MUST NOT EAT THE MAP.
   *
   * Found by measuring the phone layout, not by reading it: the stage on a
   * 390px screen is about 320px tall, the warning strip took 185 of them, and
   * the map was left FIVE PIXELS high — with the legend, the search pill and
   * the storm strip drawn on top of one another because the region they sit
   * in had collapsed. The strip is worth having on a phone; it is not worth
   * the map.
   *
   * So the map keeps a floor it cannot be squeezed below, the strip is made
   * compact, and the stage scrolls if the two together still do not fit.
   * "The page does not scroll, the regions do" is the rule, and the stage is
   * a region.
   */
  .stage{overflow-y:auto}
  /* 150, measured rather than picked. On a 390x844 phone the stage gets about
     320px; the storm strip with a live system in it is about 155. A map floor
     of 150 leaves both fully visible at rest, with the warning strip below the
     fold where the stage scrolls to it. A taller floor pushed the legend —
     which is pinned to the map's own bottom edge — through the boundary of
     the scrolling region and cut it in half. */
  .mapwrap{flex:0 0 auto;min-height:150px}

  .storms{display:flex;flex-direction:column}
  .cycsum{order:1;margin:0}
  .cychead{order:2;margin:.4rem 0 0}
  .secbody{order:3}
  .dock{order:2;width:auto;border-left:0;border-top:1px solid var(--line);
    max-height:44vh;flex:0 0 auto;overflow-y:auto}
  .docklist{max-height:none;overflow:visible}
  .detail{max-height:none;overflow:visible}

  /* The rail becomes a tab bar. A thumb reaches the bottom of a phone and not
     the top, and 44px is a floor here rather than a guideline. It scrolls
     sideways rather than shrinking the labels into illegibility. */
  .rail{order:3;width:auto;flex-direction:row;padding:0;border-top:1px solid var(--line);
    overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
  .railcap{display:none}
  .metrics,.railadd,.railrisk,.railfoot{flex-direction:row;padding:0;flex:0 0 auto;gap:0}
  /* ELEVEN ENTRIES DO NOT FIT ACROSS A PHONE, SO THE BAR SCROLLS — AND A BAR
     THAT SCROLLS WITH NOTHING SAYING SO IS A BAR WHOSE RIGHT-HAND HALF DOES
     NOT EXIST. Three named groups made this worse: it was eight buttons and
     already overflowed a 390px screen.

     Table is pinned to the right rather than left to be scrolled to. It is the
     fallback when the map cannot load — the one view that still works when
     everything else on this page has failed — so it is the last entry that
     should ever be out of reach. Its shadow falls across whatever is passing
     underneath, which is what tells a reader the row continues. */
  .railfoot{position:sticky;right:0;background:var(--navy);z-index:2;
    box-shadow:-12px 0 14px -8px rgba(0,0,0,.5)}
  .railfoot{margin-top:0;padding-top:0}
  .metrics button,.railbtn{min-width:60px;min-height:52px;border-radius:0;padding:.4rem .25rem;
    font-size:.6rem}

  .flyout{position:absolute;inset:0;z-index:800;width:auto;border-right:0}
  .legend{max-height:34%;width:12.5rem}
  .mapsearch{max-width:none;right:64px}
  .mapsearch input{width:auto;flex:1 1 auto}
  .mapsearch .count{display:none}
  .mapnote{max-width:none;right:64px}
  .tiles{gap:.3rem}
  /* The short labels come back — without this the masthead loses both links. */
  .lgshort{display:inline}

  /* The storm strip on a phone is the sentence and the two actions. Its
     heading is the one thing it can spare: the sentence names the storm. */
  .cychead h2{display:none}
  .cycacts{gap:.35rem}
  .cycfit,a.cyclink,.sectoggle{font-size:.66rem;padding:.18rem .4rem}
  .cycacts{flex-wrap:wrap}
  .cycsum{font-size:.82rem}

  /* The legend becomes a strip along the bottom of the map. As a card it
     covered half a 390px map — which is the map it was explaining. */
  /* Clear of Leaflet's own bottom row. At 8px the legend sat on top of the
     attribution and the scale bar, and three unrelated things overlapped into
     one unreadable line along the bottom edge of a 210px map. */
  .legend{left:8px;right:8px;bottom:26px;width:auto;max-height:none;display:flex;
    align-items:center;gap:.7rem;overflow-x:auto;padding:.4rem .55rem}
  .legend h3,.legend .prov{display:none}
  .legend .key{padding:0;white-space:nowrap;font-size:.72rem}
  .legend .sw{width:.8rem;height:.8rem}
}

/* ---------- tropical cyclones ----------
 *
 * The violet is the storm ramp and appears nowhere else on this page, for the
 * reason recorded in Ews\Cyclone: green→orange→red is live weather and the
 * DHSUD ramp is susceptibility, and a storm drawn in either would read as a
 * third warning level issued by this page. It is not one. PAGASA issues those.
 *
 * The strip is quiet when there is nothing to say and loud only when there is
 * — one border colour changes, not a red banner. A page that shouts on an
 * ordinary Tuesday is a page nobody reads on the day it matters.
 */
.storms{flex:0 0 auto;max-height:38%;overflow-y:auto;border:0;border-bottom:1px solid var(--line);
  border-left:4px solid transparent;padding:.6rem .9rem;margin:0;background:var(--panel)}
.storms[data-cyc="active"]{border-color:#6353c4;border-left-width:4px}
.storms[data-cyc="unavailable"]{border-color:#B85A10;border-left-width:4px}
/* ALIGNED ON THE CONTROLS, NOT ON THE TEXT BASELINE.
   `align-items:baseline` lines up the heading's baseline with the TEXT INSIDE
   each button, which puts the buttons themselves a few pixels low and at
   three different heights — a heading, a bordered button, a bare link and
   another button, none of them agreeing. The row now centres its items and
   the controls share one height, which is most of what separates a toolbar
   from four things that happen to be on the same line. */
.cychead{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  flex-wrap:wrap;margin:0 0 .45rem}
.cychead h2{margin:0;font-size:1.05rem;letter-spacing:-.012em}
/* The controls at the right of that row, as one group with one rhythm. */
.cychead>span,.cychead>div{display:flex;align-items:center;gap:.45rem;flex-wrap:wrap}
.cychead .cycfit,.cychead .sectoggle,.cychead a.cyclink{
  display:inline-flex;align-items:center;min-height:1.85rem}
/* The bulletin link sits between two buttons and was the only bare underline
   in the row. It keeps its underline on hover — it is a link and should say
   so — but at rest it carries the same padding and height as its neighbours
   so the row reads as one strip. */
.cychead a.cyclink{padding:.25rem .5rem;border-radius:var(--r-sm);
  text-decoration:none;transition:background-color var(--t)}
.cychead a.cyclink:hover{background-color:var(--line);text-decoration:underline}
.cycnote{margin:.2rem 0 .6rem;font-size:.85rem;color:var(--ink-soft)}
/* The collapsed strip's one line: the storm, the chance, the time. */
.cycsum{margin:.15rem 0 0;font-size:.88rem;line-height:1.4}
/* A DIFFERENT CLASS FROM THE MASTHEAD'S `.pagasa`, and the reason is a bug this
   file already shipped once. That class is styled `color:#fff` for the dark
   header; reusing the name here put white text on a near-white panel, and the
   most important link on the page — the one to the actual bulletin during a
   storm — rendered invisible. Caught by looking at the page, not by any
   assertion, which is the whole argument for looking at the page. */
a.cyclink{font-size:.82rem;font-weight:600;white-space:nowrap;color:var(--navy)}
/* The date beside each daily track position. A white plate travels with the
   label because it crosses sea, land and whatever choropleth is switched on —
   the same reason the category glyphs carry their own. */
.cyclabel span{display:inline-block;white-space:nowrap;font:600 .68rem var(--ui);
  color:#271c60;background:rgba(255,255,255,.92);border:1px solid #d7d2ee;border-radius:var(--r-sm);
  padding:.05rem .3rem;box-shadow:0 1px 2px rgba(20,28,82,.18)}
/* background-color, not `background:none`. The shorthand clears the image and
   leaves the user-agent's ButtonFace behind, which is near-white — so in dark
   mode this rendered as a white rectangle with white text in it and the
   control disappeared. Another one only the rendered page showed. */
.cycfit{font-size:.78rem;font-weight:600;cursor:pointer;background-color:transparent;
  -webkit-appearance:none;appearance:none;
  border:1px solid var(--line);border-radius:var(--r-sm);padding:.25rem .55rem;color:var(--ink);
  white-space:nowrap;font-family:inherit}
/* --line, not an invented token. `var(--sunk,#EDEFF5)` was copied from the
   assessment site's stylesheet, where --sunk exists; it does not exist here,
   so the fallback applied and hovering this button in dark mode painted it
   light grey under white text. A fallback made the mistake silent, which is
   what fallbacks do. */
.cycfit:hover{background-color:var(--line)}
.cycacts{display:flex;gap:.4rem;align-items:center;flex-wrap:wrap}

/* ---------- folding a section away ----------
   The heading and its button always stay; only .secbody goes. A collapsed
   section that also loses its title reads as one that failed to render, and
   the control that brings it back would go with it. */
.sectoggle{font-size:.72rem;font-weight:700;letter-spacing:.02em;cursor:pointer;
  background-color:transparent;-webkit-appearance:none;appearance:none;
  border:1px solid var(--line);border-radius:var(--r-sm);padding:.2rem .5rem;
  color:var(--ink-soft);font-family:inherit;white-space:nowrap}
.sectoggle:hover{background-color:var(--line);color:var(--ink)}

/* ---------- the quiet button ----------
 *
 * `.cycfit`, `.sectoggle` and the wind controls were three rules describing
 * the same control with slightly different padding and no shared behaviour —
 * transparent, hard-bordered rectangles that read as a wireframe of a button
 * rather than a button. They keep their own sizes; everything that makes them
 * feel like controls is stated once here.
 *
 * A RESTING SURFACE, not transparency. A control with no fill sitting on the
 * same white as the text beside it has nothing but its border to say it can be
 * pressed, which is why these read as boxes drawn around words.
 *
 * The press moves ONE PIXEL. Enough to feel a mouse-down land, small enough
 * that a row of them does not lurch — and it is a transform, so it costs no
 * layout and cannot nudge anything around it.
 */
.cycfit,.sectoggle,.windctl button{background-color:var(--panel);
  transition:background-color var(--t),border-color var(--t),transform var(--t),
    box-shadow var(--t)}
.cycfit:hover,.sectoggle:hover,.windctl button:hover{
  background-color:var(--bg);border-color:var(--ink-soft);color:var(--ink);
  box-shadow:var(--e1)}
.cycfit:active,.sectoggle:active,.windctl button:active{
  transform:translateY(1px);box-shadow:none}
.sectitle{margin:0;font-size:.95rem;white-space:nowrap}
.dhead{display:flex;align-items:baseline;justify-content:space-between;gap:.8rem}

/* One row per storm, inside the cyclone layer group. */
/* The probability cell in the cyclone table carries its own swatch, so the
   column reads without going back to the legend for every row. */
.probcell{display:inline-flex;align-items:center;gap:.4rem;justify-content:flex-end}
.probsw{width:11px;height:11px;border-radius:var(--r-xs);flex:0 0 auto;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.18)}

/* ---------- the wind field ----------
 *
 * The bar is the neutral speed ramp; the only COLOUR on it is the two warning
 * thresholds, drawn where they actually fall. That is the whole design in one
 * component: darkness carries speed, colour carries a PAGASA band, and the two
 * cannot be confused because they are different channels on the same bar.
 */
.windbar{position:relative;margin:.5rem 0 .1rem;height:10px}
.windgrad{height:10px;border-radius:var(--r-sm)}
.windmark{position:absolute;top:-3px;transform:translateX(-50%);pointer-events:none}
.windmark i{display:block;width:2px;height:16px;margin:0 auto;border-radius:1px;
  box-shadow:0 0 0 1px rgba(255,255,255,.75)}
.windmark em{position:absolute;top:17px;left:50%;transform:translateX(-50%);
  font-style:normal;font-size:.62rem;font-weight:700;white-space:nowrap}
.windmark.b-orange i{background:var(--orange-ink)}
.windmark.b-orange em{color:var(--orange-ink)}
.windmark.b-red i{background:var(--red-ink)}
.windmark.b-red em{color:var(--red-ink)}
.windscale{display:flex;justify-content:space-between;font-size:.65rem;
  color:var(--ink-soft);margin-top:1.1rem}

.windctl{display:flex;align-items:center;gap:.3rem;margin:.4rem 0 .2rem}
.windctl button{font-size:.72rem;font-weight:700;cursor:pointer;font-family:inherit;
  background-color:transparent;-webkit-appearance:none;appearance:none;
  border:1px solid var(--line);border-radius:var(--r-sm);padding:.15rem .45rem;color:var(--ink)}
.windctl button:hover{background-color:var(--line)}
.windrate{font-size:.72rem;color:var(--ink-soft);min-width:2.2rem;text-align:center;
  font-variant-numeric:tabular-nums}

/* The canvas is positioned by Leaflet on every pan; it must never take a
   pointer event or the whole map stops being clickable through it. */
.windcanvas{position:absolute;top:0;left:0;pointer-events:none}

.stormrows{margin:.1rem 0 .3rem}
.stormrows .lrow{gap:.35rem}

/* The systems the map deliberately leaves off. Set apart with a rule above it
   rather than a box: it is a footnote to the cards, not a fourth card. The
   opt-in sits at the end of the sentence it belongs to, so the reader has read
   why before they can act on it. */
.cycoff{border-top:1px solid var(--line);padding-top:.5rem;margin:.6rem 0 .4rem}
.cycall{display:inline-flex;align-items:center;gap:.3rem;white-space:nowrap;
  margin-left:.35rem;cursor:pointer}
.cycall input{margin:0}
.cyccards{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:.7rem}
.cyccard{border:1px solid var(--line);border-radius:var(--r-sm);padding:.6rem .7rem;
  background:var(--bg)}
.cyccard h3{margin:0 0 .1rem;font-size:.95rem;display:flex;align-items:center;gap:.4rem}
.cyccat{margin:0 0 .4rem;font-size:.8rem;color:var(--ink-soft)}
/* An 8px minimum so the swatch is a mark, not a speck; a ring so it holds its
   edge against both the card and a dark surface. */
/* THE CATEGORY GLYPH, wherever it appears — map marker, legend key, card
   heading, barangay approach row. One shape per category, so the mark on the
   map and the mark in the key are the same object rather than two things a
   reader has to match by colour.

   Each glyph draws its own white disc inside the SVG rather than taking one
   from CSS. These marks go over sea, land, and whichever choropleth is
   switched on, and the plate has to travel with the mark — a background
   applied here would not reach the Leaflet markers. It also means dark mode
   needs no special case: the plate is part of the drawing. */
.cycglyph{display:inline-block;width:22px;height:22px;flex:0 0 auto;vertical-align:-.35em}
.cycglyph.sm{width:17px;height:17px;vertical-align:-.25em}
.cycglyph svg{display:block}

/* Leaflet gives a divIcon a white box and a border by default, which would
   put a square behind every round glyph. */
/* The DIV is the hit target, not the drawing inside it. Leaflet binds the
   tooltip to the marker element; letting the SVG take the pointer makes the
   hit area the painted pixels rather than the marker's box, so the corners of
   an 18px mark stop responding. */
.cycmark{background:none;border:0}
.cycmark svg{pointer-events:none}
.cyclabel{background:none;border:0;pointer-events:none;white-space:nowrap}
.cyclabel span{font-size:.72rem;font-weight:700;color:#241a55;
  background:rgba(255,255,255,.82);border-radius:var(--r-xs);padding:1px 5px}

/* A meter: one ratio against a fixed limit. The track is the whole ensemble,
   the fill is the part of it that agrees. The VALUE wears text ink and the
   swatch carries identity — a number in the series colour reads as decoration
   and loses contrast the moment the ramp step is light. */
.meter{margin:.35rem 0 .2rem}
.mtop{display:flex;justify-content:space-between;align-items:baseline;gap:.5rem;
  font-size:.8rem;color:var(--ink-soft)}
.mtop strong{color:var(--ink);font-variant-numeric:tabular-nums;font-size:.95rem}
.mtrack{height:8px;border-radius:4px;background:var(--line);overflow:hidden;
  margin:.2rem 0 .15rem}
.mfill{height:100%;background:#6353c4;border-radius:4px}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .storms[data-cyc="active"]{border-color:#9c8ee4}
  :root:not([data-theme="light"]) .mfill{background:#9c8ee4}
}

/* Storm approach, inside the barangay detail panel. Kept visually part of the
   panel rather than boxed again — it is one more thing about this barangay,
   not a separate report. */
.approach{margin:.2rem 0 .9rem;padding:.6rem .7rem;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--bg)}
.approach h4{margin:0 0 .25rem;font-size:.9rem}
.approach.none{border-style:dashed}
.apitem+.apitem{margin-top:.6rem;padding-top:.6rem;border-top:1px solid var(--line)}
.aphead{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.aphead .src{margin:0}

/* The map, the legend and the layer panel are placed by the shell above; what
   is left here is what goes INSIDE them. The boxes those three used to be —
   bordered cards in a sidebar — are gone with the sidebar. */
.layers h3{margin:0 0 .5rem;font-size:.9rem}
.lgroup{border-top:1px solid var(--line);padding:.6rem 0 .2rem}
.lgroup:first-of-type{border-top:0;padding-top:0}
/* The satellite and wind panels hold one group each, and the flyout's header
   already names it — so the group's own heading was the word "Satellite"
   printed twice, one line apart. Its rule and top border go with it. */
.lgroup[data-group="satellite"],.lgroup[data-group="wind"]{border-top:0;padding-top:0}
.lgroup[data-group="satellite"]>strong,.lgroup[data-group="wind"]>strong{display:none}
.lgroup>strong{font-size:.85rem}
/* Checkbox in its own column so the agency name, which wraps onto a second
   line for the longer labels, stays aligned under the label it belongs to
   rather than reading as another item in the list. */
.lrow{display:grid;grid-template-columns:auto 1fr;gap:0 .4rem;align-items:start;
  font-size:.82rem;padding:.2rem 0;cursor:pointer}
.lrow input{margin:.2rem 0 0}
.lrow .src{grid-column:2;display:block;margin:0}
.lfail{color:var(--red-ink)}
.svclegend{margin:.1rem 0 .5rem 1.35rem}
.svclegend .lent{display:flex;align-items:center;gap:.4rem;font-size:.76rem;padding:.1rem 0}
.svclegend .lent img{width:16px;height:16px;object-fit:contain;flex:0 0 auto;
  background:#fff;border:1px solid var(--line);border-radius:var(--r-xs)}
.svclegend .src{margin:0 0 .25rem}
.slide{display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:var(--ink-soft);
  margin-top:.45rem}
.slide input{flex:1;accent-color:var(--navy)}
.ramp{margin:.5rem 0 0}
.ramp .key{padding:.12rem 0;font-size:.78rem;align-items:flex-start}
.ramp .sw{border:1px solid rgba(0,0,0,.2)}
.lkey{margin:0 0 .3rem;font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;
  color:var(--ink-soft)}
.legend h3,.detail h3{margin:0 0 .5rem;font-size:.9rem}
.key{display:flex;align-items:center;gap:.5rem;font-size:.85rem;padding:.18rem 0}
.sw{width:1rem;height:1rem;border-radius:var(--r-xs);flex:0 0 auto;border:1px solid rgba(0,0,0,.15)}
.src{font-size:.72rem;color:var(--ink-soft);margin:.6rem 0 0;line-height:1.4}
.muted{color:var(--ink-soft)}
.dl{margin:0;font-size:.85rem} .dl div{display:flex;justify-content:space-between;gap:.6rem;
  padding:.22rem 0;border-bottom:1px solid var(--line)} .dl div:last-child{border-bottom:0}
.dl dt{color:var(--ink-soft)} .dl dd{margin:0;font-family:var(--mono)}

/* --- the selected barangay, in the dock --- */
.detail{padding:0 1.15rem 1.1rem}
.dhead{position:sticky;top:0;z-index:2;background:var(--bg);padding:.7rem 0 .6rem;
  border-bottom:1px solid var(--line);margin-bottom:.7rem;align-items:center}
.dback{display:flex;align-items:center;gap:.3rem;font:600 .78rem var(--ui);color:var(--link);
  background-color:transparent;-webkit-appearance:none;appearance:none;border:0;cursor:pointer;
  padding:.2rem .3rem .2rem 0;border-radius:var(--r-sm)}
.dback:hover{background-color:var(--panel)}
.dhead h3{margin:0;font-size:1.05rem;flex:1 1 auto}
.dhead .src{margin:.2rem 0 .8rem}
/* Three across the dock, not a wrapping row across a page. At 360px a
   flex-basis of 9rem wrapped them one per line and the panel became a column
   of three big cards nobody could compare. */
.tiles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.4rem;margin:0 0 .9rem}
.tile{display:grid;gap:.15rem;justify-items:start;text-align:left;cursor:pointer;min-width:0;
  border:1px solid var(--line);border-radius:var(--r);padding:.5rem .55rem;background:var(--bg);
  color:var(--ink);font:inherit}
.tile .tlabel{font-size:.62rem;letter-spacing:.06em}
.tile .tval{font-size:1.05rem}
.tile{transition:border-color var(--t),box-shadow var(--t),background-color var(--t)}
.tile:hover{border-color:var(--ink-soft)}
.tile.on{border-color:var(--navy);box-shadow:inset 0 0 0 1px var(--navy)}
.tlabel{font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft)}
.tval{font-family:var(--mono);font-size:1.25rem}
.chart{margin:0;position:relative}
.chart figcaption{font-size:.85rem;margin:0 0 .5rem}
.plot svg{display:block;overflow:visible}
/* One series, so it wears the brand ink rather than a categorical slot: there
   is no second series for it to be told apart from. */
.plot .mark{fill:var(--navy)}
.plot .mark.now{fill:var(--navy-deep)}
.plot .line{fill:none;stroke:var(--navy);stroke-width:2;stroke-linejoin:round;
  stroke-linecap:round}
.plot .dot{fill:var(--navy);stroke:var(--panel);stroke-width:2}
.plot .dot.now{fill:var(--navy-deep)}
.plot .split{stroke:var(--ink-soft);stroke-width:1;opacity:.45}
.plot .axis{stroke:var(--line);stroke-width:1}
.plot .tick{font:500 10px var(--ui);fill:var(--ink-soft)}
.plot .thr{font:600 10px var(--ui);fill:var(--ink-soft)}
.plot .hit{cursor:crosshair}
.tip{position:absolute;top:0;background:var(--navy-deep);color:#fff;font:600 .78rem var(--ui);
  padding:.25rem .5rem;border-radius:var(--r-sm);pointer-events:none;white-space:nowrap}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .plot .mark,
  :root:not([data-theme="light"]) .plot .dot{fill:#9DB2FF}
  :root:not([data-theme="light"]) .plot .mark.now,
  :root:not([data-theme="light"]) .plot .dot.now{fill:#C7D3FF}
  :root:not([data-theme="light"]) .plot .line{stroke:#9DB2FF}
  :root:not([data-theme="light"]) .tip{background:#2C3242}
}

/* The title takes the left, the toggle the right, and the search sits between
   them — so the button lands in the same corner on every foldable section. */
/* The old filter bar sat above a table in the page column; the search box has
   moved onto the map, where finding a barangay is a map question, and the
   table keeps only its heading and its count. */
.count{font-size:.8rem;color:var(--ink-soft)}
.tw{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r)}
table{border-collapse:collapse;width:100%;font-size:.88rem}
th,td{text-align:left;padding:.5rem .7rem;border-bottom:1px solid var(--line);white-space:nowrap}
thead th{background:var(--navy);color:#fff;font-size:.72rem;text-transform:uppercase;
  letter-spacing:.07em;position:sticky;top:0}
tbody tr:hover{background:var(--panel)} tbody tr.on{outline:2px solid var(--gold);outline-offset:-2px}
td.num{font-family:var(--mono)} td.from{white-space:normal;font-size:.78rem;color:var(--ink-soft)}

.pill{display:inline-block;font:600 .72rem var(--ui);text-transform:uppercase;letter-spacing:.06em;
  padding:.15rem .55rem;border-radius:var(--r-pill)}
.b-green{background:var(--green-bg);color:var(--green-ink)}
.b-orange{background:var(--orange-bg);color:var(--orange-ink)}
.b-red{background:var(--red-bg);color:var(--red-ink)}
.b-unknown{background:var(--unknown-bg);color:var(--unknown-ink)}

/* ---------- the warning meter, under the map ----------
 *
 * HEIGHT IS THE ENCODING. The .wbar fill is set inline from WARN_FILL in
 * site.js, and it is what a reader sees first; the band colour agrees with it
 * and never carries it alone. Orange against green is a ΔE of 3.4 under
 * protanopia, so a strip that said "warning" in hue only would say nothing at
 * all to a reader with the commonest form of colour blindness — and these
 * cells are far smaller than the chart's bands.
 *
 * The strip does not scroll. Nine steps across a phone is 34px a cell, which
 * is tight but readable; a horizontally scrolling warning summary would hide
 * the end of the day off-screen, which is where the warnings usually are.
 */
.warnbar{flex:0 0 auto;border-top:1px solid var(--line);background:var(--panel);
  padding:.55rem .8rem .7rem}
.wtop{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem}
.wtitle{font:600 .8rem var(--ui);margin:0 0 .45rem;color:var(--ink)}
.wall{font:600 .72rem var(--ui);color:var(--link);background:none;border:0;
  padding:.15rem .3rem;cursor:pointer;border-radius:var(--r-sm)}
.wall:hover{background:var(--bg)}
.wstrip{display:flex;gap:3px;align-items:flex-end}
.wcell{flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;gap:.15rem;
  background:none;border:0;padding:0 0 .1rem;cursor:pointer;border-radius:var(--r-sm)}
.wcell:hover{background:var(--bg)}
.wcell.on{outline:2px solid var(--navy);outline-offset:1px}
/* The bar is a fixed-height track the fill grows inside, so every cell's
   baseline lines up — a bar chart whose bars start at different heights is
   not a bar chart. */
.wbar{width:100%;height:34px;display:flex;align-items:flex-end;
  border-radius:var(--r-xs);overflow:hidden;background:var(--line)}
.wbar i{display:block;width:100%;border-radius:var(--r-xs) 3px 0 0}
.wcell.b-green .wbar i{background:var(--green-ink)}
.wcell.b-orange .wbar i{background:var(--orange-ink)}
.wcell.b-red .wbar i{background:var(--red-ink)}
.wcell.b-unknown .wbar i{background:var(--unknown-ink)}
.wnum{font:700 .74rem var(--mono);color:var(--ink);line-height:1}
.wtime{font:500 .66rem var(--ui);color:var(--ink);line-height:1.1;text-align:center;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
/* The weekday appears only on the cell where the day turns over, so a strip
   crossing midnight says so once instead of printing the date twelve times —
   which is what pushed every label into an ellipsis. Reserved height even when
   empty, or the cells that carry one would sit lower than the cells that
   do not. */
.wday{font:700 .58rem var(--ui);color:var(--ink-soft);line-height:1.1;min-height:.7rem;
  text-transform:uppercase;letter-spacing:.04em}
/* "Now" is a different claim from a forecast period — the model's analysis of
   this hour, not a prediction of a later one — so it is ruled off rather than
   sitting in the row as the first of nine equivalent boxes. */
.wcell.isnow{border-right:1px solid var(--line);padding-right:3px;margin-right:2px}
.wcell.isnow .wtime{color:var(--ink);font-weight:700}
.wopen{margin-top:.5rem}
.wline{font-size:.8rem;margin:.3rem 0;line-height:1.45}
.wgridwrap{margin-top:.55rem;max-height:216px;overflow-y:auto;border:1px solid var(--line);
  border-radius:var(--r-sm);background:var(--bg)}
/* FIXED LAYOUT, so the step columns divide the remaining width evenly. Left to
   size themselves they took whatever their headers needed and pushed the last
   column — which on a rising day is the worst one — off the right-hand edge
   behind a horizontal scrollbar. They are not the same width as the strip's
   cells above, because the name column has to come from somewhere; they are
   in the same ORDER, which is what a reader follows down from the strip. */
.wgrid{border-collapse:collapse;width:100%;table-layout:fixed;font-size:.7rem}
.wgrid th{position:sticky;top:0;background:var(--panel);text-align:center;font-weight:600;
  padding:.25rem .1rem;border-bottom:1px solid var(--line);
  /* Set, not inherited. The barangay table's own thead is navy with white
     text, and without this the header of this grid was white on white. */
  color:var(--ink)}
.wgrid th:first-child{text-align:left;width:8.5rem;padding-left:.45rem}
.wgt{display:block;line-height:1.1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wgd{display:block;font-size:.56rem;line-height:1.1;min-height:.66rem;color:var(--ink-soft);
  text-transform:uppercase;letter-spacing:.04em}
.wgrid th[scope="row"]{position:static;background:var(--bg);border-bottom:0;color:var(--ink);
  border-right:1px solid var(--line);font-weight:500;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.wgrid td.wg{height:1.15rem;border:1px solid var(--panel)}
@media (max-width:860px){
  .wbar{height:26px}
  .wtime{font-size:.58rem}
}
/* THIRTEEN LABELS DO NOT FIT ON A PHONE, AND SHRINKING THEM IS NOT THE FIX.
   At 390px a cell is about 30px, where "11 AM" renders as "11 ..." — which is
   not a shortened label, it is a deleted one. Every SECOND label is hidden
   instead: the bars all stay, so the shape of the day is intact, and the
   remaining labels anchor the axis at three-hourly intervals.
   `visibility`, not `display`, so the hidden ones still hold their line and
   the cells keep a common baseline. "Now" and any cell where the day turns
   over keep their label whatever position they fall in — those two are the
   ones a reader orients by. */
@media (max-width:620px){
  .wtime{font-size:.54rem}
  .wday{font-size:.52rem}
}
/* Below this even a tight label cannot be fitted thirteen times, so every
   SECOND one is hidden rather than shrunk into an ellipsis. The bars all stay,
   so the shape of the day is intact, and the surviving labels still anchor the
   axis. `visibility`, not `display`, so the hidden ones hold their line and the
   cells keep a common baseline. "Now" and any cell where the day turns over
   keep their label wherever they fall — those are what a reader orients by. */
@media (max-width:430px){
  .wcell:nth-child(even):not(.wkeep):not(.isnow) .wtime{visibility:hidden}
}

.callout{border-left:4px solid var(--gold);background:var(--panel);padding:.7rem .9rem;
  margin:1rem 0;font-size:.9rem}
.snap{margin-top:.6rem}
.snap caption{text-align:left;font-size:.8rem;color:var(--ink-soft);padding-bottom:.3rem}

.foot{background:var(--navy-deep);color:#DDE1EE;margin-top:2rem;padding:1.6rem 1.1rem}
.foot>*{max-width:1180px;margin-left:auto;margin-right:auto}
.foot h2{color:var(--gold);font-size:.75rem;text-transform:uppercase;letter-spacing:.12em}
.foot p,.foot li{font-size:.85rem;line-height:1.6}
.foot a{color:#fff} .fine{color:#9AA3C0;font-size:.75rem}

/* When the feed is dark the numbers are hidden rather than shown as current.
   Grey is the honest colour for "nobody knows"; green would be a lie. */
body[data-state="dark"] .num-live{visibility:hidden}
body[data-state="dark"] .pill{background:var(--unknown-bg);color:var(--unknown-ink)}
/* PRINTING IS THE TABLE. A screenshot of a map is not a document somebody can
   act on in a barangay hall; the grid of every figure for every barangay is. */
/* ═══════════════════════════════════════ small screens, stated LAST
 *
 * SOURCE ORDER, NOT THE BREAKPOINT, DECIDES THESE.
 *
 * A media query adds no specificity. The stacking block up at `max-width:820px`
 * sits ABOVE the cyclone and warning-strip sections, so every `.storms` and
 * `.warnbar` rule written inside it loses to the plain rule further down the
 * file — silently, and for as long as nobody measures. Its `.storms
 * {max-height:46%}` had never once applied; the computed value was the base
 * rule's 38%.
 *
 * These are the same corrections, placed where they win. Anything that has to
 * override a component defined below the stacking block belongs here.
 */
@media (max-width:820px){
  /* THE MAP KEPT FIVE PIXELS. Measured, not read: the stage on a 390px screen
     is about 320px, the warning strip took 185 of them, and the map collapsed
     to five — with the legend, the search pill and the storm strip drawn on
     top of one another. The strip is worth having on a phone; it is not worth
     the map. So the map keeps a floor, the strip goes compact, and the stage
     scrolls if the two still do not fit. */
  .storms{max-height:none;overflow:visible}
  .warnbar{padding:.45rem .7rem .6rem}
  .warnbar .wbar{height:20px}
  .warnbar .wtitle{font-size:.74rem;margin:0 0 .35rem}
  .warnbar .src{font-size:.66rem;margin-top:.4rem}
  .warnbar .wgridwrap{max-height:150px}
}

@media print{
  html,body{height:auto;overflow:visible}
  .topbar,.claim,.rail,.flyout,.dock,.storms,.mapwrap,.helpwrap,.filterbar{display:none!important}
  .app,.shell,.stage{display:block;height:auto}
  .tableview{display:block!important}
  .tableview .tw{overflow:visible;padding:0}
}