@import url('fonts.css');
/* ── Agilysis brand palette ───────────────────────────────────────────────────
   Primary and secondary palettes as published in design_assets/colour palette.pdf.
   Two notes on how they are applied, both deliberate:

   1. Text colours are chosen for contrast, not just for brand fidelity. The brand cyan
      #1DAFC2 is only 2.39:1 on the light panel and cannot carry small text; the primary
      slate #496F81 reaches 4.90:1 and does the work instead. --accent2 is the brand cyan
      darkened to 4.79:1, kept in the cyan family so interactive text still reads as
      brand while passing AA. (For the record, the pre-brand palette was worse on both
      counts: the old --mist was 2.64:1 and the old --accent2 3.14:1.)

   2. The severity, crime and cost colours below are NOT brand colours and are not
      changed. Red/amber/green for fatal/serious/slight is a road safety convention that
      readers bring with them, and the cost gold has to stay separable from the serious
      amber - the brand amber #F49600 sits only 44 RGB units from #E67E22, close enough
      to confuse "serious casualties" with "cost". Brand colour governs the chrome;
      meaning governs the data.
   ─────────────────────────────────────────────────────────────────────────── */
:root{
  /* Primary palette: hue, tint 1, tint 2 */
  --ink:#2E3B48;--ink2:#2A333A;--ink3:#23272B;
  --brand:#1DAFC2;--brand-lt:#37C8D3;--brand-pale:#52D3DD;
  --slate:#496F81;--slate-lt:#5F8493;--slate-pale:#7297A5;
  --blue-pale:#ABD9E0;--blue-pale-2:#CEE7EA;--blue-pale-3:#DFEEEF;
  --grey:#575756;
  /* Secondary palette */
  --amber:#F49600;--amber-lt:#FCB400;
  --teal:#349095;--teal-lt:#5AA3A3;--teal-pale:#83B7B7;
  --teal-wash:#92C6C8;--teal-wash-2:#AAD1D1;--teal-wash-3:#C7DDDD;

  /* App tokens, mapped onto the brand */
  --white:#FFF;
  --mist:#496F81;          /* secondary text on light - 4.90:1 */
  --fog:#C7DDDD;           /* rules and dividers */
  --accent:#1DAFC2;        /* fills, bars, active states, dark-background accents */
  --accent2:#267585;       /* interactive text on light - 4.79:1, derived from --brand */
  --border:rgba(46,59,72,0.13);
  --panel:rgba(242,247,248,0.97);

  /* Functional - see note 2 above. Do not rebrand these. */
  --fatal:#C0392B;--serious:#E67E22;--slight:#27AE60;
  --crime-hi:#8B2FC9;
  --gold:#B7860D;--gold-l:#FEF9E7;

  /* Theme-aware "chrome" tokens - dark is the default and needs no attribute. The
     filter panel, detail panel and ranked-list panel (--panel above) were always a
     near-white tone regardless of theme, sitting beside a dark map/header/legend/
     status bar - light mode is really about that other half: the header, the map's
     tile-darkening filter, the legend, the status bar, tooltips and the detail panel's
     header strip. Severity/crime/cost colours are untouched either way - see note 2. */
  --chrome-bg:#2E3B48;
  --chrome-bg-2:rgba(46,59,72,.94);
  --chrome-text:#FFFFFF;
  --chrome-text-dim:rgba(255,255,255,.5);
  --chrome-text-dim-2:rgba(255,255,255,.35);
  --chrome-border:rgba(255,255,255,.08);
  --chrome-hover:rgba(255,255,255,.06);
  /* Turns OSM's light-styled tiles into a dark map. Light theme just switches this off
     and lets the tiles render as drawn, rather than needing a second tile provider. */
  --tile-filter:invert(1) hue-rotate(180deg) brightness(.95) contrast(.9) saturate(.6);
}
[data-theme="light"]{
  --chrome-bg:#F2F7F8;
  --chrome-bg-2:rgba(242,247,248,.96);
  --chrome-text:#2E3B48;
  --chrome-text-dim:rgba(46,59,72,.55);
  --chrome-text-dim-2:rgba(46,59,72,.4);
  --chrome-border:rgba(46,59,72,.1);
  --chrome-hover:rgba(46,59,72,.06);
  --tile-filter:none;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;overflow:hidden;font-family:'DM Sans',sans-serif;background:var(--chrome-bg);color:var(--ink)}
/* HEADER */
#hdr{position:fixed;top:0;left:0;right:0;z-index:1000;background:var(--chrome-bg);border-bottom:1px solid var(--chrome-border);display:flex;align-items:center;height:52px}
/* No background on the logo tile. Unlike the old Agilysis mark (a filled circular
   badge that read fine on any surface behind it), the Risk Intelligence hexagons are
   transparent outside the mark itself, so the two supplied variants have to be swapped
   with the theme rather than shown as one file everywhere: the dark-hexagon mark reads
   on the light theme's near-white header, the teal-hexagon mark on the dark theme's
   navy one - each would nearly disappear on the other. */
.hlogo{width:52px;height:52px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:none}
.hlogo img{width:38px;height:38px;display:block}
.hlogo .hlogo-light{display:none}
[data-theme="light"] .hlogo .hlogo-dark{display:none}
[data-theme="light"] .hlogo .hlogo-light{display:block}
.htitle{padding:0 16px;border-right:1px solid var(--chrome-border);cursor:pointer;display:flex;align-items:center}
.htitle h1{font-size:13px;font-weight:600;color:var(--chrome-text);letter-spacing:-.2px}
.hstats{display:flex}
.hst{padding:0 13px;border-right:1px solid var(--chrome-border);display:flex;flex-direction:column;justify-content:center}
.hstv{font-size:13px;font-weight:700;color:var(--chrome-text);font-family:'IBM Plex Mono',monospace;white-space:nowrap}
.hstl{font-size:9px;color:var(--chrome-text-dim-2);text-transform:uppercase;letter-spacing:.5px;margin-top:1px}
.hstv.fatal{color:var(--fatal)}.hstv.serious{color:var(--serious)}.hstv.crime{color:var(--accent)}.hstv.gold{color:#F0C040}
.hbc{margin-left:auto;padding-right:14px;display:flex;align-items:center;gap:6px;font-size:11px;font-family:'IBM Plex Mono',monospace;color:var(--chrome-text-dim)}
.hbc button{background:none;border:none;color:var(--accent);cursor:pointer;font-family:inherit;font-size:inherit;text-decoration:underline}
/* Theme toggle, docked at the far right of the header stat tiles. */
#theme-toggle{background:none;border:none;color:var(--chrome-text-dim);cursor:pointer;padding:0 14px;height:100%;display:flex;align-items:center;font-size:15px;border-left:1px solid var(--chrome-border)}
#theme-toggle:hover{color:var(--chrome-text);background:var(--chrome-hover)}
/* LAYOUT */
#app{display:flex;height:100vh;padding-top:52px}
/* PANEL */
#pnl{width:310px;flex-shrink:0;background:var(--panel);display:flex;flex-direction:column;border-right:1px solid var(--border);overflow:hidden;z-index:100;transition:width .18s ease,transform .18s ease}
/* The ranked list used to live here as a second scroll zone (#pnl-results), sized to
   squeeze the filters above it down to a 58% cap whenever it had content - which on
   anything shorter than a tall desktop monitor meant the filter list itself became the
   cramped, barely-scrollable part of the panel. It now lives in #ranked-panel instead
   (see below), so the filters simply get the whole panel, all the time. */
#pnl-filters{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}
/* Panel collapsed on desktop: the map takes the full width and the toggle in the
   header brings it back. */
#pnl.collapsed{width:0;border-right:none}
#pnl.collapsed>*{display:none}
/* Header toggle */
#pnl-toggle{height:52px;padding:0 12px;background:none;border:none;border-right:1px solid var(--chrome-border);color:var(--chrome-text-dim);font-family:'DM Sans',sans-serif;font-size:11px;font-weight:500;cursor:pointer;display:flex;align-items:center;gap:7px;flex-shrink:0}
#pnl-toggle:hover{background:var(--chrome-hover);color:var(--chrome-text)}
#pnl-toggle .pt-icon{width:13px;height:11px;position:relative;flex-shrink:0}
#pnl-toggle .pt-icon::before,#pnl-toggle .pt-icon::after{content:'';position:absolute;left:0;right:0;height:1.5px;background:currentColor;border-radius:1px}
#pnl-toggle .pt-icon::before{top:0;box-shadow:0 4.75px 0 currentColor}
#pnl-toggle .pt-icon::after{bottom:0}
/* Backdrop only exists on the mobile/tablet overlay layout */
#pnl-backdrop{display:none}
.ps{padding:10px 13px;border-bottom:1px solid var(--border)}
.st{font-size:10px;font-weight:600;color:var(--mist);text-transform:uppercase;letter-spacing:.8px;margin-bottom:7px;display:flex;align-items:center;gap:6px}
.st::after{content:'';flex:1;height:1px;background:var(--border)}
/* Filter groups are native <details>, so collapsing needs no state machine and keeps
   keyboard and screen-reader behaviour for free. 720px of controls closes to ~250px,
   which is what makes the panel usable on a short screen rather than merely scrollable. */
.fg{margin-bottom:0}
.fg>.st{cursor:pointer;margin-bottom:0;list-style:none;user-select:none}
.fg[open]>.st{margin-bottom:7px}
.fg>.st::marker,.fg>.st::-webkit-details-marker{display:none}
.fg>.st::before{content:'';width:0;height:0;border-left:4px solid var(--mist);border-top:3.5px solid transparent;border-bottom:3.5px solid transparent;flex-shrink:0;transition:transform .15s}
.fg[open]>.st::before{transform:rotate(90deg)}
.fg>.st:hover{color:var(--ink)}
.pshead{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:10px;font-weight:600;color:var(--mist);text-transform:uppercase;letter-spacing:.8px;background:rgba(255,255,255,.5);position:sticky;top:0;z-index:2}
.pshead .fcl{margin-left:0;background:none;border:none;font-family:inherit;text-transform:none;letter-spacing:0}
.fcl{font-size:10px;color:var(--accent2);cursor:pointer;text-decoration:underline;margin-left:auto;font-weight:500}
/* Button rows */
.brow{display:flex;gap:3px;flex-wrap:wrap}
.bb{flex:1;padding:6px 3px;border:1px solid var(--border);border-radius:6px;background:white;font-size:11px;font-weight:500;color:var(--mist);cursor:pointer;text-align:center;transition:all .15s;line-height:1.3;min-width:60px}
.bb.on{background:var(--ink);color:white;border-color:var(--ink)}
.bb:hover:not(.on){border-color:var(--mist);color:var(--ink)}
/* DoW */
.dow-row{display:flex;gap:3px}
.dw{flex:1;padding:6px 2px;border:1px solid var(--border);border-radius:6px;background:white;font-size:11px;font-weight:600;color:var(--mist);cursor:pointer;text-align:center;transition:all .15s;font-family:'IBM Plex Mono',monospace}
.dw.on{background:var(--ink2);color:white;border-color:var(--ink2)}
.dw:hover:not(.on){border-color:var(--ink2);color:var(--ink)}
/* ToD */
.tod-row{display:grid;grid-template-columns:1fr 1fr;gap:3px}
.td{padding:7px 4px;border:1px solid var(--border);border-radius:6px;background:white;font-size:10px;font-weight:500;color:var(--mist);cursor:pointer;text-align:center;transition:all .15s;line-height:1.3}
.td.on{color:white;border-color:transparent}
.td[data-td="0"].on{background:#1A3A5C}
.td[data-td="1"].on{background:#B7770D}
.td[data-td="2"].on{background:#1E8449}
.td[data-td="3"].on{background:#6C3483}
.td:hover:not(.on){border-color:var(--ink2);color:var(--ink)}
.tod-sw{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:3px;vertical-align:middle}
/* Severity */
.sv{flex:1;padding:6px 2px;border:1px solid var(--border);border-radius:6px;background:white;font-size:10px;font-weight:600;color:var(--mist);cursor:pointer;text-align:center;transition:all .15s}
.sv.on{color:white;border-color:transparent}
.sv[data-sv="Fatal"].on{background:var(--fatal)}
.sv[data-sv="Serious"].on{background:var(--serious)}
.sv[data-sv="Slight"].on{background:var(--slight)}
/* Mode buttons share .sv with severity but key off data-mode, not data-sv, so none of
   the three rules above ever matched them - only the generic .sv.on above applied,
   which sets white text with no background override, leaving .sv's own white
   background in place. The result was an active mode button rendering as invisible
   white-on-white text, indistinguishable from an inactive one except by toggling it.
   Modes have no natural per-item colour the way severities do, so they get one
   consistent "on" treatment instead. */
.sv[data-mode].on{background:var(--ink2);color:white;border-color:var(--ink2)}
.sv:hover:not(.on){border-color:var(--ink2);color:var(--ink)}
/* Year range */
.fl{font-size:10px;font-weight:600;color:var(--mist);text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px;display:block}
.fg{margin-bottom:8px}
select{width:100%;border:1px solid var(--border);border-radius:6px;padding:6px 28px 6px 9px;font-size:12px;font-family:'DM Sans',sans-serif;color:var(--ink);background:white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%238899AA'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer}
select:focus{outline:2px solid var(--accent2);border-color:transparent}
.yr-row{display:flex;gap:5px;align-items:center}
.yr-row select{flex:1}
/* results list reused as area rank list */
#rlist{display:block}
.ri{padding:10px 13px;border-bottom:1px solid var(--border);cursor:pointer;transition:background .1s}
.ri:hover{background:white}
.ri-top{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:3px}
.ri-nm{font-size:12px;font-weight:600;color:var(--ink);line-height:1.3;flex:1;margin-right:7px}
.ri-rk{font-size:11px;font-family:'IBM Plex Mono',monospace;padding:1px 6px;border-radius:4px;flex-shrink:0}
.rh{background:#FDEDEC;color:var(--fatal)}.rm{background:#FEF9E7;color:#B7770D}.rl{background:#EAFAF1;color:#1E8449}
.ri-meta{font-size:10px;color:var(--mist);margin-top:3px;font-family:'IBM Plex Mono',monospace}
.ri-rate{font-size:13px;font-weight:600;color:var(--accent2);font-family:'IBM Plex Mono',monospace;margin-top:3px}
.ri-rate span{font-size:10px;font-weight:400;color:var(--mist);font-family:'DM Sans',sans-serif}
.ri-ci{font-size:10px;color:var(--mist);font-family:'IBM Plex Mono',monospace;margin-top:1px}
.ri-sig{display:inline-block;font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:2px 6px;border-radius:3px;margin-top:4px}
.sig-higher{background:#FDEDEC;color:var(--fatal)}
.sig-outlier{background:#FEF3E2;color:#B25E09;cursor:help}
.sig-similar{background:#EEF1F4;color:var(--mist)}
.sig-lower{background:#EAFAF1;color:#1E8449}
.cbr-inline{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--ink2);cursor:pointer;margin-top:7px}
.cbr-inline input{accent-color:var(--accent2);width:13px;height:13px}
.bb:disabled{opacity:.4;cursor:not-allowed}
.bb:disabled:hover{border-color:var(--border);color:var(--mist)}
.ri-cost{font-size:10px;color:var(--gold);font-weight:600;font-family:'IBM Plex Mono',monospace;margin-top:2px}
.no-res{padding:24px 13px;text-align:center;color:var(--mist);font-size:12px;line-height:1.6}
/* DETAIL */
#dp{position:absolute;left:310px;top:52px;width:272px;background:var(--panel);border-right:1px solid var(--border);display:none;flex-direction:column;z-index:99;max-height:calc(100vh - 52px);overflow-y:auto}
#dp.open{display:flex}
.dphd{padding:12px 13px;background:var(--chrome-bg);color:var(--chrome-text);position:sticky;top:0;z-index:2}
.dpclose{float:right;background:none;border:none;color:var(--chrome-text-dim);cursor:pointer;font-size:20px;line-height:1;margin-top:-3px}
.dplsoa{font-size:9px;font-family:'IBM Plex Mono',monospace;color:var(--chrome-text-dim-2);margin-bottom:2px}
.dpnm{font-size:13px;font-weight:600;color:var(--chrome-text);margin-bottom:5px}
.dpsc{display:flex;gap:5px;flex-wrap:wrap}
.dptag{font-size:10px;padding:2px 7px;border-radius:4px;font-family:'IBM Plex Mono',monospace;font-weight:500}
.dpbd{padding:12px 13px;display:flex;flex-direction:column;gap:10px}
.dpst{font-size:10px;font-weight:600;color:var(--mist);text-transform:uppercase;letter-spacing:.6px;margin-bottom:5px}
/* COST BOX */
.cost-box{background:linear-gradient(135deg,#1A2E1A,#0D1F2D);border-radius:8px;padding:12px 14px;border:1px solid rgba(240,192,64,.2)}
.cost-box-title{font-size:9px;text-transform:uppercase;letter-spacing:.7px;color:rgba(240,192,64,.6);margin-bottom:8px;font-family:'IBM Plex Mono',monospace}
.cost-row{display:flex;justify-content:space-between;align-items:center;padding:4px 0;border-bottom:1px solid rgba(255,255,255,.06);font-size:12px}
.cost-row:last-child{border-bottom:none;padding-top:7px;margin-top:3px}
.cost-lbl{color:rgba(255,255,255,.55)}
.cost-val{font-family:'IBM Plex Mono',monospace;font-weight:600;color:#F0C040}
.cost-val.total{font-size:14px;color:white}
.cost-bar-wrap{margin-top:8px}
.cost-bar-track{background:rgba(255,255,255,.08);border-radius:3px;height:8px;overflow:hidden;display:flex;margin-top:3px}
.cost-seg-col{background:#C0392B;height:100%}
.cost-seg-cri{background:#8B2FC9;height:100%}
/* metric rows */
.mr{display:flex;justify-content:space-between;align-items:center;padding:4px 0;border-bottom:.5px solid var(--border);font-size:12px}
.mr:last-child{border-bottom:none}
.ml{color:var(--mist)}.mv{font-weight:600;color:var(--ink);font-family:'IBM Plex Mono',monospace}
.mv.f{color:var(--fatal)}.mv.s{color:var(--serious)}.mv.c{color:var(--crime-hi)}.mv.g{color:var(--gold)}
/* MAP */
#map{flex:1;z-index:1}.leaflet-container{background:var(--chrome-bg)}
/* Marks whichever point is open in the detail panel, so a selection from the ranked
   list (or a click on a marker) is findable on the map afterwards - without this, a
   dense city centre gives no way to tell which of several similarly-sized, similarly-
   coloured circles at one location is the one actually selected. Pulses rather than
   sitting static so it reads as "this one" even against a busy, similarly-white-ringed
   collision marker underneath it. */
/* Leaflet's SVG renderer draws a CircleMarker as a <path> (an arc), not a <circle> -
   confirmed by inspecting the live element - so this cannot animate the SVG `r`
   attribute (paths don't have one) or rely on transform:scale (no reliable centre to
   scale from without knowing Leaflet's own path-centring convention). stroke-width and
   stroke-opacity both apply to any SVG shape regardless, so the pulse is a fade rather
   than a grow - still unambiguous against a static marker underneath it. */
.selection-ring{animation:selection-pulse 1.4s ease-out infinite;pointer-events:none}
@keyframes selection-pulse{
  0%{stroke-width:4;stroke-opacity:1}
  70%{stroke-width:1;stroke-opacity:.2}
  100%{stroke-width:1;stroke-opacity:0}
}
@media (prefers-reduced-motion:reduce){.selection-ring{animation:none;stroke-opacity:.9}}
.leaflet-tile-pane{filter:var(--tile-filter)}
/* RANKED LIST: a right-hand twin of #dp, off-canvas until expanded, entered via the
   teaser button docked under the legend (below). Moved out of the filter sidebar
   entirely - see the #pnl-filters note above - because it used to force a 58%-height
   cap on the filters whenever it had rows in it. The two now each get their own
   full-height, independently scrolling home instead of competing for one column. */
/* translateX(100%) is relative to the element's OWN width, which is safe here since
   the width is a fixed min(88vw,320px) - but the phone bottom-sheet variant below hides
   the same way relative to its own (variable, content-driven) height, which on a very
   short viewport can be taller than the viewport itself, leaving a sliver on screen
   even "fully" translated. Both use a viewport-relative distance instead, which clears
   regardless of how tall or wide the panel's content makes it. */
#ranked-panel{position:fixed;top:52px;right:0;bottom:30px;width:min(88vw,320px);background:var(--panel);border-left:1px solid var(--border);z-index:480;display:flex;flex-direction:column;transform:translateX(120vw);transition:transform .18s ease;box-shadow:-6px 0 24px rgba(15,25,35,.18)}
#ranked-panel.open{transform:translateX(0)}
#ranked-panel #ranked-areas-header{position:sticky;top:0;background:var(--panel);z-index:2;flex:0 0 auto}
#ranked-panel #rlist{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain}
/* LEGEND */
#leg{position:fixed;right:14px;bottom:46px;z-index:500;background:var(--chrome-bg-2);color:var(--chrome-text);border-radius:8px;padding:11px 13px;font-size:11px;min-width:170px;max-width:min(300px,calc(100vw - 28px));max-height:calc(100vh - 130px);overflow-y:auto;backdrop-filter:blur(8px);border:1px solid var(--chrome-border)}
/* The ranked-list entry point. A plain row rather than a floating pill of its own,
   docked to the bottom of the legend box precisely so it reads as "the other list
   that goes with this map", not as an unrelated third floating widget. */
#ranked-teaser{width:100%;margin-top:9px;padding-top:9px;border-top:1px solid var(--chrome-border);border-left:none;border-right:none;border-bottom:none;background:none;color:var(--chrome-text);font-family:'DM Sans',sans-serif;font-size:11px;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:8px}
#ranked-teaser:hover{opacity:.85}
.rt-count{font-family:'IBM Plex Mono',monospace;font-weight:400;color:var(--chrome-text-dim);margin-right:auto}
.rt-arrow{color:var(--accent);font-size:13px;transition:transform .15s}
#ranked-teaser[aria-expanded="true"] .rt-arrow{transform:rotate(90deg)}
#ranked-teaser[hidden]{display:none}
/* The legend grew a lot once it started spelling out real class breaks, and on a phone
   it would cover most of the map. Collapsible, with the toggle only shown where the
   screen is small enough for it to matter. */
#leg-toggle{display:none;background:none;border:none;color:var(--chrome-text-dim);font-family:'IBM Plex Mono',monospace;font-size:10px;text-transform:uppercase;letter-spacing:.6px;cursor:pointer;padding:0}
#leg.collapsed{padding:8px 11px;min-width:0}
#leg.collapsed #leg-inner{display:none}
.legt{font-size:9px;text-transform:uppercase;letter-spacing:.6px;color:var(--chrome-text-dim);margin-bottom:7px;font-family:'IBM Plex Mono',monospace}
.legr{display:flex;align-items:center;gap:7px;margin-bottom:5px}
.legc{width:11px;height:11px;border-radius:50%;flex-shrink:0}
.legsq{width:11px;height:7px;border-radius:2px;flex-shrink:0}
/* BOTTOM */
#bot{position:fixed;bottom:0;left:310px;right:0;height:30px;background:var(--chrome-bg-2);border-top:1px solid var(--chrome-border);display:flex;align-items:center;padding:0 13px;gap:14px;font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--chrome-text-dim-2);backdrop-filter:blur(6px);z-index:400}
.bi{display:flex;align-items:center;gap:5px}
.bd{width:6px;height:6px;border-radius:50%}
#fstatus{margin-left:auto;color:var(--accent);font-size:10px}
::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--fog);border-radius:2px}
.map-tooltip{background:var(--chrome-bg-2)!important;color:var(--chrome-text)!important;border:none!important;border-radius:6px!important;font-family:'DM Sans',sans-serif!important;font-size:12px!important;padding:8px 12px!important;box-shadow:0 4px 20px rgba(0,0,0,.4)!important}
.map-tooltip::before{display:none!important}
.ttnm{font-weight:600;margin-bottom:4px}
.ttr{display:flex;justify-content:space-between;gap:14px;font-size:11px;color:var(--chrome-text-dim)}
.ttv{font-family:'IBM Plex Mono',monospace;color:var(--chrome-text);font-weight:500}
.ttv.gold{color:#F0C040}
/* NEW: loading / empty states */
#loading{position:fixed;inset:0;background:var(--chrome-bg);z-index:2000;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;color:var(--chrome-text);font-family:'IBM Plex Mono',monospace}
#loading.hidden{display:none}
.spinner{width:34px;height:34px;border-radius:50%;border:3px solid rgba(255,255,255,.15);border-top-color:var(--accent);animation:spin 0.9s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.no-data-banner{position:absolute;top:66px;left:324px;z-index:600;background:rgba(192,57,43,.95);color:white;padding:9px 16px;border-radius:8px;font-size:12px;max-width:360px;box-shadow:0 4px 16px rgba(0,0,0,.3)}
.export-btn{width:100%;padding:8px;border-radius:6px;border:1px solid var(--accent2);background:white;color:var(--accent2);font-size:11px;font-weight:600;cursor:pointer;text-transform:uppercase;letter-spacing:.4px}
.export-btn:hover{background:var(--accent2);color:white}
.trend-svg{width:100%;height:90px;display:block}
.trend-axis{stroke:rgba(15,25,35,.15);stroke-width:1}
.trend-line{fill:none;stroke:var(--accent2);stroke-width:2}
.trend-area{fill:rgba(0,180,216,.12);stroke:none}
.scot-badge{display:inline-block;font-size:9px;padding:2px 7px;border-radius:4px;background:var(--gold-l);color:var(--gold);font-weight:600;text-transform:uppercase;letter-spacing:.4px;margin-bottom:6px}

/* ══ RESPONSIVE ═══════════════════════════════════════════════════════════════
   There were no media queries at all before this: the layout assumed a wide desktop
   with a 310px panel permanently beside the map, so on a tablet the map was a sliver
   and on a phone the header alone overflowed the viewport.

   Two breakpoints:
     <=1000px  the panel becomes an overlay drawer over the map instead of taking width
               from it, so the map is always full-size and the panel is a deliberate
               action. Same markup, same controls - nothing is dropped.
     <=680px   phone: the header sheds its title and scrolls its stat tiles, the detail
               panel becomes a bottom sheet, and the legend starts collapsed.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width:1000px){
  /* Drawer, not a column. Off-canvas by default so the map gets the whole viewport. */
  #pnl{position:fixed;left:0;top:52px;bottom:0;width:min(88vw,330px);transform:translateX(-100%);box-shadow:6px 0 24px rgba(15,25,35,.18);z-index:900}
  #pnl.open{transform:translateX(0)}
  #pnl.collapsed{width:min(88vw,330px);transform:translateX(-100%)}
  #pnl.collapsed>*{display:flex}
  #pnl.collapsed #pnl-filters{display:block}
  #pnl-backdrop{display:block;position:fixed;inset:52px 0 0 0;background:rgba(15,25,35,.35);opacity:0;pointer-events:none;transition:opacity .18s;z-index:899}
  #pnl-backdrop.on{opacity:1;pointer-events:auto}
  #dp{left:0}
  #bot{left:0}
  #ranked-panel{width:min(88vw,330px)}
  /* The toggle stops being optional here, so give it a filled look. */
  #pnl-toggle{background:rgba(255,255,255,.08)}
}

@media (max-width:680px){
  #hdr{height:48px}
  #app{padding-top:48px}
  #pnl,#pnl-backdrop{top:48px}
  .hlogo{width:44px;height:48px}
  #pnl-toggle{height:48px;padding:0 10px}
  #pnl-toggle .pt-label{display:none}
  /* The title is the first thing to go: the stat tiles are the useful part of the
     header and they need the width. */
  .htitle{display:none}
  .hstats{overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .hstats::-webkit-scrollbar{display:none}
  .hst{padding:0 10px}
  .hstv{font-size:12px}
  .hbc{padding-right:10px;font-size:10px}
  /* Bottom sheet rather than a side panel - a 272px panel on a 360px screen leaves
     no map. */
  #dp{left:0;right:0;top:auto;bottom:30px;width:auto;max-height:56vh;border-right:none;border-top:1px solid var(--border);box-shadow:0 -6px 24px rgba(15,25,35,.2)}
  /* Same bottom-sheet treatment as #dp, and for the same reason - a fixed side panel
     on a 360px screen leaves no map either side of it. */
  #ranked-panel{left:0;right:0;top:auto;bottom:30px;width:auto;max-height:56vh;border-left:none;border-top:1px solid var(--border);box-shadow:0 -6px 24px rgba(15,25,35,.2);transform:translateY(120vh)}
  #ranked-panel.open{transform:translateY(0)}
  #leg{right:8px;bottom:38px;max-width:calc(100vw - 16px);max-height:44vh}
  #leg-toggle{display:block}
  #leg.collapsed{padding:7px 10px}
  #bot{gap:10px;padding:0 10px;font-size:9px}
  #bot .bi:nth-child(n+4){display:none}
  /* Touch targets: 11px buttons in a 3px gap are not usable with a thumb. */
  .bb,.sv,.dw,.td{min-height:34px}
  .brow{gap:4px}
  .ri{padding:12px 13px}
}

/* Short-but-wide windows (a landscape tablet, or a half-height desktop window). The
   ranked list has its own panel now (see #ranked-panel), so this just buys back a
   little filter padding rather than fighting for a shared results floor. */
@media (max-height:620px){
  .ps{padding:8px 13px}
}

/* Respect a reduced-motion preference for the drawer and disclosure animations. */
@media (prefers-reduced-motion:reduce){
  #pnl,#pnl-backdrop,.fg>.st::before{transition:none}
}

/* ══ STAGE 5: BREAKDOWNS ══════════════════════════════════════════════════════
   Bars rather than a pie or a stacked column: the values within a dimension are
   ordinal as often as not (age bands, deprivation deciles, speed limits), and a
   horizontal bar keeps that order readable while still showing magnitude. */
.bkd{border-top:1px solid var(--border);padding-top:9px}
.bkd:first-of-type{border-top:none;padding-top:0}
.bkd-note{font-size:10px;color:var(--mist);line-height:1.45;margin:-3px 0 7px}
.bkd-measure{font-size:9px;color:var(--mist);line-height:1.45;margin-top:6px;padding-top:5px;border-top:1px dashed var(--border);font-style:italic}
.bkr{margin-bottom:7px}
.bkr-top{display:flex;justify-content:space-between;align-items:baseline;gap:8px}
.bkr-lb{font-size:11px;color:var(--ink);line-height:1.3}
.bkr-v{font-size:11px;font-weight:600;font-family:'IBM Plex Mono',monospace;color:var(--ink);flex-shrink:0}
.bkr-bar{height:5px;background:rgba(15,25,35,.07);border-radius:3px;margin:3px 0 2px;overflow:hidden}
.bkr-bar>div{height:100%;background:var(--accent2);border-radius:3px;min-width:1px}
.bkr-meta{font-size:9px;color:var(--mist);font-family:'IBM Plex Mono',monospace}
.src-use{font-size:10px;color:var(--ink);line-height:1.5;margin-top:5px}
.src-attr{font-size:10px;color:var(--mist);line-height:1.5;margin-top:5px;padding:6px 8px;background:rgba(15,25,35,.04);border-left:2px solid var(--accent2);border-radius:0 3px 3px 0}
.src-link{font-size:9px;font-family:'IBM Plex Mono',monospace;margin-top:4px;word-break:break-all}
.src-link a{color:var(--accent2)}

/* Agilysis credit in the sources panel. */
.brand-credit{display:flex;align-items:center;gap:10px;padding:9px 0 2px;border-top:1px solid var(--border);margin-top:4px}
.brand-credit img{height:26px;width:auto;display:block}
.brand-credit span{font-size:10px;color:var(--mist);line-height:1.45}
