/* Bootstrap HazeSG — opt-in Bootstrap 5 theme for haze.sg. Load after Bootstrap (and Leaflet).
   Layout, spacing and most components are plain Bootstrap (utilities, card, input-group, alert).
   This file holds all of haze.sg's own CSS (the page has no <style> block and no style="" attributes):
   button sizing, the seven haze.sg colour classes, bar heights, chart and map details, and, last, the home page's
   layout and selection rules. Colours: two neutrals (light-hazesg #fff, dark-hazesg #1e1e1e) fed into Bootstrap's colour
   modes (data-bs-theme light/dark, set by the page from the device setting), plus the five fixed NEA band colours.
   Leaflet's own colours and Bootstrap's alert/danger colours are left as they are.
   Theme rules need a -hazesg class on the element or an ancestor; the page section uses the page's own ids.

   Components
     .btn.btn-hazesg    buttons and toggle labels; compact (Bootstrap btn-sm) by default, .btn-lg for large;
                        do not add .btn-sm. Selected = solid 3 px purple (#9b5de5) outline
     .zoom-hazesg       input-group of − <output class="input-group-text"> +: steady width for the reading
     .select-hazesg     a card used as a selector (label for a hidden radio): pointer cursor
     .legend-hazesg     the band legend tables (PSI names in blocks of their band colour)
     .panel-hazesg      chart panel: bar colour, hour-label size
     .map-hazesg        Leaflet background and attribution size inside the map frame
     .hr-hazesg         section divider: a solid line in the text colour (#1e1e1e by day, #fff by night), not Bootstrap's 25%
     .bubble-hazesg     map value bubble: one SVG path (box + pointer) filled with the band colour, value as SVG text
     Colours: haze.sg's own colours, one per class (the class colours an element's background;
       text on it is light or dark, whichever is readable):
       light-hazesg #fff, dark-hazesg #1e1e1e, and NEA's bands good-hazesg #479b02, moderate-hazesg #006fa1,
       unhealthy-hazesg #FFCE03, very-unhealthy-hazesg #FFA800, hazardous-hazesg #d60000; pm25-hazesg #595959
       (the grey NEA uses for its 1-hr PM2.5 chart).
     Selection colour: #9b5de5 (medium violet, 4:1 on both the white and the dark page; no NEA band is purple).
     .h-hazesg-0…100    bar height in percent of the chart (1% steps)
     Font sizes: one standard size (16 px) for all text, plus
       .fs-title-hazesg      site title and the two headline figures (28–40 px, follows screen width)
       .fs-health-hazesg     health band under the headline figures (20 px)
       .fs-map-figure-hazesg value in a map bubble (16 px; SVG text does not inherit the page size)
       .fs-graph-x-hazesg    chart x-axis labels (10–12 px, follows the chart container width)
*/

/* Bootstrap colour modes on two neutrals: light-hazesg #fff and dark-hazesg #1e1e1e. Only the variables haze.sg uses
   are set; Bootstrap's components (and btn-hazesg) read them, so light and dark follow data-bs-theme. */
[data-bs-theme=light] {
  --bs-body-bg: #fff;
  --bs-body-color: #1e1e1e;
  --bs-border-color: #1e1e1e;
  --bs-secondary-color: #1e1e1e;
  --bs-tertiary-bg: #fff;
}
[data-bs-theme=dark] {
  --bs-body-bg: #1e1e1e;
  --bs-body-color: #fff;
  --bs-border-color: #fff;
  --bs-secondary-color: #fff;
  --bs-tertiary-bg: #1e1e1e;
}

/* Buttons and toggle labels use compact sizing (Bootstrap's btn-sm values). */
.btn.btn-hazesg {
  --bs-btn-padding-y: .25rem;
  --bs-btn-padding-x: .5rem;
  --bs-btn-font-size: var(--hazesg-fs-base);
  --bs-btn-border-radius: var(--bs-border-radius-sm);
  /* colours: Bootstrap's own page colours (body background, text, border), so light and dark follow Bootstrap */
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-bg: var(--bs-body-bg);
  --bs-btn-border-color: var(--bs-border-color);
  --bs-btn-hover-color: var(--bs-body-color);
  --bs-btn-hover-bg: var(--bs-body-bg);
  --bs-btn-hover-border-color: var(--bs-border-color);
  --bs-btn-active-color: var(--bs-body-color);
  --bs-btn-active-bg: var(--bs-body-bg);
  --bs-btn-active-border-color: var(--bs-body-color);
  --bs-btn-disabled-color: var(--bs-secondary-color);
  --bs-btn-disabled-bg: var(--bs-body-bg);
  --bs-btn-disabled-border-color: var(--bs-border-color);
}
.btn.btn-hazesg.btn-lg {
  --bs-btn-padding-y: .5rem;
  --bs-btn-padding-x: 1rem;
  --bs-btn-font-size: 1.25rem;
  --bs-btn-border-radius: var(--bs-border-radius-lg);
}

/* Zoom reading: fixed width so the buttons do not move as the percentage changes. */
.zoom-hazesg .input-group-text {min-width: 5.5ch; justify-content: center; font-variant-numeric: tabular-nums}


.select-hazesg {cursor: pointer}
.card.select-hazesg {border-color: var(--bs-border-color)}   /* follows light/dark, not the band fill */

/* "Data delayed" inside a filled card uses the card's text colour (red would vanish on a red card). */
.select-hazesg .text-danger {color: inherit !important}


.legend-hazesg td {font-variant-numeric: tabular-nums}   /* range digits line up */

.panel-hazesg .hl {font-variant-numeric: tabular-nums}

.map-hazesg .leaflet-control-attribution {font-size: 10px}

.bubble-hazesg svg {display: block; overflow: visible}
.bubble-hazesg label {background: none}   /* the surface class colours the SVG, not the label box */
.bubble-hazesg .face {fill: var(--hazesg-band); stroke: var(--bs-border-color); stroke-width: 1}   /* 1 px theme border, as cards and buttons */
.bubble-hazesg .ring {display: none; fill: none; stroke: #9b5de5; stroke-width: 5}   /* selection: 3 px purple outside the face edge */
.bubble-hazesg text {fill: var(--hazesg-ink); font-family: var(--bs-body-font-family); font-weight: 700; text-anchor: middle; dominant-baseline: central}

/* The seven colours. Each class colours the background; --hazesg-ink is the readable text on it
   (dark on light, yellow, orange; white on dark, green, blue, red). dark-hazesg (#1e1e1e) is the lightest grey on which
   every band colour still meets 3:1 (Moderate blue 3.01:1). 1-hr PM2.5 uses NEA's grey. */
.light-hazesg {--hazesg-band: #fff; --hazesg-ink: #1e1e1e}
.dark-hazesg {--hazesg-band: #1e1e1e; --hazesg-ink: #fff}
.good-hazesg {--hazesg-band: #479b02; --hazesg-ink: #fff}   /* white on green by choice (3.5:1, large-text level) */
.moderate-hazesg {--hazesg-band: #006fa1; --hazesg-ink: #fff}
.unhealthy-hazesg {--hazesg-band: #FFCE03; --hazesg-ink: #1e1e1e}
.very-unhealthy-hazesg {--hazesg-band: #FFA800; --hazesg-ink: #1e1e1e}
.hazardous-hazesg {--hazesg-band: #d60000; --hazesg-ink: #fff}
.pm25-hazesg {--hazesg-band: #595959; --hazesg-ink: #fff}   /* NEA haze.gov.sg 1-hr PM2.5 chart grey */
:is(.light-hazesg, .dark-hazesg, .good-hazesg, .moderate-hazesg, .unhealthy-hazesg, .very-unhealthy-hazesg, .hazardous-hazesg,
    .pm25-hazesg) {
  background-color: var(--hazesg-band);
  border-color: var(--hazesg-band);
  color: var(--hazesg-ink);
}

/* Bar heights, percent of the chart height (1% steps). */
.h-hazesg-0{height:0}
.h-hazesg-1{height:1%}
.h-hazesg-2{height:2%}
.h-hazesg-3{height:3%}
.h-hazesg-4{height:4%}
.h-hazesg-5{height:5%}
.h-hazesg-6{height:6%}
.h-hazesg-7{height:7%}
.h-hazesg-8{height:8%}
.h-hazesg-9{height:9%}
.h-hazesg-10{height:10%}
.h-hazesg-11{height:11%}
.h-hazesg-12{height:12%}
.h-hazesg-13{height:13%}
.h-hazesg-14{height:14%}
.h-hazesg-15{height:15%}
.h-hazesg-16{height:16%}
.h-hazesg-17{height:17%}
.h-hazesg-18{height:18%}
.h-hazesg-19{height:19%}
.h-hazesg-20{height:20%}
.h-hazesg-21{height:21%}
.h-hazesg-22{height:22%}
.h-hazesg-23{height:23%}
.h-hazesg-24{height:24%}
.h-hazesg-25{height:25%}
.h-hazesg-26{height:26%}
.h-hazesg-27{height:27%}
.h-hazesg-28{height:28%}
.h-hazesg-29{height:29%}
.h-hazesg-30{height:30%}
.h-hazesg-31{height:31%}
.h-hazesg-32{height:32%}
.h-hazesg-33{height:33%}
.h-hazesg-34{height:34%}
.h-hazesg-35{height:35%}
.h-hazesg-36{height:36%}
.h-hazesg-37{height:37%}
.h-hazesg-38{height:38%}
.h-hazesg-39{height:39%}
.h-hazesg-40{height:40%}
.h-hazesg-41{height:41%}
.h-hazesg-42{height:42%}
.h-hazesg-43{height:43%}
.h-hazesg-44{height:44%}
.h-hazesg-45{height:45%}
.h-hazesg-46{height:46%}
.h-hazesg-47{height:47%}
.h-hazesg-48{height:48%}
.h-hazesg-49{height:49%}
.h-hazesg-50{height:50%}
.h-hazesg-51{height:51%}
.h-hazesg-52{height:52%}
.h-hazesg-53{height:53%}
.h-hazesg-54{height:54%}
.h-hazesg-55{height:55%}
.h-hazesg-56{height:56%}
.h-hazesg-57{height:57%}
.h-hazesg-58{height:58%}
.h-hazesg-59{height:59%}
.h-hazesg-60{height:60%}
.h-hazesg-61{height:61%}
.h-hazesg-62{height:62%}
.h-hazesg-63{height:63%}
.h-hazesg-64{height:64%}
.h-hazesg-65{height:65%}
.h-hazesg-66{height:66%}
.h-hazesg-67{height:67%}
.h-hazesg-68{height:68%}
.h-hazesg-69{height:69%}
.h-hazesg-70{height:70%}
.h-hazesg-71{height:71%}
.h-hazesg-72{height:72%}
.h-hazesg-73{height:73%}
.h-hazesg-74{height:74%}
.h-hazesg-75{height:75%}
.h-hazesg-76{height:76%}
.h-hazesg-77{height:77%}
.h-hazesg-78{height:78%}
.h-hazesg-79{height:79%}
.h-hazesg-80{height:80%}
.h-hazesg-81{height:81%}
.h-hazesg-82{height:82%}
.h-hazesg-83{height:83%}
.h-hazesg-84{height:84%}
.h-hazesg-85{height:85%}
.h-hazesg-86{height:86%}
.h-hazesg-87{height:87%}
.h-hazesg-88{height:88%}
.h-hazesg-89{height:89%}
.h-hazesg-90{height:90%}
.h-hazesg-91{height:91%}
.h-hazesg-92{height:92%}
.h-hazesg-93{height:93%}
.h-hazesg-94{height:94%}
.h-hazesg-95{height:95%}
.h-hazesg-96{height:96%}
.h-hazesg-97{height:97%}
.h-hazesg-98{height:98%}
.h-hazesg-99{height:99%}
.h-hazesg-100{height:100%}

/* ---- Page: haze.sg home (v0) — map size, bubble offset, selection state, chart geometry ---- */
#lmap {aspect-ratio: 2 / 1}   /* Singapore is ~2:1 (island 1.93:1, the five region points 1.98:1) */
/* Bubble SVG is 52 x 39 px with the pointer tip at (26, 37): put the tip on the region's point. */
.bub {transform: translate(-26px, -37px)}
/* Selection state. Each button, summary card and the Me button holds its own radio (Bootstrap's btn-check, hidden inside
   it); the page reacts with :has(). Map bubbles are labels for the region radios. Selected button, card or bubble =
   solid 3 px purple (#9b5de5): 1 px border + 2 px inner ring, in both themes. Keyboard focus = a text-colour outline. */
.bub label {display: block}
.bub .k-pm, .split-hazesg:has(#mv-pm:checked) .map .k-psi {display: none}   /* map measure buttons choose the bubbles */
.split-hazesg:has(#mv-pm:checked) .map .k-pm {display: block}
:is(.bar, .rgb) label:has(:checked), label.card:has(:checked) {border-color: #9b5de5; box-shadow: inset 0 0 0 2px #9b5de5}
label:has(> .btn-check:focus-visible) {outline: 2px solid var(--bs-body-color); outline-offset: 2px}
.rgb .btn {flex: 1 1 auto; min-width: 0}   /* region buttons: widths follow the text, spare space shared */
/* A region chosen by its button, its bubble or Me (data-region = nearest region): only its bars, its min · max · latest,
   its bubble outlined (also while its radio has keyboard focus). Bars keep the all-regions scale. */
.pn [class^=mx-]:not(.mx-all) {display: none}
.split-hazesg:has(#rg-north:checked, #rg-me[data-region=north]:checked) :is(.mx-all, .pns .g b:not(.r-north)) {display: none}
.split-hazesg:has(#rg-north:checked, #rg-me[data-region=north]:checked) .mx-north, .split-hazesg:has(#rg-north:checked, #rg-me[data-region=north]:checked, #rg-north:focus-visible) .bub-north .ring {display: inline}
.split-hazesg:has(#rg-north:checked, #rg-me[data-region=north]:checked, #rg-north:focus-visible) .bub-north .face {stroke: #9b5de5}
.split-hazesg:has(#rg-south:checked, #rg-me[data-region=south]:checked) :is(.mx-all, .pns .g b:not(.r-south)) {display: none}
.split-hazesg:has(#rg-south:checked, #rg-me[data-region=south]:checked) .mx-south, .split-hazesg:has(#rg-south:checked, #rg-me[data-region=south]:checked, #rg-south:focus-visible) .bub-south .ring {display: inline}
.split-hazesg:has(#rg-south:checked, #rg-me[data-region=south]:checked, #rg-south:focus-visible) .bub-south .face {stroke: #9b5de5}
.split-hazesg:has(#rg-east:checked, #rg-me[data-region=east]:checked) :is(.mx-all, .pns .g b:not(.r-east)) {display: none}
.split-hazesg:has(#rg-east:checked, #rg-me[data-region=east]:checked) .mx-east, .split-hazesg:has(#rg-east:checked, #rg-me[data-region=east]:checked, #rg-east:focus-visible) .bub-east .ring {display: inline}
.split-hazesg:has(#rg-east:checked, #rg-me[data-region=east]:checked, #rg-east:focus-visible) .bub-east .face {stroke: #9b5de5}
.split-hazesg:has(#rg-west:checked, #rg-me[data-region=west]:checked) :is(.mx-all, .pns .g b:not(.r-west)) {display: none}
.split-hazesg:has(#rg-west:checked, #rg-me[data-region=west]:checked) .mx-west, .split-hazesg:has(#rg-west:checked, #rg-me[data-region=west]:checked, #rg-west:focus-visible) .bub-west .ring {display: inline}
.split-hazesg:has(#rg-west:checked, #rg-me[data-region=west]:checked, #rg-west:focus-visible) .bub-west .face {stroke: #9b5de5}
.split-hazesg:has(#rg-central:checked, #rg-me[data-region=central]:checked) :is(.mx-all, .pns .g b:not(.r-central)) {display: none}
.split-hazesg:has(#rg-central:checked, #rg-me[data-region=central]:checked) .mx-central, .split-hazesg:has(#rg-central:checked, #rg-me[data-region=central]:checked, #rg-central:focus-visible) .bub-central .ring {display: inline}
.split-hazesg:has(#rg-central:checked, #rg-me[data-region=central]:checked, #rg-central:focus-visible) .bub-central .face {stroke: #9b5de5}
/* Charts keep one shape at every width: 12:1 (width:height); each hour is a cell with 0.75% padding each side (1.5% between hours, half at the ends); hour labels 2.2% of the
   chart column (container query units). */
.pns {container-type: inline-size}
.ch {aspect-ratio: 12 / 1; touch-action: pan-y; user-select: none; -webkit-user-select: none}   /* tap and hold reads a bar; vertical swipes scroll */
.pns .g b.pick {background-color: #9b5de5; min-height: 100%}

.g {flex: 1; display: flex; align-items: flex-end; gap: 1px; padding: 0 .75%}   /* hour cell: half-gap each side */
.g b {flex: 1}
.hl span {flex: 1; padding: 0 .75%; text-align: center; overflow: hidden; white-space: nowrap}

/* Past years (below the two panes): one row of 12 square months per year, 31 day-bars each; tap or drag to read a day. */
.past-hazesg .mbars {display: flex; align-items: flex-end; aspect-ratio: 1 / 1; border-bottom: 1px solid #fff}
.past-hazesg .mbars b {flex: 1}
#past-panel {touch-action: pan-y; user-select: none; -webkit-user-select: none}
.past-hazesg .mbars b.pick {background-color: #9b5de5; min-height: 100%}

/* Me: the visitor's position on the map (selection purple) and its accuracy. */
.me-dot-hazesg {fill: #9b5de5; fill-opacity: 1; stroke: #fff; stroke-width: 2}
.me-area-hazesg {fill: #9b5de5; fill-opacity: .12; stroke: #9b5de5; stroke-width: 1}

/* Font sizes: one standard size for all text, set once here; the classes mark the exceptions. */
:root {
  --hazesg-fs-base: 1rem;
  --hazesg-fs-title: clamp(1.75rem, 1.25rem + 2vw, 2.5rem);
  --hazesg-fs-health: 1.25rem;
  --hazesg-fs-map-figure: 16px;
  --hazesg-fs-graph-x: clamp(.625rem, 2.2cqw, .75rem);
}
.fs-title-hazesg {font-size: var(--hazesg-fs-title)}
.fs-title-hazesg small {font-size: var(--hazesg-fs-base)}   /* the unit after a headline figure */
.fs-health-hazesg {font-size: var(--hazesg-fs-health)}
.fs-map-figure-hazesg {font-size: var(--hazesg-fs-map-figure)}
.fs-graph-x-hazesg {font-size: var(--hazesg-fs-graph-x)}
.past-hazesg {container-type: inline-size}   /* cqw for the month labels */
/* Section divider: full strength, the text colour of the theme (dark-hazesg by day, light-hazesg by night). */
.hr-hazesg {color: var(--bs-body-color); opacity: 1}
