
  :root{
    --bg:#f4f5f2; --card:#ffffff; --ink:#1d2b23; --muted:#5f6f66;
    --accent:#1b6e4b; --accent-soft:#e3f0e9; --line:#e3e6e0;
    --amber:#b06a00; --amber-soft:#fdf1dd; --red:#b0322c; --red-soft:#fbe7e5;
    --blue:#22577c; --blue-soft:#e4eef5; --fun:#6b3fa0; --fun-soft:#efe8f7;
    --travel:#946c00; --travel-soft:#fdf3d0;   /* Travel tab, 17 Oct 2026; sunny amber 19 Oct 2026 (owner: "yellow, not a bright yellow, same palette as the existing tabs") — white on #946c00 is 4.8:1, above the 4.5:1 accessibility line */
    /* World-news place chip (3 Sep 2026). Its own tokens rather than
       reusing --fun: a country tag and the Games tab have no reason to
       change colour together. Violet because the category chip beside
       it is already blue, and two blues would read as one thing. */
    --place:#6b3fa0; --place-soft:#efe8f7;
    /* Flood likelihood bands. ORDINAL, not categorical — swap two of these
       and the meaning changes — so it is ONE hue in monotone lightness
       steps, more ink for more risk. Checked, not eyeballed: monotone L,
       every adjacent step >= 0.06 apart, light end 2.09:1 on white. Never
       red/amber/green: a colour scale that says "safe" would be a verdict,
       and Very low means "less than 1 in 1000 a year", not "fine". */
    --fb-verylow:#95b8d1; --fb-low:#6698bc; --fb-med:#37759f; --fb-high:#17496b;
    --fb-none:#dfe2dc;
    /* Flood defences. EMPHASIS, not a comparison: the only thing worth
       seeing is how many are below the condition the EA requires, so that
       segment carries the only colour and everything else is de-emphasis
       grey. Amber against the mid grey clears both CVD separation (13.1)
       and the normal-vision floor (18.6); the palest grey sits under 3:1 on
       purpose and is covered by the labelled count beside it. */
    --fd-below:#c47a05; --fd-ok:#5f6f66; --fd-ungraded:#b4b9b1;
    --fd-track:#e8e6df;
    --radius:14px;
    font-size:16px;
  }
  *{box-sizing:border-box; margin:0}
  body{background:var(--bg); color:var(--ink);
    font-family:-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
    line-height:1.45; padding-bottom:60px}
  a{color:var(--accent)}
  header{background:var(--ink); color:#fff; padding:14px 20px}
  .mast{max-width:1100px; margin:0 auto; display:flex; flex-wrap:wrap; gap:10px 18px; align-items:center}
  .brand{font-size:1.5rem; font-weight:800; letter-spacing:-.02em; cursor:pointer; user-select:none}
  .brand small{font-weight:400; opacity:.55; font-size:.65rem; display:block; letter-spacing:0}
  .pc-pill{background:#ffffff22; border:1px solid #ffffff44; border-radius:999px; padding:4px 14px; font-weight:600}
  .pc-search{display:flex}
  .pc-search input{border:1px solid #ffffff44; background:#ffffff18; color:#fff; border-radius:999px 0 0 999px;
    padding:5px 10px; font:inherit; width:5.5em; text-align:center; text-transform:uppercase}
  .pc-search input::placeholder{color:#ffffffaa; text-transform:none}
  .pc-search button{border:1px solid #ffffff44; border-left:0; background:#ffffff30; color:#fff;
    border-radius:0; padding:5px 12px; font:inherit; cursor:pointer}
  .pc-search .locate{border-radius:0 999px 999px 0; padding:5px 9px; display:flex;
    align-items:center; justify-content:center}
  .pc-search .locate svg{width:15px; height:15px; display:block}
  .pc-search .locate:hover{background:#ffffff45}
  .pc-search .locate[disabled]{opacity:.55; cursor:default}
  #pc-saved{background:#ffffff22; border:1px solid #ffffff44; color:#fff; border-radius:999px;
    padding:5px 10px; font:inherit; max-width:180px}
  #pc-saved option{color:var(--ink)}
  .mast .spacer{flex:1}
  /* On desktop the right-hand group is not a group at all — its children sit
     in the mast's flex row exactly as before. Mobile turns it into a column. */
  .mast-right{display:contents}
  .who{font-size:.85rem; opacity:.8}
  .who a{color:#fff; text-decoration:underline; text-underline-offset:2px}
  .who a:hover{opacity:.85}
  .acct{background:#ffffff22; border:1px solid #ffffff44; color:#fff; border-radius:999px;
        padding:5px 14px; font-weight:600; font-size:.85rem; cursor:pointer; white-space:nowrap}
  .acct:hover{background:#ffffff33}
  .signin-opt{display:flex; align-items:center; gap:11px; width:100%; padding:13px 15px;
        border:1px solid var(--line); border-radius:10px; background:#fff; cursor:pointer;
        font-size:.95rem; font-weight:600; margin-bottom:9px; text-align:left}
  .signin-opt:hover{background:#f6f8f7}
  .signin-opt svg{width:19px; height:19px; flex:0 0 19px}
  /* Sign-in as a bottom sheet on phones: it rises into the thumb, the two
     provider buttons become full-width targets, and the privacy note is one
     line linking to About rather than a paragraph of small print. */
  #acct-dialog{border:0; border-radius:14px; padding:0; max-width:420px; width:calc(100% - 32px)}
  #acct-dialog::backdrop{background:#0b1a13aa}
  .sheet-grab{display:none}
  .acct-in{padding:20px}
  .acct-in h3{margin:0 0 4px; font-size:1.1rem}
  .acct-sub{color:var(--muted); font-size:.88rem; margin:0 0 16px; line-height:1.45}
  .acct-note{color:var(--muted); font-size:.76rem; line-height:1.5; margin:12px 0 0}
  @media(max-width:640px){
    #acct-dialog{margin:0 0 0 0; top:auto; bottom:0; width:100%; max-width:100%;
                 border-radius:16px 16px 0 0; animation:sheetUp .18s ease-out}
    .sheet-grab{display:block; width:34px; height:4px; border-radius:2px;
                background:var(--line); margin:0 auto 14px}
    .acct-in{padding:16px 16px 22px}
    #acct-dialog form[method=dialog]{display:none}
  }
  @keyframes sheetUp{from{transform:translateY(14px); opacity:.6} to{transform:none; opacity:1}}
  .acct-row{display:flex; justify-content:space-between; align-items:center; gap:12px;
        padding:8px 0; border-bottom:1px solid var(--line)}
  .acct-danger{color:#a3282a; background:none; border:none; cursor:pointer; padding:0;
        font-size:.85rem; text-decoration:underline}
  /* Mobile header: two columns, four corners. Logo top-left with the search
     directly beneath it; postcode, saved list and sign-in stack down the
     right. Halves the green band (~200px -> ~110px) and puts the brand where
     a reader's eye starts. CSS grid rather than flex order juggling, so the
     two columns can size independently. */
  @media(max-width:640px){
    header{padding:12px 14px}
    /* minmax(0,…) not 1fr: a grid track's default min-content floor lets a
       long pill push the row wider than the screen, which is exactly how the
       page ended up scrolling sideways. */
    .mast{display:grid; grid-template-columns:minmax(0,1fr) auto;
          grid-template-areas:"brand right" "search right";
          column-gap:10px; row-gap:9px; align-items:start; max-width:100%}
    .mast > *{min-width:0}
    .brand{grid-area:brand; font-size:1.25rem; align-self:center; min-width:0}
    .brand small{font-size:.6rem}
    .brand svg{width:30px; height:30px}
    .pc-search{grid-area:search; justify-self:start}
    .pc-search input{width:6.2em; padding:6px 8px; font-size:.9rem}
    .pc-search button{padding:6px 12px; font-size:.9rem}
    .mast .spacer, .mast .spacer2{display:none}
    .mast .who{display:none}
    .mast-right{grid-area:right; display:flex; flex-direction:column;
                align-items:flex-end; gap:7px; min-width:0; max-width:50vw}
    .mast-right > *{max-width:100%}
    .pc-pill{font-size:.7rem; padding:3px 11px; white-space:nowrap;
             overflow:hidden; text-overflow:ellipsis}
    #pc-saved{font-size:.7rem; padding:3px 9px; max-width:100%}
    .acct{font-size:.78rem; padding:6px 16px; border-color:#ffffff77}
  }
  @media(max-width:359px){
    .brand small{display:none}
    .brand{font-size:1.1rem}
    .pc-pill, #pc-saved{font-size:.66rem}
    .mast-right{max-width:46vw}
    .pc-search input{width:5.6em}
  }
  /* Nothing on this page is meant to scroll sideways. `clip` rather than
     `hidden`: hidden makes body a scroll container, which iOS Safari will
     still let you pan; clip genuinely refuses to scroll. Applied to html
     only — setting it on both elements is the combination iOS ignores. */
  html{max-width:100%; overflow-x:clip}
  body{max-width:100%; overflow-x:clip}
  /* Long unbroken strings (URLs, council names) are the usual culprits for a
     few stray pixels of horizontal scroll on a narrow screen. Applied to
     prose only — see the badge exemption below. */
  .meta, .src{overflow-wrap:anywhere}
  /* Badges and chips must never break: `anywhere` let LIVE collapse to one
     character wide and wrap down the page. Short labels, fixed size, no
     shrinking. */
  .badge, .chip, .share-btn, .hide-btn, .help-btn{
    overflow-wrap:normal; word-break:keep-all; white-space:nowrap; flex-shrink:0}
  /* Card titles never break mid-word. overflow-wrap is an inherited property,
     so a rule set on a container reaches headings inside it — which is how
     "Planning" ended up split across two lines. */
  h1, h2, h3{overflow-wrap:normal; word-break:keep-all; hyphens:none}
  .strip{background:var(--accent); color:#fff}
  /* COLUMN GAP 12px, MEASURED NOT CHOSEN (7 Sep 2026). The strip is capped
     at 1100px, so desktop always has the same space however wide the screen;
     with "Ranks" added, a 26px gap pushed it onto a second line by itself and
     16px still wrapped. 14px was the exact threshold — too close, because
     Segoe UI on Windows sets a little wider than the headless browser these
     measurements came from — so 12px, which clears it with room and still
     reads as separated. Row gap stays 6px. Re-measure at 1100px AND 360px
     before adding a ninth item. */
  .strip-in{max-width:1100px; margin:0 auto; padding:10px 20px; display:flex;
    flex-wrap:wrap; gap:6px 12px; font-size:.9rem}
  /* PLANNING SAYS LESS ON A PHONE (owner, 7 Sep): "reduce planning further
     to '3 plans nearby' on smaller devices". BOTH wordings are rendered and
     CSS picks one, so there is no resize listener to get wrong and turning
     the phone sideways is handled by the browser. The renderer writes both
     every time — never one or the other — so no code path can leave the item
     empty. */
  .strip .pl-narrow{display:none}
  @media (max-width:560px){
    .strip .pl-wide{display:none}
    .strip .pl-narrow{display:inline}
  }
  .strip b{font-weight:700}
  /* EVERY ITEM IN THE STRIP IS A DOOR (owner, 4 Sep 2026). The strip is a
     summary OF the tiles below it, so the weather figure, the sun times, the
     flood line, the pollen count, the money line and the planning count each
     link INTO the tile they were summarised from. Before this the First aid
     cross was the only clickable thing in the row, which quietly taught
     readers that the rest was decoration.
     These are the SAME elements the renderers write into: the ids did not
     move, the tags were promoted from <span> to <a>, so every existing
     $("s-...").textContent = ... still lands in exactly the same place. */
  .strip a.jump{color:#fff; text-decoration:none; white-space:nowrap;
    border-radius:8px; padding:2px 6px; margin:-2px -6px;
    transition:background .12s}
  .strip a.jump:hover{background:rgba(255,255,255,.18);
    text-decoration:underline; text-underline-offset:3px}
  .strip a.jump:focus-visible{outline:2px solid #fff; outline-offset:2px}
  /* FLOOD, WHEN SOMETHING IS ACTUALLY IN FORCE (owner, 4 Sep 2026: "make it
     flash, turn red, get your attention"). TWO states, not one, because the
     Environment Agency publishes two different meanings and flattening them
     would be the site telling a fib: an ALERT means flooding is POSSIBLE
     (amber), a WARNING means flooding is EXPECTED (red). A severe warning is
     a warning that says the word "severe" in its own label.
     The pulse is a halo on the pill, not a fade on the text -- text that
     fades out is text that cannot be read while it is fading. One cycle per
     1.6s, comfortably under the three-flashes-per-second ceiling that makes
     flashing a seizure risk, and it stops dead for a reader whose system
     asks for reduced motion, who still gets the solid colour. */
  .strip a.jump.fl-alert, .strip a.jump.fl-warn{
    font-weight:700; animation:floodpulse 1.6s ease-in-out infinite}
  .strip a.jump.fl-alert{background:#ffc233; color:#3b2a00}
  .strip a.jump.fl-alert:hover{background:#ffd166}
  .strip a.jump.fl-warn{background:#d0342c; color:#fff}
  .strip a.jump.fl-warn:hover{background:#e2483f}
  @keyframes floodpulse{
    0%,100%{box-shadow:0 0 0 0 rgba(255,255,255,.9)}
    55%    {box-shadow:0 0 0 7px rgba(255,255,255,0)}
  }
  @media (prefers-reduced-motion: reduce){
    .strip a.jump.fl-alert, .strip a.jump.fl-warn{animation:none}
  }
  /* FIRST AID, in the green strip beside planning. The strip is the row a
     reader already scans for "what do I need to know right now", which is the
     same question an emergency asks. It is a LINK among status text, so it is
     given a white cross on a red badge to say so.

     JUST THE ICON IS RED. A red pill was tried and was too loud for a strip
     of quiet status text — it stopped being one item in the row and became a
     button shouting over the weather. The mark is a Swiss-flag square, red
     with a white cross; the words are ordinary white strip text.

     THE CROSS, NOT THE SQUARE, IS WHAT CARRIES THE SHAPE. Measured: this red
     against the strip's green is about 1.3:1 and the site's own --red is
     1.01:1 — near-identical luminance, so the square's outline does not read
     against the green for anyone not separating them by hue. The white cross
     inside it is 5:1 and is what the eye actually resolves, which is why the
     cross runs nearly to the edges with square ends rather than sitting small
     and rounded in the middle. It sits IMMEDIATELY after
     planning rather than being pushed to the far edge: "next to planning"
     was the instruction, and a right-aligned button reads as a separate
     control rather than the last item in the row. */
  /* ONE HOVER FOR EVERY STRIP ITEM (owner, 22 Sep 2026): First aid used to
     underline only and Ranks to highlight only, while every other item in
     the strip does BOTH. The two .aid links now carry exactly the .jump
     rules — padding, the cancelling negative margin, the rounded
     highlight, the underline and the focus ring. If .jump's hover changes,
     change this with it. */
  .strip .aid{display:inline-flex; align-items:center; gap:7px; color:#fff;
    text-decoration:none; font-weight:inherit; white-space:nowrap;
    border-radius:8px; padding:2px 6px; margin:-2px -6px;
    transition:background .12s}
  .strip .aid:hover{background:rgba(255,255,255,.18);
    text-decoration:underline; text-underline-offset:3px}
  .strip .aid:focus-visible{outline:2px solid #fff; outline-offset:2px}
  .strip .aid svg{width:16px; height:16px; display:block; flex:none}
  .controls{max-width:1100px; margin:18px auto 6px; padding:0 20px; display:flex;
    align-items:center; gap:12px; flex-wrap:wrap}
  .controls h1{font-size:1.15rem; font-weight:700}
  .controls .date{color:var(--muted); font-size:.9rem}
  .controls .spacer{flex:1}
  .toggle{display:flex; border:1px solid var(--line); border-radius:999px; overflow:hidden; background:#fff}
  .toggle button{border:0; background:none; padding:7px 18px; font:inherit; cursor:pointer; color:var(--muted)}
  .toggle button.on{background:var(--ink); color:#fff}
  .wrap{max-width:1100px; margin:0 auto; padding:8px 20px}
  /* The tab bar FLOATS with the category strip (owner, 6 Sep 2026): both are
     sticky, the strip sits directly beneath the tabs. The tab bar's height
     changes when its buttons wrap on a phone, so it is measured into
     --tabbar-h by JS (setTabbarH) and the strip's top offset reads that
     variable rather than guessing a number. z-index one above the strip so
     the strip slides under the tabs, never over them. */
  .tabbar{position:sticky; top:0; z-index:8; background:var(--bg);
    max-width:1100px; margin:0 auto; padding:6px 20px 8px; display:flex; gap:10px; flex-wrap:wrap}
  /* ONE PATTERN FOR ALL FIVE TABS (owner, 16 Sep 2026, chose "E" of five
     mock-ups: "I want more synergy"). Every tab is a tint of its colour
     when off and solid with white text when on, and the colour also runs
     as a thick 5px bottom edge, so a tab's colour is present even when it
     is off. UK is Union-flag red (Pantone 186, #C8102E; tint #fbe4e8) and
     World is the EU flag's blue (Pantone Reflex Blue, #003399; tint
     #e3e8f5) — the same red/white and blue/white the owner asked for, now
     in the same off/on rhythm as My area, Money and Games. */
  .tab{border:2px solid; border-bottom-width:5px; border-radius:11px; padding:12px 26px;
    font:700 .95rem/1 inherit; font-family:inherit; cursor:pointer;
    box-shadow:0 2px 6px rgba(29,43,35,.10); transition:transform .12s, box-shadow .12s}
  .tab:hover{transform:translateY(-1px); box-shadow:0 4px 10px rgba(29,43,35,.16)}
  .tab.on{color:#fff; transform:none; box-shadow:inset 0 2px 4px rgba(0,0,0,.18)}
  .tab[data-tab="local"]{background:var(--accent-soft); border-color:var(--accent); color:var(--accent)}
  .tab[data-tab="local"].on{background:var(--accent); color:#fff}
  .tab[data-tab="money"]{background:var(--blue-soft); border-color:var(--blue); color:var(--blue)}
  .tab[data-tab="money"].on{background:var(--blue); color:#fff}
  .tab[data-tab="national"]{background:#fbe4e8; border-color:#c8102e; color:#c8102e}
  .tab[data-tab="national"].on{background:#c8102e; color:#fff}
  .tab[data-tab="world"]{background:#e3e8f5; border-color:#003399; color:#003399}
  .tab[data-tab="world"].on{background:#003399; color:#fff}
  .tab[data-tab="fun"]{background:var(--fun-soft); border-color:var(--fun); color:var(--fun)}
  .tab[data-tab="fun"].on{background:var(--fun); color:#fff}
  .tab[data-tab="travel"]{background:var(--travel-soft); border-color:var(--travel); color:var(--travel)}
  .tab[data-tab="travel"].on{background:var(--travel); color:#fff}
  /* The Striker Force widget ships as a self-contained bordered box
     (max-width:440px, margin:auto, own border+radius). In our card it IS the
     card: full bleed, its border and radius stripped, the card's own rounded
     corners doing the clipping — so its green masthead and Quiz King's purple
     one start at the exact same height side by side. */
  #card-fun{padding:0; overflow:hidden}
  #card-fun .sfw{margin:0; max-width:none; width:100%; border:0; border-radius:0}
  #card-fun > .meta{margin:12px 20px 18px}
  /* quiz */
  /* Quiz King's masthead: a thick full-bleed banner like Striker Force's, so
     the two games read as siblings side by side. The h2 stays an h2 — the
     share/hide buttons are injected into every card's h2, so restyling beats
     replacing. Negative margins counteract the card's 18/20px padding. */
  #card-quiz h2{margin:-18px -20px 14px; padding:15px 18px;
    background:linear-gradient(135deg, var(--fun) 0%, #8f5bc7 100%); color:#fff;
    border-radius:var(--radius) var(--radius) 0 0; display:flex; align-items:center;
    gap:10px; font-size:1.15rem}
  #card-quiz h2 .badge{background:rgba(255,255,255,.22)}
  #card-quiz h2 .fresh{color:rgba(255,255,255,.85)}
  #card-quiz h2 .share-btn{background:rgba(255,255,255,.18); color:#fff}
  #card-quiz h2 .share-btn:hover{background:rgba(255,255,255,.32)}
  #card-quiz h2 .hide-btn{background:rgba(255,255,255,.18); color:#fff}
  #card-quiz h2 .hide-btn:hover{background:rgba(255,255,255,.32); color:#fff}
  .qz-hero{background:linear-gradient(135deg, var(--fun) 0%, #8f5bc7 100%);
    border-radius:11px; color:#fff; padding:14px 16px; margin-bottom:12px}
  .qz-hero .qz-row{display:flex; align-items:center; gap:10px}
  .qz-hero .qz-n{font-weight:800; font-size:1.05rem}
  .qz-hero .qz-cat{font-size:.75rem; opacity:.85}
  .qz-hero .qz-clock{margin-left:auto; font-variant-numeric:tabular-nums; font-weight:800;
    background:rgba(255,255,255,.18); border-radius:999px; padding:4px 12px}
  .qz-hero .qz-clock.pen{animation:qz-flash .6s}
  @keyframes qz-flash{0%{background:var(--red)} 100%{background:rgba(255,255,255,.18)}}
  .qz-bar{height:6px; background:rgba(255,255,255,.25); border-radius:99px; margin-top:10px; overflow:hidden}
  .qz-bar i{display:block; height:100%; background:#fff; border-radius:99px; transition:width .4s}
  .qz-btn{display:flex; align-items:center; gap:12px; width:100%; text-align:left; margin:9px 0;
    padding:13px 15px; border:1.5px solid var(--line); border-radius:12px; background:#fff;
    font-size:.97rem; cursor:pointer;
    transition:opacity .45s, transform .15s, box-shadow .15s, max-height .45s, margin .45s, padding .45s}
  .qz-btn:hover{transform:translateY(-1px); box-shadow:0 4px 12px rgba(107,63,160,.18);
    border-color:var(--fun)}
  .qz-btn .qz-letter{flex:0 0 26px; height:26px; display:flex; align-items:center;
    justify-content:center; border-radius:8px; background:var(--fun-soft); color:var(--fun);
    font-weight:800; font-size:.8rem}
  .qz-btn.gone{opacity:0; max-height:0; margin:0; padding:0 15px; border-width:0;
    pointer-events:none; overflow:hidden}
  .qz-btn.wrong{background:var(--red-soft); border-color:var(--red); animation:qz-shake .35s}
  @keyframes qz-shake{0%,100%{transform:translateX(0)} 25%{transform:translateX(-6px)}
    75%{transform:translateX(6px)}}
  .qz-pen-note{color:var(--red); font-weight:700; font-size:.8rem; animation:qz-rise .9s forwards}
  @keyframes qz-rise{0%{opacity:1} 100%{opacity:0}}
  /* The Shore (12 Oct 2026): a sea-blue masthead in the same full-bleed shape as
     Quiz King's, a still of the beach with the half-set light drawn over it in
     CSS, and one button to the page itself. */
  #card-shore h2{margin:-18px -20px 14px; padding:15px 18px;
    background:linear-gradient(135deg, #12607d 0%, #3b98b8 100%); color:#fff;
    border-radius:var(--radius) var(--radius) 0 0; display:flex; align-items:center;
    gap:10px; font-size:1.15rem}
  #card-shore h2 .fresh{color:rgba(255,255,255,.85)}
  #card-shore h2 .share-btn, #card-shore h2 .hide-btn{background:rgba(255,255,255,.18); color:#fff}
  #card-shore h2 .share-btn:hover, #card-shore h2 .hide-btn:hover{background:rgba(255,255,255,.32); color:#fff}
  .sh-hero{position:relative; display:block; border-radius:12px; overflow:hidden; aspect-ratio:16/9; background:#9fc6e6}
  .sh-hero img{display:block; width:100%; height:100%; object-fit:cover}
  /* the light: centred, its lower half below the horizon (40% down the still) */
  .sh-sun{position:absolute; left:50%; top:40%; width:13%; aspect-ratio:2/1; transform:translate(-50%,-100%);
    border-radius:999px 999px 0 0; background:radial-gradient(circle at 50% 100%, rgba(255,244,226,.95) 0 58%, rgba(255,214,170,.55) 72%, rgba(255,200,140,0) 100%);
    box-shadow:0 -6px 26px 8px rgba(255,206,140,.35)}
  .sh-go{display:inline-block; margin-top:14px; padding:12px 24px; border-radius:12px;
    background:#12607d; color:#fff; font-weight:600; text-decoration:none}
  .sh-go:hover{background:#0e4f67}
  .sh-go:focus-visible, .sh-hero:focus-visible{outline:3px solid #3b98b8; outline-offset:3px}
  .qz-start{display:inline-block; margin-top:16px; padding:13px 28px; border-radius:12px; border:0;
    background:linear-gradient(135deg, var(--fun) 0%, #8f5bc7 100%); color:#fff;
    font-size:1rem; font-weight:700; cursor:pointer; box-shadow:0 4px 14px rgba(107,63,160,.3);
    transition:transform .15s}
  .qz-start:hover{transform:translateY(-1px)}
  .qz-big{font-size:1.9rem; font-weight:800; color:var(--fun); margin:6px 0;
    animation:qz-pop .45s}
  @keyframes qz-pop{0%{transform:scale(.6); opacity:0} 100%{transform:scale(1); opacity:1}}
  .qz-table{width:100%; border-collapse:collapse; font-size:.85rem; margin-top:8px}
  .qz-table td{padding:6px 6px; border-bottom:1px solid var(--line)}
  .qz-table td.ms{text-align:right; font-variant-numeric:tabular-nums}
  .qz-me{background:var(--fun-soft); font-weight:700}
  @media(max-width:820px){
    .tabbar{gap:6px}
    .tab{flex:1 1 0; padding:10px 6px; font-size:.82rem; text-align:center; white-space:nowrap}
  }
  /* masonry: JS-balanced columns — each card goes to the currently shorter
     column, so no voids (CSS multicol balances by total height and leaves gaps).
     Tab filtering is handled by the same JS (hidden cards live in a stash). */
  /* ---- category strip + sections + collapsed cards (7 Aug 2026) ---- */
  /* Same max-width and left padding as .tabbar, so the first category icon
     sits directly under the "My area" tab rather than drifting. */
  .sectbar{position:sticky; top:var(--tabbar-h, 56px); z-index:7; display:flex; gap:6px;
    max-width:1100px; margin:0 auto 12px; padding:8px 20px;
    background:var(--bg); border-bottom:1px solid var(--line);
    overflow-x:auto; scrollbar-width:none}
  /* THE GRID IS A FLEX ROW — that is how the two-column masonry works. Section
     blocks dropped into it therefore became flex ITEMS and laid out ACROSS the
     page, each squeezed to a sixth of the width (seen live, 8 Aug 2026). A
     sectioned tab must switch the container back to normal block flow, so
     sections stack down the page as intended. */
  .grid.sectioned{display:block}
  .grid.sectioned > .sect-h, .grid.sectioned > .sect{width:100%}
  /* One column, full width — sections are runs of cards down the page, never
     across it. The two-column masonry is only for tabs without sections. */
  .sect{display:block}
  .sect > .card{width:100%; margin-bottom:14px}
  /* Two cards sharing a desktop row. min-width:0 stops a long unbroken string
     inside either card from pushing its half wider than the pair. */
  /* stretch, not flex-start: a pair should have one bottom edge, not two
     ragged ones (owner, 10 Aug 2026). The shorter card gains a little empty
     space, which reads as deliberate; mismatched heights read as broken. */
  .pair{display:flex; gap:16px; align-items:stretch}
  .pair > .card{display:flex; flex-direction:column}
  /* Feed cards start at a SET HEIGHT rather than a set number of items.
     Four-items-each could never line up: four wrapped headlines are taller
     than four one-line events. The clamp is a height, so both start
     identical whatever the text does; "More" lifts it. Phones show the lot —
     nothing to line up in one column. */
  .clampy{max-height:var(--feed-h, 300px); overflow:hidden}
  .clampy.open{max-height:none}
  /* Directory cards — schools, care homes, funeral directors, permitted sites
     — are long lists you read once and then scroll past forever (owner,
     16 Aug 2026). Same idea as the feed clamp and deliberately the same
     mechanism, but at every width: a ten-inch card is worse on a phone, not
     better, so this one does NOT lift on mobile. The fade is the honest part
     — it says there is more below rather than ending on a clean edge that
     reads as the end of the list. */
  .svcy{max-height:var(--svc-h, 320px); overflow:hidden; position:relative}
  .svcy.open{max-height:none}
  .svcy::after{content:""; position:absolute; left:0; right:0; bottom:0;
    height:38px; pointer-events:none;
    background:linear-gradient(to bottom, transparent, var(--card))}
  .svcy.open::after{display:none}
  /* FOLDING — a row COUNT, not a pixel height (owner, 1 Sep 2026).
     .clampy and .svcy above hide whatever overflows a max-height. That is
     right when the job is "make these two cards share a bottom edge" and
     wrong when the job is "show three": a row wraps to one, two or three
     lines, so a fixed height is a fixed number of PIXELS and the row count
     drifts with the wording (world news learned this on 30 Aug). A folded
     list hides named rows instead, so "3" means 3 at every width.
     !important because the hidden row may be an <li> or anything else that
     carries its own display — an author display rule beating a hide is the
     single most repeated bug in this file. */
  .fold-hide{display:none !important}
  .foldmore{border-top:0; padding-top:4px}
  .foldmore[hidden]{display:none}
  /* fly-tipping league strips (9 Sep 2026) */
  .ft-row{font-size:.8rem; margin-top:6px}
  .ft-strip{display:flex; align-items:center; gap:8px; margin-top:4px; font-size:.66rem; color:var(--muted)}
  .ft-strip .ft-bar{position:relative; flex:1; height:6px; border-radius:3px; background:linear-gradient(90deg, #e5e7eb, var(--accent))}
  .ft-strip .ft-bar i{position:absolute; top:-4px; width:14px; height:14px; margin-left:-7px; border-radius:50%; background:#fff; border:3px solid var(--accent); box-sizing:border-box}
  .pair > .card{flex:1 1 0; min-width:0; width:auto}
  @media (max-width:820px){
    .pair{display:block}          /* phones: one column, as before */
    .clampy{max-height:none}      /* and no clamp: nothing to line up */
  }
  /* WORLD-TAB BUG, fixed 19 Aug 2026. `display:flex` above is an author
     rule and beats the user agent's `[hidden]{display:none}`, so setting
     sectbar.hidden did nothing: the World and Relax tabs kept showing
     whichever categories the previous tab had built. Hiding must be stated
     in the same cascade layer as showing. */
  .sectbar[hidden]{display:none}
  .sectbar::-webkit-scrollbar{display:none}
  /* Tighter pills (owner, 23 Sep 2026: "the category label doesn't fit on the
     page … white space between the name and icon … too much border"). The
     gap was 7px and the left padding 12px either side of an emoji that
     already carries its own side-bearing, so the icon sat well in from the
     edge. 4px and 8px on the icon side; the label side keeps 11px. */
  .sectbar button{display:inline-flex; align-items:center; gap:4px; flex:none;
    border:1px solid var(--line); background:var(--card); color:var(--muted);
    border-radius:999px; padding:6px 11px 6px 8px; font:inherit; font-size:.8rem;
    font-weight:600; cursor:pointer; line-height:1}
  .sectbar button .ic{font-size:1.05rem; line-height:1; display:inline-flex;
    align-items:center}
  .sectbar button .ic svg{display:block}
  .sectbar button.on{background:var(--accent); border-color:var(--accent); color:#fff}
  .sect-h{font-size:.78rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
    color:var(--accent); margin:18px 0 10px; padding-bottom:6px;
    border-bottom:1px solid var(--line); scroll-margin-top:calc(var(--tabbar-h, 56px) + 72px)}
  .sect-h:first-of-type{margin-top:2px}
  /* THE FIRST HEADING ON A TAB IS NOT DRAWN (owner, 1 Oct 2026: "the
     categories 'Right Now' and 'News' take up space at the top of the page
     and they're not needed. The user knows they are looking at news because
     the category button is lit up right above the heading"). It stays in the
     page at no height, because the strip's button scrolls to it and the
     scroll-spy measures from it; removing the element would break both.
     Headings further down still mark where one section ends and the next
     begins. */
  .sect-h.lead{height:0; margin:0; padding:0; border:0; overflow:hidden}
  /* …and the rule that opens each section's run of tiles goes with it, or
     the first tile would sit under a stray line with nothing above it. */
  .grid.sectioned > .sect-h.lead + .sect{border-top:0; margin-top:0; padding-top:4px}
  /* Collapsed: the heading IS the card. Everything else is hidden, so a card
     the reader never opens costs one row rather than a screen of scrolling. */
  .card.collapsed{padding:12px 16px; cursor:pointer; margin-bottom:8px}
  .card.collapsed > *:not(h2){display:none}
  .card.collapsed h2{margin:0; font-size:.95rem; gap:7px}
  .card.collapsed h2 .badge{display:none}
  .card.collapsed h2 .fresh{font-size:.72rem}
  .card.collapsed h2 .share-btn, .card.collapsed h2 .hide-btn, .card.collapsed h2 .page-btn,
  .card.collapsed h2 .move-btn, .card.collapsed h2 .help-btn{display:none}
  .card.collapsed::after{content:"⌄"; position:absolute; right:14px; top:9px;
    color:var(--muted); font-size:1.1rem; line-height:1}
  .card.collapsed:hover{border-color:var(--accent)}
  @media (max-width:820px){
    .sectbar{padding:7px 12px; margin-bottom:10px}
    .sectbar button{padding:7px 9px}
    .sectbar button .lb{display:none}   /* icons only on a phone */
    .sectbar button .ic{font-size:1.15rem}
  }
  .grid{display:flex; gap:16px; align-items:flex-start}
  .grid .gcol{flex:1 1 0; min-width:0}
  /* raw tiles wait for the first layout — see the inline script at the top of the head */
  .js .grid:not(.laid) > .card{display:none}
  /* drag to reorder */
  .card{touch-action:manipulation}
  /* 20 Sep 2026: a card scrolled to its TOP (gotoHash, for tall tiles) must
     clear the sticky tab bar and section strip — the same clearance the
     section headings already take. */
  .card{scroll-margin-top:calc(var(--tabbar-h, 56px) + 72px)}
  body.is-dragging{user-select:none; -webkit-user-select:none}
  body.is-dragging .card{touch-action:none}
  .card.dragging{opacity:.92; z-index:20; cursor:grabbing;
    box-shadow:0 10px 28px rgba(29,43,35,.28); border-color:var(--accent);
    transition:none}
  .card.dragging *{pointer-events:none}
  .card{background:var(--card); border:1px solid var(--line); border-radius:var(--radius);
    padding:18px 20px; display:block; width:100%; margin-bottom:16px; position:relative}
  .share-btn{width:24px; height:24px; border-radius:50%; border:0;
    background:var(--accent); color:#fff; cursor:pointer; padding:0; flex-shrink:0;
    display:inline-flex; align-items:center; justify-content:center;
    box-shadow:0 1px 4px rgba(27,110,75,.45); transition:transform .12s, background .12s}
  .share-btn:hover, .share-btn:focus{background:#15563b; transform:scale(1.18)}
  .share-btn svg{width:13px; height:13px}
  .hide-btn{width:24px; height:24px; border-radius:50%; border:0; background:#eceee9;
    color:var(--muted); cursor:pointer; padding:0; flex-shrink:0; display:inline-flex;
    align-items:center; justify-content:center; transition:transform .12s, background .12s}
  .hide-btn:hover, .hide-btn:focus{background:#dde1db; color:var(--ink); transform:scale(1.12)}
  .hide-btn svg{width:14px; height:14px}
  /* The link to a tile's own page: the hide button's quiet look, as a link. */
  .page-btn{width:24px; height:24px; border-radius:50%; background:#eceee9; color:var(--muted);
    flex-shrink:0; display:inline-flex; align-items:center; justify-content:center;
    text-decoration:none; transition:transform .12s, background .12s}
  .page-btn:hover, .page-btn:focus{background:#dde1db; color:var(--ink); transform:scale(1.12)}
  .page-btn svg{width:13px; height:13px}
  /* Move arrows: ALL sizes since 1 Aug 2026 (owner: drag is fiddly with tall
     cards on desktop too — see moveCard(). Previously mobile-only; drag still
     works alongside). Original note: drag covers desktop, where
     there's room to actually drag. */
  .move-btn{display:inline-flex; width:24px; height:24px; border-radius:50%; border:0;
    background:#eceee9; color:var(--muted); cursor:pointer; padding:0; flex-shrink:0;
    align-items:center; justify-content:center; transition:background .12s, transform .12s}
  .move-btn svg{width:14px; height:14px; display:block}
  .move-btn:hover:not([disabled]), .move-btn:focus-visible:not([disabled]){
    background:#dde1db; color:var(--ink); transform:scale(1.12)}
  .move-btn[disabled]{opacity:.3; cursor:default}
  .hidden-tray{display:flex; flex-wrap:wrap; gap:8px; align-items:center;
    margin-top:14px; padding-top:14px; border-top:1px dashed var(--line)}
  .hidden-tray .htl{font-size:.75rem; color:var(--muted); font-weight:700; margin-right:2px}
  .hidden-chip{border:1px solid var(--line); background:#fff; color:var(--muted);
    border-radius:999px; padding:6px 13px; font:600 .8rem/1 inherit; font-family:inherit;
    cursor:pointer; display:inline-flex; align-items:center; gap:7px}
  .hidden-chip:hover, .hidden-chip:focus{border-color:var(--accent); color:var(--accent)}
  .hidden-chip svg{width:13px; height:13px}
  .share-pop{position:absolute; top:44px; right:14px; z-index:6; background:var(--card);
    border:1px solid var(--line); border-radius:14px; box-shadow:0 14px 38px rgba(29,43,35,.30);
    padding:12px; display:flex; gap:10px}
  .share-pop a, .share-pop button{width:42px; height:42px; border-radius:50%; border:0; cursor:pointer;
    display:flex; align-items:center; justify-content:center; color:#fff; text-decoration:none;
    padding:0; box-shadow:0 3px 8px rgba(0,0,0,.28), inset 0 1px 0 rgba(255,255,255,.25);
    background-image:linear-gradient(rgba(255,255,255,.20), rgba(255,255,255,0) 60%);
    transition:transform .12s, box-shadow .12s}
  .share-pop a:hover, .share-pop button:hover{transform:translateY(-2px) scale(1.1);
    box-shadow:0 6px 14px rgba(0,0,0,.32), inset 0 1px 0 rgba(255,255,255,.25)}
  .share-pop svg{width:20px; height:20px}
  .sp-x{background-color:#000} .sp-fb{background-color:#1877f2} .sp-wa{background-color:#25d366}
  .sp-li{background-color:#0a66c2} .sp-em{background-color:#5f6f66} .sp-cp{background-color:var(--accent)}
  .layerbar{display:flex; gap:8px; flex-wrap:wrap}
  .layer-btn{border:1.5px solid var(--red); background:var(--bg); border-radius:999px;
    --lc:var(--red);
    padding:7px 18px; font:600 .85rem/1 inherit; font-family:inherit; color:var(--red); cursor:pointer}
  .layer-btn{border-color:var(--lc); color:var(--lc)}
  .layer-btn.on{background:var(--lc); border-color:var(--lc); color:#fff}
  .layer-btn[data-layer="travel"]{--lc:#1e3a8a}
  .layer-btn[data-layer="conflict"]{--lc:#5c1024}
  .layer-btn[data-layer="famine"]{--lc:#e65100}
  .layer-btn[data-layer="disease"]{--lc:#00695c}
  .layer-btn[data-layer="disasters"]{--lc:#c62828}
  .layer-btn[data-layer="energy"]{--lc:#4527a0}
  /* Every layer sets its own --lc; without one the button falls back to
     var(--red), the danger colour, which is the wrong signal entirely for a
     layer that is just the shape of the ground. This brown is the high-ground
     end of the legend's own ramp, so the button matches the scale it opens. */
  .layer-btn[data-layer="relief"]{--lc:#6f4a2a}
  .layer-btn[data-layer="climate"]{--lc:#c2541f}   /* 18 Oct 2026 */
  /* Full screen for demonstrating on a big screen (owner, 11 Sep 2026).
     The whole TILE goes fullscreen, not just the map, because the layer
     buttons and the legend are what you are demonstrating with. A fullscreen
     element gets a black backdrop by default, so the background is set
     explicitly or the card floats in a void. */
  .card:fullscreen{border-radius:0; padding:18px 26px 14px; margin:0;
    background:var(--bg); max-width:none; width:100%; box-shadow:none;
    /* A FLEX COLUMN, not a height guess. The first version capped the map at
       calc(100vh - 250px); measured at 1920x1080 the legend then landed at
       1086px — six pixels below the fold — and the tile scrolled, which is
       precisely what a demonstration must not do. The chrome's real height
       depends on how the buttons and legend wrap, so it cannot be guessed:
       let the header, buttons and legend take what they need and give the
       map the remainder. */
    display:flex; flex-direction:column; overflow:hidden}
  .card:fullscreen #world-wrap{flex:1 1 auto; min-height:0}
  /* min-height:0 above is load-bearing: a flex item's default min-height is
     auto, which refuses to shrink below its content and would push the
     legend off screen again. */
  .card:fullscreen #world-map{height:100%; width:100%; max-height:none}
  /* The explainer prose is for a reader, not a demonstration, and it is the
     single tallest block in the tile. */
  .card:fullscreen .means{display:none}
  /* The icon was off centre because .help-btn is a 24px box with padding:0
     and no flex, and the svg was display:block — a block child sits flush
     left in the line box, text-align:center only moves INLINE content. So it
     sat ~4.5px left of centre. Centring the button itself fixes it for good. */
  .fs-btn{display:inline-flex; align-items:center; justify-content:center; gap:5px}
  .fs-btn svg{width:15px; height:15px; display:block; flex:none}
  /* The same control ON the map. The header button is easy to miss when your
     eye is on the map, which is exactly where you are looking when you decide
     you want it bigger — so it is repeated in the map's own top-right corner,
     with the words, not just an icon. */
  .map-fs{position:absolute; right:10px; top:10px; z-index:2;
    width:auto; height:auto; border-radius:999px; padding:5px 10px 5px 8px;
    background:rgba(255,255,255,.93); color:var(--ink);
    border:1px solid rgba(0,0,0,.14); box-shadow:0 1px 5px rgba(0,0,0,.16);
    font:600 .7rem/1 inherit; font-family:inherit; cursor:pointer}
  .map-fs:hover, .map-fs:focus{background:#fff; transform:none}
  .card:fullscreen .map-fs{padding:7px 13px 7px 10px; font-size:.78rem}
  @media(max-width:640px){ .fs-btn{display:none} }
  #world-wrap{position:relative}
  /* Overlays (11 Sep 2026). These are CHECKBOXES, not another radio row, and
     the distinction is the whole point: a country can only be one colour, so
     the base layers must exclude each other, but earthquakes / hotspots /
     craters are points drawn ON TOP and only mean anything stacked — quakes
     sitting on the plate lines, hotspots sitting away from them. Lower left,
     over the map, as asked.
     On a phone the map is only ~150px tall, so a floating panel would cover a
     third of it: below 640px this drops out of the corner and becomes a normal
     row under the map. */
  #world-overlays{position:absolute; left:10px; bottom:10px; z-index:2;
    background:rgba(255,255,255,.93); border:1px solid rgba(0,0,0,.14);
    border-radius:8px; padding:6px 9px; font-size:.68rem; line-height:1.3;
    box-shadow:0 1px 5px rgba(0,0,0,.14); max-width:186px;
    /* 30 Sep 2026: with Energy and Minerals open the panel grew ten rows of
       sub-headings and ran off the top of the map. It is anchored to the
       bottom, so it grows upwards -- capping it to the map's own height and
       letting it scroll is the fix that does not depend on how many overlays
       exist. overscroll-behavior stops a scroll inside the panel from
       carrying on into the page once it reaches the end. */
    max-height:calc(100% - 20px); overflow-y:auto; overscroll-behavior:contain}
  #world-overlays label{margin-top:2px}
  /* The handle. It has to LOOK draggable or nobody will try: a grip glyph,
     the move cursor, and the words. Three signals, because the box only gets
     in the way once every overlay is on and by then the reader is annoyed. */
  #world-overlays .ov-h{font-weight:700; color:var(--muted); font-size:.68rem;
    text-transform:uppercase; letter-spacing:.04em; margin:-2px -3px 3px;
    padding:2px 3px; cursor:move; user-select:none; -webkit-user-select:none;
    touch-action:none; border-radius:4px; display:flex; align-items:center; gap:5px}
  #world-overlays .ov-h:hover{background:rgba(0,0,0,.06)}
  #world-overlays .ov-grip{letter-spacing:-1px; opacity:.55; font-size:.8rem}
  #world-overlays .ov-hint{margin-left:auto; text-transform:none; letter-spacing:0;
    font-weight:400; opacity:.7; font-size:.62rem}
  #world-overlays.dragging{box-shadow:0 4px 14px rgba(0,0,0,.28); opacity:.97}
  @media(max-width:640px){
    /* below 640 the panel is a normal row under the map, so there is nothing
       to drag it around inside */
    #world-overlays .ov-h{cursor:default}
    #world-overlays .ov-hint{display:none}
  }
  #world-overlays label{display:flex; gap:6px; align-items:flex-start; cursor:pointer}
  #world-overlays input{margin:2px 0 0; flex:none; cursor:pointer}
  #world-overlays .ov-sub{display:block; color:var(--muted); font-size:.63rem}
  #world-overlays .ov-msg{display:block; font-size:.63rem; color:var(--muted)}
  @media(max-width:640px){
    #world-overlays{position:static; max-width:none; margin-top:8px;
      box-shadow:none; background:var(--bg); max-height:none; overflow-y:visible}
  }
  /* Overlay marks sit above every base layer and are never restyled by one:
     they are painted last, so document order already puts them on top. */
  #world-map .ovq{fill:#e8442e; fill-opacity:.78; stroke:#fff; stroke-width:.7}
  #world-map .ovq.big{fill:#a81b0c; fill-opacity:.85}
  #world-map .lmk:hover circle.ovq, #world-map g:hover > circle.ovq{fill-opacity:1; stroke-width:1.4}
  /* THE HIT TARGET (11 Sep 2026, owner: "the small dot is so hard to hover").
     A quake dot is 1.8–9 units across in a 1000-unit viewBox — about two
     pixels on a desktop card. Asking a reader to land a mouse on that is not
     a tooltip, it is a game. Every mark therefore gets an INVISIBLE companion
     circle with a much larger radius; the <title> sits on their shared <g>,
     so hovering anywhere NEAR the mark shows the callout, not only on top
     of it.
     `fill:none` alone would not be hit-tested at all — the default
     pointer-events:visiblePainted needs a painted fill — so pointer-events:all
     is what makes an invisible circle catch the pointer.
     cursor is DEFAULT, not help: the question-mark pointer read as "click for
     documentation", which is not what these are. */
  #world-map .hit{fill:none; stroke:none; pointer-events:all; cursor:default}
  /* Plate boundaries. Coloured by what the boundary DOES, not by Bird's
     seven codes: a reader does not need OSR/CRB/OTF/CTF/OCB/CCB/SUB, they
     need pulling apart / pushing together / sliding past. The codes stay in
     the callout for anyone who wants them. */
  #world-map .pb{fill:none; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round;
    pointer-events:none; stroke-opacity:.95;
    /* a dark casing drawn under the stroke by paint-order, so a bright line
       stays legible over pale desert and over deep ocean alike */
    paint-order:stroke; stroke-miterlimit:2}
  #world-map .pb-case{fill:none; stroke:rgba(0,0,0,.45); stroke-width:3.4;
    stroke-linecap:round; stroke-linejoin:round; pointer-events:none}
  /* Defined once and referenced by BOTH the line and its swatch in the key.
     Two copies of a palette is how a legend comes to describe a map it no
     longer matches — the same reason paintWorldLayer reads its legend
     colours off a probe element rather than repeating them. */
  :root{--pb-div:#ff7a33; --pb-con:#b451e8; --pb-tra:#19d3e8}
  #world-map .pb-div{stroke:var(--pb-div)}
  #world-map .pb-con{stroke:var(--pb-con)}
  #world-map .pb-tra{stroke:var(--pb-tra)}
  #world-overlays .sw{display:inline-block; width:9px; height:3px; border-radius:2px;
    margin-right:4px; vertical-align:2px}
  #world-overlays .sw-div{background:var(--pb-div)}
  #world-overlays .sw-con{background:var(--pb-con)}
  #world-overlays .sw-tra{background:var(--pb-tra)}
  #world-overlays .ov-key{display:block}
  /* A line is as hard to hover as a dot. Same answer: an invisible companion
     with a fat stroke. pointer-events:stroke hit-tests the stroke area of a
     path whose fill is none — which `all` would also do, but `stroke` says
     what is meant. */
  #world-map .pb-hit{fill:none; stroke:rgba(0,0,0,0); stroke-width:7;
    pointer-events:stroke; cursor:default}
  #world-map g.pbg:hover .pb{stroke-width:3.4; stroke-opacity:1}
  /* Impact craters: hollow rings, so a 160 km crater does not become a blob
     that hides the ground it sits on. */
  /* Hotspots. Gold, so they cannot be confused with the red earthquakes or
     the white crater rings, and sized by the compilation's OWN confidence
     tier rather than by anything we decided. */
  /* Tropical cyclones. The cone is the uncertainty in where it goes, so it
     is drawn as a translucent wash rather than a hard shape — a crisp edge
     would read as a boundary, which is the opposite of what a cone means. */
  #world-map .cyc-cone{fill:#ff2d6f; fill-opacity:.22; stroke:#ff2d6f;
    stroke-width:.9; stroke-opacity:.75; stroke-dasharray:3 2; pointer-events:all; cursor:default}
  /* pointer-events:all since 7 Oct 2026: hovering the shaded area itself now
     opens the storm's callout, with the places predicted to be in its path. */
  #world-map .cyc-track{fill:none; stroke:#ff2d6f; stroke-width:1.6;
    stroke-linecap:round; pointer-events:none}
  /* A GDACS-only storm has no official cone, and its "track" is the centres
     of six-hourly uncertainty circles rather than a forecast line. Dashed,
     so the map never implies more precision than the source gave. */
  #world-map .cyc-track-est{stroke-dasharray:4 3; stroke-width:1.3; stroke-opacity:.85}
  #world-map .cyc-eye{fill:#fff; stroke:#c40040; stroke-width:1.6; pointer-events:none}
  /* GDACS's own alert level, in the disasters palette already on this page,
     so red means the same thing on both layers. */
  #world-map .cyc-a-Red{stroke:#c62828}
  #world-map .cyc-a-Orange{stroke:#ef6c00}
  #world-map .cyc-a-Green{stroke:#5c7d5c}
  #world-map g.cycg:hover .cyc-cone{fill-opacity:.36}
  #world-map g.cycg:hover .cyc-eye{stroke-width:2.6}
  #world-map .hs{stroke:#4a3000; stroke-width:.8; pointer-events:none}
  #world-map .hs-deep{fill:#ffc61a; fill-opacity:1}
  #world-map .hs-secondary{fill:#ffd76b; fill-opacity:.75}
  #world-map .hs-unclassified{fill:#ffe9ab; fill-opacity:.5; stroke-opacity:.5}
  #world-map g.hsg:hover .hs{stroke-width:1.8; fill-opacity:1}
  #world-map .cr{fill:none; stroke:#1f1f1f; stroke-width:.9; stroke-opacity:.85; pointer-events:none}
  #world-map .cr-in{fill:#fff; fill-opacity:.4; pointer-events:none}
  #world-map g.crg:hover .cr{stroke-width:2.2; stroke:#000}
  /* SHIPPING CHOKEPOINTS (7 Oct 2026). Diamonds, so they cannot be mistaken
     for the round earthquakes, hotspots or craters. The colour is how busy
     the passage is against the same week a year ago. Owner's colours
     (7 Oct 2026, replacing blue/amber): RED far quieter, AMBER quieter,
     GREY much the same, GREEN busier (darker green for far busier). Colour
     is never the only signal: every chip and callout says the verdict in
     words, which is what a colour-blind reader relies on. The same five
     colours are the chips on the tile below the map (--ck-*). */
  :root{--ck-fq:#c62828; --ck-q:#f0a020; --ck-s:#9aa0a6; --ck-b:#43a047; --ck-fb:#1b6e2d}
  #world-map .chk{stroke:#16202b; stroke-width:.8; pointer-events:none}
  #world-map .chk-far-quieter{fill:var(--ck-fq)}
  #world-map .chk-quieter{fill:var(--ck-q)}
  #world-map .chk-same{fill:var(--ck-s)}
  #world-map .chk-busier{fill:var(--ck-b)}
  #world-map .chk-far-busier{fill:var(--ck-fb)}
  #world-map .chk-none{fill:#fff; fill-opacity:.55; stroke-dasharray:1.6 1.2}
  #world-map g.chkg:hover .chk{stroke-width:1.9}
  #world-overlays .ck-k{display:inline-block; width:8px; height:8px; margin:0 3px 0 6px;
    transform:rotate(45deg); border:1px solid #16202b; vertical-align:middle}
  #world-overlays .ck-k:first-child{margin-left:1px}
  /* The tile. One row per chokepoint: what it is, the count, the verdict,
     and a year of weekly averages. A grid rather than a table so that on a
     phone each row can fold into two lines instead of scrolling sideways. */
  .ck-sum{font-size:.9rem; margin:8px 0 2px; line-height:1.45}
  .ck-rows{margin-top:6px}
  .ck-wait{font-size:.82rem; color:var(--muted); padding:8px 0}
  .ck-row{display:grid; grid-template-columns:minmax(0,1.7fr) minmax(104px,.7fr) minmax(128px,.8fr) 132px;
    gap:4px 14px; align-items:center; padding:9px 0; border-top:1px solid var(--line)}
  .ck-row:first-child{border-top:0}
  .ck-name{font-weight:700; font-size:.92rem}
  .ck-what{display:block; font-weight:400; font-size:.78rem; color:var(--muted); line-height:1.4; margin-top:1px}
  .ck-bill{display:block; font-weight:400; font-size:.78rem; line-height:1.4; margin-top:1px}
  .ck-bill b{font-weight:600}
  .ck-now{font-size:1.15rem; font-weight:700; line-height:1.15; white-space:nowrap}
  .ck-now small{font-size:.72rem; font-weight:400; color:var(--muted)}
  .ck-sub{display:block; font-size:.74rem; color:var(--muted); line-height:1.35}
  .ck-chip{display:inline-block; font-size:.76rem; font-weight:700; padding:2px 9px; border-radius:999px;
    color:#fff; white-space:nowrap}
  .ck-chip.far-quieter{background:var(--ck-fq)}
  .ck-chip.quieter{background:var(--ck-q); color:#3a2500}
  .ck-chip.same{background:var(--ck-s); color:#1b1f23}
  .ck-chip.busier{background:var(--ck-b)}
  .ck-chip.far-busier{background:var(--ck-fb)}
  .ck-chip.none{background:none; color:var(--muted); border:1px dashed var(--line); font-weight:400}
  .ck-spark{display:block; width:132px; height:30px; color:var(--muted)}
  .ck-spark polyline{fill:none; stroke:currentColor; stroke-width:1.4; stroke-linejoin:round; stroke-linecap:round}
  .ck-spark .base{stroke:var(--line); stroke-width:1}
  .ck-cap{display:block; font-size:.66rem; color:var(--muted); text-align:right; width:132px}
  .ck-tools{display:flex; flex-wrap:wrap; gap:8px; margin:8px 0 2px}
  .ck-btn{border:1px solid var(--line); background:var(--card); color:inherit; border-radius:8px;
    padding:5px 11px; font:inherit; font-size:.8rem; cursor:pointer}
  .ck-btn:hover{border-color:var(--accent)}
  .ck-src{font-size:.74rem; color:var(--muted); margin:8px 0 2px; line-height:1.45}
  @media (max-width:700px){
    .ck-row{grid-template-columns:minmax(0,1fr) auto; gap:6px 12px}
    .ck-row > .ck-c1{grid-column:1 / -1}
    .ck-row > .ck-c4{grid-column:1 / -1}
    .ck-spark, .ck-cap{width:100%}
    .ck-cap{text-align:left}
    /* On a phone the description goes: the name, the bill it feeds and the
       numbers are the tile; the sentence is one tap away on the map. */
    .ck-what{display:none}
    .ck-spark{height:22px}
  }
  /* OIL TO PUMP (7 Oct 2026). Three figures, then a litre of each fuel
     drawn as one bar cut into what it is made of. HTML bars, not SVG, so the
     labels scale with the page's own text (the Falklands ledger lesson),
     and widths are true shares of the pump price -- no max-width caps. */
  :root{--op-duty:#5b6b7a; --op-vat:#9aa9b8; --op-crude:#22262b; --op-rest:#c98a2b}
  .op-stats{display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:10px; margin:10px 0 4px}
  .op-stat{border:1px solid var(--line); border-radius:10px; padding:9px 11px}
  .op-stat .k{display:block; font-size:.72rem; color:var(--muted); text-transform:uppercase; letter-spacing:.04em}
  .op-stat .v{display:block; font-size:1.35rem; font-weight:700; line-height:1.2; margin-top:2px}
  .op-stat .v small{font-size:.74rem; font-weight:400; color:var(--muted)}
  .op-stat .s{display:block; font-size:.76rem; color:var(--muted); line-height:1.4; margin-top:2px}
  .op-h{font-size:.86rem; font-weight:700; margin:14px 0 4px}
  .op-fuel{margin:8px 0 10px}
  .op-fuel .t{font-size:.86rem; margin-bottom:4px}
  .op-fuel .t b{font-size:1rem}
  .op-fuel .t span{color:var(--muted); font-size:.78rem}
  .op-bar{display:flex; height:26px; border-radius:6px; overflow:hidden; background:var(--line)}
  .op-bar i{display:block; height:100%}
  .op-bar .duty{background:var(--op-duty)} .op-bar .vat{background:var(--op-vat)}
  .op-bar .crude{background:var(--op-crude)} .op-bar .rest{background:var(--op-rest)}
  .op-bar .both{background:repeating-linear-gradient(135deg, var(--op-crude) 0 8px, var(--op-rest) 8px 16px)}
  .op-key{display:flex; flex-wrap:wrap; gap:3px 14px; font-size:.76rem; margin-top:5px}
  .op-key i{display:inline-block; width:9px; height:9px; border-radius:2px; margin-right:5px; vertical-align:baseline}
  .op-note{font-size:.84rem; line-height:1.5; margin:8px 0 2px}
  .op-chart{margin-top:6px}
  @media (max-width:700px){
    .op-stats{grid-template-columns:1fr}
    .op-stat .v{font-size:1.2rem}
  }
  /* GAS TO ENERGY BILL (7 Oct 2026) reuses the Oil to pump blocks (.op-*);
     only the cap bar's two colours are its own. */
  :root{--gb-whole:#1f4e9c; --gb-rest:#b9c6d6}
  .op-bar .whole{background:var(--gb-whole)} .op-bar .other{background:var(--gb-rest)}
  .gb-warn{font-size:.8rem; border:1px dashed var(--line); border-radius:8px; padding:6px 10px; margin:8px 0}
  /* HEALTH FOOD & SUPPLEMENTS (9 Oct 2026): cost a day against strength of
     trial evidence. Shape AND colour say food or supplement (a circle is a
     food, a square a supplement), so the chart does not depend on colour. */
  :root{--hf-food:#1b6e4b; --hf-supp:#22577c; --hf-best:#e3f0e9}
  .hf-summary{font-size:.9rem; line-height:1.45; margin:8px 0 6px}
  .hf-legend{display:flex; flex-wrap:wrap; gap:4px 14px; font-size:.74rem; color:var(--muted); margin:6px 0 2px}
  .hf-legend svg{vertical-align:-2px; margin-right:4px}
  .hf-chart{position:relative; margin:4px 0 0}
  .hf-chart svg{display:block; width:100%; height:auto; overflow:visible}
  .hf-chart text{font-family:inherit; fill:var(--ink)}
  .hf-chart .ax{font-size:10.5px; fill:var(--muted)}
  .hf-chart .q{font-size:9.5px; font-weight:700; letter-spacing:.05em; fill:var(--muted); opacity:.75}
  .hf-chart .lab{font-size:10.5px; pointer-events:none}
  .hf-dot{cursor:pointer; outline:none}
  .hf-dot:focus-visible .ring, .hf-dot.sel .ring{stroke:var(--ink); stroke-width:2}
  .hf-detail{border:1px solid var(--line); border-radius:10px; padding:10px 12px; margin:8px 0; font-size:.86rem; line-height:1.45}
  .hf-detail p{margin:6px 0 0}
  .hf-d-name{font-weight:700; font-size:.98rem}
  .hf-pill{display:inline-block; font-size:.7rem; font-weight:700; border-radius:999px; padding:1px 8px; margin-left:6px; border:1px solid var(--line); white-space:nowrap}
  .hf-pill.hi{background:var(--hf-best); border-color:var(--hf-food); color:var(--hf-food)}
  .hf-pill.lo{background:var(--red-soft); border-color:var(--red); color:var(--red)}
  .hf-caution{background:var(--amber-soft); border-left:4px solid var(--amber); padding:5px 9px; border-radius:4px}
  .hf-src{font-size:.76rem; color:var(--muted)}
  .hf-h{font-size:.78rem; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); margin:12px 0 4px}
  .hf-prow{display:grid; grid-template-columns:minmax(7.5em, 34%) 1fr 3.2em; gap:8px; align-items:center; padding:4px 0; border-top:1px solid var(--line); font-size:.84rem; cursor:pointer; background:none; border-left:0; border-right:0; border-bottom:0; width:100%; text-align:left; color:inherit; font-family:inherit}
  .hf-prow:first-of-type{border-top:0}
  .hf-prow .bar{height:10px; border-radius:3px; background:var(--hf-food)}
  .hf-prow .bar.supp{background:var(--hf-supp)}
  .hf-prow .n{text-align:right; font-variant-numeric:tabular-nums; font-weight:700}
  .hf-prow small{display:block; color:var(--muted); font-size:.72rem}
  .hf-all{margin:10px 0 4px; font-size:.84rem}
  .hf-all > summary{cursor:pointer; font-weight:700}
  .hf-li{padding:7px 0; border-top:1px solid var(--line); line-height:1.4}
  .hf-li .c{font-variant-numeric:tabular-nums; font-weight:700}
  .hf-key{font-size:.76rem; color:var(--muted); line-height:1.45; margin:6px 0}
  .hf-key b{color:var(--ink)}
  /* WORLD EVENTS AND YOUR BILLS (8 Oct 2026): a short list and one article. */
  #card-world-brief[hidden]{display:none}   /* .card's display:block would beat [hidden] */
  #card-money-brief[hidden], #card-uk-brief[hidden]{display:none}   /* 12 Oct 2026: same rule, same reason */
  .wb-head{font-size:.78rem; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); margin:6px 0 2px}
  .wb-list{list-style:none; margin:0; padding:0}
  .wb-list li{display:grid; grid-template-columns:6.4em 1fr; gap:10px; padding:7px 0; border-top:1px solid var(--line); font-size:.9rem; line-height:1.4}
  .wb-list li:first-child{border-top:0}
  .wb-topic{font-weight:700}
  .wb-list a{white-space:nowrap; font-size:.8rem}
  .wb-art{border:1px solid var(--line); border-radius:8px; padding:9px 12px; margin:10px 0 4px}
  .wb-art .k{font-size:.72rem; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--muted)}
  .wb-art a.t{display:block; font-weight:700; font-size:1rem; margin:2px 0}
  .wb-art p{margin:0; font-size:.86rem; color:var(--muted)}
  @media (max-width:520px){ .wb-list li{grid-template-columns:1fr; gap:1px} }
  .ck-how{font-size:.74rem; color:var(--muted); margin:4px 0 2px; line-height:1.45}
  .ck-how summary{cursor:pointer}
  /* ENERGY AND MINERALS (30 Sep 2026). Two deliberate weight choices.
     Petroleum provinces are FILLED but faint: a basin covers a chunk of a
     continent, so anything stronger buries the countries underneath it.
     Mineral deposits are drawn smaller and duller than the hotspots and
     earthquakes on purpose -- there are 2,079 of them, and at hotspot
     weight they would read as the main subject of the map rather than an
     overlay on it. */
  :root{--en-oil:#1b7a4b; --en-gas:#1565c0; --en-una:#6d7f8c; --en-us:#7d7f86;
    --mn-bat:#5b2c9e; --mn-elec:#0f6fbd; --mn-mag:#00897b; --mn-cat:#8d6e63;
    --mn-str:#546e7a; --mn-ind:#8a8f1e; --mn-mix:#37474f}
  /* fill and stroke are both set explicitly because #world-map path{} below
     gives every unclaimed path a grey fill and a white stroke. */
  #world-map .bsn{stroke-width:.8; fill-opacity:.2; stroke-opacity:.85;
    pointer-events:none}
  #world-map .bsn-Oil{fill:var(--en-oil); stroke:var(--en-oil)}
  #world-map .bsn-Gas{fill:var(--en-gas); stroke:var(--en-gas)}
  #world-map .bsn-NA{fill:var(--en-una); stroke:var(--en-una)}
  /* The US provinces are BOUNDARIES with no assessment behind them, so they
     are drawn as an outline rather than a solid: a dashed edge and barely any
     fill says "we know the shape, not the answer" without a legend. */
  #world-map .bsn-US{fill:var(--en-us); stroke:var(--en-us); stroke-dasharray:3 2}
  #world-map .bsn.bsn-US{fill-opacity:.09}
  #world-map g.bsng:hover .bsn.bsn-US{fill-opacity:.3}
  /* The same answer as the plate lines: an invisible companion that is easy
     to hit. Here the shape has a fill, so pointer-events is `all` rather
     than `stroke` -- the inside of the basin should be hoverable too. */
  #world-map .bsn-hit{fill:rgba(0,0,0,0); stroke:rgba(0,0,0,0);
    stroke-width:4; pointer-events:all; cursor:default}
  #world-map g.bsng:hover .bsn{fill-opacity:.42; stroke-width:1.8}
  #world-map .mnd{stroke:#fff; stroke-width:.5; stroke-opacity:.75;
    pointer-events:none}
  #world-map .mnd-bat{fill:var(--mn-bat)}
  #world-map .mnd-elec{fill:var(--mn-elec)}
  #world-map .mnd-mag{fill:var(--mn-mag)}
  #world-map .mnd-cat{fill:var(--mn-cat)}
  #world-map .mnd-str{fill:var(--mn-str)}
  #world-map .mnd-ind{fill:var(--mn-ind)}
  #world-map .mnd-mix{fill:var(--mn-mix)}
  #world-map g.mng:hover .mnd{stroke:#000; stroke-width:1.4; stroke-opacity:1}
  /* Sub-headings inside the panel. Indented under their parent and hidden
     until it is ticked, so the panel is no taller than before for the five
     overlays that have none. */
  #world-overlays .ov-parts{margin:1px 0 4px 19px}
  #world-overlays .ov-parts label{font-size:.63rem; gap:5px; margin-top:1px}
  #world-overlays .ov-parts input{transform:scale(.85); transform-origin:left}
  #world-overlays .swd{display:inline-block; width:8px; height:8px;
    border-radius:2px; margin-right:4px; vertical-align:-1px}
  #world-overlays .swd-en-oil{background:var(--en-oil)}
  #world-overlays .swd-en-gas{background:var(--en-gas)}
  #world-overlays .swd-en-una{background:var(--en-una)}
  #world-overlays .swd-en-us{background:var(--en-us)}
  #world-overlays .swd-mn-bat{background:var(--mn-bat)}
  #world-overlays .swd-mn-elec{background:var(--mn-elec)}
  #world-overlays .swd-mn-mag{background:var(--mn-mag)}
  #world-overlays .swd-mn-cat{background:var(--mn-cat)}
  #world-overlays .swd-mn-str{background:var(--mn-str)}
  #world-overlays .swd-mn-ind{background:var(--mn-ind)}
  #world-overlays .swd-mn-mix{background:var(--mn-mix)}
  #world-map .ovq, #world-map .lmk circle.mk{pointer-events:none}
  #world-map{width:100%; height:auto; display:block; background:#eef2ee; border-radius:10px}
  #world-map path{fill:#d5dcd6; stroke:#fff; stroke-width:.5}
  /* One hue per layer, two shades: severe darker, heightened lighter.
     Conflict burgundy, disasters red (owner's palette); travel navy.
     Famine's orange follows the IPC's phase palette. Layers show one at a
     time, so hues only need to differ on the buttons, not side by side. */
  #world-map path.w-sev, #world-map path.w-hei{cursor:pointer}
  #world-map path.w-sev:hover, #world-map path.w-hei:hover{opacity:.75}
  #world-map[data-layer="travel"]    path.w-sev{fill:#1e3a8a}
  #world-map[data-layer="travel"]    path.w-hei{fill:#8da9dd}
  #world-map[data-layer="conflict"]  path.w-sev{fill:#5c1024}
  #world-map[data-layer="conflict"]  path.w-hei{fill:#a3556b}
  #world-map[data-layer="famine"]    path.w-sev{fill:#e65100}
  #world-map[data-layer="famine"]    path.w-hei{fill:#ffb74d}
  #world-map[data-layer="disease"]   path.w-sev{fill:#00695c}
  #world-map[data-layer="disease"]   path.w-hei{fill:#4db6ac}
  #world-map[data-layer="disasters"] path.w-sev{fill:#c62828}
  #world-map[data-layer="disasters"] path.w-hei{fill:#ef5350}
  /* third tier, disasters only: GDACS Green = tracked, no international
     response needed — where most newsworthy-but-managed disasters sit */
  #world-map[data-layer="disasters"] path.w-mon{fill:#f8bcb9}
  /* Energy (30 Aug 2026): every scored region is painted — the layer
     assesses eight regions, so a pale country is a WATCH/SECURE verdict,
     not an absence of data; unscored countries stay grey as elsewhere. */
  #world-map[data-layer="energy"]    path.w-sev{fill:#4527a0}
  #world-map[data-layer="energy"]    path.w-hei{fill:#9575cd}
  #world-map[data-layer="energy"]    path.w-mon{fill:#ded6f0}
  /* NUMERIC layers ("In numbers", 28 Aug 2026): a choropleth, not a
     severity map. One hue light->dark for magnitude (five quantile bins),
     and for the one signed layer (natural change) a blue<->red pair with
     no grey midpoint on the map itself — zero is the boundary between the
     two arms, so a shrinking country is blue and a growing one red, and
     the legend says so in words. Steps from the validated default ramps. */
  .layerbar.nums{margin-top:6px; align-items:center}
  .layerbar[hidden]{display:none}   /* display:flex above would beat [hidden] — HANDOVER §5, third time */
  .layerbar.nums .lbl-nums{font-size:.72rem; color:var(--muted); font-weight:600;
    letter-spacing:.04em; text-transform:uppercase; margin-right:2px}
  .layer-btn[data-layer^="n:"]{--lc:#1c5cab; padding:6px 14px; font-size:.8rem}
  /* Phones: eight pills of numbers took more height than the map itself
     (owner, 30 Aug). Below 640px the row collapses to ONE pill — a native
     <select> dressed as a layer button, so the picker is the phone's own
     and never needs positioning. The buttons stay the source of truth on
     wider screens; the select mirrors the same groups. */
  .layer-sel{display:none; --lc:#1c5cab; border:1.5px solid var(--lc); color:var(--lc);
    background:var(--bg); border-radius:999px; padding:6px 24px 6px 12px;
    font:600 .8rem/1 inherit; font-family:inherit; cursor:pointer; -webkit-appearance:none; appearance:none;
    background-image: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='%231c5cab'/%3E%3C/svg%3E");
    background-repeat:no-repeat; background-position:right 9px center;
    /* lives in the judgement row on phones and takes whatever is left of
       the second line — flex-basis 0 with min-width 0 means it can shrink
       rather than wrap to a third line (owner, 30 Aug); the option labels
       are kept short for the same reason (see SHORT_GROUP) */
    flex:1 1 0; min-width:0; max-width:100%; text-overflow:ellipsis; white-space:nowrap; overflow:hidden}
  .layer-sel.on{background-color:var(--lc); color:#fff;
    background-image: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='%23fff'/%3E%3C/svg%3E")}
  @media(max-width:640px){
    .layerbar.nums{display:none}
    .layer-sel{display:inline-block}
    /* Phones: Famine and Energy move INTO the More menu so the visible row
       is Travel advice / Conflict / Disease / Disasters / Elevation + More,
       which fits two lines at 360px with room left for the menu itself.
       KEEP THIS LIST IN STEP WITH `IN_MORE_MENU` in the script — CSS hides
       the pill, JS puts the option in the menu, and if they disagree a layer
       becomes unreachable on a phone. */
    .layer-btn[data-layer="famine"],
    .layer-btn[data-layer="energy"]{display:none}
  }
  /* Narrow phones (360/375): slimmer pills so the visible row stays on two
     lines with real room for the More menu at the end.
     Re-tightened 11 Sep 2026. "GeoDynamics" is 106px against "Elevation"'s
     79px, which at 360px pushed Conflict onto line two and squeezed the menu
     to 34px — a chevron and nothing else. 12px of side padding became 9px,
     measured: line one back to three pills and the menu back to ~140px. */
  @media(max-width:400px){
    .layer-btn{padding:7px 9px; font-size:.8rem}
    .layer-sel{font-size:.78rem; padding:6px 20px 6px 10px; background-position:right 7px center}
  }
  #world-map path.nq0{fill:#cde2fb} #world-map path.nq1{fill:#86b6ef}
  #world-map path.nq2{fill:#3987e5} #world-map path.nq3{fill:#1c5cab}
  #world-map path.nq4{fill:#0d366b}
  /* Fixed-band layers get their own ramp (17 Sep 2026). Six steps, not five,
     because the interesting range runs from "earns more than it pays" to
     "six pounds in ten", and a fifth step would have put Kenya at 30% in the
     same colour as Egypt at 61%. Deliberately NOT the nq/nd classes: those
     belong to every equal-count layer on the map and must not shift. */
  #world-map path.nb0{fill:#dbe9fa} #world-map path.nb1{fill:#a9c9f2}
  #world-map path.nb2{fill:#6ea5e8} #world-map path.nb3{fill:#3878cf}
  #world-map path.nb4{fill:#1d4f96} #world-map path.nb5{fill:#0a2a55}
  #world-map path.nd0{fill:#1c5cab} #world-map path.nd1{fill:#86b6ef}
  #world-map path.nd2{fill:#f6c3c2} #world-map path.nd3{fill:#ec8583}
  #world-map path.nd4{fill:#c62828}
  #world-map path[class^="n"]{cursor:pointer}
  #world-map path[class^="n"]:hover{opacity:.75}
  /* Elevation layer (11 Sep 2026). The relief is ONE static image of the
     whole world, not tiles: this map never pans or zooms, so a single
     GetMap answer covers every view it will ever have. It is hidden until
     the layer is chosen AND its href is only set on first choice, so a
     reader who never opens the layer never pays for the image. */
  #world-map image{display:none}
  #world-map[data-layer="relief"] image{display:block}
  /* Climate layer (18 Oct 2026): the WorldClim grid drawn as one image per
     month and variable, in the map's own frame (lon -180..180, lat 84..-56),
     under the borders as white hairlines — the relief pattern. The relief
     rule above shows EVERY image, so the climate image opts out of it. */
  #world-map[data-layer="relief"] image#world-climate{display:none}
  #world-map[data-layer="climate"] image#world-climate{display:block}
  #world-map[data-layer="climate"] path[data-iso]{fill:none; stroke:#fff; stroke-width:.4;
    stroke-opacity:.6; pointer-events:none}
  #world-map[data-layer="climate"]{cursor:crosshair}
  #world-nlegend .cl-ctl{display:flex; flex-wrap:wrap; gap:6px 12px; align-items:center; font-size:.8rem; flex-basis:100%}
  #world-nlegend .cl-ctl select{font:inherit; font-size:.8rem; padding:3px 6px; border:1px solid var(--line); border-radius:7px;
    background:var(--bg-soft,#fff); color:inherit}
  #world-nlegend .cl-seg{display:inline-flex; border:1px solid var(--line); border-radius:8px; overflow:hidden}
  #world-nlegend .cl-seg button{font:inherit; font-size:.78rem; border:0; padding:4px 9px; background:transparent; color:inherit; cursor:pointer}
  #world-nlegend .cl-seg button.on{background:#c2541f; color:#fff}
  #world-nlegend .cl-ramp{display:block; height:10px; border-radius:3px; max-width:340px}
  #world-nlegend .cl-ends{position:relative; display:block; height:1.2em; max-width:340px; font-size:.72rem; color:var(--muted)}
  #world-nlegend .cl-ends span{position:absolute; top:0; transform:translateX(-50%); white-space:nowrap}
  #world-nlegend .cl-ends span:first-child{transform:none}
  #world-nlegend .cl-ends span:last-child{transform:translateX(-100%)}
  #world-nlegend .cl-read{flex-basis:100%; font-size:.82rem; min-height:2.6em}
  #world-nlegend .cl-bars{display:flex; gap:2px; align-items:flex-end; height:34px; margin-top:4px; max-width:340px}
  #world-nlegend .cl-bars span{flex:1; background:#c2541f; opacity:.55; border-radius:2px 2px 0 0; min-height:2px}
  #world-nlegend .cl-bars span.now{opacity:1}
  #world-nlegend .cl-mon{display:flex; gap:2px; max-width:340px; font-size:.62rem; color:var(--muted)}
  #world-nlegend .cl-mon span{flex:1; text-align:center}
  /* Borders stay, as white hairlines over the relief: without them nobody
     can tell which country a mountain range is in. Fills go, or the
     countries would paint over the very thing the layer exists to show. */
  /* pointer-events:none is deliberate and must stay EXPLICIT. With fill:none
     the paths already fall through to the image behind them -- hit-testing
     needs a painted fill -- so the countries stop being hoverable whether we
     say so or not. Saying so is the difference between a decision and an
     accident that a later fill:rgba(...) would silently undo. This layer is
     about the ground, not about countries: the callout would have nothing to
     put in it, and the eight landmark markers carry the hover instead. */
  /* SCOPED TO COUNTRY PATHS ONLY, and it must stay that way.
     `#world-map[data-layer="relief"] path` is specificity (1,1,1) — higher
     than `#world-map .pb-div` (1,1,0) — so the bare version repainted every
     overlay line white at 0.4 width as well as the countries, and the plate
     boundaries shipped as faint grey hairlines. Only country paths carry
     data-iso, so that is the hook. */
  #world-map[data-layer="relief"] path[data-iso]{fill:none; stroke:#fff; stroke-width:.4;
    stroke-opacity:.5; cursor:default; pointer-events:none}
  #world-map[data-layer="relief"] path[data-iso]:hover{opacity:1}
  #world-map .lmk{display:none}
  #world-map[data-layer="relief"] .lmk{display:block}
  #world-map .lmk circle.mk{stroke:#fff; stroke-width:1.4; fill:#1d2b23}
  #world-map .lmk circle.up{fill:#7a3b12}
  #world-map .lmk circle.down{fill:#07305e}
  #world-map .lmk:hover circle.mk{stroke-width:2.4}
  /* Indicative only, and the legend text says so. Stops read off GEBCO's own
     rendering; sea level sits at the 50% mark, where the "sea level" label
     lands under it. */
  #rel-legend .ramp{display:block; width:100%; max-width:330px; height:13px; border-radius:3px;
    border:1px solid rgba(0,0,0,.18);
    background:linear-gradient(to right,#08246b 0%,#1663c7 24%,#45d6f2 46%,
      #48b551 50%,#a8d18a 62%,#e3d3a2 74%,#b07b48 88%,#6f4a2a 100%)}
  #rel-legend .ends{display:flex; justify-content:space-between; font-size:.7rem; opacity:.8}
  /* ELEVATION 3D OVERLAY (2 Oct 2026, owner: "the geodynamics layer can stay,
     just add an overlay for this ... the top one, above tectonic plates", then
     "Elevation 2D, Elevation 3D" for the pair -- the base layer was GeoDynamics).
     Unlike every other overlay it is drawn UNDER the countries, not over
     them: a picture of the ground belongs beneath the borders, and beneath
     the plate lines and quake dots that stack on top of it. The rule above
     hides every map <image> except on Elevation 2D, so this one needs its own
     -- the id makes it (2,0,1), which wins. */
  #world-map image#ov-relief{display:block; pointer-events:none}
  /* Countries go see-through so the relief shows; borders stay as hairlines.
     Scoped to the PARKED base layer: if the reader picks a judgement layer
     while this is on, that was their choice and its colours win.
     pointer-events:fill, not none: fill-based hit-testing ignores the fill's
     value, so countries stay hoverable with no fill at all -- which keeps the
     Minerals overlay's country box working on top of the relief. */
  #world-map.relief-under[data-layer="none"] path[data-iso]{fill:none; stroke:#fff;
    stroke-width:.4; stroke-opacity:.5; pointer-events:fill}
  /* The eight measured landmarks belong with any picture of the ground. */
  #world-map.relief-under .lmk{display:block}
  /* The overlay's colour key, small enough for the panel. Its stops ARE
     tools/relief3d.py's SEA and LAND ramps, and `relief3d.py selftest`
     compares the two, so re-colouring the picture without the page fails.
     Spaced by colour stop, not by metre: the sea half covers 5,500 m and the
     land half 4,200. */
  #world-overlays .rl-ramp{display:block; height:8px; border-radius:2px; margin:3px 0 1px;
    border:1px solid rgba(0,0,0,.18);
    background:linear-gradient(to right,#0e2256 0%,#1e488c 12.5%,#3a78b2 25%,#68b2ce 37.5%,
      #96d2de 50%,#568c5c 50%,#80a668 58.33%,#c4c080 66.67%,#ba9662 75%,#967864 83.33%,
      #cdc8c4 91.67%,#fafafc 100%)}
  #world-overlays .rl-ends{display:flex; justify-content:space-between; font-size:.6rem}
  #world-overlays .r3-open{display:inline-block; margin:4px 0 1px; border:1px solid var(--line); background:var(--card);
    color:inherit; border-radius:999px; padding:2px 9px; font:inherit; font-size:.64rem;
    font-weight:600; cursor:pointer; text-decoration:none}
  #world-overlays .r3-open:hover{border-color:#6f4a2a}
  /* The 3D view is its own page now, /world3d (owner, 7 Oct: "as soon as you click on view
     the world in 3d, it should go straight into the world3d page"); the dialog that held
     it here, and its styles, are gone. */
  #rel-legend .facts{flex-basis:100%; font-size:.72rem; line-height:1.5}
  #world-nlegend{flex-wrap:wrap; row-gap:6px}
  #world-tip .nl-row{display:block; margin-top:3px}
  #world-tip .nl-row .yr{color:var(--muted); font-size:.76rem}
  #world-tip .nl-row .ours{color:var(--muted); font-size:.72rem; font-style:normal;
    border:1px solid var(--line); border-radius:999px; padding:0 6px}
  #world-tip .act{margin-top:6px}
  #world-nlegend .nl-note{flex-basis:100%; font-size:.72rem}
  #world-tip.pinned{pointer-events:auto}
  #world-tip{position:absolute; z-index:7; max-width:340px; background:var(--card);
    border:1px solid var(--line); border-radius:11px; box-shadow:0 12px 32px rgba(29,43,35,.28);
    padding:12px 14px; font-size:.84rem; pointer-events:none}
  #world-tip b{display:block; margin-bottom:3px}
  /* A forecast, set apart from the facts above it (7 Oct 2026). */
  #world-tip .tip-pred{display:block; margin-top:7px; padding-top:6px; border-top:1px dashed var(--line)}
  #world-tip .tip-pred strong{text-transform:uppercase; font-size:.72rem; letter-spacing:.04em;
    background:#ff2d6f; color:#fff; border-radius:4px; padding:1px 6px; margin-right:2px}
  #world-tip .mc-row{display:block; margin-top:6px}
  #world-tip .mc-row b{display:inline; margin:0}
  #world-tip .mc-n{color:var(--muted)}
  #world-tip .mc-has{display:block; font-size:.78rem}
  #world-tip .mc-miss{display:block; font-size:.78rem; color:#a8322a}
  #world-tip .mc-all{display:block; font-size:.78rem; color:#1b7a4b}
  #world-tip .mc-none{display:block; font-size:.78rem; color:var(--muted)}
  #world-tip .src{display:block; margin-top:6px; font-size:.7rem; color:var(--muted)}
  /* World news: the category picker. A wrapping row of icon buttons — the
     tile must survive eleven of them on a 320px phone, so they wrap rather
     than scroll sideways (a horizontal scroller hides categories with no
     hint that they exist). */
  .wn-cats{display:flex; flex-wrap:wrap; gap:6px; margin:8px 0 2px}
  .wn-cat{width:38px; height:38px; padding:0; font-size:1.05rem; line-height:1;
    display:inline-flex; align-items:center; justify-content:center;
    background:#fff; border:1px solid var(--line); border-radius:9px;
    cursor:pointer; position:relative; transition:border-color .12s, background .12s}
  .wn-cat:hover{border-color:var(--blue); background:var(--blue-soft)}
  .wn-cat:focus-visible{outline:2px solid var(--blue); outline-offset:2px}
  .wn-cat.on{background:var(--blue); border-color:var(--blue)}
  /* The emoji itself cannot be recoloured, so the selected state is the
     chip behind it plus a ring — never colour alone. */
  .wn-cat.on{box-shadow:0 0 0 3px var(--blue-soft)}
  .wn-cat.empty{opacity:.38; cursor:default}
  /* A two-glyph icon (CO₂ is an earth plus a temperature gauge) has to fit
     the same 38px button as a single one, or the row stops being a tidy
     grid of equal squares. */
  .wn-cat.duo{font-size:.72rem; letter-spacing:-.08em}
  .wn-cat .n{position:absolute; top:-5px; right:-5px; min-width:15px; height:15px;
    padding:0 3px; border-radius:8px; background:var(--blue); color:#fff;
    font-size:.58rem; font-weight:700; line-height:15px; text-align:center}
  .wn-cat.on .n{background:var(--ink); color:#fff}
  .wn-cat.all{width:auto; padding:0 11px; font-size:.78rem; font-weight:600; color:var(--blue)}
  .wn-cat.all.on{color:#fff}
  /* SUB-CATEGORIES (16 Sep 2026). A second, wrapping row of small text
     chips that appears under the icon bar only while a category that HAS
     sub-categories is selected — the reader clicks a main category and
     "the sub categories open up below". Text, not icons: thirteen sports
     have no thirteen unambiguous glyphs, and a chip that has to be hovered
     to be understood is a chip nobody uses. Hidden (not emptied) when no
     category is selected, so the tile's height does not jump on "All". */
  .wn-subs{display:flex; flex-wrap:wrap; gap:5px; margin:6px 0 0; padding:6px 0 2px;
    border-top:1px dashed var(--line)}
  .wn-subs[hidden]{display:none}
  .wn-sub{padding:3px 7px; font-size:.72rem; line-height:1.3; font-weight:600;
    background:#fff; color:var(--blue); border:1px solid var(--line); border-radius:999px;
    cursor:pointer; transition:border-color .12s, background .12s}
  .wn-sub:hover{border-color:var(--blue); background:var(--blue-soft)}
  .wn-sub:focus-visible{outline:2px solid var(--blue); outline-offset:2px}
  .wn-sub.on{background:var(--blue); border-color:var(--blue); color:#fff}
  .wn-sub.empty{opacity:.38; cursor:default}
  .wn-sub .n{opacity:.7; font-weight:500; margin-left:3px}
  .wn-sub.on .n{opacity:.85}
  /* One measured row, then the rest. The main line wraps in the DOM so it
     can be measured; the chips that wrapped are moved below by script. */
  .wn-subs{display:block}
  .wn-subs-main, .wn-subs-over{display:flex; flex-wrap:wrap; gap:5px}
  .wn-subs-over{margin-top:5px; padding-top:5px; border-top:1px dotted var(--line)}
  .wn-subs-over[hidden]{display:none}
  .wn-sub.more{color:var(--ink); opacity:.8}
  .wn-sub.more[hidden]{display:none}
  .wn-head{margin:6px 0 0}
  /* The money tile's pinned line (23 Sep 2026): one dated notice above the
     categories — the price cap — hand-kept in app/moneynews.PINNED. */
  .news-pin{display:flex; gap:9px; align-items:flex-start; background:var(--blue-soft);
    border-radius:10px; padding:9px 12px; margin:6px 0 10px; font-size:.88rem}
  .news-pin a{white-space:nowrap}
  .wn-head b{font-size:.95rem}
  .wn-head .meta{display:block; font-size:.72rem; color:var(--muted); margin-top:1px}
  /* Citizens v the state (14 Sep 2026). The court picker is a row of WORD
     chips rather than the icon squares the news tiles use: six courts whose
     NAMES are the whole point — nobody recognises an emoji for "Upper
     Tribunal (Tax and Chancery Chamber)". They wrap rather than scroll
     sideways, for the same reason .wn-cats does: a horizontal scroller hides
     options with no hint that they exist. */
  .cr-cats{display:flex; flex-wrap:wrap; gap:6px; margin:8px 0 2px}
  .cr-cat{padding:5px 11px; font-size:.78rem; font-weight:600; line-height:1.15;
    background:#fff; border:1px solid var(--line); border-radius:999px;
    color:var(--blue); cursor:pointer; transition:border-color .12s, background .12s}
  .cr-cat:hover{border-color:var(--blue); background:var(--blue-soft)}
  .cr-cat:focus-visible{outline:2px solid var(--blue); outline-offset:2px}
  .cr-cat.on{background:var(--blue); border-color:var(--blue); color:#fff;
    box-shadow:0 0 0 3px var(--blue-soft)}
  .cr-cat .n{font-weight:700; opacity:.75; margin-left:5px}
  .cr-cat.on .n{opacity:.9}
  /* A court that did not answer is still DRAWN — dimmed, unclickable, with
     the reason in its tooltip. Silence and absence are different answers, and
     a court that simply vanished from the row would read as the second. */
  .cr-cat.dead{opacity:.45; cursor:default; color:var(--muted); background:var(--bg)}
  .cr-chips{margin-top:4px; display:flex; flex-wrap:wrap; gap:5px}
  .cr-chip{font-size:.68rem; font-weight:600; border-radius:999px; padding:2px 8px;
    border:1px solid var(--line); color:var(--muted); background:var(--bg)}
  .cr-chip.jr{color:var(--blue); background:var(--blue-soft); border-color:var(--blue-soft)}
  .cr-chip.body{color:var(--amber); background:var(--amber-soft); border-color:var(--amber-soft)}
  /* The route map under the list is HAND-WRITTEN and static — how a challenge
     to the state actually travels. It is ours, so it costs no licence, cannot
     go stale with the feed, and is the demystification layer the site
     promises over anything hard to read. */
  .cr-route{border-top:1px solid var(--line); padding-top:10px; font-size:.82rem}
  .cr-flow{display:flex; flex-wrap:wrap; align-items:stretch; gap:6px; margin:8px 0 0}
  .cr-step{flex:1 1 170px; min-width:0; background:var(--bg);
    border:1px solid var(--line); border-radius:9px; padding:7px 9px}
  .cr-step b{display:block; font-size:.8rem}
  .cr-step span{display:block; font-size:.7rem; color:var(--muted); margin-top:2px}
  .cr-arrow{align-self:center; color:var(--muted); font-size:.95rem}
  /* On a phone the three steps stack, so the arrow between them has to point
     DOWN or it contradicts the layout it is describing.
     ⚠ AND `flex-basis` FOLLOWS THE MAIN AXIS, WHICH HAS JUST TURNED. The
     170px basis that is a sensible minimum WIDTH in a row becomes a minimum
     HEIGHT in a column, which padded "The first appeal." out to an inch of
     empty box. Found by a phone screenshot, not by reading — the layout rule
     in HANDOVER §3 earning its place again. */
  @media (max-width:560px){
    .cr-flow{flex-direction:column; align-items:stretch}
    .cr-step{flex:0 0 auto}
    .cr-arrow{align-self:flex-start; margin-left:16px; transform:rotate(90deg)}
  }
  .cr-route dl{margin:10px 0 0}
  .cr-route dt{font-size:.76rem; font-weight:700; margin-top:8px}
  .cr-route dd{margin:1px 0 0; font-size:.78rem; color:var(--muted)}
  .w-legend{display:flex; gap:18px; font-size:.75rem; color:var(--muted)}
  .w-legend[hidden]{display:none}   /* the author display rule would beat [hidden] (HANDOVER §5) */
  .w-legend i{display:inline-block; width:11px; height:11px; border-radius:3px; margin-right:5px; vertical-align:-1px}
  .flash{animation:flash-ring 2.2s ease-out}
  @keyframes flash-ring{0%,60%{box-shadow:0 0 0 4px var(--amber)} 100%{box-shadow:0 0 0 0 transparent}}
  .card > * + *{margin-top:10px}
  /* The heading is a flex row: title, badge, share/hide buttons, freshness
     note. Everything except the title is fixed-size, so the title was the
     only thing that could shrink — down to one word per line. Give it the
     growth and push the freshness note onto its own row. */
  .card h2{font-size:1rem; display:flex; flex-wrap:wrap; align-items:center; gap:8px;
    overflow-wrap:normal; word-break:keep-all}
  .card h2 > .fresh{flex:1 0 100%; margin-top:1px}
  .badge{font-size:.62rem; font-weight:700; letter-spacing:.06em; color:#fff;
    border-radius:4px; padding:2px 6px; text-transform:uppercase}
  .badge.live{background:var(--red)}
  .badge.snap{background:var(--muted)}
  .badge.upd{background:#d97706; animation:badge-pulse 1.3s ease-in-out infinite}
  @keyframes badge-pulse{0%,100%{opacity:1} 50%{opacity:.45}}
  .card h2 .fresh{margin-left:auto; font-size:.72rem; font-weight:400; color:var(--muted)}
  .item{border-top:1px solid var(--line); padding:9px 0 2px; font-size:.92rem}
  .item:first-of-type{border-top:0}
  .item .meta{color:var(--muted); font-size:.8rem; margin-top:2px}
  .tile-report{font-size:.78rem; color:var(--muted); margin:2px 0 8px; line-height:1.4}
  .tile-report a{font-weight:600}
  .chip{display:inline-block; font-size:.7rem; font-weight:700; border-radius:5px; padding:2px 7px; vertical-align:1px}
  .chip.new{background:var(--accent-soft); color:var(--accent)}
  .chip.amber{background:var(--amber-soft); color:var(--amber)}
  .chip.red{background:var(--red-soft); color:var(--red)}
  .chip.blue{background:var(--blue-soft); color:var(--blue)}
  /* Enforcement note under a permitted site. Marked out, but deliberately
     not alarm-coloured: it is a matter of public record, not a warning. */
  .meta.enf{margin-top:4px; padding:5px 8px; border-left:3px solid var(--amber);
            background:var(--amber-soft); border-radius:0 5px 5px 0}
  /* Inspection line under a permitted site (5 Sep 2026). Blue, not amber: an
     inspection having happened is neutral — it is the regulator doing its job,
     and the finding is in the linked PDF, which we do not read. */
  .meta.insp{margin-top:4px; padding:5px 8px; border-left:3px solid var(--blue);
             background:var(--blue-soft); border-radius:0 5px 5px 0}
  .meta.insp a{margin-left:4px}
  .act{display:inline-block; margin:4px 8px 0 0; font-size:.8rem; font-weight:600;
    color:var(--accent); border:1px solid var(--accent); border-radius:7px;
    padding:3px 10px; text-decoration:none}
  .act:hover{background:var(--accent-soft)}
  /* .act is used on <button> too (News "More") — buttons don't inherit these. */
  button.act{background:none; cursor:pointer; font-family:inherit; line-height:1.4}

  /* Rank within the Ombudsman's own peer group. A position bar, not a
     medal: the reader sees where their council sits among councils doing
     the SAME job, which is the only comparison the data supports. */
  .cmp-rank{margin-top:9px; padding-top:9px; border-top:1px dashed var(--line)}
  .cmp-rank .line{font-size:.82rem}
  .cmp-rank .line b{font-size:1.05rem}
  .cmp-scale{position:relative; height:22px; margin-top:7px}
  .cmp-scale .track{position:absolute; top:9px; left:0; right:0; height:5px;
    border-radius:999px; background:linear-gradient(90deg,var(--accent-soft),var(--amber-soft))}
  .cmp-scale .you{position:absolute; top:2px; width:3px; height:19px; border-radius:2px;
    background:var(--ink); transform:translateX(-1.5px)}
  .cmp-ends{display:flex; justify-content:space-between; font-size:.7rem;
    color:var(--muted); margin-top:1px}
  /* Mobile & broadband. Two sections because there are two resolutions:
     broadband per postcode, mobile per council. Never one blurred list. */
  .cov-sec{margin-top:12px}
  .cov-sec:first-child{margin-top:2px}
  .cov-sec h3{font-size:.92rem; letter-spacing:-.01em}
  .cov-sub{font-size:.76rem; color:var(--muted); margin:2px 0 8px}
  .cov-row{display:grid; grid-template-columns:minmax(9em,auto) 1fr auto; gap:8px;
    align-items:center; font-size:.82rem; margin-top:5px}
  .cov-bar{background:var(--bg); border-radius:999px; height:9px; overflow:hidden}
  .cov-bar i{display:block; height:100%; background:var(--accent); border-radius:999px}
  .cov-bar i.bad{background:var(--red)}
  .cov-row .n{font-variant-numeric:tabular-nums; color:var(--muted); min-width:3em; text-align:right}
  .cov-fine{font-size:.72rem; color:var(--muted); margin:1px 0 0 calc(9em + 8px)}
  .cov-warn{background:var(--amber-soft); border:1px solid #ecd3a8; color:#7a4a00;
    border-radius:8px; padding:8px 11px; font-size:.79rem; margin-top:9px}
  .cov-ok{font-size:.76rem; color:var(--muted); margin-top:8px}
  /* Council complaints (LGSCO, 27 Aug 2026). Two councils where an area is
     two-tier, so everything is per-council rather than per-tile. */
  .cmp-auth{border:1px solid var(--line); border-radius:10px; padding:12px 14px; margin-top:10px}
  .cmp-auth h3{font-size:.95rem; letter-spacing:-.01em; margin-bottom:2px}
  .cmp-auth .role{font-size:.76rem; color:var(--muted); margin-bottom:8px}
  .cmp-head{display:flex; flex-wrap:wrap; gap:8px 18px; align-items:baseline; margin-bottom:8px}
  .cmp-big{font-size:1.5rem; font-weight:800; letter-spacing:-.02em; line-height:1}
  .cmp-vs{font-size:.78rem; color:var(--muted)}
  .cmp-vs b{color:var(--ink)}
  /* One bar per category. Width is the share of that council's own total —
     one denominator per tile, and it is stated. */
  .cmp-cats{display:grid; gap:5px; margin-top:8px}
  .cmp-row{display:grid; grid-template-columns:minmax(9.5em,auto) 1fr auto; gap:8px;
    align-items:center; font-size:.82rem}
  .cmp-bar{background:var(--bg); border-radius:999px; height:9px; overflow:hidden}
  .cmp-bar i{display:block; height:100%; background:var(--accent); border-radius:999px}
  .cmp-row .n{font-variant-numeric:tabular-nums; color:var(--muted); min-width:2.2em; text-align:right}
  .cmp-row.zero .cmp-bar i{background:var(--line)}
  .cmp-note{font-size:.76rem; color:var(--muted); margin-top:8px}
  .cmp-warn{background:var(--amber-soft); border:1px solid #ecd3a8; color:#7a4a00;
    border-radius:8px; padding:7px 10px; font-size:.79rem; margin-top:8px}

  /* recipes */
  .rcp-shot{width:100%; aspect-ratio:16/10; object-fit:cover; border-radius:10px;
    display:block; background:var(--accent-soft)}
  .rcp-row{display:flex; gap:10px; align-items:center; padding:8px 0;
    border-top:1px solid var(--line); cursor:pointer}
  .rcp-row:hover b{color:var(--accent)}
  .rcp-row img{width:54px; height:54px; object-fit:cover; border-radius:8px;
    flex:none; background:var(--accent-soft)}
  .rcp-row b{font-size:.9rem}
  .rcp-open{padding-bottom:4px}
  .rcp-head{display:flex; align-items:baseline; gap:8px; margin:10px 0 2px; flex-wrap:wrap}
  .rcp-head b{font-size:1.02rem}
  .rcp-cost{font-weight:800; color:var(--accent); white-space:nowrap}
  .rcp-ings{font-size:.85rem; color:var(--muted); line-height:1.5; margin-top:4px}
  .rcp-nav{display:flex; align-items:center; gap:10px; margin-top:10px}
  .rcp-nav button{width:30px; height:30px; border-radius:50%; border:1px solid var(--line);
    background:var(--card); color:var(--ink); cursor:pointer; font-size:1rem; line-height:1}
  .rcp-nav button:disabled{opacity:.35; cursor:default}
  .rcp-dots{font-size:.78rem; color:var(--muted); margin-left:auto}
  /* saving a dish to the account (30 Aug 2026) */
  .rcp-save.is-saved{color:#14503a; background:var(--accent-soft)}
  .rcp-save[disabled]{opacity:.55; cursor:default}
  .rcp-savedhead{margin-top:14px; padding-top:10px; border-top:2px solid var(--line);
    font-size:.85rem; font-weight:700}
  .rcp-savedhead .meta{font-weight:400; display:inline; margin-left:6px}
  .rcp-saved-row{display:flex; gap:10px; align-items:center; padding:8px 0;
    border-top:1px solid var(--line); color:inherit; text-decoration:none}
  .rcp-saved-row:hover b{color:var(--accent)}
  .rcp-saved-row img{width:44px; height:44px; object-fit:cover; border-radius:8px;
    flex:none; background:var(--accent-soft)}
  .rcp-saved-row b{font-size:.88rem}
  .rcp-unsave{margin-left:auto; flex:none; width:26px; height:26px; border-radius:50%;
    border:1px solid var(--line); background:var(--card); color:var(--muted);
    cursor:pointer; font-size:.9rem; line-height:1}
  .rcp-unsave:hover{color:var(--red); border-color:var(--red)}
  /* travel hot spots (1 Sep 2026) — the recipe tile's twin, so the pair reads
     as one row. Same photo proportions, same compact rows, same save button;
     what differs is the rank pill, because "hot" is a measured claim here. */
  /* 21:9, not 16:10, because the hero is Wikivoyage's page BANNER and a banner
     is a panorama (roughly 7:1 by their convention). A panorama in a 16:10 box
     is cropped to the middle quarter of itself, which reliably turns a skyline
     into an unidentifiable detail. Matching the box to the picture also makes
     the tile shorter, which the paired recipe row was glad of. */
  .dst-shot{width:100%; aspect-ratio:21/9; object-fit:cover; border-radius:10px;
    display:block; background:var(--accent-soft)}
  .dst-row{display:flex; gap:10px; align-items:center; padding:8px 0;
    border-top:1px solid var(--line); cursor:pointer}
  .dst-row:hover b{color:var(--accent)}
  /* Row thumbnails are LANDSCAPE for the same reason: a square crop of a
     panorama is 14% of it, chosen by nothing. */
  .dst-row img, .dst-row .noshot{width:82px; height:46px; object-fit:cover;
    border-radius:8px; flex:none; background:var(--accent-soft)}
  .dst-row .noshot, .dst-saved-row .noshot{display:flex; align-items:center;
    justify-content:center; color:var(--accent); font-size:1rem}
  .dst-row b{font-size:.9rem}
  .dst-open{padding-bottom:4px}
  .dst-head{display:flex; align-items:baseline; gap:8px; margin:10px 0 2px; flex-wrap:wrap}
  .dst-head b{font-size:1.02rem}
  .dst-rank{font-weight:800; color:var(--accent); white-space:nowrap}
  .dst-teaser{font-size:.86rem; color:var(--muted); line-height:1.5; margin-top:4px}
  .dst-credit{font-size:.72rem; color:var(--muted); margin-top:6px}
  .dst-save.is-saved{color:#14503a; background:var(--accent-soft)}
  .dst-save[disabled]{opacity:.55; cursor:default}
  .dst-savedhead{margin-top:14px; padding-top:10px; border-top:2px solid var(--line);
    font-size:.85rem; font-weight:700}
  .dst-savedhead .meta{font-weight:400; display:inline; margin-left:6px}
  .dst-saved-row{display:flex; gap:10px; align-items:center; padding:8px 0;
    border-top:1px solid var(--line); color:inherit; text-decoration:none}
  .dst-saved-row:hover b{color:var(--accent)}
  .dst-saved-row img, .dst-saved-row .noshot{width:68px; height:38px;
    object-fit:cover; border-radius:8px; flex:none; background:var(--accent-soft)}
  .dst-saved-row b{font-size:.88rem}
  .dst-unsave{margin-left:auto; flex:none; width:26px; height:26px; border-radius:50%;
    border:1px solid var(--line); background:var(--card); color:var(--muted);
    cursor:pointer; font-size:.9rem; line-height:1}
  .dst-unsave:hover{color:var(--red); border-color:var(--red)}
  .banner{border-radius:9px; padding:9px 12px; font-size:.9rem; font-weight:600}
  .banner.ok{background:var(--accent-soft); color:var(--accent)}
  /* The tile's own banner takes the SAME two states as the strip, so the
     colour a reader saw at the top of the page is the colour they find when
     they land on the tile. Left bar rather than a full-strength fill: this
     one sits inside a card of prose, where a solid red block would shout
     down the river gauge underneath it. */
  .banner.warn{background:var(--red-soft); color:var(--red);
    border-left:5px solid var(--red)}
  .banner.alert{background:var(--amber-soft); color:var(--amber);
    border-left:5px solid #ffc233; font-weight:600}
  .banner.dim{background:var(--bg); color:var(--muted); font-weight:400}
  .sun{display:flex; gap:24px; font-size:.9rem; color:var(--muted); flex-wrap:wrap}
  .bars{display:flex; align-items:flex-end; gap:3px; height:44px; margin-top:4px}
  .bars i{flex:1; background:var(--blue-soft); border-radius:3px 3px 0 0; min-height:3px}
  .bars i.wet{background:var(--blue)}
  .bars-lbl{display:flex; justify-content:space-between; font-size:.68rem; color:var(--muted)}
  .rep{display:flex; gap:12px; align-items:center; border-top:1px solid var(--line); padding:10px 0}
  .rep:first-of-type{border-top:0}
  .rep .face{width:40px; height:40px; border-radius:50%; background:var(--accent-soft);
    color:var(--accent); font-weight:700; display:flex; align-items:center; justify-content:center; flex-shrink:0}
  .rep .face.lab{background:#fbe7e5; color:#b0322c}
  .rep .nm{font-weight:600; font-size:.93rem}
  .rep .meta{font-size:.8rem; color:var(--muted)}
  .cede{background:none; border-style:dashed; color:var(--muted)}
  .pollen-row{display:flex; align-items:center; gap:10px}
  .p-lbl{width:48px; font-size:.85rem; font-weight:600}
  .p-meter{flex:1; height:10px; background:var(--bg); border-radius:999px; overflow:hidden}
  .p-meter i{display:block; height:100%; border-radius:999px; transition:width .6s}
  .p-hi{background:var(--red)} .p-md{background:var(--amber)} .p-lo{background:var(--accent)}
  .stat-tiles{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px}
  /* Political money (5 Oct 2026): one neutral colour for every party — no
     party colours, so no bar looks like it is taking a side. */
  .pm-row{display:grid; grid-template-columns:minmax(0,9.5em) 1fr auto; gap:8px; align-items:center; font-size:.85rem; margin:3px 0}
  .pm-lbl{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .pm-bar{height:10px; background:var(--accent-soft); border-radius:5px; overflow:hidden}
  .pm-bar i{display:block; height:100%; background:var(--accent)}
  .pm-val{font-variant-numeric:tabular-nums; min-width:4.5em; text-align:right}
  .pm-row.pm-wide{grid-template-columns:1fr auto; row-gap:2px; margin:6px 0}
  .pm-wide .pm-lbl{grid-column:1 / -1; white-space:normal}
  /* Prescribing (6 Oct 2026): the GP tile's surgery card and the UK tile. */
  .rx-g{display:grid; grid-template-columns:minmax(0,1fr) auto auto; gap:4px 10px; align-items:center; font-size:.83rem; margin-top:6px}
  .rx-g .h{font-size:.72rem; color:var(--muted)}
  .rx-g .n{font-variant-numeric:tabular-nums; text-align:right}
  .rx-g .sub{grid-column:1 / -1; font-size:.74rem; color:var(--muted); margin:-2px 0 4px}
  .rx-spark{vertical-align:middle}
  .rx-two{display:grid; grid-template-columns:1fr 1fr; gap:10px}
  @media (max-width:560px){ .rx-two{grid-template-columns:1fr} }
  .rx-two ol{margin:4px 0 0 1.2em; padding:0; font-size:.83rem}
  .pm-h{font-size:1rem; margin:16px 0 8px; padding-top:12px; border-top:1px solid var(--line)}
  .pm-case{padding:6px 0; border-bottom:1px solid var(--line); font-size:.85rem}
  .pm-case:last-child{border-bottom:0}
  .tile{background:var(--card); border:1px solid var(--line); border-radius:9px; padding:8px 10px}
  .tile b{font-size:1.05rem; color:var(--ink)}
  .tile span{display:block; font-size:.68rem; line-height:1.3; margin-top:2px}
  #feedView{display:none; max-width:760px; margin:0 auto; padding:8px 20px}
  .fitem{background:var(--card); border:1px solid var(--line); border-radius:var(--radius);
    padding:14px 18px; margin-bottom:10px; display:flex; gap:14px}
  .fitem[hidden]{display:none}   /* display:flex above would otherwise beat the hidden attribute */
  .fitem .t{font-size:.72rem; color:var(--muted); min-width:74px; padding-top:2px; flex:none}
  .fitem .body{min-width:0}
  .fitem .body b{font-size:.93rem}
  .fitem .body b a{color:inherit; text-decoration:none}
  .fitem .body b a:hover{text-decoration:underline}
  .fitem .body p{font-size:.86rem; color:var(--muted); margin-top:2px; overflow-wrap:anywhere}
  .fitem .src{display:inline-block; font-size:.68rem; color:var(--muted); border:1px solid var(--line);
    border-radius:999px; padding:0 7px; margin-left:6px; vertical-align:1px; white-space:nowrap}
  /* Feed groups: a date band, not a card — the items are the cards. */
  .fgroup{font-size:.72rem; font-weight:600; letter-spacing:.04em; text-transform:uppercase;
    color:var(--muted); margin:18px 2px 8px}
  .fgroup:first-child{margin-top:4px}
  .fintro{font-size:.8rem; color:var(--muted); margin:0 2px 10px}
  .fmore{display:block; width:100%; border:1px dashed var(--line); background:none; border-radius:var(--radius);
    padding:10px; font:inherit; font-size:.84rem; color:var(--muted); cursor:pointer; margin:4px 0 16px}
  .fmore:hover{color:var(--ink); border-color:var(--ink)}
  @media (max-width:600px){ .fitem{padding:12px 14px; gap:10px} .fitem .t{min-width:56px} }
  footer{max-width:1100px; margin:28px auto 0; padding:0 20px; font-size:.75rem; color:var(--muted)}
  .mode{display:none}
  body[data-view="grid"] #gridView{display:block}
  body[data-view="feed"] #feedView{display:block}
  /* The section strip JUMPS to grid sections — it has nothing to point at
     in the feed, so it goes with the grid. */
  body[data-view="feed"] #sectbar{display:none !important}
  /* fr tracks floor at min-content, so one long unbroken word (a council
     name, a station) widens the whole row past the card. minmax(0,…) lets
     them shrink and wrap instead — the header had the same fault. */
  .rib{display:grid; grid-template-columns:minmax(0,1.5fr) minmax(0,1.1fr) minmax(0,.9fr) minmax(0,.9fr); gap:2px 8px;
    font-size:.84rem; align-items:baseline}
  .rib .hd{font-size:.66rem; font-weight:700; color:var(--muted); text-transform:uppercase;
    letter-spacing:.05em; border-bottom:1px solid var(--line); padding-bottom:3px}
  .rib .it{padding:5px 0 2px}
  .rib .sub{font-size:.7rem; color:var(--muted)}
  .dg-row{border-top:1px solid var(--line); padding:8px 0 5px; font-size:.85rem}
  .dg-row:first-of-type{border-top:0}
  .dg-bar{height:9px; background:var(--bg); border-radius:999px; overflow:hidden; margin-top:3px}
  .dg-bar i{display:block; height:100%; border-radius:999px}
  .dg-you i{background:var(--accent)}
  .dg-eng i{background:#b9c4bd}
  /* Bar length is the magnitude; colour is the direction. Needed because on a
     metric where low is good (waits over a year) a long accent-coloured bar
     reads as success at a glance and means the opposite. */
  .dg-bad i{background:var(--amber)}
  .dg-lbl{display:flex; justify-content:space-between; font-size:.72rem; color:var(--muted)}
  /* Energy security tile (30 Aug 2026). A "banded meter" per indicator: the
     track IS the ruleset — its three soft segments are the published
     green/amber/red thresholds drawn to scale — and the dark tick is where
     the UK sits today. The reader sees the value AND the rule it was scored
     against; neither can drift from the other because both come from the
     same server payload. Severity vocabulary matches the scorecard: good /
     warn / serious / critical. The site is light-only, so fixed hexes are
     fine (HANDOVER: first-aid decision). */
  .es-row{border-top:1px solid var(--line); padding:7px 0 6px}
  .es-row:first-of-type{border-top:0}
  .es-lbl{display:flex; justify-content:space-between; gap:8px; font-size:.78rem}
  .es-lbl .v{white-space:nowrap; font-variant-numeric:tabular-nums}
  .es-track{position:relative; display:flex; height:8px; border-radius:999px; overflow:hidden; margin-top:4px; background:var(--bg)}
  .es-track i{display:block; height:100%}
  .es-seg-good{background:#cfe5d8} .es-seg-warn{background:#f2ddb8} .es-seg-crit{background:#f0c4c1}
  .es-mark{position:absolute; top:-1px; bottom:-1px; width:3px; border-radius:2px; background:var(--ink); transform:translateX(-50%)}
  .es-sub{font-size:.68rem; color:var(--muted); margin-top:3px}
  .es-dot{display:inline-block; width:9px; height:9px; border-radius:50%; vertical-align:-1px}
  .es-good{background:var(--accent)} .es-warn{background:var(--amber)}
  .es-ser{background:#c2410c} .es-crit{background:var(--red)}
  .es-chip{display:inline-block; font-size:.7rem; font-weight:700; border-radius:5px; padding:2px 7px; vertical-align:1px; color:#fff}
  .es-chip.good{background:var(--accent)} .es-chip.warn{background:var(--amber)}
  .es-chip.ser{background:#c2410c} .es-chip.crit{background:var(--red)}
  /* Insurance risk gauges (3 Oct 2026) — prefix irk-. The five-step ramp is
     the site's own (es-chip), with one olive step between green and amber. */
  #card-insurance-risk{--irk1:var(--accent); --irk2:#6f8f2e; --irk3:var(--amber); --irk4:#c2410c; --irk5:var(--red)}
  .irk-grid{display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:8px}
  @media (max-width:720px){ .irk-grid{grid-template-columns:1fr} }
  .irk-g{border:1px solid var(--line); border-radius:10px; padding:10px 12px}
  .irk-g h3{margin:0; font-size:.98rem}
  .irk-dial{display:block; width:100%; max-width:260px; margin:4px auto 0}
  .irk-verdict{text-align:center; font-weight:700; font-size:1.15rem; margin-top:-4px}
  .irk-sub{text-align:center; font-size:.78rem; color:var(--muted); margin-bottom:6px}
  .irk-f{padding:7px 0; border-top:1px solid var(--line)}
  .irk-ft{display:flex; justify-content:space-between; gap:8px; font-size:.86rem}
  .irk-ft b{font-weight:600}
  .irk-fv{color:var(--muted); font-size:.78rem; text-align:right}
  .irk-bar{position:relative; height:8px; border-radius:4px; margin:5px 0 3px;
    background:linear-gradient(90deg,var(--irk1),var(--irk2) 30%,var(--irk3) 50%,var(--irk4) 70%,var(--irk5))}
  .irk-bar::after{content:""; position:absolute; inset:0; border-radius:4px; background:var(--card); opacity:.7}
  .irk-mid{position:absolute; top:-1px; left:50%; width:1px; height:10px; background:var(--muted); z-index:1}
  .irk-pin{position:absolute; top:-3px; width:4px; height:14px; border-radius:2px; background:var(--ink);
    transform:translateX(-2px); z-index:2}
  .irk-note{font-size:.76rem; color:var(--muted)}
  .irk-grain{display:inline-block; font-size:.7rem; border:1px solid var(--line); border-radius:4px;
    padding:0 5px; margin-right:4px; background:var(--accent-soft); color:var(--ink)}
  .irk-grain.warn{border-color:#ecd3a8; background:var(--amber-soft); color:#7a4a00}
  .irk-flag{margin-top:6px; font-size:.78rem; padding:5px 8px; border-left:3px solid var(--accent);
    background:var(--accent-soft); border-radius:0 5px 5px 0}
  .irk-flag.warn{border-left-color:var(--amber); background:var(--amber-soft)}
  .irk-src{margin-top:8px; font-size:.72rem; color:var(--muted)}
  .es-pill{display:inline-block; font-size:.6rem; font-weight:700; letter-spacing:.02em; border:1px solid var(--line); border-radius:4px; padding:0 4px; margin-left:4px; color:var(--muted); vertical-align:1px}
  .es-pill.stale{border-color:var(--amber); color:var(--amber)}
  .es-pill.ambiguous, .es-pill.low_confidence{border-color:var(--red); color:var(--red)}

  /* LONDON LINE STATUS (17 Sep 2026). Four steps, and they are THE SITE'S OWN
     RAMP - the energy-security tile's green / amber / deep-orange / red -
     rather than a fifth private palette invented for one block.

     THE BAND IS CHOSEN ON THE SERVER, in app/tflstatus.py's BAND table, and
     arrives in the payload. Nothing here decides what is serious: the words
     on a scale belong to the measure and so do its colours (Section 3), and a
     stylesheet that matched on TfL's wording would quietly paint a status
     TfL invents tomorrow as if it were routine.

     COLOUR IS REDUNDANT, NEVER THE ONLY CHANNEL. TfL's own words fill the
     chip the colour paints, so the block reads identically to a reader who
     cannot tell amber from deep orange. Never "tidy" the words away.

     The row shape - 3px left rule over a soft wash - is the one .meta.enf and
     .meta.insp already use, so a disrupted line looks like the rest of the
     site rather than like a new component. */
  .tfl-row{margin-top:6px; padding:5px 8px; border-left:3px solid var(--line);
           background:var(--bg); border-radius:0 5px 5px 0}
  .tfl-row.band-none{border-left-color:var(--red); background:var(--red-soft)}
  .tfl-row.band-severe{border-left-color:#c2410c; background:#fdece4}
  /* ⚠ NOT var(--amber). Measured, not eyeballed: white on --amber (#b06a00)
     is 4.28:1, and this chip is .68rem bold - small text, so it needs 4.50.
     #9c5c00 is the same hue two steps down and measures 5.32:1 on white and
     4.77:1 against its own wash. The border takes the same value so the row
     reads as one colour. */
  .tfl-row.band-minor{border-left-color:#9c5c00; background:var(--amber-soft)}
  .tfl-row.band-notice{border-left-color:var(--blue); background:var(--blue-soft)}
  .tfl-row.band-unknown{border-left-color:var(--fun); background:var(--fun-soft)}
  .tfl-chip{display:inline-block; font-size:.68rem; font-weight:700; border-radius:5px;
            padding:1px 6px; vertical-align:1px; color:#fff; margin-left:4px;
            white-space:nowrap}
  .tfl-chip.band-none{background:var(--red)}
  .tfl-chip.band-severe{background:#c2410c}
  .tfl-chip.band-minor{background:#9c5c00}
  .tfl-chip.band-notice{background:var(--blue)}
  .tfl-chip.band-unknown{background:var(--fun)}
  /* ⚠ NOT var(--muted). At .74rem this is small text needing 4.50:1, and
     --muted (#5f6f66) measures 4.44:1 on the violet wash and 4.47:1 on the
     red one - under the line on two of the six bands. #586860 is the same
     neutral one step darker and its WORST band measures 4.93:1. Checked
     against every wash, not against the one that happened to be on screen. */
  .tfl-why{font-size:.74rem; margin-top:3px; line-height:1.35; color:#586860}
  .tfl-more{font-size:.68rem; color:var(--muted); margin-top:2px}
  /* Good service is a STATE, not the absence of a row, so it gets a row.
     Green, and last: what is wrong is what the reader came for. */
  .tfl-good{margin-top:6px; padding:5px 8px; border-left:3px solid var(--accent);
            background:var(--accent-soft); border-radius:0 5px 5px 0;
            font-weight:600; color:var(--accent)}
  .tfl-src{display:block; font-size:.66rem; color:var(--muted); margin-top:7px;
           line-height:1.4}
  .es-verdicts{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:8px; margin:8px 0 2px}
  .es-verdicts .v-card{border:1px solid var(--line); border-radius:8px; padding:7px 9px; font-size:.72rem; color:var(--muted)}
  .es-verdicts .v-card b{display:block; color:var(--ink); font-size:.76rem}
  .es-cmp-bar{height:8px; background:var(--bg); border-radius:999px; overflow:hidden; margin-top:2px}
  .es-cmp-bar i{display:block; height:100%; border-radius:999px}
  .eth-bar{display:flex; height:11px; border-radius:999px; overflow:hidden; margin-top:4px; background:var(--bg)}
  .eth-bar i{display:block; height:100%}
  .e-w{background:#cbd5ce} .e-a{background:var(--blue)} .e-b{background:var(--ink)}
  .e-m{background:var(--amber)} .e-o{background:var(--red)}
  .eth-leg{display:flex; flex-wrap:wrap; gap:3px 12px; font-size:.7rem; color:var(--muted); margin-top:5px}
  .eth-leg i{display:inline-block; width:8px; height:8px; border-radius:2px; margin-right:4px}
  .eth-leg em{font-style:normal; opacity:.65}
  .help-in form{display:grid; gap:10px; margin-top:6px}
  .help-in form label{font-size:.8rem; font-weight:600; display:grid; gap:4px}
  .help-in form input{font:inherit; padding:7px 10px; border:1px solid var(--line); border-radius:7px}
  .help-in form button{justify-self:start; font:600 .85rem/1 inherit; font-family:inherit; color:#fff;
    background:var(--accent); border:0; border-radius:7px; padding:9px 16px; cursor:pointer}
  .help-in form button:hover{opacity:.9}
  .sect{border-top:2px solid var(--line); margin-top:8px; padding-top:14px;
    display:flex; flex-direction:column; gap:10px}
  .sect h3{font-size:.95rem; display:flex; align-items:center; gap:8px}
  .sect h3 .fresh{margin-left:auto; font-size:.72rem; font-weight:400; color:var(--muted)}
  /* "What this means for you" — the whole point of the site (owner, 2 Aug
     2026: "the detour maths is what this site is all about but it's hidden in
     small text"). These read as CALLOUTS now: accent bar, room to breathe,
     body-size text — never footnote-size. */
  /* Motoring "best place to fill up": a table on wide screens, one sentence
     on phones (owner, 4 Oct 2026). */
  .means .fbe-text{display:none}
  @media (max-width:560px){ .means .fbe-text{display:block} .means .fbe-table{display:none} }
  .means{background:var(--accent-soft); border-left:4px solid var(--accent);
    border-radius:10px; padding:12px 14px; font-size:.93rem; line-height:1.55}
  /* Bullet-point "what this means" (owner, 4 Oct 2026: "too verbose, on mobile
     it'll take up an entire page"). A few short points, no paragraphs. */
  .means-pts{margin:0; padding-left:18px}
  .means-pts li{margin:0 0 4px}
  .means-pts li:last-child{margin-bottom:0}
  /* The "i" sits beside the points rather than on a line of its own. */
  .means:has(.means-pts){display:flex; gap:4px; align-items:flex-start}
  .means:has(.means-pts) .lbl{flex:none; margin-top:2px}
  .means .lbl{display:inline-flex; align-items:center; justify-content:center;
    width:19px; height:19px; border-radius:50%; background:var(--accent); color:#fff;
    font-size:.72rem; font-weight:700; font-style:italic; font-family:Georgia,"Times New Roman",serif;
    vertical-align:1px; margin-right:8px; cursor:help; position:relative}
  .means .lbl::after{content:attr(data-tip); position:absolute; left:-4px; bottom:calc(100% + 7px);
    background:var(--ink); color:#fff; font-family:-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
    font-style:normal; font-weight:600; font-size:.72rem; line-height:1.3; padding:5px 9px;
    border-radius:6px; white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .15s; z-index:5}
  .means .lbl:hover::after, .means .lbl:focus::after{opacity:1}
  .means.heads{background:var(--amber-soft); border-left-color:var(--amber)}
  .means.heads .lbl{background:var(--amber)}
  /* Light bulb, not an amber "?" (owner, 8 Aug 2026): it reads as "here's the
     insight" rather than "you must be confused". Pale yellow disc so the bulb
     glyph carries the colour instead of being knocked out in white. */
  .help-btn{width:24px; height:24px; border-radius:50%; border:0;
    background:#fdf0c9; color:#7a5200; font:400 .82rem/1 inherit; font-family:inherit;
    cursor:pointer; padding:0; flex-shrink:0; box-shadow:0 1px 3px rgba(176,106,0,.22);
    transition:transform .12s, background .12s}
  .help-btn:hover, .help-btn:focus{background:#fbe29a; transform:scale(1.15)}
  .help-btn.inl{width:19px; height:19px; font-size:.7rem; vertical-align:-3px; margin:0 1px}
  dialog.help{border:0; border-radius:var(--radius); max-width:580px; width:calc(100% - 40px);
    padding:0; box-shadow:0 20px 60px rgba(0,0,0,.3); color:var(--ink)}
  dialog.help::backdrop{background:rgba(29,43,35,.55)}
  .help-in{padding:22px 24px 20px}
  .help-in h3{font-size:1.05rem; display:flex; align-items:center; gap:8px; margin-bottom:10px}
  .help-in h3 button{margin-left:auto; border:0; background:none; font-size:1.1rem;
    cursor:pointer; color:var(--muted); padding:2px 6px}
  .help-in p{font-size:.88rem; margin:8px 0}
  .help-in p.now{background:var(--amber-soft); border-radius:9px; padding:10px 12px; font-weight:600}
  .help-in .small{font-size:.75rem; color:var(--muted)}
  .help-in dl{margin:8px 0}
  .help-in dt{font-size:.85rem; font-weight:700; margin-top:8px}
  .help-in dd{font-size:.85rem; color:var(--muted); margin-left:0}

  /* ---- ten-day weather card -------------------------------------------
     Full-width card, so the chips can be generous: five per row on
     desktop, a swipeable row on phones. Colour does one job per element —
     the chip TINT is the sky (soft yellow sun, soft blue rain, amber edge
     for storms), the up-arrow figure is the day's high, the down-arrow the
     overnight low, and the droplet is the chance of rain. Every figure is
     labelled by its symbol, and the legend under the grid says so in
     words, once, instead of ten times. */
  /* Nine days since today moved to its own card (19 Aug 2026): on a
     full-width desktop card that is one clean row of nine. */
  .tenday{display:grid; grid-template-columns:repeat(9,1fr); gap:8px; margin-top:10px}
  @media (max-width:1000px){ .tenday{grid-template-columns:repeat(5,1fr)} }
  .tenday .td{background:var(--bg); border-radius:12px; padding:10px 6px 8px; text-align:center;
    font-size:.8rem; line-height:1.5; border:1px solid transparent}
  .tenday .td.sun{background:var(--amber-soft)}
  .tenday .td.rain{background:var(--blue-soft)}
  .tenday .td.storm{background:var(--amber-soft); border-color:var(--amber)}
  .tenday .td .dy{font-weight:700; font-size:.8rem}
  .tenday .td .ic{font-size:1.7rem; display:block; margin:2px 0}
  .tenday .td .cond{display:block; font-size:.68rem; color:var(--muted); min-height:1.2em}
  .tenday .td .hi{font-size:1.05rem; font-weight:800}
  .tenday .td .lo{color:var(--blue); font-weight:600; margin-left:4px}
  .tenday .td .rp{display:block; color:var(--muted); font-size:.78rem; margin-top:1px}
  .tenday .td .rp b{color:var(--blue)}
  .tenday .td .flag{display:block; color:var(--amber); font-weight:700; font-size:.66rem}
  @media (max-width:560px){
    .tenday{display:flex; overflow-x:auto; -webkit-overflow-scrolling:touch}
    .tenday .td{flex:0 0 6.2em}
  }
  .tenday-note{font-size:.75rem; color:var(--muted); margin-top:7px}

  /* ---- migration demographics -----------------------------------------
     Figures, not bars. A stacked bar on this subject invites a reader to
     judge by the shape of a block of colour; the whole point here is that
     the numbers get read. Plain rows, one group at a time, each group
     against its own denominator. */
  .mig-g{border-top:1px solid var(--line); padding-top:8px; margin-top:10px}
  .mig-g:first-child{border-top:0; padding-top:0; margin-top:6px}
  .mig-g > b{display:block; margin-bottom:4px}
  .mig-t{width:100%; border-collapse:collapse; font-size:.84rem}
  .mig-t th{font-weight:700; text-align:right; padding:2px 0 4px; color:var(--muted);
    font-size:.74rem; text-transform:uppercase; letter-spacing:.03em}
  .mig-t th:first-child{text-align:left}
  .mig-t td{padding:2px 0; text-align:right}
  .mig-t td:first-child{text-align:left; color:var(--muted)}
  .mig-t td b{font-weight:700}
  .mig-t tr.hl td{border-top:1px solid var(--line); padding-top:5px}

  /* ---- charts -----------------------------------------------------------
     Series colours are the validated categorical palette, not the site's
     brand greens and navies: those failed the chroma floor (they read as
     grey once they are a 2px line) and put red beside green in the
     colourblind warn band. Checked with the palette validator, both modes.
     Three light-mode hues sit under 3:1 on white, which obliges relief —
     hence direct labels on every line AND a table view, both shipped. */
  .viz{ --s1:#2a78d6; --s2:#eb6834; --s3:#1baf7a; --s4:#eda100;
        --s5:#e87ba4; --s6:#4a3aa7; --s7:#e34948;
        --viz-grid:var(--line); --viz-ink:var(--ink); --viz-mute:var(--muted);
        margin-top:8px }
  @media (prefers-color-scheme: dark){
    :root:where(:not([data-theme="light"])) .viz{
      --s1:#3987e5; --s2:#d95926; --s3:#199e70; --s4:#c98500;
      --s5:#d55181; --s6:#9085e9; --s7:#e66767 }
  }
  :root[data-theme="dark"] .viz{
    --s1:#3987e5; --s2:#d95926; --s3:#199e70; --s4:#c98500;
    --s5:#d55181; --s6:#9085e9; --s7:#e66767 }
  .viz svg{ width:100%; height:auto; display:block; overflow:visible }
  .viz .ax{ stroke:var(--viz-grid); stroke-width:1 }
  .viz .gl{ stroke:var(--viz-grid); stroke-width:1; stroke-dasharray:2 3 }
  .viz .tick{ fill:var(--viz-mute); font-size:9px }
  .viz .ref{ stroke:var(--viz-mute); stroke-width:1.5; stroke-dasharray:5 4 }
  .viz .refl{ fill:var(--viz-mute); font-size:9px; font-weight:700 }
  .viz path.ln{ fill:none; stroke-width:2; stroke-linejoin:round; stroke-linecap:round }
  .viz path.ln.em{ stroke-width:3.5 }
  /* Dotted means projected. The OBR publishes endpoints and scenarios, not an
     annual path, so the connector must not look like an observation. */
  .viz path.ln.dash{ stroke-dasharray:2 5; stroke-width:2.5 }
  .viz circle.mk{ stroke:var(--card); stroke-width:2 }
  .viz .dl{ font-size:9.5px; font-weight:700; fill:var(--viz-ink) }
  /* The debt tile's two charts (15 Sep 2026): 9px ticks in a 720-unit
     drawing render at ~6px on a card, and the owner could not read them.
     Sized here, not site-wide, and larger again on a phone where the
     drawing halves (HANDOVER §5, "a 660-unit viewBox at phone width"). */
  .viz.viz-lg .tick{ font-size:13px }
  .viz.viz-lg .refl{ font-size:12px }
  .viz.viz-lg .dl{ font-size:12.5px }
  @media (max-width:600px){
    /* 15px is the largest a three-character y label fits in the 34-unit
       left gutter. The end labels come OFF on a phone: the right gutter is
       sized for 9.5px text and they spilled past the card; the legend below
       the chart carries the identity, as it does on every chart here. */
    .viz.viz-lg .tick{ font-size:15px } .viz.viz-lg .refl{ font-size:14px } .viz.viz-lg .dl{ display:none }
  }
  .viz .cross{ stroke:var(--viz-mute); stroke-width:1; stroke-dasharray:3 3 }
  .viz .dot{ r:4; stroke:var(--card); stroke-width:2 }
  .s1{ stroke:var(--s1) } .s2{ stroke:var(--s2) } .s3{ stroke:var(--s3) }
  .s4{ stroke:var(--s4) } .s5{ stroke:var(--s5) } .s6{ stroke:var(--s6) }
  .s7{ stroke:var(--s7) }
  .f1{ fill:var(--s1) } .f2{ fill:var(--s2) } .f3{ fill:var(--s3) }
  .f4{ fill:var(--s4) } .f5{ fill:var(--s5) } .f6{ fill:var(--s6) }
  .f7{ fill:var(--s7) }
  .viz-legend{ display:flex; flex-wrap:wrap; gap:4px 14px; margin-top:7px; font-size:.78rem }
  .viz-legend span{ display:inline-flex; align-items:center; gap:5px; color:var(--muted) }
  .viz-legend i{ width:14px; height:3px; border-radius:2px; display:inline-block }
  .viz-tip{ position:absolute; pointer-events:none; background:var(--card);
    border:1px solid var(--line); border-radius:8px; padding:6px 9px;
    font-size:.76rem; box-shadow:0 6px 18px rgba(0,0,0,.13); z-index:5;
    white-space:nowrap }
  .viz-wrap{ position:relative }
  .viz-tbl{ width:100%; border-collapse:collapse; font-size:.74rem; margin-top:6px }
  .viz-tbl th, .viz-tbl td{ padding:2px 5px; text-align:right; border-top:1px solid var(--line) }
  .viz-tbl th:first-child, .viz-tbl td:first-child{ text-align:left }
  .viz-more{ background:none; border:0; color:var(--accent); font:inherit;
    font-size:.76rem; cursor:pointer; padding:4px 0; text-decoration:underline }
  .mig-bands{font-size:.76rem; color:var(--muted); margin-top:5px; line-height:1.7}
  .mig-bands b{color:var(--ink)}
  /* Asylum: same figures-not-bars rule as the migration block. */
  .asy-rows{font-size:.84rem; margin-top:6px}
  .asy-rows > div{display:flex; justify-content:space-between; gap:12px; padding:2px 0}
  .asy-rows > div > span:last-child{white-space:nowrap}
  .asy-rows .sub{color:var(--muted); padding-left:14px; font-size:.79rem}
  .asy-h{font-weight:700; margin-top:10px}
  .asy-h:first-child{margin-top:4px}

  /* ---- today's weather -------------------------------------------------
     One big number for the thing being asked ("what is it out there"),
     then the day's own figures on one labelled line. Same symbols as the
     nine-day strip so the two cards read as one idea. */
  .tw-now{display:flex; align-items:center; gap:12px; margin-top:6px}
  .tw-now .tw-ic{font-size:2.6rem; line-height:1}
  .tw-now .tw-t{font-size:2.1rem; font-weight:800; letter-spacing:-.02em}
  .tw-now .tw-c{color:var(--muted); font-size:.95rem}
  .tw-figs{display:flex; flex-wrap:wrap; gap:6px 16px; margin-top:8px; font-size:.85rem}
  .tw-figs .lo{color:var(--blue)}
  .tw-figs b{font-weight:700}

  /* ---- flood risk card -------------------------------------------------
     No new colours: risk counts are text; the below-target defence list is
     a plain table row per asset. Alarm is not a design goal — the copy
     rule is "below its required condition", never "failing". */
  .fr-block{border-top:1px solid var(--line); padding-top:9px; margin-top:9px}
  .fr-block h4{font-size:.85rem; margin-bottom:2px}
  .fr-note{font-size:.72rem; color:var(--muted); margin-bottom:6px}
  /* THE LIVE COUNT, which is the tile's first job (owner, 4 Sep 2026: "4
     live warnings/alerts, its just hidden in small text"). The number is the
     mark; the words are the label beside it. It sits in the same banner the
     colour and the pulse already live on, so there is one loud thing in the
     tile and not two competing for the eye. */
  .fl-live{display:flex; align-items:center; gap:13px}
  .fl-live .n{font-size:2.2rem; font-weight:800; line-height:1; flex:none}
  .fl-live .w{font-weight:700; font-size:.95rem}
  .fl-live .a{display:block; font-weight:400; font-size:.78rem; opacity:.85;
    margin-top:2px}
  /* A figure, not a paragraph: "29%" then the sentence that qualifies it.
     Proportional figures, not tabular — tabular-nums gives every digit the
     width of a zero and reads loose at this size. */
  .fr-fig{display:flex; align-items:baseline; gap:11px; margin:2px 0 4px}
  .fr-fig .n{font-size:2rem; font-weight:700; line-height:1; color:var(--ink); flex:none}
  .fr-fig .t{font-size:.84rem; color:var(--ink)}
  /* AN ALL-CLEAR IS AN ANSWER TOO (owner, 4 Sep 2026: "an all clear, no risk
     needs to be more prevalent"). It used to be three lines of grey small
     print, so the one postcode in the country with nothing to worry about
     was the one that looked like the tile had failed to load. It gets the
     same weight as the figure it replaces — and it has to be GIVEN that
     weight, because unlike the at-risk case it has no bar underneath to
     carry the eye. Green with a tick and the words: never colour alone. */
  .fr-fig.ok{background:var(--accent-soft); border-radius:9px; padding:10px 13px;
    margin:4px 0 7px; align-items:center}
  .fr-fig.ok .n{color:var(--accent); font-size:1.7rem}
  .fr-fig.ok .t{color:var(--accent)}
  /* Part-to-whole. 2px of SURFACE between segments does the separating —
     never a border, which would add ink that is not data. */
  .fr-stack{display:flex; gap:2px; height:15px; margin:7px 0 6px}
  .fr-stack i{border-radius:3px; min-width:4px}
  /* The key is the identity channel, and the counts in it are what let the
     palest segments sit below 3:1 without hiding anything. */
  .fr-key{display:flex; flex-wrap:wrap; gap:3px 13px; font-size:.73rem;
    color:var(--muted); margin-bottom:4px}
  .fr-key span{display:inline-flex; align-items:center; gap:5px; white-space:nowrap}
  .fr-key i{width:10px; height:10px; border-radius:3px; flex:none}
  .fr-key b{color:var(--ink)}
  /* A bullet meter for one defence: filled cells are the condition it is IN,
     the ringed cell is the condition it is REQUIRED to be in. The words sit
     beside it, always — the meter is the quick read, never the only one. */
  .fr-meter{display:inline-flex; gap:2px; align-items:center}
  .fr-meter i{width:9px; height:9px; border-radius:2px; background:var(--fd-track); flex:none}
  .fr-meter i.on{background:var(--fd-below)}
  .fr-meter i.tg{box-shadow:inset 0 0 0 2px var(--ink)}
  .fr-bands{display:flex; gap:8px; flex-wrap:wrap; margin:6px 0}
  .fr-band{background:var(--bg); border-radius:9px; padding:6px 10px; font-size:.78rem}
  .fr-band b{display:block; font-size:1rem}
  /* The use split under each band's count. Muted and small: the number is
     the answer, the split is the working. */
  .fr-band .sub{display:block; font-size:.7rem; color:var(--muted); margin-top:2px}
  /* One ROW per defence rather than a flat grid of spans, so the row can
     re-lay itself on a phone. As a four-column grid it crushed "Very poor,
     needs Good" into a three-line stack against a two-line name. */
  .fr-def{font-size:.76rem; margin-top:5px}
  .fr-drow{display:grid; grid-template-columns:minmax(0,1.5fr) 5.6em minmax(0,1fr) 6.6em;
    gap:3px 9px; align-items:baseline; padding:3px 0}
  .fr-drow + .fr-drow{border-top:1px solid var(--line)}
  .fr-def .hd{font-weight:700; color:var(--muted); font-size:.68rem}
  .fr-def .when{text-align:right; color:var(--muted); font-size:.7rem; white-space:nowrap}
  @media (max-width:560px){
    /* The name takes the whole first line and the reading sits under it —
       which is how you would say it out loud: what it is, then how it is
       doing, then when anyone last looked. */
    .fr-drow{display:flex; flex-wrap:wrap; gap:3px 10px; align-items:center}
    .fr-drow.hdrow{display:none}
    .fr-drow .nm{flex:1 1 100%}
    .fr-def .when{text-align:left; margin-left:auto}
  }

  /* ---- who lives here (area statistics card) --------------------------
     No new colours: the accent green is "your area" exactly as in the
     bars above it, and England is a tick on the same track rather than a
     second hue — the comparison is one value against one benchmark, which
     is a position, not a category. Both numbers are printed as text, so
     nothing here depends on seeing colour at all. */
  .wl{border-top:1px solid var(--line); padding-top:9px; margin-top:9px}
  .wl h4{font-size:.85rem; margin-bottom:1px}
  .wl .wl-note{font-size:.72rem; color:var(--muted); margin-bottom:6px}
  .wl .wl-hl{font-size:.8rem; margin:4px 0 7px; padding:5px 8px;
    background:var(--accent-soft); border-radius:8px}
  .wl-g{display:grid; grid-template-columns:minmax(0,1.35fr) minmax(28px,.85fr) 3.5em 4.9em;
    gap:4px 8px; align-items:center; font-size:.78rem}
  .wl-g .nm{overflow-wrap:anywhere}
  .wl-g .b{position:relative; background:var(--line); border-radius:3px; height:8px}
  .wl-g .b i{display:block; height:100%; background:var(--accent); border-radius:3px}
  /* England as a tick on the same track: above/below is readable at a glance
     without asking anyone to compare two separate bars. */
  .wl-g .b u{position:absolute; top:-2px; width:2px; height:12px;
    background:var(--ink); opacity:.55; border-radius:1px}
  .wl-g .h{text-align:right; font-weight:700; font-variant-numeric:tabular-nums}
  .wl-g .e{text-align:right; color:var(--muted); font-size:.71rem;
    font-variant-numeric:tabular-nums}
  @media (max-width:430px){
    .wl-g{grid-template-columns:minmax(0,1.3fr) 3.4em 4.7em}
    .wl-g .b{display:none}
  }

  /* ---- prices-vs-pay chart (card-money) ------------------------------
     Series colours are declared once here as roles, so the chart body is
     written against --pg-1..5 rather than raw hex. The five hues are the
     validated set: they clear colour-blind separation on EVERY pair, not
     just neighbouring ones, which matters because these five lines bundle
     and cross in a narrow band rather than staying in a fixed order.
     Two of them (yellow, magenta) sit under 3:1 against white — that is
     allowed only because the legend prints each series' figure as text
     and the table view repeats every number, so colour is never the only
     way to read a value. Don't add a sixth line: the set was validated as
     five, and a sixth hue would have to be re-validated, not invented. */
  .pgw{border-top:1px solid var(--line); padding-top:10px; margin-top:9px;
    --pg-1:#2a78d6; --pg-2:#4a3aa7; --pg-3:#008300; --pg-4:#eda100; --pg-5:#e87ba4}
  .pg-fig{position:relative; margin:6px 0 0}
  .pg-fig svg{display:block; width:100%; height:auto; overflow:visible; touch-action:pan-y}
  /* Falklands tile (5 Sep 2026). The for/against pair sits side by side on a
     desktop and stacks on a phone; each list keeps its own accent so the two
     never read as one argument. The SVG text is sized in SVG units and bumped
     under the phone query, because a 600-unit viewBox drawn at phone width
     halves its text (HANDOVER §5). */
  .fk-pc{display:grid; grid-template-columns:1fr 1fr; gap:10px}
  .fk-pc .item{margin:0}
  .fk-pc ul{margin:6px 0 0; padding-left:18px; font-size:.9rem; line-height:1.5}
  .fk-pc li{margin:4px 0}
  .fk-for{border-left:4px solid var(--accent)}
  .fk-against{border-left:4px solid var(--amber)}
  .fk-ledger{font-size:.9rem}
  .fk-grp{color:var(--muted); font-size:.82rem; margin:10px 0 4px}
  .fk-row{margin:4px 0 8px}
  .fk-lbl{margin-bottom:2px}
  /* 7 Sep 2026: flex-wrap added. `white-space:nowrap` keeps the bar and its
     value on one line, but it also stopped the trailing note wrapping, so a
     long note on a long bar was silently CLIPPED rather than moved -- seen on
     the Chagos and Antarctic tiles at both desktop and phone width. The note
     may now wrap and drop to its own line; nothing that already fitted moves. */
  .fk-bar{display:flex; align-items:center; flex-wrap:wrap; gap:2px 8px; white-space:nowrap}
  .fk-bar span{display:block; height:16px; border-radius:3px; flex:0 0 auto}
  .fk-bar i{font-style:normal; color:var(--muted); font-size:.82rem;
    white-space:normal; min-width:0}
  .fk-svg text{font-size:12px; fill:var(--ink)}
  .fk-svg text.mut{fill:var(--muted); font-size:11px}
  .fk-svg text.val{font-weight:700}
  .fk-svg .lab-s{display:none}
  @media (max-width:640px){
    .fk-pc{grid-template-columns:1fr}
    .fk-svg text{font-size:22px} .fk-svg text.mut{font-size:19px}
    .fk-svg .lab-l{display:none} .fk-svg .lab-s{display:initial}
  }
  /* Brexit tile (owner, 16 Sep 2026). Reuses the .fk-* bars and for/against
     pair and the .ov-* steps. New: the scorecard — one block per theme, a
     "2016" line and a "2026" line, two columns on a desktop and one on a
     phone, so a reader can compare before and after without a table that
     would crush to nothing at 390px (HANDOVER §5, the table rule); and the
     "universal / only Britain" pair, which is the same two-column idea. */
  .bx-grid{display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:8px}
  .bx-item{border:1px solid var(--line); border-radius:8px; padding:8px 10px; font-size:.88rem; line-height:1.45}
  .bx-h{display:flex; flex-wrap:wrap; align-items:center; gap:4px 8px; margin-bottom:4px}
  .bx-h .chip{white-space:normal}
  .bx-then,.bx-now{margin:4px 0}
  .bx-then i,.bx-now i{font-style:normal; font-weight:700; font-size:.72rem; color:var(--muted); margin-right:6px;
    letter-spacing:.04em}
  .bx-now{border-top:1px dashed var(--line); padding-top:4px}
  .bx-two{display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:6px}
  .bx-col > i{font-style:normal; font-weight:700; font-size:.78rem; color:var(--muted); text-transform:uppercase;
    letter-spacing:.04em}
  @media (max-width:640px){ .bx-grid,.bx-two{grid-template-columns:1fr} }
  /* Chagos and British Antarctic Survey tiles (owner, 7 Sep 2026). Both sit
     in the same "Britain overseas" section and deliberately REUSE the .fk-*
     classes above — the bars, the two-column for/against pair and the SVG
     text sizing are the same design decisions, and one place to change how
     an overseas tile draws a bar is better than three. Only two things are
     new: a row of stages (the Chagos tile's whole point is WHERE the deal
     stopped, which a reader should see without reading a paragraph) and a
     small table (the BAS estate, whose interesting column is "winter" —
     a chart would bury it). The table gets its own overflow box because a
     four-column table on a phone must scroll inside itself, never push the
     page sideways (HANDOVER §5). */
  .ov-steps{display:flex; flex-wrap:wrap; gap:6px; margin:8px 0 0}
  .ov-step{flex:1 1 150px; border-left:4px solid var(--accent); background:var(--accent-soft);
    padding:5px 8px; font-size:.82rem; line-height:1.4; border-radius:0 6px 6px 0}
  .ov-step.stop{border-left-color:var(--amber); background:var(--amber-soft)}
  .ov-step b{display:block; font-size:.9rem; margin:1px 0}
  .ov-step i{font-style:normal; color:var(--muted); font-size:.78rem}
  .ov-list{margin:8px 0 0; padding-left:18px; font-size:.9rem; line-height:1.5}
  /* Britain's defence (23 Sep 2026). Tables scroll inside their own box on a
     phone (HANDOVER §5: a wide table needs its own overflow box). */
  .df-scroll{overflow-x:auto; margin-top:6px}
  .df-tbl{border-collapse:collapse; width:100%; font-size:.86rem; line-height:1.4}
  .df-tbl th, .df-tbl td{border-top:1px solid var(--line); padding:6px 8px 6px 0; text-align:left; vertical-align:top}
  .df-tbl thead th{border-top:0; color:var(--muted); font-weight:600; font-size:.78rem}
  .df-tbl tbody th{font-weight:600; min-width:9em}
  .df-tbl td.n{font-weight:700; font-variant-numeric:tabular-nums; white-space:nowrap}
  .df-grp{margin-top:10px}
  .df-why{color:var(--muted)}
  .df-actor{border:1px solid var(--line); border-radius:10px; padding:10px 12px; margin-top:10px}
  .df-actor-h{font-size:1rem}
  .df-cols{display:grid; grid-template-columns:1fr 1fr; gap:4px 18px}
  .df-cols i{font-size:.78rem; color:var(--muted); font-style:normal; font-weight:600}
  @media (max-width:700px){ .df-cols{grid-template-columns:1fr} }
  .df-logic{margin-top:6px; font-size:.86rem; background:var(--bg); border-radius:8px; padding:7px 10px}
  .df-ladder{display:flex; flex-wrap:wrap; gap:6px; margin:8px 0}
  .df-rung{flex:1 1 110px; border:1px solid var(--line); border-radius:8px; padding:6px 8px; font-size:.78rem; color:var(--muted); background:var(--card)}
  .df-rung b{display:block; font-size:.82rem; color:var(--ink)}
  .df-rung.on{background:var(--red); border-color:var(--red); color:#fff}
  .df-rung.on b{color:#fff}
  .df-rev{color:var(--muted)}
  .df-rev-step{display:block; margin-top:6px; font-size:.78rem; font-weight:600; color:var(--ink)}
  .ov-list li{margin:7px 0}
  .ov-wrap{overflow-x:auto; margin:6px 0 0}
  /* Two columns, so it fits a phone without scrolling -- the .ov-wrap box is
     the safety net, not the plan. The four-column version crushed the winter
     figure, which is the one worth reading (HANDOVER §5, the table rule). */
  .ov-tab{width:100%; border-collapse:collapse; font-size:.9rem}
  .ov-tab th,.ov-tab td{text-align:left; padding:6px 10px 6px 0; border-bottom:1px solid var(--line);
    vertical-align:top}
  .ov-tab th{color:var(--muted); font-weight:600; font-size:.82rem}
  .ov-tab th.n,.ov-tab td.n{text-align:right; white-space:nowrap; padding-right:0; padding-left:10px}
  .ov-tab .meta{margin-top:1px}
  .ov-tab .mut{color:var(--muted)}
  .pg-grid{stroke:var(--line); stroke-width:1}
  .pg-zero{stroke:var(--muted); stroke-width:1; opacity:.55}
  .pg-ax{fill:var(--muted); font-size:11px}
  .pg-ln{fill:none; stroke-width:2; stroke-linejoin:round; stroke-linecap:round}
  /* the 2px surface ring keeps an end-dot legible where lines overlap */
  .pg-dot{stroke:var(--card); stroke-width:2}
  .pg-cross{stroke:var(--muted); stroke-width:1; opacity:.5}
  .pg-hit{fill:transparent; cursor:crosshair}
  .pg-tip{position:absolute; pointer-events:none; z-index:3; background:var(--card);
    border:1px solid var(--line); border-radius:9px; padding:5px 7px; font-size:.68rem;
    line-height:1.3; box-shadow:0 3px 12px #0002}
  .pg-tip b{display:block; margin-bottom:2px; font-size:.72rem}
  .pg-tip div{display:flex; align-items:center; gap:5px; white-space:nowrap}
  .pg-tip i{width:7px; height:7px; border-radius:2px; flex:0 0 7px}
  .pg-tip span{margin-left:auto; padding-left:6px; font-variant-numeric:tabular-nums; font-weight:700}
  .pg-key{display:grid; gap:2px 12px; margin-top:9px;
    grid-template-columns:repeat(auto-fit,minmax(15em,1fr))}
  .pg-key div{display:flex; align-items:baseline; gap:7px; font-size:.8rem; padding:2px 0}
  .pg-key i{width:14px; height:3px; border-radius:2px; flex:0 0 14px; align-self:center}
  .pg-key b{margin-left:auto; font-variant-numeric:tabular-nums}
  .pg-key small{color:var(--muted); font-size:.72rem}
  .pg-tbl{margin-top:8px; font-size:.8rem}
  .pg-tbl summary{cursor:pointer; color:var(--accent); font-size:.78rem}
  .pg-tbl table{border-collapse:collapse; width:100%; margin-top:6px;
    font-variant-numeric:tabular-nums}
  .pg-tbl th,.pg-tbl td{border-bottom:1px solid var(--line); padding:3px 5px;
    text-align:right; font-size:.74rem}
  .pg-tbl th:first-child,.pg-tbl td:first-child{text-align:left}
  .pg-tbl thead th{color:var(--muted); font-weight:600}
  @media (max-width:520px){ .pg-key{grid-template-columns:1fr} }

  /* ---- inflation calculator (card-inflation-calculator) ---------------
     Reuses the prices-vs-pay chart classes (.pg-*) for grid, axes, line,
     dots and tooltip so the two Money charts read as one system. Its own
     rules are only the input row and the result box. The value line takes
     --pg-1 (blue) and the annual-rate bars --pg-2; deflation bars (a rate
     below zero) use the red role, because "prices fell" is the one thing
     on this chart a reader should never mistake for a small rise. */
  .infc-form{display:flex; flex-wrap:wrap; gap:8px 12px; align-items:flex-end;
    margin:8px 0 6px}
  .infc-form label{display:flex; flex-direction:column; gap:3px;
    font-size:.72rem; color:var(--muted); font-weight:600}
  .infc-form input,.infc-form select{font:inherit; font-size:.95rem;
    padding:6px 8px; border:1px solid var(--line); border-radius:8px;
    background:var(--card); color:inherit; min-width:0}
  .infc-form input[type=number]{width:8.5em; font-variant-numeric:tabular-nums}
  .infc-form .infc-amt{position:relative}
  .infc-form .infc-amt input{padding-left:1.35em}
  .infc-form .infc-amt::after{content:"£"; position:absolute; left:8px; bottom:7px;
    font-size:.95rem; color:var(--muted); pointer-events:none}
  .infc-form select{width:7.6em}
  .infc-result{margin:4px 0 8px; padding:9px 11px; border-radius:10px;
    background:var(--blue-soft); font-size:.92rem; line-height:1.4}
  .infc-result b{font-variant-numeric:tabular-nums}
  .infc-result .big{font-size:1.25rem; font-weight:800; display:block;
    margin-bottom:2px}
  .infc-result .small{font-size:.75rem; color:var(--muted); display:block;
    margin-top:3px}
  .infc-rate .pg-bar{fill:var(--pg-2); opacity:.75}
  .infc-rate .pg-bar.neg{fill:var(--red)}
  .infc-rate .pg-target{stroke:var(--muted); stroke-width:1; stroke-dasharray:3 3}
  /* On a phone the 660-unit viewBox is drawn at half size, so 11px axis text
     becomes 5px. Scale the tick labels up in SVG units (they shrink back to
     ~9px on screen); the caption keeps its size and is short enough to fit. */
  @media (max-width:520px){
    .infc-form input[type=number]{width:7em}
    #infc-svg .pg-ax, #infc-ratesvg .pg-ax{font-size:18px}
    #infc-ratesvg .pg-cap{font-size:13px}
  }

  /* ---- is the economy healthy? (card-economic-health, 3 Sep 2026) -----
     Its own namespace rather than more .es-* rules. The energy scorecard's
     meters answer "how bad is this, on a fixed scale"; these answer "which
     way is it moving", which is a different shape and wants different
     furniture: a rung ladder for the state, a verdict chip per measure, and
     a long line with the recessions shaded behind it.
     Everything here is painted from the server's payload — the rung labels,
     the dial rows, the bands on the chart — so adding a measure in
     app/ukecon.py needs no edit in this file. */
  .ue-head{margin:9px 0 2px}
  .ue-state{font-size:1.5rem; font-weight:800; line-height:1.15}
  .ue-state.bad{color:var(--red)}
  .ue-state.warn{color:var(--amber)}
  .ue-state.good{color:var(--accent)}
  .ue-because{font-size:.8rem; color:var(--muted); margin-top:3px; line-height:1.45}
  .ue-ladder{display:flex; gap:3px; margin:8px 0 0}
  /* A SCALE WITH YOUR POSITION ON IT, not a progress bar. The first version
     filled every rung up to the current one in one colour, which lit "In a
     deep recession" and "In recession" in green on the way to "Growing" —
     the exact opposite of what those words mean. Each rung now wears its
     OWN colour, pale, and only the rung the country is actually on is
     solid. */
  .ue-ladder i{flex:1; height:8px; border-radius:3px; background:var(--line)}
  .ue-ladder i.r-bad{background:var(--red-soft)}
  .ue-ladder i.r-warn{background:var(--amber-soft)}
  .ue-ladder i.r-good{background:var(--accent-soft)}
  .ue-ladder i.on.r-bad{background:var(--red)}
  .ue-ladder i.on.r-warn{background:var(--amber)}
  .ue-ladder i.on.r-good{background:var(--accent)}
  .ue-rungs{display:flex; justify-content:space-between; gap:6px;
    font-size:.62rem; color:var(--muted); margin-top:4px; line-height:1.2}
  .ue-rungs span{flex:1; text-align:center}
  .ue-rungs span.on{color:var(--ink); font-weight:700}
  /* The count. Deliberately a sentence with three numbers in it rather than
     one big score: the whole point is that no single figure stands for the
     economy (owner, 3 Sep 2026). */
  .ue-count{margin:10px 0 2px; padding:9px 11px; border-radius:10px;
    background:var(--blue-soft); font-size:.92rem; line-height:1.45}
  .ue-count b{font-variant-numeric:tabular-nums}
  .ue-dial{display:grid; grid-template-columns:1fr auto; gap:2px 10px;
    align-items:baseline; border-top:1px solid var(--line); padding:7px 0 6px}
  .ue-dial:first-of-type{border-top:0}
  .ue-dial .ue-l{font-size:.86rem; font-weight:600}
  .ue-dial .ue-v{font-size:.86rem; font-variant-numeric:tabular-nums;
    white-space:nowrap; text-align:right}
  .ue-dial .ue-w{grid-column:1/-1; font-size:.7rem; color:var(--muted);
    line-height:1.4}
  .ue-vd{display:inline-block; font-size:.66rem; font-weight:700;
    border-radius:5px; padding:1px 6px; margin-left:6px; vertical-align:1px;
    white-space:nowrap}
  .ue-vd.better{background:var(--accent-soft); color:var(--accent)}
  .ue-vd.worse{background:var(--red-soft); color:var(--red)}
  .ue-vd.same{background:var(--bg); color:var(--muted)}
  /* Unemployment by industry (UNEM03). Same grid as .ue-dial — this is the
     same kind of row and should not look like a second design — plus a
     sparkline column, because a sector's ten-year shape is the point and a
     single percentage cannot carry it. */
  .ind-row{display:grid; grid-template-columns:1fr auto; gap:2px 10px;
    align-items:baseline; border-top:1px solid var(--line); padding:7px 0 6px}
  .ind-row:first-of-type{border-top:0}
  .ind-l{font-size:.86rem; font-weight:600}
  .ind-v{font-size:.86rem; font-variant-numeric:tabular-nums;
    white-space:nowrap; text-align:right}
  .ind-w{grid-column:1/-1; font-size:.7rem; color:var(--muted); line-height:1.4;
    display:flex; align-items:center; gap:8px; flex-wrap:wrap}
  .ind-spark{flex:0 0 auto; width:96px; height:22px; overflow:visible}
  /* NOT var(--pg-1): that variable is defined on .pgw, the chart wrapper,
     and a sparkline out here would inherit nothing and draw an invisible
     line. Neutral ink also says the right thing — this is a shape, not a
     series in the five-hue chart palette. */
  .ind-spark path{fill:none; stroke:var(--ink); opacity:.45; stroke-width:1.6;
    stroke-linejoin:round; stroke-linecap:round}
  .ind-spark circle{fill:var(--ink)}
  /* The small-sample flag is a fact ONS publishes as a cell shade. It is
     carried through to the reader rather than quietly dropped. */
  /* The year change, coloured by the same rule as the ten-year chip: the
     colour follows whether MORE or FEWER people are out of work, never the
     direction of the arrow. A rise in unemployment is red however it is
     drawn. Text colour rather than a second pill — one chip per row is
     enough furniture, and this sits inside a line of grey meta text. */
  .ind-yr{font-weight:700}
  .ind-yr.better{color:var(--accent)}
  .ind-yr.worse{color:var(--red)}
  .ind-yr.same{color:var(--muted); font-weight:600}
  /* The size-versus-risk note. Amber, because it is a correction to what
     the big percentage beside it appears to say, not a decoration. */
  .ind-note{grid-column:1/-1; font-size:.7rem; line-height:1.4;
    color:var(--ink); background:var(--amber-soft);
    border-radius:7px; padding:4px 8px; margin-top:3px}
  .ind-ss{font-size:.62rem; font-weight:700; letter-spacing:.02em;
    background:var(--bg); color:var(--muted); border:1px solid var(--line);
    border-radius:5px; padding:0 5px; white-space:nowrap;
    display:inline-block}
  /* The long line. Reuses the .pg-* chart furniture so the three Money
     charts read as one system; only the shaded recession bands are new. */
  .ue-band{fill:var(--red); opacity:.10}
  .ue-line{fill:none; stroke:var(--pg-1); stroke-width:2;
    stroke-linejoin:round; stroke-linecap:round}
  .ue-now{fill:var(--pg-1); stroke:var(--card); stroke-width:2}
  /* The recession table has five columns and a phone has none to spare, so
     it scrolls inside its own box rather than pushing the page sideways.
     min-width keeps the columns legible instead of wrapping "Back to peak"
     onto four lines. */
  #ue-recs-in{overflow-x:auto; -webkit-overflow-scrolling:touch}
  .ue-tbl table{border-collapse:collapse; width:100%; min-width:340px;
    margin-top:6px; font-variant-numeric:tabular-nums; font-size:.76rem}
  .ue-tbl th,.ue-tbl td{border-bottom:1px solid var(--line); padding:3px 5px;
    text-align:right}
  .ue-tbl th:first-child,.ue-tbl td:first-child{text-align:left}
  .ue-tbl thead th{color:var(--muted); font-weight:600}
  .ue-tbl summary{cursor:pointer; color:var(--accent); font-size:.78rem}
  .ue-miss{font-size:.72rem; color:var(--amber); margin-top:6px}
  /* The yield-curve record. Framed as history, and styled as history: no
     traffic light, no arrow, nothing that reads as a prediction. The one
     visual emphasis is on where the spread stands TODAY, because that is
     the only part of it that is a fact about the present. */
  .ue-yc{border-top:1px solid var(--line); margin-top:12px; padding-top:10px}
  .ue-yc h4{margin:0 0 4px; font-size:.86rem}
  .ue-yc .ue-yc-now{font-size:.82rem; font-variant-numeric:tabular-nums}
  .ue-yc .ue-yc-rec{font-size:.8rem; line-height:1.5; margin-top:5px}
  .ue-yc .ue-yc-rec b{font-variant-numeric:tabular-nums}
  .ue-yc .ue-yc-small{font-size:.7rem; color:var(--muted); margin-top:6px;
    line-height:1.45}
  .ue-yc .chip{vertical-align:1px}
  /* Same trap as the inflation chart: a 660-unit viewBox drawn at phone
     width halves its text, so 11px axis labels land at 5px. Scale the tick
     text up in SVG units instead of shrinking the box (2 Sep 2026). */
  @media (max-width:520px){
    #ue-svg .pg-ax{font-size:18px}
    .ue-rungs{font-size:.58rem}
  }

  /* Evolving religion: one table per nation. Tabular numerals so the columns
     line up down the page, and a muted em dash for "not published" — which is
     a different statement from zero and must not read like one. */
  .reltab{margin-top:12px}
  .reltab h3{font-size:.82rem; margin:0 0 2px; font-weight:650}
  table.rel{border-collapse:collapse; width:100%;
    font-variant-numeric:tabular-nums}
  table.rel th,table.rel td{border-bottom:1px solid var(--line);
    padding:3px 5px; text-align:right; font-size:.76rem}
  table.rel th[scope=row]{text-align:left; font-weight:500}
  table.rel thead th{color:var(--muted); font-weight:600; font-size:.72rem}
  table.rel thead th:first-child{text-align:left}
  table.rel td.na{color:var(--muted)}
  .reltab .src{font-size:.68rem; margin-top:3px}
  /* Fact grid — shared by The ground under your street and Air, noise and
     sewage (redesign, owner 2 Sep 2026: "messy, not great on the eye").
     One row per question: a short label, then a verdict CHIP in colour
     plus one plain sentence; sources printed once at the foot, never per
     line. Chips use the site's colour roles: green = nothing to act on,
     amber = worth a question, red = a reason to act. */
  .fg{margin:8px 0 2px; border-top:1px solid var(--line)}
  /* Wildlife near you (21 Sep 2026). wlf-, NOT wl-: the hospital waiting-list tile already owns .wl-g and
     .wl-note, and the first draft of this tile collided with both — found by rendering it, not by reading it. */
  .wlf-stats{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; margin:10px 0 2px}
  .wlf-stat{background:var(--bg); border-radius:10px; padding:8px 10px}
  .wlf-stat b{display:block; font-size:1.25rem; line-height:1.15; font-variant-numeric:tabular-nums}
  .wlf-stat span{font-size:.72rem; color:var(--muted)}
  .wlf-h{font-size:.84rem; font-weight:700; margin:16px 0 4px}
  .wlf-h small{font-weight:400; color:var(--muted); font-size:.74rem; margin-left:6px}
  .wlf-row{display:grid; grid-template-columns:minmax(0,1fr) auto; gap:2px 12px; padding:7px 0; border-top:1px solid var(--line)}
  .wlf-row:first-child{border-top:0}
  .wlf-nm{font-weight:700; font-size:.9rem}
  .wlf-nm i{font-weight:400; color:var(--muted); font-size:.78rem; margin-left:4px}
  .wlf-rt{font-size:.78rem; color:var(--muted); text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums}
  .wlf-rt b{color:var(--ink)}
  .wlf-chips{grid-column:1 / -1; display:flex; flex-wrap:wrap; gap:4px}
  .wlf-chips .chip.plain{background:var(--card); color:var(--muted); border:1px solid var(--line); font-weight:600}
  .wlf-btn, .wlf-g{border:1px solid var(--line); background:var(--card); border-radius:999px; cursor:pointer; font:inherit}
  .wlf-btn{color:var(--accent); font-weight:700; font-size:.8rem; padding:7px 12px; margin-top:8px}
  .wlf-gs{display:flex; flex-wrap:wrap; gap:6px}
  .wlf-g{color:var(--ink); font-size:.8rem; padding:6px 11px}
  .wlf-g[aria-expanded="true"]{background:var(--accent); border-color:var(--accent); color:#fff}
  .wlf-panel{background:var(--bg); border-radius:10px; padding:9px 12px; font-size:.8rem; line-height:1.6; margin-top:8px}
  .wlf-note{background:var(--bg); border-radius:10px; padding:9px 12px; font-size:.78rem; margin-top:10px}
  @media (max-width:460px){ .wlf-stats{grid-template-columns:1fr 1fr} .wlf-stat:last-child{grid-column:1 / -1} }
  .fg-row{display:grid; grid-template-columns:9.5em minmax(0,1fr); gap:2px 12px;
    padding:8px 0; border-bottom:1px solid var(--line); align-items:start}
  .fg-k{font-weight:700; font-size:.86rem; padding-top:1px}
  .fg-v{font-size:.86rem; line-height:1.45}
  .fg-v .chip{margin-right:6px; vertical-align:0}
  .fg-v .sub{display:block; font-size:.72rem; color:var(--muted); margin-top:3px}
  .fg-v ul{margin:4px 0 0; padding-left:18px}
  .fg-v li{margin:2px 0}
  .chip.green{background:var(--accent-soft); color:var(--accent)}
  .chip.grey{background:var(--line); color:var(--muted)}
  .ofs-chips{display:flex; flex-wrap:wrap; gap:4px; margin:4px 0}   /* Ofsted grades, 29 Sep 2026 */
  /* Charities (30 Sep 2026): where the money comes from, as one bar. */
  .chb{display:flex; height:9px; border-radius:999px; overflow:hidden; background:var(--bg); margin:6px 0 3px}
  .chb i{display:block; height:100%}
  .chb .d, .chkey .d{background:var(--accent)} .chb .f, .chkey .f{background:var(--blue)}
  .chb .t, .chkey .t{background:var(--amber)} .chb .v, .chkey .v{background:#7a5ea8}
  .chb .o, .chkey .o{background:var(--muted)}
  .chkey{font-size:.72rem; color:var(--muted); display:flex; flex-wrap:wrap; gap:4px 10px}
  .chkey i{display:inline-block; width:8px; height:8px; border-radius:2px; margin-right:4px; vertical-align:0}
  .chlist details summary{cursor:pointer; font-size:.8rem; color:var(--accent); margin:4px 0}
  /* The world-news place chip (3 Sep 2026): sits after the headline, and
     never breaks across lines — "United" on one row and "Kingdom" on the
     next reads as two tags. Grey first time out; the owner's verdict was
     "grey is dull", and he was right — it read as disabled rather than as
     information, next to a chip that was actually coloured. */
  .chip.wn-where{margin-left:6px; white-space:nowrap; font-weight:700;
                 background:var(--place-soft); color:var(--place)}
  .fg-tags{display:flex; flex-wrap:wrap; gap:6px 8px; margin-top:2px}
  .fg-tags span{font-size:.78rem}
  /* Meter: one horizontal track with marked thresholds and two dots — the
     nearest square here (solid) and the national median (hollow). */
  .mtr{position:relative; height:24px; margin:6px 0 2px}
  .mtr-bar{position:absolute; left:0; right:0; top:9px; height:6px; border-radius:3px;
    background:linear-gradient(90deg, var(--accent-soft), var(--amber-soft) 55%, var(--red-soft))}
  .mtr-t{position:absolute; top:4px; width:1px; height:16px; background:var(--muted); opacity:.6}
  .mtr-t i{position:absolute; top:16px; left:-2.5em; width:5em; text-align:center;
    font-style:normal; font-size:.62rem; color:var(--muted); white-space:nowrap}
  .mtr-v{position:absolute; top:5px; width:14px; height:14px; margin-left:-7px; border-radius:50%;
    background:var(--blue); border:2px solid var(--card); box-shadow:0 0 0 1px var(--blue)}
  .mtr-n{position:absolute; top:6px; width:12px; height:12px; margin-left:-6px; border-radius:50%;
    background:var(--card); border:2px solid var(--muted)}
  .mtr-key{font-size:.7rem; color:var(--muted); margin-top:14px}
  .mtr-key b{color:var(--blue)}
  @media (max-width:520px){ .fg-row{grid-template-columns:1fr} .fg-k{padding-top:0} }
  /* Valuation and sold prices: two named sections inside one tile (owner,
     3 Sep 2026). The rule reuses the .sect h3 idea without the flex column
     wrapper, because these headings sit inside a card body, not between
     cards. First heading carries no top rule — the card's own meta line is
     already a divider. */
  .sub-h{font-size:.95rem; font-weight:700; margin:16px 0 2px; padding-top:12px;
    border-top:2px solid var(--line); display:flex; align-items:baseline;
    gap:8px; flex-wrap:wrap}
  .sub-h.first{border-top:0; padding-top:0; margin-top:10px}
  /* .fresh is styled via `.card h2 .fresh` and `.sect h3 .fresh` — a new
     element gets none of it and inherits the heading's bold (handover:
     "scoped styles don't follow a pattern to a new element"). */
  .sub-h .fresh{margin-left:auto; font-weight:400; font-size:.72rem; color:var(--muted)}
  /* The one-line valuation calculator. Same shapes as the Money tab's
     inflation calculator (.infc-*) so the two read as one system; its own
     rules are only the month/year pair. */
  .vc-form{display:flex; flex-wrap:wrap; gap:8px 10px; align-items:flex-end;
    margin:8px 0 6px}
  .vc-form label{display:flex; flex-direction:column; gap:3px; font-size:.72rem;
    color:var(--muted); font-weight:600}
  .vc-form input,.vc-form select{font:inherit; font-size:.95rem; padding:6px 8px;
    border:1px solid var(--line); border-radius:8px; background:var(--card);
    color:inherit; min-width:0}
  .vc-amt{position:relative}
  .vc-amt input{padding-left:1.35em; width:9em; font-variant-numeric:tabular-nums}
  .vc-amt::after{content:"£"; position:absolute; left:8px; bottom:7px;
    font-size:.95rem; color:var(--muted); pointer-events:none}
  .vc-form select.mon{width:6.4em}
  .vc-form select.yr{width:5.8em}
  .vc-result{margin:4px 0 2px; padding:10px 12px; border-radius:10px;
    background:var(--blue-soft); font-size:.92rem; line-height:1.45}
  .vc-result .big{font-size:1.3rem; font-weight:800; display:block; margin-bottom:3px}
  .vc-result .small{font-size:.75rem; color:var(--muted); display:block; margin-top:5px}
  .vc-result b{font-variant-numeric:tabular-nums}
  /* Phone: the source note drops under the heading, left-aligned — pushed
     right by margin-left:auto it read as a stray right-hand column. */
  @media (max-width:520px){
    .vc-amt input{width:7em}
    .vc-form select.mon{width:5.6em}
    .vc-form select.yr{width:5.2em}
    .sub-h .fresh{margin-left:0; flex-basis:100%}
  }
  /* Sold prices (card-sold-prices): one row per sale, the price as paid and
     the same price in today's money side by side — the second column is the
     point of the tile, so it is the bold one. Chips are the existing .chip. */
  table.ppd{border-collapse:collapse; width:100%; font-variant-numeric:tabular-nums;
    margin-top:6px}
  table.ppd th,table.ppd td{border-bottom:1px solid var(--line); padding:5px 5px;
    text-align:right; font-size:.8rem; vertical-align:top}
  table.ppd th:first-child,table.ppd td:first-child,
  table.ppd th:nth-child(2),table.ppd td:nth-child(2){text-align:left}
  table.ppd thead th{color:var(--muted); font-weight:600; font-size:.72rem}
  table.ppd td.pct{font-weight:700; white-space:nowrap}
  table.ppd td.pct.up{color:var(--accent)}
  table.ppd td.pct.down{color:var(--red)}
  table.ppd td.first{white-space:nowrap}
  table.ppd th:nth-child(n+4){max-width:7em; white-space:normal}
  table.ppd td .meta{font-size:.68rem; display:block; color:var(--muted); margin-top:1px}
  table.ppd tr.addl td{color:var(--muted)}
  table.ppd tr.addl td.real{font-weight:500}
  .ppd-dec{display:grid; gap:2px 14px; margin:10px 0 4px;
    grid-template-columns:repeat(auto-fit,minmax(13em,1fr)); font-size:.8rem}
  .ppd-dec div{display:flex; align-items:baseline; gap:6px; padding:2px 0;
    border-bottom:1px dotted var(--line)}
  .ppd-dec b{margin-left:auto; font-variant-numeric:tabular-nums}
  .ppd-dec small{color:var(--muted); font-size:.7rem}
  /* Phone: the "paid" column folds INTO the today's-money cell as a small
     line rather than disappearing — both figures are the tile's point. */
  /* Phone: the first-sale column goes (its year survives in the % headers'
     meaning); paid, its change and inflation stay side by side. */
  @media (max-width:520px){
    table.ppd th:nth-child(4),table.ppd td:nth-child(4){display:none}
    table.ppd td .meta{white-space:normal}
    table.ppd th,table.ppd td{padding:5px 3px}
  }
  /* EPC band bar: one stacked row, official band colours, dark text only on
     the light middle bands where white would fail contrast. */
  .epcbar{display:flex; height:26px; border-radius:6px; overflow:hidden;
    margin:8px 0 6px}
  .epcseg{display:flex; align-items:center; justify-content:center;
    color:#fff; font-size:.72rem; font-weight:700; min-width:0}
  .epcseg[style*="#8dce46"],.epcseg[style*="#ffd500"],.epcseg[style*="#fcaa65"]{
    color:#1d2b23}
  .epclegend{display:flex; flex-wrap:wrap; gap:4px 12px; font-size:.74rem;
    margin-bottom:8px}
  .epck i{display:inline-block; width:9px; height:9px; border-radius:2px;
    margin-right:4px}
  .epcrow{font-size:.85rem; margin:4px 0}
  .epcstat{display:inline-block; margin-right:16px}
  .epclow{margin-top:10px; font-size:.85rem}
  .epclow summary{cursor:pointer; font-weight:600}
  .epclow ul{margin:8px 0 4px; padding-left:4px; list-style:none}
  .epclow li{margin:5px 0}
  .epcchip{display:inline-flex; width:20px; height:20px; border-radius:4px;
    color:#fff; font-size:.72rem; font-weight:700; align-items:center;
    justify-content:center; margin-right:7px; vertical-align:middle}
  .epcchip[style*="#8dce46"],.epcchip[style*="#ffd500"],.epcchip[style*="#fcaa65"]{
    color:#1d2b23}
  .epcnotice{margin-top:8px}
  .epcnotice summary{font-size:.72rem; color:var(--muted); cursor:pointer}
  /* Food recalls — check your cupboard. One alert is a <details>: the summary
     is what you scan (brand, what is wrong, when), the body is what you check
     against the tin in your hand. */
  .rcsearch{display:flex; gap:10px; align-items:center; flex-wrap:wrap;
    margin:10px 0 2px}
  .rcsearch input{flex:1 1 240px; min-width:0; font:inherit; padding:7px 14px;
    border:1px solid var(--line); border-radius:999px; background:var(--card);
    color:var(--ink)}
  .rcsearch .cnt{font-size:.76rem; color:var(--muted)}
  .rcalert{border:1px solid var(--line); border-radius:10px; padding:9px 12px;
    margin:8px 0}
  .rcalert[open]{background:var(--bg)}
  .rcalert summary{cursor:pointer; display:flex; gap:9px; align-items:baseline;
    flex-wrap:wrap; list-style:none}
  .rcalert summary::-webkit-details-marker{display:none}
  .rcchip{border-radius:999px; padding:1px 9px; font-size:.68rem;
    font-weight:700; white-space:nowrap}
  .rc-PRIN{background:var(--red-soft); color:var(--red)}
  .rc-AA{background:var(--amber-soft); color:var(--amber)}
  .rc-FAFA{background:var(--blue-soft); color:var(--blue)}
  .rcupd{background:var(--accent-soft); color:var(--accent); border-radius:999px;
    padding:1px 8px; font-size:.66rem; font-weight:700}
  .rcname{font-weight:600; font-size:.88rem; flex:1 1 12em; min-width:0}
  .rcwhen{color:var(--muted); font-size:.73rem; white-space:nowrap}
  .rcrisk{font-size:.82rem; margin:8px 0 4px}
  .rcprods{list-style:none; padding:0; margin:8px 0 4px; font-size:.85rem}
  .rcprods li{margin:7px 0; padding-left:11px; border-left:3px solid var(--line)}
  .rcbatch{display:block; color:var(--muted); font-size:.76rem}
  .rcadvice{background:var(--accent-soft); border-radius:8px; padding:8px 11px;
    font-size:.82rem; margin:9px 0 4px}
  .rcnone{font-size:.86rem}
  .rcmore{font:inherit; font-size:.82rem; font-weight:600; cursor:pointer;
    color:var(--accent); background:var(--accent-soft); border:0;
    border-radius:999px; padding:6px 15px; margin-top:6px}
  /* [hidden] needs the rule wherever an author display could win. It has bitten
     this file twice (.sectbar, .w-legend); a button is display:inline-block by
     default, so make the intent explicit rather than rely on it. */
  .rcmore[hidden]{display:none}
  /* Businesses opening and closing: two registers, two columns on a desktop,
     stacked on a phone. Each column can be empty or failed on its own. */
  .bizcols{display:grid; grid-template-columns:1fr; gap:18px}
  .biz-ta{font-weight:400; font-size:.82em; color:var(--muted)}
  /* WHAT LINKS IT HERE (25 Sep 2026): the tag on a Gazette row is the one
     thing that ties the notice to the reader's area, and it IS the link to
     the notice. The office holder's own address is drawn amber — that row
     is about a business somewhere else. */
  .biz-where{display:inline-block; font-size:.7rem; font-weight:700; border-radius:5px;
    padding:1px 6px; margin-left:4px; vertical-align:1px; text-decoration:none;
    background:var(--accent-soft, #e3f0e9); color:var(--accent, #1b6e4b)}
  .biz-where.officeholder{background:var(--amber-soft); color:var(--amber)}
  .biz-where.none{background:#eef0ec; color:var(--muted); font-weight:600}
  .biz-tabs{display:flex; flex-wrap:wrap; gap:5px; margin:0 0 8px}
  .biz-tab{font:inherit; font-size:.74rem; font-weight:600; padding:3px 9px; border-radius:999px;
    border:1px solid var(--line); background:#fff; color:var(--ink); cursor:pointer}
  .biz-tab[aria-selected="true"]{background:var(--blue); border-color:var(--blue); color:#fff}
  .biz-tab:disabled{opacity:.45; cursor:default}
  @media(min-width:720px){ .bizcols{grid-template-columns:1fr 1fr} }
  .bizh{font-size:.74rem; font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:var(--muted); margin-bottom:2px}
  .bizsub{font-size:.74rem; color:var(--muted); margin-bottom:7px}
  .bizlist{list-style:none; padding:0; margin:0; font-size:.86rem}
  .bizlist li{margin:0; padding:7px 0; border-bottom:1px solid var(--line)}
  .bizlist li:last-child{border-bottom:0}
  .bizname{font-weight:600}
  .bizmeta{color:var(--muted); font-size:.75rem}
  .bizwhen{color:var(--muted); font-size:.73rem; float:right; margin-left:8px}
  .bizflag{background:var(--amber-soft); color:var(--amber); border-radius:8px;
    padding:7px 10px; font-size:.78rem; margin:8px 0}
  .care-age{font-size:.74rem; color:var(--muted); white-space:nowrap}
  .care-age.old{color:var(--amber); font-weight:600}
  .pend{margin-top:12px; font-size:.72rem}
  .pend ul{margin:3px 0 0; padding-left:18px}
  .pend li{margin:1px 0}
  /* ---------- HOLIDAY MONEY (3 Oct 2026) — a UK bureau de change board in
     StreetOwl green (owner: "rather than blue"). The board is dark in both
     themes, as a real one is, so its colours are its own and do not follow
     the page's tokens. LED red digits as on the owner's photo, brightened
     until they clear 4.5:1 on the dark green. Every verdict colour also
     carries its word: colour is never the only signal. The board lays out
     by ITS OWN width (container query), not the window's — the tile can be
     a third of a desktop or the whole of a phone. */
  #card-holiday-money{--hm-bg:#0c3d29; --hm-bg2:#082c1d; --hm-ink:#eef7f1; --hm-dim:#a9c7b5;
    --hm-led:#ff5a48; --hm-ledbg:#160806; --hm-frame:#c9a227; --hm-rule:rgba(255,255,255,.13)}
  .hm-pound{font-size:.88rem; margin:6px 0 4px}
  .hm-picks{display:flex; flex-wrap:wrap; gap:6px; margin:6px 0 2px}
  .hm-pick{border:1px solid var(--accent); background:var(--accent-soft); color:var(--accent);
    border-radius:999px; padding:3px 10px; font-weight:600; font-size:.8rem; line-height:1.3; font-family:inherit; cursor:pointer}
  .hm-pick:hover,.hm-pick:focus-visible{background:var(--accent); color:#fff}
  .hm-board{container-type:inline-size; background:linear-gradient(180deg,var(--hm-bg),var(--hm-bg2));
    color:var(--hm-ink); border:5px solid var(--hm-frame); border-radius:12px;
    padding:12px 12px 10px; margin:10px 0 12px;
    box-shadow:inset 0 0 0 1px rgba(255,255,255,.07), 0 2px 10px rgba(0,0,0,.18)}
  .hm-top{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap}
  .hm-title{font:800 1.45rem/1.1 -apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
    letter-spacing:.01em; color:#fff}
  .hm-asof{font-size:.72rem; color:var(--hm-dim)}
  .hm-sort{margin-left:auto; display:inline-flex; border:1px solid var(--hm-rule); border-radius:8px; overflow:hidden}
  .hm-sort button{background:none; border:0; color:var(--hm-dim); font-weight:600; font-size:.74rem; line-height:1; font-family:inherit;
    padding:6px 9px; cursor:pointer}
  .hm-sort button[aria-pressed="true"]{background:#fff; color:var(--hm-bg)}
  .hm-row{display:grid; gap:4px 8px; align-items:center; padding:8px 0 7px;
    border-top:1px solid var(--hm-rule);
    grid-template-columns:auto auto minmax(0,1fr);
    grid-template-areas:"cur cur verd" "rate buy fly" "ago ago ago" "hist hist cost"}
  .hm-head{border-top:0; padding:10px 0 4px; font-weight:800; font-size:.7rem; line-height:1.2; font-family:inherit; letter-spacing:.07em;
    text-transform:uppercase; color:#fff}
  .hm-head{display:none !important}       /* a phone reads the labels in each row */
  .hm-head > *{font-size:inherit; color:inherit}
  .hm-row > *{min-width:0}
  .c-cur{grid-area:cur; display:flex; align-items:center; gap:7px; font-weight:700}
  .c-rate{grid-area:rate} .c-buy{grid-area:buy}
  .hm-ll{display:block; font-size:.66rem; color:var(--hm-dim); text-transform:uppercase; letter-spacing:.06em}
  .c-ago{grid-area:ago; font-size:.76rem; color:var(--hm-dim)}
  .c-hist{grid-area:hist; font-size:.76rem; white-space:nowrap}
  .c-cost{grid-area:cost; font-size:.76rem; text-align:right; line-height:1.25}
  .c-cost .hm-small{display:block}
  .c-verd{grid-area:verd; text-align:right} .c-fly{grid-area:fly; text-align:right}
  /* PHONE OVERFLOW (owner, 19 Oct 2026, screenshot): the verdict and the FCDO
     chip share the narrow right-hand column, and the chip would not wrap
     ("FCDO: parts, essential only" ran off the screen). They now stack,
     right-aligned, and the chip may wrap inside the column's width. */
  .c-verd{display:flex; flex-wrap:wrap; justify-content:flex-end; align-items:center; gap:4px}
  .c-verd .hm-fc{white-space:normal; max-width:100%; text-align:right}
  .hm-flag{font-size:1.15rem; line-height:1}
  .hm-nm{display:block}
  .hm-code{font:700 .72rem/1 ui-monospace,"Cascadia Mono",Consolas,monospace; color:var(--hm-dim)}
  .hm-led{display:inline-block; min-width:5.4em; text-align:right; padding:3px 7px 2px;
    background:var(--hm-ledbg); color:var(--hm-led); border:1px solid #3c1914; border-radius:3px;
    font:700 1.02rem/1.15 ui-monospace,"Cascadia Mono",Consolas,"Courier New",monospace;
    letter-spacing:.05em; font-variant-numeric:tabular-nums; text-shadow:0 0 5px rgba(255,90,72,.55)}
  .hm-row:not(.hm-head) .hm-led{min-width:0}
  @container (min-width:640px){ .hm-row:not(.hm-head) .hm-led{min-width:5.4em} }
  .hm-up{color:#7fe3a6} .hm-down{color:#ffb0a6}
  .hm-bar{display:inline-flex; gap:2px; vertical-align:-1px; margin-right:5px}
  .hm-bar i{display:block; width:7px; height:10px; border-radius:1px; background:rgba(255,255,255,.18)}
  .hm-bar i.on{background:#7fe3a6}
  .hm-v{display:inline-block; white-space:nowrap; border-radius:5px; padding:4px 10px; font-weight:800; font-size:.86rem; line-height:1.2; font-family:inherit}
  .hm-v.great{background:#2fd47a; color:#05261a} .hm-v.good{background:#a6e36a; color:#15300a}
  .hm-v.fair{background:#e9d55a; color:#2f2903} .hm-v.pricey{background:#ffa552; color:#3a1c02}
  .hm-v.poor{background:#ff7466; color:#3a0904}
  /* FCDO line on the board (15 Oct 2026, app/fcdo.py): a small chip beside
     the verdict; at level 3-4 it REPLACES the verdict, because "book here?"
     has no honest answer where the FCDO advises against going. */
  .hm-fc{display:inline-block; white-space:nowrap; border-radius:5px; padding:3px 7px; font-weight:700; font-size:.7rem; line-height:1.2;
    text-decoration:none; border:1px solid transparent}
  .hm-fc.essential_parts,.hm-fc.parts{background:#fff3cd; color:#5a3d00; border-color:#f0c36d}
  .hm-fc.essential,.hm-fc.all{background:#ffd3cf; color:#5a0a04; border-color:#ff8f85; font-size:.78rem; padding:4px 9px}
  .hm-fc.unknown{background:#e4e7e4; color:#333}
  .hm-fc:hover,.hm-fc:focus-visible{filter:brightness(.95)}
  .hm-detail .hm-fcline{margin:4px 0 6px; font-size:.82rem}
  .hm-detail .hm-fcline a{font-weight:700}
  .hm-ex{font-size:.76rem; color:var(--muted); margin:4px 0 0}
  /* ---- Flight deals tile (17 Oct 2026) ---- */
  .fd-list{list-style:none; margin:8px 0 0; padding:0}
  .fd-from{display:flex; flex-wrap:wrap; gap:6px; margin:8px 0 2px}
  .fd-from button{border:1px solid var(--travel); background:var(--travel-soft); color:var(--travel); border-radius:999px;
    padding:4px 11px; font:inherit; font-size:.8rem; font-weight:700; cursor:pointer}
  .fd-from button[aria-pressed="true"]{background:var(--travel); color:#fff}
  .fd-row{display:grid; grid-template-columns:1fr auto; gap:4px 12px; padding:8px 0; border-top:1px solid var(--line); align-items:center}
  .fd-row:first-child{border-top:0}
  .fd-where{font-weight:700; font-size:.95rem}
  .fd-where small{display:block; font-weight:400; color:var(--muted); font-size:.76rem}
  .fd-price{text-align:right; font-weight:800; font-size:1.15rem; color:var(--ink); white-space:nowrap}
  /* The price's colour says what the alert said about it (owner, 20 Oct 2026:
     "lower than typical, it should be green, amber for typical"). It was the
     Travel tab's own amber for every fare, which said nothing. High is red;
     a fare with no verdict stays the page's ordinary ink. Same greens and
     ambers as the Low / Typical badges beside it. */
  .fd-price.low{color:#1d7a3a} .fd-price.typical{color:var(--amber)} .fd-price.high{color:var(--red)}
  .fd-price small{display:block; font-weight:600; font-size:.72rem; color:#1d7a3a}
  /* Return or one way, under every price (owner, 20 Oct 2026: "make it clear
     the prices are return fares, not singles"). In the page's ordinary ink,
     never the price's colour, so it reads as a label and not a verdict. */
  .fd-price small.fd-trip{color:var(--muted); font-weight:700; text-transform:uppercase; letter-spacing:.04em; font-size:.66rem}
  .fd-guide{grid-column:1 / -1; display:flex; flex-wrap:wrap; gap:4px 10px; align-items:center; font-size:.78rem}
  .fd-lvl{border-radius:5px; padding:2px 7px; font-weight:700; font-size:.74rem; border:1px solid transparent}
  .fd-lvl.low{background:#e3f4e6; color:#14532d; border-color:#9fd5a9}
  .fd-lvl.typical{background:#fff3cd; color:#5a3d00; border-color:#f0c36d}
  .fd-lvl.high{background:#ffe1dd; color:#6b0f06; border-color:#ff9f95}
  .fd-rng{color:var(--muted)}
  .fd-meta{grid-column:1 / -1; font-size:.76rem; color:var(--muted); display:flex; flex-wrap:wrap; gap:6px 14px}
  .fd-meta a{font-weight:700}
  /* ---- Jake Longhall tile (17 Oct 2026) ---- */
  .em-list{list-style:none; margin:8px 0 0; padding:0}
  .em-item{padding:9px 0; border-top:1px solid var(--line)}
  .em-item:first-child{border-top:0}
  .em-item a{font-weight:700; font-size:.95rem; text-decoration:none}
  .em-item a:hover{text-decoration:underline}
  .em-item .em-stand{display:block; font-size:.82rem; color:var(--muted); margin-top:2px}
  .em-item .em-when{display:block; font-size:.72rem; color:var(--muted); margin-top:3px}
  .em-empty{font-size:.86rem; line-height:1.55}
  /* Jake's latest piece, read in full in the tile (18 Oct 2026) */
  .em-art{margin-top:8px; font-size:.9rem; line-height:1.6}
  .em-art p{margin:8px 0}
  .em-art h4{margin:14px 0 4px; font-size:.95rem}
  .em-art ul, .em-art ol{margin:6px 0; padding-left:20px}
  .em-hero{margin:0 0 10px}
  .em-hero img{display:block; width:100%; height:auto; max-height:320px; object-fit:cover; border-radius:8px}
  .em-hero figcaption{font-size:.7rem; color:var(--muted); margin-top:4px}
  .em-title{margin:0; font-size:1.12rem; line-height:1.3}
  .em-title a{text-decoration:none} .em-title a:hover{text-decoration:underline}
  .em-by{font-size:.74rem; color:var(--muted); margin-top:3px}
  .em-standfirst{font-weight:600}
  .em-steps{background:var(--accent-soft, rgba(0,0,0,.04)); border-radius:8px; padding:8px 12px; margin:10px 0}
  .em-srcs{font-size:.78rem; margin-top:10px} .em-srcs li{margin:3px 0} .em-srcs span{color:var(--muted)}
  .em-disc{font-size:.72rem; color:var(--muted)}
  .em-own{font-size:.82rem; margin-top:6px}
  .em-past{margin-top:14px; padding-top:10px; border-top:1px solid var(--line); font-size:.85rem}
  .em-all{margin-top:8px; font-size:.85rem; font-weight:600}
  /* ---- FCDO travel warnings tile ---- */
  .fc-wrap{display:grid; gap:10px; margin-top:8px}
  .fc-counts{display:flex; flex-wrap:wrap; gap:6px}
  .fc-count{border:1px solid var(--line); border-radius:8px; padding:6px 10px; font-size:.78rem; line-height:1.3; background:var(--bg-soft,#fff)}
  .fc-count b{display:block; font-size:1.1rem; line-height:1.1}
  .fc-count.all{border-color:#ff8f85; background:#fff1f0} .fc-count.essential{border-color:#ffb3ab; background:#fff6f5}
  .fc-count.parts,.fc-count.essential_parts{border-color:#f0c36d; background:#fffaf0}
  .fc-list{font-size:.82rem; line-height:1.55}
  .fc-list b{display:inline-block; min-width:9.5em}
  .fc-list a{color:inherit}
  .fc-changes{list-style:none; margin:0; padding:0; font-size:.8rem}
  .fc-changes li{padding:5px 0; border-top:1px solid var(--line)}
  .fc-changes .fc-when{color:var(--muted); font-size:.74rem; margin-left:4px}
  .fc-changes .fc-note{display:block; color:var(--muted); margin-top:2px}
  .fc-find{display:flex; gap:6px; align-items:center; flex-wrap:wrap; margin-top:4px}
  .fc-find input{flex:1 1 180px; min-width:0; font:inherit; font-size:.86rem; padding:6px 9px; border:1px solid var(--line); border-radius:8px; background:var(--bg-soft,#fff); color:inherit}
  .fc-hit{border:1px solid var(--line); border-radius:10px; padding:9px 11px; margin-top:6px; font-size:.84rem; line-height:1.5}
  .fc-hit h4{margin:0 0 3px; font-size:.95rem}
  .fc-hit .hm-fc{vertical-align:middle}
  .fc-hit ul{margin:4px 0 0; padding-left:18px}
  .fc-hit .fc-avoid{border-left:3px solid #ff8f85; padding:4px 8px; margin:5px 0; background:#fff6f5; font-size:.8rem}
  .fc-hit .fc-avoid small{display:block; color:var(--muted)}
  .hm-btn{background:#fff; color:var(--hm-bg); border:0; border-radius:7px; padding:6px 12px;
    font-weight:700; font-size:.84rem; line-height:1.2; font-family:inherit; cursor:pointer; white-space:nowrap; text-decoration:none;
    display:inline-block}
  .hm-btn:hover,.hm-btn:focus-visible{background:var(--hm-frame); color:#1d1500}
  .hm-btn.ghost{background:none; color:#fff; border:1px solid var(--hm-rule)}
  .hm-detail{grid-column:1/-1; margin:6px 0 2px; padding:10px 11px; border-radius:8px;
    background:rgba(0,0,0,.24); font-size:.82rem; line-height:1.5}
  .hm-detail p{margin:0 0 7px}
  .hm-detail a{color:#fff}
  .hm-detail a.hm-btn{color:var(--hm-bg); cursor:pointer}
  .hm-detail a.hm-btn[aria-disabled]{opacity:.5}
  .hm-dest{width:100%; border-collapse:collapse; font-size:.78rem; margin:4px 0 8px}
  .hm-dest td{padding:3px 4px; border-top:1px solid var(--hm-rule)}
  .hm-dest td.n{text-align:right; font-variant-numeric:tabular-nums}
  /* align-items:flex-end, not center (owner, 3 Oct 2026): the "Fly from"
     label sits ABOVE its select, so centring the buttons on the whole label
     left them floating above the select's line. Bottom-aligned, and the
     select and both buttons given one height, they sit on a single line. */
  .hm-fly{display:flex; flex-wrap:wrap; gap:6px 8px; align-items:flex-end; margin-top:6px}
  .hm-fly select, .hm-fly .hm-btn{height:2.15rem; box-sizing:border-box}
  .hm-fly .hm-btn{display:inline-flex; align-items:center; padding:0 12px}
  .hm-fly label{font-size:.74rem; color:var(--hm-dim); display:flex; flex-direction:column; gap:2px}
  .hm-fly select{font-weight:600; font-size:.82rem; font-family:inherit; padding:5px 6px; border-radius:6px; border:0;
    background:#fff; color:#10291c; max-width:15rem}
  .hm-fly .hm-btn[disabled]{opacity:.45; cursor:not-allowed}
  .hm-small{font-size:.72rem; color:var(--hm-dim)}
  .hm-row.hm-more{display:none}
  .hm-row.hm-group{display:block; margin-top:14px; padding:10px 0 8px; border-top:2px solid var(--hm-frame);
    font-size:.78rem; line-height:1.45; color:var(--hm-dim)}
  .hm-row.hm-group b{color:#fff; font-size:.9rem; letter-spacing:.02em}
  .hm-row.hm-group.hm-more{display:none}      /* folded with the rows under it */
  .hm-mon{display:block; font-size:.7rem; font-weight:600; color:#e9d55a; white-space:normal; line-height:1.2; margin-top:2px}
  .hm-board.open .hm-row.hm-more{display:grid}
  .hm-board.open .hm-row.hm-group.hm-more{display:block}
  /* "Show N more" is a real button (owner, 3 Oct 2026: "a button, rather than
     a title in a row") — solid brass, the frame's colour, centred, with a
     chevron that turns when the board is open. */
  .hm-morebtn{display:flex; align-items:center; gap:8px; margin:12px auto 2px;
    background:var(--hm-frame); color:#1d1500; border:0; border-radius:999px;
    padding:9px 20px; font-weight:800; font-size:.86rem; line-height:1.2; font-family:inherit; cursor:pointer;
    box-shadow:0 2px 0 rgba(0,0,0,.35)}
  .hm-morebtn:hover,.hm-morebtn:focus-visible{background:#e2bd45; color:#1d1500}
  .hm-morebtn .chev{display:inline-block; transition:transform .15s}
  .hm-board.open .hm-morebtn .chev{transform:rotate(180deg)}
  .hm-ticker{margin-top:10px; overflow:hidden; white-space:nowrap; background:var(--hm-ledbg);
    border:1px solid #3c1914; border-radius:4px; padding:6px 0;
    font:700 1rem/1 ui-monospace,"Cascadia Mono",Consolas,monospace; letter-spacing:.12em;
    color:var(--hm-led); text-shadow:0 0 5px rgba(255,90,72,.55); text-transform:uppercase}
  .hm-ticker span{display:inline-block; padding-left:100%; animation:hm-scroll 38s linear infinite}
  @keyframes hm-scroll{to{transform:translateX(-100%)}}
  @media (prefers-reduced-motion:reduce){
    .hm-ticker span{animation:none; padding-left:10px; white-space:normal}
  }
  @container (min-width:640px){
    .hm-row{grid-template-columns:minmax(130px,1.4fr) 6.6rem 7.4rem minmax(104px,1fr) minmax(112px,1fr) minmax(70px,.7fr) 9.2rem 6.2rem;
      grid-template-areas:"cur rate buy ago hist cost verd fly"}
    .hm-head{display:grid !important}
    .c-ago{text-align:right} .c-rate,.c-buy{text-align:right} .c-verd{text-align:left; justify-content:flex-start}
    .hm-lbl,.hm-ll{display:none}
  }
