/*  The @font-face rules live here, not in an imported fonts.css.

    An import has no cache-buster of its own, and web.config gives static files a 30-day client
    cache, so a browser holding the old fonts.css kept requesting the old font filename and the
    new axis silently did nothing. site.css is versioned by its own timestamp, so anything
    inside it reaches everyone on the next deploy.  */

/*  Self-hosted, latin subset, two families.

    Not linked from fonts.googleapis.com on purpose: hotlinking Google Fonts sends every visitor's
    IP address to a third party on every page load. That has been ruled a GDPR breach in the EU,
    and it is a poor look on a site whose whole position is that it is not doing anything with you.

    NEWSREADER carries an OPTICAL SIZE axis (6-72). Optical sizing is left on automatic, which is
    the point of the axis: the headline at 4rem gets a display cut with tight spacing and fine
    hairlines, the figure at 2.7rem gets a slightly sturdier one, and the italic captions at
    .92rem get a text cut that does not fall apart. One file, several genuine cuts.

    SCHIBSTED GROTESK carries weight only, and is deliberately quiet. It sets everything that is
    read rather than looked at: body, tables, labels, nav, the chart and map furniture.

    FILENAMES NAME THEIR AXES. Font URLs carry no cache-buster and web.config gives static files a
    30-day client cache, so replacing a font in place leaves every returning visitor on the old
    one. A new axis means a new filename.  */

@font-face {
    font-family: 'Newsreader';
    font-style: normal;
    font-weight: 200 800;
    font-display: swap;
    src: url('fonts/newsreader-opsz-wght.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                   U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Newsreader';
    font-style: italic;
    font-weight: 200 800;
    font-display: swap;
    src: url('fonts/newsreader-opsz-wght-italic.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                   U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: normal;
    font-weight: 400 900;
    font-display: swap;
    src: url('fonts/schibsted-grotesk-wght.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                   U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: italic;
    font-weight: 400 900;
    font-display: swap;
    src: url('fonts/schibsted-grotesk-wght-italic.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                   U+2212, U+2215, U+FEFF, U+FFFD;
}

/*  Identity: a British data publication, not a component library.

    Bone paper, no cards, no shadows, no rounded boxes. Deep clay as the single working accent,
    and the teal-to-rust band ramp shared by the map, the chips and the headline figure.

    TYPOGRAPHY IS TWO FAMILIES WITH ONE JOB EACH, and the split is the whole voice:

        --serif   Newsreader    headlines, section heads, the big figures, captions
        --sans    Schibsted     body, tables, labels, nav, chart and map furniture

    The serif is set LARGE AND LIGHT - 400 to 500, never bold. That is the entire trick: at
    display sizes a light high-contrast serif reads as written, where a heavy grotesque reads as
    engineered. Anything reaching for a bold serif here has misunderstood it.

    Two weights are deliberate exceptions. The masthead h1 and the big figures sit white on the
    dark chrome, and a light serif thins out on a dark ground, so those carry 500 where the same
    type on paper carries 400.

    An earlier pass ran one family (Archivo) across a 62-125% WIDTH axis instead, with named
    width steps doing what weight and case do here. The width axis is gone; nothing in this file
    should set font-stretch. What it used to carry is now:

        serif, 400-500, large            headline, section head, the big figures
        sans, 600                        nav, jump links, small subheads
        sans, 400                        body, tables
        sans, 600, uppercase, tracked    column labels, kickers, legends, stamps
        serif italic                     captions and source notes

    That last one is a straight upgrade: Newsreader's italic is a true italic drawn as one,
    where the old family needed a second file to fake what a variable axis cannot interpolate.

    RULE WEIGHTS ARE A HIERARCHY, and every line on the page obeys it. There is exactly one
    stroke width, 1px, and three tones:

        --line      hairline   rows inside a table, list items
        --line-2    mid        a table's head and its total row
        --ink-3     structural a section boundary, and nothing else

    Sections are told apart by SPACE, not by weight: 3.4rem above, 1.5rem below the rule. An
    earlier pass gave section tops, table heads and table totals the same 2px near-black rule,
    which left three competing heavy lines inside one screen and no way to see where a section
    began.

    Breadcrumbs live at the top of the content, above the headline, where every reader expects
    them. The dark bar carries the brand and three links, nothing else.  */

:root {
    --paper:     #faf9f5;
    --paper-2:   #f2f0e9;
    --ink:       #191919;
    --ink-2:     #4d4d4b;
    --ink-3:     #807f7a;
    --line:      #e4e0d6;
    --line-2:    #cbc5b7;
    --rule:      #191919;

    /*  Warm ink, not neutral. On a bone ground a blue-black bar reads as a different site's
        header dropped on top; #26221e is the same near-black carrying the paper's own warmth.  */
    --chrome:    #26221e;
    --chrome-2:  #a8a196;

    /*  Deep clay: links, series, focus.

        NOT the #cc785c clay this was taken from - that is 2.6:1 on the paper and fails WCAG for
        link text outright. This is 7.8:1.

        It is also deeper than a link alone needs, and that is the second job. On this site rust
        already MEANS something: --band-5 is "more than double the average", and .ratio-5 sets it
        as text in the same tables that carry links. A link and a verdict a shade apart would be
        one colour saying two things. At 7.8:1 against the band's 4.9:1 the two separate on
        luminance, not just on hue, which is what holds up in the table.

        Measured, not guessed. Move either one and re-check both.  */
    --accent:      #7d3d22;
    /*  The accent split into components, for the one place that needs to vary only its alpha:
        the weekday-by-hour grid, where every cell is the same hue at a different strength.  */
    --accent-rgb:  125, 61, 34;
    --accent-deep: #5c2c17;
    --accent-soft: #f3e7e0;

    /*  Band ramp, ratio to the England and Wales average. Repeated in map.js; change together.
        Teal to rust, never green/red: green/red passes a verdict and vanishes for the
        colour-blind.  */
    --band-1: #3f6b6f;
    --band-2: #9ab8b5;
    --band-3: #e0dbcd;
    --band-4: #d9a077;
    --band-5: #b0512a;
    --band-0: #cdc9bd;

    --up:   #b0512a;
    --down: #3f6b6f;

    --warn-bg:   #f9f1dc;
    --warn-line: #dcc07a;
    --stop-bg:   #f8eae3;
    --stop-line: #d5a184;

    --serif: 'Newsreader', Georgia, 'Times New Roman', serif;
    --sans:  'Schibsted Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;

    /*  The one shared micro-label treatment: uppercase, tracked, sans 600. Column heads,
        kickers, legends and stamps all use it at their own size, and the tracking is a token so
        they cannot drift apart.  */
    --track-label: .12em;

    --measure:  72rem;
    --header-h: 3.25rem;
    --jump-h:   2.9rem;
}

* { box-sizing: border-box; }

::selection { background: var(--accent); color: #fff; }

/*  The canvas is chrome, not paper.

    A background on body and none on html propagates the body's colour to the browser canvas,
    which is what gets painted when a phone rubber-bands past the end of the page. That showed a
    band of light paper under the dark footer on every overscroll, and read as the page having
    run out early. Desktop never shows it because desktop has no overscroll, which is why this
    only ever appeared on a phone.

    Chrome is the right colour at both ends: the header is chrome and so is the footer, so a
    stretch past either edge continues the furniture instead of interrupting it. body keeps paper
    for the content itself.  */
html {
    font-size: 16px; -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
    background: var(--chrome);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--sans);
    font-size: 1rem;
    line-height: 1.6;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
}

.wrap { max-width: var(--measure); margin: 0 auto; padding: 0 1.5rem; }

.skip { position: absolute; left: -9999px; background: var(--ink); color: #fff; padding: .6rem 1rem; }
.skip:focus { left: 1rem; top: 1rem; z-index: 900; }

.visually-hidden {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
}

a { color: var(--accent); text-decoration-thickness: .07em; text-underline-offset: .16em; }
a:hover { color: var(--accent-deep); }

/* ==================================================================== chrome */

.site-header {
    position: sticky; top: 0; z-index: 700;
    background: var(--chrome);
    color: #fff;
}

/*  There used to be a five-stop gradient hairline here, and another one over the footer, on the
    grounds that the band ramp was the site's signature.

    It was decoration wearing data's clothes. Everywhere else on the site those five colours mean
    something exact - this area against the England and Wales average - and a reader can point at
    a chip and say which band it is. Stretched into a smooth gradient across the full width of the
    window, above content it had no relationship to, it meant nothing and could not be read. The
    ramp still appears in the scale, the map, the legend and the table chips, where it is doing
    the job it was designed for.

    The dark bar meeting the paper is its own edge and needs no help.  */

.header-inner {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1.25rem; height: var(--header-h);
}

.brand {
    display: flex; align-items: center; gap: .6rem;
    font-weight: 450; font-size: 1.16rem; line-height: 1;
    color: #fff; text-decoration: none; white-space: nowrap;
}
.brand:hover { color: #fff; opacity: .85; }

/*  The mark is the site's own scale bar with the tick at the England and Wales average - the
    question every page answers, drawn. An earlier square-with-two-colours was removed for
    meaning nothing; this one is the masthead graphic reduced to a glyph.  */
/*  Sized so the mark is legible rather than decorative: at the old 27x16px the needle was one
    device pixel wide and the whole idea of the drawing was invisible.  */
.brand-mark { width: 2.3rem; height: 1.5rem; flex: 0 0 auto; display: block; }

.brand-word { letter-spacing: .01em; }
.brand-word b { font-weight: 800; }

.site-nav { display: flex; gap: 1.55rem; }
.site-nav a {
    font-size: .84rem; font-weight: 600; letter-spacing: .005em;
    color: var(--chrome-2); text-decoration: none;
    padding: .3rem 0; border-bottom: 2px solid transparent;
    transition: color .12s ease;
}
.site-nav a:hover { color: #fff; }
.site-nav a.here { color: #fff; border-bottom-color: var(--band-5); }

/*  The drawer furniture: the Menu button, the panel's own heading and footer, and the scrim.
    All of it is phone-only AND script-only. On a desktop, and on a phone with no JavaScript,
    none of it renders and the nav is the row of links it has always been.  */
.nav-toggle, .nav-scrim, .nav-head, .nav-stamp { display: none; }

main { padding: 0 0 4.5rem; }

/* ================================================================== masthead */

/*  THE FRONT PAGE. The hero fuses with the dark chrome into one ink slab, so every page opens
    the way a newspaper does: the masthead, the headline, and the number of the day, in the colour
    of the verdict. The band ramp under the nav is the only seam, and the slab simply ends where
    the paper (and the reading) begins.

    Everything inside is re-inked for the dark ground; the band colours need no help, which is
    half the reason this works: the site's one colour system reads on both grounds.  */
.masthead {
    background: var(--chrome);
    color: #e9e5dc;
    padding: 1.7rem 0 2.4rem;
}

.mast-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
.mast-stamp {
    margin: 0; padding-top: .15rem;
    font-size: .68rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--chrome-2); white-space: nowrap;
}

.masthead .breadcrumb li { color: var(--chrome-2); }
.masthead .breadcrumb li + li::before { color: rgba(255, 255, 255, .28); }
.masthead .breadcrumb a { color: var(--chrome-2); }
.masthead .breadcrumb a:hover { color: #fff; }
.masthead .breadcrumb [aria-current] { color: #fff; }

/*  The kicker: what kind of place, flagged with the area's own band colour. The square is the
    brand mark's language, and it is the first thing on the page that tells you which end of the
    ramp this place sits at.  */
.kicker {
    display: flex; align-items: center; gap: .55rem;
    margin: 1rem 0 .65rem;
    font-size: .70rem; font-weight: 600; letter-spacing: .15em; text-transform: uppercase;
    color: var(--chrome-2);
}
/*  ONLY when there is a band to show. The base rule used to default to --band-3, "about
    average", so a kicker on a page that is not a place - the home page, which is not anywhere -
    painted a square that stated a fact about nothing.  */
.kicker[class*="band-"]::before { content: ""; width: .62rem; height: .62rem; flex: 0 0 auto; }
.kicker.band-0::before { background: var(--band-0); }
.kicker.band-1::before { background: var(--band-1); }
.kicker.band-2::before { background: var(--band-2); }
.kicker.band-3::before { background: var(--band-3); }
.kicker.band-4::before { background: var(--band-4); }
.kicker.band-5::before { background: var(--band-5); }

.masthead h1 { color: #fff; }
.masthead .standfirst { color: #bdb7ac; }
.masthead .standfirst strong { color: #fff; }

.masthead .figures { border-top-color: rgba(255, 255, 255, .18); border-bottom-color: rgba(255, 255, 255, .18); }
.masthead .figure + .figure { border-left-color: rgba(255, 255, 255, .1); }
.masthead .figure-value { color: #fff; }
.masthead .figure-label { color: var(--chrome-2); }
.masthead .figure-kicker { color: var(--chrome-2); }

/*  The verdict colours, brightened for the ink ground; the paper versions vanish on it.  */
.masthead .up   { color: #e6906a; }
.masthead .down { color: #8fc0bd; }
.masthead .flat { color: var(--chrome-2); }

.masthead .rank-line { color: #bdb7ac; }
.masthead .rank-line strong { color: #fff; }
.masthead .callout-quiet { color: #a9a297; }
.masthead .callout-quiet strong { color: #fff; }

.masthead .notice { background: rgba(255, 255, 255, .06); }

.masthead figcaption { color: var(--chrome-2); }

.masthead .scale .avg        { stroke: #e9e5dc; }
.masthead .scale .avg-label  { fill: var(--chrome-2); }
.masthead .scale .self-line  { stroke: #fff; }
.masthead .scale .pin        { fill: #fff; }
.masthead .scale .self-label { fill: #fff; }

.masthead .picker label { color: var(--chrome-2); }
.masthead .picker button { background: var(--band-5); border-color: var(--band-5); color: #fff; }
.masthead .picker button:hover { background: #8a3a1a; border-color: #8a3a1a; }

/*  EVERY CRUMB, ALWAYS. The trail is five deep at the bottom of the tree - England and Wales,
    England, the region, the county, the place - and the phone rules used to delete all but the
    last two and mark the cut with an ellipsis. Those are exactly the levels a reader arriving
    from a search result needs in order to climb: deleting them left the trail saying "... > Leeds"
    on the one device where wayfinding matters most.

    It scrolls sideways instead. Nothing is hidden, and the narrow screens get a fade on the right
    edge so the overflow announces itself rather than looking like the end of the line.  */
/*  min-width: 0 is what makes the scrolling work at all. The breadcrumb is a flex item inside
    .mast-top, and a flex item defaults to min-width: auto, which means "never shrink below your
    own content". The trail is one nowrap line, so instead of scrolling inside its 340px slot it
    grew to its full 525px and pushed the whole document sideways: every page on the site had a
    horizontal scrollbar on a phone, and the overflow-x below never engaged because there was no
    overflow left to catch.  */
.breadcrumb { min-width: 0; }

.breadcrumb ol {
    list-style: none; margin: 0 0 1.2rem; padding: 0; font-size: .8125rem;
    white-space: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.breadcrumb ol::-webkit-scrollbar { display: none; }
.breadcrumb li { display: inline; color: var(--ink-3); }
.breadcrumb li + li::before { content: "›"; margin: 0 .45em; color: var(--line-2); }
.breadcrumb a { color: var(--ink-3); text-decoration: none; }
.breadcrumb a:hover { color: var(--accent); text-decoration: underline; }
.breadcrumb [aria-current] { color: var(--ink-2); font-weight: 600; }

/*  500 rather than the 400 h2 carries: the headline sits white on the dark chrome, and a light
    serif thins out on a dark ground. Optical sizing is automatic, so at 4rem this is Newsreader's
    display cut - tight spacing, fine hairlines - not the text cut scaled up.  */
h1 {
    font-family: var(--serif);
    font-weight: 500;
    font-size: clamp(2.35rem, 1.25rem + 3.4vw, 4.15rem);
    line-height: 1.02; letter-spacing: -.018em;
    margin: 0 0 .9rem; max-width: 20ch; text-wrap: balance;
}

.standfirst {
    font-size: 1.12rem; line-height: 1.6; font-weight: 400;
    color: var(--ink-2); max-width: 54ch; margin: 0 0 1.8rem;
}
.standfirst strong { color: var(--ink); font-weight: 600; }

/*  The figures: one instrument band between two ink rules, numbers doing the talking. The lead
    figure carries the area's own band colour as a flag, the same colour its polygons get.  */
.figures {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0; margin: 0 0 1.6rem;
    border-top: 1px solid var(--line-2); border-bottom: 1px solid var(--line-2);
}
.figure { padding: 1.05rem 1.4rem 1.15rem 0; }
.figure + .figure { border-left: 1px solid var(--line); padding-left: 1.4rem; }
.figure-value {
    display: block; font-family: var(--serif); font-size: 2.75rem; font-weight: 500;
    line-height: 1; letter-spacing: -.018em;
    font-variant-numeric: tabular-nums lining-nums;
}
.figure-label {
    display: block; margin-top: .5rem; font-size: .76rem; line-height: 1.45;
    font-weight: 500; letter-spacing: .015em; color: var(--ink-3);
}

/*  Same rule as the kicker square: the flag appears only when the area has a band. On the ranked
    lists, which lead with a count rather than a place, it used to render as an invisible 3px bar
    - a rail above a figure, present in every DOM but carrying nothing.  */
.figure-lead { position: relative; }
.figure-lead[class*="band-"]::before {
    content: ""; position: absolute; top: -1px; left: 0; width: 3.2rem; height: 3px;
}
.figure-lead.band-0::before { background: var(--band-0); }
.figure-lead.band-1::before { background: var(--band-1); }
.figure-lead.band-2::before { background: var(--band-2); }
.figure-lead.band-3::before { background: var(--band-3); }
.figure-lead.band-4::before { background: var(--band-4); }
.figure-lead.band-5::before { background: var(--band-5); }

.delta-up   { color: var(--up); }
.delta-down { color: var(--down); }
.delta-flat { color: var(--ink-3); }

.rank-line { color: var(--ink-2); font-size: .95rem; max-width: 58ch; margin: 0; }
.rank-line strong { color: var(--ink); font-weight: 600; }

/* ============================================ where this area sits on the scale */

/*  Fills come from the band custom properties, so the scale, the map, the legend keys and the
    table chips are one colour system with one place to change it.  */
.scale { margin: 1.4rem 0 0; max-width: 50rem; }
.scale svg { width: 100%; height: auto; display: block; overflow: visible; }
.scale .seg-1 { fill: var(--band-1); }
.scale .seg-2 { fill: var(--band-2); }
.scale .seg-3 { fill: var(--band-3); }
.scale .seg-4 { fill: var(--band-4); }
.scale .seg-5 { fill: var(--band-5); }
/*  The boundary hairlines are the page's own ground showing through, so they change with it:
    paper on the article, chrome inside the masthead slab.  */
.scale .seg-edge   { stroke: var(--paper); stroke-width: 2; }
.masthead .scale .seg-edge { stroke: var(--chrome); }

.scale .avg        { stroke: var(--ink); stroke-width: 2; }
.scale .avg-label  { fill: var(--ink-3); font-size: 11px; font-weight: 500; letter-spacing: .3px; }
.scale .self-line  { stroke: var(--ink); stroke-width: 3; }
.scale .pin        { fill: var(--ink); }
.scale .self-label { fill: var(--ink); font-size: 13.5px; font-weight: 700; }
.scale text { font-family: var(--sans); }

/* ================================================================== jump nav */

.jump {
    position: sticky; top: var(--header-h); z-index: 600;
    display: flex; flex-wrap: nowrap; overflow-x: auto; gap: 1.3rem; align-items: center;
    height: var(--jump-h);
    background: var(--paper);
    border-bottom: 1px solid var(--line-2);
    margin: 1.7rem 0 0;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
/*  The strip scrolls sideways when it overflows, and SAYS SO. It used to hide its scrollbar
    entirely, which on a phone is fine - thumbs try - but a mouse gives no hint and no easy
    gesture, so the links past the edge were simply gone for desktop readers with a narrow
    window. A slim scrollbar under the strip plus a fade at the clipped edge are the two
    affordances; either alone is missable.  */
/*  overflow-y hidden, because the horizontal scrollbar's own 4px counts against the strip's
    fixed height and tips it into vertical overflow - a second scrollbar to scroll the first
    scrollbar into view.  */
.jump { overflow-y: hidden; scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
.jump::-webkit-scrollbar { height: 4px; }
.jump::-webkit-scrollbar-thumb { background: var(--line-2); }
.jump::-webkit-scrollbar-track { background: transparent; }
.jump-label {
    font-size: .68rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--ink-3); white-space: nowrap;
}
.jump a {
    font-size: .8125rem; font-weight: 600;
    color: var(--ink-2); text-decoration: none;
    white-space: nowrap; padding: .55rem 0; border-bottom: 2px solid transparent;
}
.jump a:hover { color: var(--accent-deep); border-bottom-color: var(--accent); }

/* ================================================================== sections */

/*  A section is marked by the space around it and the size of its heading. The rule is a
    hairline in the structural tone: enough to close the previous section, not enough to compete
    with the heading that follows it.  */
main section[id] {
    border-top: 1px solid var(--ink-3);
    margin-top: 3.4rem;
    padding-top: 1.5rem;
    scroll-margin-top: calc(var(--header-h) + var(--jump-h) + 1rem);
}

h2 {
    font-family: var(--serif);
    font-weight: 400;
    font-size: 2.05rem; line-height: 1.14;
    letter-spacing: -.012em; margin: 0 0 .5rem; text-wrap: balance;
}

/*  h3 stays in the sans. It sits at 1.02rem, and at that size the serif stops being a voice and
    starts being a second body face fighting the first.  */
h3 { font-weight: 650; font-size: 1.02rem; letter-spacing: -.004em; margin: 1.8rem 0 .3rem; }

.section-intro { color: var(--ink-2); margin: .1rem 0 1.2rem; font-size: .9rem; max-width: 60ch; }
p { max-width: 66ch; }
.empty { color: var(--ink-3); font-style: italic; }

.callout {
    font-size: 1.12rem; line-height: 1.6; font-weight: 400;
    color: var(--ink); max-width: 58ch; margin: .5rem 0 1.3rem;
}
.callout strong { font-weight: 650; }
.callout::before {
    content: ""; display: block; width: 2.4rem;
    border-top: 3px solid var(--accent); margin-bottom: .6rem;
}
.prose-note { color: var(--ink-2); font-size: .9rem; max-width: 62ch; margin: 0 0 1.1rem; }

.source-note {
    font-family: var(--serif); font-size: .92rem; line-height: 1.6; font-style: italic;
    color: var(--ink-3); max-width: 62ch; margin: .9rem 0 0;
}
.source-note a { color: var(--ink-3); }

/*  A source note is a CLOSING aside - quiet, italic, in the serif - and it ends a block. Body
    text following one reads as a paragraph that changed its mind halfway about what it was, and
    since .prose-note carries no top margin the two also butted together with no gap at all.

    The order is fixed at the two places it happened; this stops the gap collapsing anywhere the
    pair ends up adjacent again.  */
.source-note + .prose-note,
.prose-note + .source-note,
.source-note + .source-note { margin-top: 1.1rem; }

.notice {
    padding: .85rem 1.1rem; margin: 0 0 1.3rem; max-width: 64ch;
    border-left: 3px solid; font-size: .9rem;
}
.notice p { margin: 0 0 .55rem; max-width: none; }
.notice p:last-child { margin-bottom: 0; }
.notice-warning { background: var(--warn-bg); border-color: var(--warn-line); }
.notice-stop    { background: var(--stop-bg); border-color: var(--stop-line); }
.notice-heading { font-size: 1.1rem; font-weight: 650; margin: 0 0 .45rem; }

.tier-line { display: none; }

/* ==================================================================== tables */

.data-table {
    width: 100%; border-collapse: collapse; margin: .5rem 0 0;
    font-size: .9rem; font-variant-numeric: tabular-nums lining-nums;
}
.data-table caption { text-align: left; }
.data-table th, .data-table td { padding: .55rem .8rem; text-align: left; }
.data-table tbody tr:hover > th, .data-table tbody tr:hover > td { background: var(--paper-2); }
.data-table th:first-child, .data-table td:first-child { padding-left: 0; }
.data-table th:last-child, .data-table td:last-child { padding-right: 0; }
.data-table thead th {
    border-bottom: 1px solid var(--line); padding-bottom: .45rem;
    font-size: .7rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
    color: var(--ink-2);
}
.data-table tbody th { font-weight: 500; }
.data-table tbody td, .data-table tbody th { border-bottom: 1px solid var(--line); }
.data-table .num { text-align: right; }
.data-table tbody tr:hover { background: var(--paper-2); }
.data-table tfoot th, .data-table tfoot td {
    border-top: 1px solid var(--line-2); border-bottom: 0; font-weight: 600; padding-top: .55rem;
}
.data-table tr.self { background: var(--accent-soft); }
.data-table tr.self th { font-weight: 600; }
.data-table.compact { font-size: .85rem; }
.data-table.compact th, .data-table.compact td { padding: .42rem .7rem; }
.data-table.compact th:first-child, .data-table.compact td:first-child { padding-left: 0; }

.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.bar-cell { display: flex; width: 100%; align-items: center; gap: .6rem; }
.bar-track {
    position: relative; display: block; flex: 1 1 auto; min-width: 2rem; height: .5rem;
    background: var(--paper-2);
}
.bar-track .bar { position: absolute; left: 0; top: 0; bottom: 0; background: var(--accent); opacity: .65; }
.bar-value { flex: 0 0 auto; min-width: 3.4em; text-align: right; }

.in-area { display: block; font-size: .72rem; font-weight: 400; color: var(--ink-3); }

/*  A row's own explanation, under its name in the first column. Same idea as .in-area but it is
    a sentence rather than a place, so it gets a readable measure instead of a label size.  */
.in-area-plain {
    display: block; margin-top: .3rem; max-width: 44ch;
    font-size: .82rem; font-weight: 400; line-height: 1.45; color: var(--ink-3);
}
.in-area::before { content: "in "; }
.gap-cell { color: var(--ink-3); font-style: italic; }

/* ============================================================ band chips */

.ratio {
    display: inline-flex; align-items: center; gap: .45em;
    font-size: .8125rem; font-weight: 500; color: var(--ink-2); white-space: nowrap;
}
.ratio::before { content: ""; width: .68em; height: .68em; flex: 0 0 auto; }
.ratio-0::before { background: var(--band-0); }
.ratio-1::before { background: var(--band-1); }
.ratio-2::before { background: var(--band-2); }
.ratio-3::before { background: var(--band-3); border: 1px solid var(--line-2); }
.ratio-4::before { background: var(--band-4); }
.ratio-5::before { background: var(--band-5); }
.ratio-5 { color: var(--up); font-weight: 600; }
.ratio-1 { color: var(--down); }

/* ===================================================================== chart */

.chart { margin: .9rem 0 0; }
.chart svg { width: 100%; height: auto; display: block; }
.chart .grid        { stroke: var(--line); stroke-width: 1; }
.chart .baseline    { stroke: var(--line-2); stroke-width: 1; }
.chart .series      { stroke: var(--accent); stroke-width: 1.9; stroke-linejoin: round; stroke-linecap: round; }
.chart .series-fill { fill: var(--accent); opacity: .07; }
.chart .axis        { fill: var(--ink-3); font-size: 10.5px; font-weight: 500; letter-spacing: .3px; }
.chart .last-dot    { fill: var(--accent); }
/*  A paper halo behind the number, because it sits on top of the faint raw-months line and a
    heavy figure over a line read as smudge. Middling weight, halo doing the separation.  */
.chart .last-label  {
    fill: var(--accent-deep); font-size: 12px; font-weight: 600;
    paint-order: stroke; stroke: var(--paper); stroke-width: 3.5px; stroke-linejoin: round;
}
.chart .gap-band    { fill: var(--ink-3); opacity: .09; }
.chart text { font-family: var(--sans); }

/*  The raw months, under the twelve-month average. Thin and pale on purpose: it is there so the
    reader can see the seasonality and the outliers the smoothed line hides, not so they can read
    values off it.  */
.chart .series-raw  { stroke: var(--accent); stroke-width: .9; opacity: .3; stroke-linejoin: round; }

/*  Year rules. Lighter than the horizontal grid, because there are eight of them and they run the
    full height; at the same weight they would read as a lattice rather than as a scale.  */
.chart .grid-year   { stroke: var(--line); stroke-width: 1; opacity: .55; }

/*  A dated event. Dashed and in ink so it cannot be mistaken for either a gridline or a series,
    and short enough a label that it never needs a second line.  */
.chart .marker      { stroke: var(--ink-3); stroke-width: 1; stroke-dasharray: 3 3; opacity: .75; }
.chart .marker-label {
    fill: var(--ink-3); font-size: 10.5px; font-weight: 500;
    letter-spacing: .3px; font-style: italic;
    paint-order: stroke; stroke: var(--paper); stroke-width: 3px; stroke-linejoin: round;
}

/*  The term strip: who held the office overseeing the force, as a band under the plot.

    This is the one place on the site party colour appears, and it stays out of the plot itself:
    the crime line remains in the site's ink, the strip sits below the axis area, and the two are
    aligned rather than blended. Muted versions of the conventional party colours, dark enough to
    carry a white label.  */
.chart .band-party-con    { fill: #2f6494; }
.chart .band-party-lab    { fill: #b03227; }
.chart .band-party-ld     { fill: #a87413; }
.chart .band-party-plaid  { fill: #2d6a5f; }
.chart .band-party-green  { fill: #3e7a44; }
.chart .band-party-reform { fill: #1f8fa8; }
.chart .band-party-ind    { fill: #7d7c76; }
.chart .band-party-other  { fill: #7d7c76; }
.chart .band-label { fill: #fff; font-size: 9.5px; font-weight: 700; letter-spacing: .4px; }
.chart .band-tick  { stroke: var(--paper); stroke-width: 1.5; }

/*  Rank over time, and the shape of a year.

    The seasonality columns grow from the average line in both directions, so they carry the band
    ramp's two poles rather than one colour: above the area's own average is the same rust that
    means "above" everywhere else on the site, below it the same teal. One colour system, used
    once more.  */
.season-col  { opacity: .9; }
.season-up   { fill: var(--band-5); }
.season-down { fill: var(--band-1); }

.rank-chart svg, .season-chart svg { width: 100%; height: auto; display: block; }
.rank-chart .series { fill: none; }

figure { margin: 0; }
/*  Captions and source notes are asides about the data, not statements of it, and the italic
    says so without another rule or another indent.  */
figcaption {
    font-family: var(--serif); font-size: .92rem; font-style: italic; line-height: 1.55;
    color: var(--ink-3); margin-top: .6rem; max-width: 66ch;
}

/*  A chart is a paragraph of the argument, not a divider: it gets a paragraph's air below it, so
    the caption stops crashing into whatever prose follows.  */
figure.chart { margin: 0 0 1.7rem; }
details.disclose { margin: 0 0 1.2rem; }

details.disclose summary {
    cursor: pointer; color: var(--accent); font-size: .8125rem; font-weight: 600;
    padding: .6rem 0; list-style: none;
}
details.disclose summary::-webkit-details-marker { display: none; }
details.disclose summary::before { content: "+ "; font-weight: 600; }
details.disclose[open] summary::before { content: "− "; }
details.disclose[open] summary { margin-bottom: .3rem; }

/* ============================================================== map + split */

/*  One pattern for both map sections: the map on the left, its list on the right, symmetric
    halves, the map pinned while the list scrolls. The neighbourhoods section and the streets
    section behave identically, so the page teaches the interaction once.  */

.map-shell { border: 1px solid var(--line-2); background: var(--paper-2); }
.map-canvas { height: 430px; width: 100%; }
.map-fallback { padding: 2.2rem 1.4rem; color: var(--ink-3); font-size: .875rem; }

/*  The loading line is the same muted note, pulsing slowly so a map that is taking its time is
    visibly working rather than visibly stuck. Anyone who has asked not to see movement gets the
    plain text, which says the same thing.  */
.map-fallback.is-loading { animation: map-pulse 1.6s ease-in-out infinite; }
@keyframes map-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .map-fallback.is-loading { animation: none; } }

.map-retry {
    display: inline-block; margin-left: .35rem; padding: .18rem .6rem;
    border: 1px solid var(--line); border-radius: 4px; background: transparent;
    color: inherit; font: inherit; cursor: pointer;
}
.map-retry:hover { background: var(--paper-2); }

/*  Where a map says what was clicked. Fixed under the canvas rather than floating over it:
    a Leaflet popup covers the feature that opened it, which on a choropleth means covering the
    shape the reader just selected. min-height holds the space so selecting does not jump the
    page.  */
.map-detail {
    padding: .5rem .7rem; margin: 0; min-height: 2.4rem;
    border-top: 1px solid var(--line);
    font-size: .82rem; line-height: 1.45; color: var(--ink-3);
}
.map-detail.is-shown { color: var(--ink); }
.map-detail strong { font-weight: 600; }
.map-detail .detail-quiet { color: var(--ink-3); }
.map-detail a { margin-left: .2rem; white-space: nowrap; }

.map-legend {
    display: flex; align-items: center; gap: .4rem 1.05rem; flex-wrap: wrap;
    border-top: 1px solid var(--line-2); background: var(--paper);
    padding: .55rem .8rem; font-size: .74rem; font-weight: 500;
    letter-spacing: .01em; color: var(--ink-2);
}
.map-legend .swatch {
    display: inline-block; border-radius: 50%; background: var(--accent);
    opacity: .5; margin-right: .4rem; vertical-align: -.1em;
}
.choropleth-legend { gap: .4rem .9rem; }
.choropleth-legend .legend-title {
    font-weight: 600; text-transform: uppercase;
    letter-spacing: .09em; font-size: .68rem; color: var(--ink-2);
}
.choropleth-legend .key {
    display: inline-block; width: 11px; height: 11px; margin-right: .3rem;
    vertical-align: -.08em;
}
.key-1 { background: var(--band-1); }
.key-2 { background: var(--band-2); }
.key-3 { background: var(--band-3); border: 1px solid var(--line-2); }
.key-4 { background: var(--band-4); }
.key-5 { background: var(--band-5); }
.key-0 { background: var(--band-0); }

.map-split {
    display: grid; grid-template-columns: 1fr 1fr;
    gap: 1.5rem; align-items: start; margin-top: .5rem;
}
.map-split.no-map { display: block; }
.map-side { position: sticky; top: calc(var(--header-h) + var(--jump-h) + 1rem); min-width: 0; }
.map-side .map-canvas { height: min(60vh, 540px); }

/*  The national map is the page rather than a panel beside a table, so it gets the height a
    whole country needs. Capped against the viewport so the legend under it stays reachable
    without scrolling on a laptop.

    The width is capped too, which looks odd written down and is right on screen: England and
    Wales is about 350 miles across and 425 tall, so a full-width box frames the country with
    half the North Sea either side. Roughly five units wide to six tall is the country's own
    shape, and Leaflet fills the frame it is given.  */
/*  The frame keeps roughly the country's own proportions, five wide to six tall, but it gets
    there through aspect-ratio rather than by deriving a width from the viewport HEIGHT.

    The old rule was max-width: min(100%, 68vh, 760px), and tying width to height is what made
    this map small. On a 1440 by 900 desktop, 68vh is 612px, so the map sat at 612 wide inside a
    1152px column and left 270px empty down each side: it was being starved by the shortness of
    the window rather than by the width of anything. Aspect-ratio asks the column how wide it can
    be and derives the height from that, which is the right way round.

    The height cap is deliberately allowed past the fold on a short window. This is the map page;
    the map is the reason anyone is on it, and map.js leaves scroll-wheel zoom off until the map
    is clicked, so a tall map scrolls the page like anything else rather than swallowing the
    wheel.  */
.map-national {
    height: auto;
    aspect-ratio: 5 / 6;
    max-height: min(1000px, 115vh);
    width: 100%;
    margin-left: auto;
    margin-right: auto;
}
.map-shell-national { max-width: none; }

/*  One column until there is genuinely room for two. The rail is the old empty gutter given a
    job: legend and readout, which both want to stay put while the map is being used.  */
.map-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.3rem; }

@media (min-width: 1080px) {
    .map-layout { grid-template-columns: minmax(0, 1fr) 18rem; align-items: start; }
    .map-rail { position: sticky; top: calc(var(--header-h) + 1rem); }
    .map-rail .map-legend { margin-top: 0; }
    /*  Stacked in the rail rather than strung along a line, because a narrow column reads down.  */
    .map-rail .choropleth-legend { display: grid; grid-template-columns: 1fr; gap: .35rem; }
    .map-rail .map-detail { margin-top: 1rem; }
}

.map-county { margin-top: 1.6rem; }
.map-county h3 { font-size: 1rem; margin-bottom: .2rem; }
.map-county h3 a { text-decoration: none; }
.map-county h3 a:hover { text-decoration: underline; }

/*  Three columns of area names, each with its band chip and rate. Not the four the compare hub
    uses: these rows carry a number as well as a name and wrap badly at that width.  */
.map-areas.columns, .map-areas { columns: 3 15rem; column-gap: 2rem; }
.map-areas li {
    break-inside: avoid; border-bottom: 0; padding: .22rem 0;
    display: flex; align-items: baseline; justify-content: space-between; gap: .6rem;
}
.map-areas .area-rate {
    display: inline-flex; align-items: center; gap: .35rem;
    font-size: .8rem; color: var(--ink-3); font-variant-numeric: tabular-nums;
}
/*  The band chip needs its own size here: the sizing rule is scoped to .choropleth-legend, so
    that a stray .key elsewhere cannot draw itself across a chart.  */
.map-areas .key { display: inline-block; width: 9px; height: 9px; flex: 0 0 auto; }
.list-side { min-width: 0; }

/*  The active row is marked with a 3px bar down the left of its first cell. That cell is
    otherwise flush with the container - .data-table zeroes the first column's left padding so
    the names line up with the prose above - so the bar was drawing straight over the first
    letter of the name. The room is reserved on every row in these two tables, header included,
    rather than added on activation: padding that appears when a row is selected would shift the
    text sideways every time someone picked a row.  */
.list-side .data-table th:first-child,
.list-side .data-table td:first-child { padding-left: .7rem; }

tr[data-nbh] { cursor: pointer; }
tr[data-nbh].is-active { background: var(--accent-soft); }
tr[data-nbh].is-active th { font-weight: 600; box-shadow: inset 3px 0 0 var(--accent); }
tr[data-street] { cursor: pointer; }
tr[data-street]:hover > th, tr[data-street]:hover > td { background: var(--paper-2); }
tr[data-street].is-active th { font-weight: 600; box-shadow: inset 3px 0 0 var(--accent); }

.leaflet-container { font-family: var(--sans) !important; background: var(--paper-2) !important; }
.leaflet-tile { filter: grayscale(0.62) contrast(0.9) brightness(1.06); }
.leaflet-popup-content { font-size: .8125rem; margin: .7rem .9rem; }
.leaflet-popup-content strong { font-weight: 600; }

/* =========================================================== lists & layout */

.area-list { list-style: none; margin: .7rem 0 0; padding: 0; }
.area-list li { padding: .3rem 0; border-bottom: 1px solid var(--line); }
.area-list.columns { columns: 4 11rem; column-gap: 2rem; }
.area-list.columns li { break-inside: avoid; border-bottom: 0; padding: .22rem 0; }
.area-list a { text-decoration: none; }
.area-list a:hover { text-decoration: underline; }

.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 0 3rem; }

.prose-page { padding-top: 1.7rem; }

/* ==================================================================== footer */

/* ==================================================================== search */

/*  One box, on the home page and on the results page. It is a plain GET form; the suggestion
    list underneath is added by search.js and never exists without it, so nothing here is styled
    on the assumption that the script ran.

    On the masthead the box sits on the dark chrome, so the input is given its own paper ground
    rather than a transparent one - a search field that is the same colour as the slab behind it
    reads as a label, not as somewhere to type.  */

.search { margin: 0 0 1.6rem; max-width: 38rem; }

.search-label {
    display: block; margin-bottom: .45rem;
    font-size: .68rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--ink-3);
}
.masthead .search-label { color: var(--chrome-2); }

/*  position: relative because the suggestion list is absolutely positioned against this row.  */
.search-row { position: relative; display: flex; gap: 0; }

.search-input {
    font: inherit; font-size: 1.02rem;
    flex: 1 1 auto; min-width: 0;
    padding: .72rem .85rem;
    background: #fff; color: var(--ink);
    border: 1px solid var(--line-2); border-right: 0;
    border-radius: 0;
    -webkit-appearance: none; appearance: none;
}
.search-input::placeholder { color: var(--ink-3); }
.search-input:focus { outline: 2px solid var(--accent); outline-offset: -2px; }

.search-go {
    font: inherit; font-size: .92rem; font-weight: 600;
    padding: .72rem 1.3rem; cursor: pointer; white-space: nowrap;
    background: var(--ink); color: var(--paper);
    border: 1px solid var(--ink);
}
.search-go:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.search-go:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.masthead .search-go { background: var(--band-5); border-color: var(--band-5); color: #fff; }
.masthead .search-go:hover { background: #8a3a1a; border-color: #8a3a1a; }

.search-hint { margin: .5rem 0 0; font-size: .8rem; color: var(--ink-3); max-width: 46ch; }
.masthead .search-hint { color: var(--chrome-2); }

/* ------------------------------------------------------------ suggestions -- */

/*  z-index above the sticky jump nav (600) and below the header (700): the list has to cover the
    page it drops onto, and must not cover the bar it hangs from.  */
.search-suggest {
    position: absolute; top: 100%; left: 0; right: 0; z-index: 650;
    list-style: none; margin: 0; padding: 0;
    max-height: 22rem; overflow-y: auto;
    background: var(--paper);
    border: 1px solid var(--ink-3); border-top: 0;
}
.search-suggest[hidden] { display: none; }
.search-suggest li + li { border-top: 1px solid var(--line); }

.search-suggest a {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 .55rem;
    padding: .6rem .85rem; text-decoration: none; color: var(--ink);
}
.search-suggest li.is-active { background: var(--accent-soft); }
.search-suggest li.is-active a { color: var(--accent-deep); }

.sg-name { font-weight: 600; }
.sg-kind {
    font-size: .66rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--ink-3);
}
.sg-via { flex: 1 0 100%; font-size: .8rem; color: var(--ink-3); }

/*  The bot check sits apart from the controls it guards, on every form it appears in.  */
.cf-turnstile { margin: 1.1rem 0 0; }

/* --------------------------------------------------------------- trust set -- */

/*  About, how-the-figures-work, data-sources and contact are one set, and the strip under each
    h1 says so: the reader who found one finds the other three. Chips, not tabs - these are four
    pages, not four states of one page.  */
.trust-nav {
    display: flex; flex-wrap: wrap; gap: .4rem;
    margin: 0 0 1.6rem;
}
.trust-nav a {
    display: block; padding: .34rem .7rem;
    font-size: .8rem; font-weight: 600; text-decoration: none;
    border: 1px solid var(--line-2); color: var(--accent);
}
.trust-nav a:hover { background: var(--accent-soft); border-color: var(--accent); }
.trust-nav a[aria-current] { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/*  On the dark slab the chips re-ink like everything else in a masthead: quiet borders, the
    current page in paper so it reads as "you are here" at a glance.  */
.masthead .trust-nav { margin-bottom: 1.2rem; }
.masthead .trust-nav a { border-color: rgba(255, 255, 255, .25); color: var(--chrome-2); }
.masthead .trust-nav a:hover { background: transparent; border-color: rgba(255, 255, 255, .6); color: #fff; }
.masthead .trust-nav a[aria-current] { background: var(--paper); border-color: var(--paper); color: var(--ink); }

.prose-page section { scroll-margin-top: calc(var(--header-h) + var(--jump-h) + 1rem); }

/* ---------------------------------------------------------------- contact -- */

.contact-form { max-width: 38rem; display: grid; gap: 1rem; margin: 1.4rem 0 0; }
.contact-message { resize: vertical; min-height: 9rem; }

/*  The honeypot: positioned out of sight rather than display:none, because the crudest bots
    skip display:none fields and the cruder ones are the ones worth catching.  */
.hp-field { position: absolute; left: -9999px; top: -9999px; height: 1px; overflow: hidden; }

/* ------------------------------------------------------------- community -- */

/*  Five numbered radios and a button, one row. The radio itself stays visible - a custom
    control here would be decoration on a five-point scale - but grows a labelled hit target
    big enough for a thumb.  */
.vote-form { margin: 1.1rem 0 0; }
.vote-fieldset { border: 0; margin: 0; padding: 0; }
.vote-legend {
    font-size: .68rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--ink-3); margin-bottom: .55rem;
}
.vote-options { display: flex; align-items: stretch; gap: .45rem; flex-wrap: wrap; }
.vote-option {
    display: flex; align-items: center; gap: .45rem;
    border: 1px solid var(--line-2); background: #fff;
    padding: .55rem .8rem; cursor: pointer;
    font-variant-numeric: tabular-nums;
}
.vote-option:hover { border-color: var(--accent); }
.vote-option input { accent-color: var(--accent); margin: 0; }

/* ---------------------------------------------------------------- results -- */

.search-results { list-style: none; margin: 1.4rem 0 0; padding: 0; max-width: 46rem; }
.search-results li {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 .6rem;
    padding: .7rem 0; border-bottom: 1px solid var(--line);
}
.search-results a { font-size: 1.05rem; font-weight: 600; }
.search-kind {
    font-size: .66rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--ink-3);
}
.search-via { flex: 1 0 100%; font-size: .85rem; color: var(--ink-3); margin-top: .15rem; }

@media (max-width: 44rem) {
    .search-row { flex-wrap: wrap; }
    .search-input { border-right: 1px solid var(--line-2); }
    .search-go { width: 100%; margin-top: .5rem; }
    /*  The button is below the field on a phone, so the list must hang from the field, not from
        the bottom of the whole row.  */
    .search-suggest { top: 3.1rem; }
}


/*  The analytics question. A bar across the foot of the page rather than a modal over it: the
    reader came for a crime figure, and a box they must dismiss before reading one is a worse
    trade than a bar they can answer in a second.

    Both buttons sit on the same row, same size, one click each. Accept is filled and decline is
    outlined, which is as far as the styling goes: under PECR a refusal has to be as easy as an
    acceptance, and a buried or greyed-out No is the thing regulators actually fine people for.
    The persuasion belongs in the sentence, not in the buttons.  */
/*  The page greys out behind the sheet until the question is answered. The scrim takes the
    clicks, so the page is dimmed and inert rather than hidden: a reader can still see what they
    came for, which keeps this a forced choice and not a wall.  */
/*  Above everything. This site's stacking runs high: the jump nav sits at 600, the header at
    700, the mobile nav drawer at 800 and 900, and Leaflet puts its own map controls at 1000,
    which is higher than anything this stylesheet declares. A scrim below those left the sticky
    furniture and the zoom buttons floating brightly over a dimmed page, which reads as a
    rendering fault rather than a question. Above all of it, with room to spare.  */
.consent-scrim {
    position: fixed; inset: 0; z-index: 2000;
    background: rgba(20, 18, 16, .55);
    animation: consent-fade .18s ease-out;
}
.consent-scrim[hidden] { display: none; }

@keyframes consent-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes consent-rise { from { transform: translateY(100%); } to { transform: translateY(0); } }

.consent {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 2010;
    background: var(--paper); color: var(--ink);
    border-top: 4px solid var(--accent);
    box-shadow: 0 -12px 40px rgba(0, 0, 0, .34);
    animation: consent-rise .22s ease-out;
}
.consent[hidden] { display: none; }
.consent-inner { padding-top: 2.1rem; padding-bottom: 1.8rem; }

.consent-title { font-size: 1.35rem; margin: 0 0 .65rem; line-height: 1.25; }
.consent-text { margin: 0 0 1.1rem; font-size: .925rem; line-height: 1.6; max-width: 78ch; }

/*  Both buttons on one row, same height, same weight of click. Accept is filled and larger by
    padding alone; the decline is a full button and never a link, because a refusal routed through
    a settings screen is the pattern regulators are actually acting on.  */
.consent-actions { display: flex; gap: .7rem; flex-wrap: wrap; align-items: center; }
.consent button {
    font: inherit; font-size: .95rem; font-weight: 600; cursor: pointer;
    padding: .75rem 1.6rem; white-space: nowrap;
}
/*  The sheet takes focus on open, not a button. Focusing the first button put a ring on
    "No thanks" purely because it comes first in the DOM, which reads as the site nominating an
    answer. The container is the neutral place for a dialog to land, and Tab moves from there.

    Rings are the site's own colour rather than the browser's blue, and :focus-visible so they
    show for keyboard use and not on a mouse click.  */
.consent:focus { outline: none; }
.consent button:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.consent-links a:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }

.consent-accept {
    background: var(--accent); border: 1px solid var(--accent); color: #fff;
    padding-left: 2.2rem; padding-right: 2.2rem;
}
.consent-accept:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.consent-decline { background: transparent; border: 1px solid var(--line-2); color: var(--ink); }
.consent-decline:hover { border-color: var(--ink); }

.consent-links { margin: .9rem 0 0; font-size: .8rem; color: var(--ink-3); }

@media (prefers-reduced-motion: reduce) {
    .consent, .consent-scrim { animation: none; }
}

.site-footer {
    position: relative;
    background: var(--chrome);
    color: var(--chrome-2);
    margin-top: 4rem;
    padding: 2.6rem 0 2.2rem;
    font-size: .8125rem; line-height: 1.65;
}
.site-footer a { color: #dcd6cc; }

/*  Identity left, two named link columns right. The long-tail column widths are fixed by their
    content; the identity column takes the slack so the grid holds its shape on any screen the
    columns fit on at all.  */
.footer-grid {
    display: grid;
    grid-template-columns: minmax(13rem, 1.3fr) auto auto auto;
    gap: 2rem 3rem;
    align-items: start;
}
.footer-id p { margin: 0 0 .7rem; }
.footer-brand {
    display: flex; align-items: center; gap: .5rem;
    font-size: .96rem; font-weight: 450;
    color: #fff; margin-bottom: .8rem;
}
.footer-brand .brand-mark { width: 2rem; height: 1.3rem; }
.footer-tag { max-width: 36ch; }
.footer-stamp { font-size: .75rem; color: #837c71; }

.footer-head {
    margin: 0 0 .55rem;
    font-size: .6875rem; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase;
    color: #837c71;
}
/*  Sits under the identity block rather than in a nav column: it is a thing to do, not a
    place to go, and a bare link among the section links would read as another page.  */
.footer-prefer {
    margin-top: 1rem; padding-top: .9rem;
    border-top: 1px solid rgba(250, 249, 245, .13);
    font-size: .78rem; line-height: 1.5; color: #837c71; max-width: 24rem;
}
.footer-prefer a { font-weight: 500; }
/*  Set while Google's library is being fetched on click. The wait is a network round trip the
    reader did not have before, so it should at least look like something is happening.  */
.footer-prefer a[aria-busy="true"] { cursor: progress; opacity: .65; }

.footer-col { display: flex; flex-direction: column; gap: .4rem; }

/*  Thirteen offences in one column would run twice the height of everything beside it and drag
    the whole footer down with it. Two short columns instead, which is also how the eye wants to
    read a list of like-for-like labels.  */
.footer-col-split { display: block; columns: 2 8.5rem; column-gap: 2.2rem; }
.footer-col-split .footer-head { column-span: all; }
.footer-col-split a { display: block; margin-bottom: .4rem; break-inside: avoid; }
.footer-col a { text-decoration: none; font-weight: 500; width: fit-content; }
.footer-col a:hover { text-decoration: underline; }

.footer-legal {
    margin-top: 2.3rem; padding-top: 1.3rem;
    border-top: 1px solid rgba(250, 249, 245, .13);
    font-size: .75rem; color: #837c71;
}
.footer-legal p { margin: 0 0 .55rem; max-width: 76ch; }
.footer-legal p:last-child { margin-bottom: 0; }
.footer-legal a { color: #a8a196; }
.stamp { color: var(--chrome-2); font-size: .75rem; }
.page-stamp { margin-top: 2.4rem; color: var(--ink-3); }

/* ================================================================ responsive */

/* ======================================================== the tiers off a place page */

/*  Offence pages, year pages, comparisons and the ranked lists reuse everything above. What is
    here is only what they add: a second series colour, twelve columns instead of a line, a
    picker, and the year strip.  */

/*  Two places on one chart. The second colour is the rust end of the band ramp, which is
    already on every page as "further above average", so it arrives meaning something rather
    than as an arbitrary second hue.  */
.chart .series-a      { stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart .series-b      { stroke: var(--band-5); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round;
                        stroke-dasharray: 5 3; }
.chart .last-dot.series-a   { fill: var(--accent); }
.chart .last-dot.series-b   { fill: var(--band-5); }
.chart .last-label.series-a { fill: var(--accent-deep); }
.chart .last-label.series-b { fill: var(--band-5); }

/*  The key is in the caption rather than floating in the plot: the caption is already where a
    reader looks to find out what they are looking at.  */
.chart-keys { display: flex; flex-wrap: wrap; gap: .25rem 1.3rem; margin: 0 0 .5rem; }

/*  Scoped under .chart-keys, and that scoping is load-bearing: the map legend also uses a span
    called .key for its 11px colour swatches, and the bare selector here was drawing this rule's
    2px line stub inside every one of them - the "weird lines" through the legend.  */
.chart-keys .key { font-size: .78rem; font-weight: 600; white-space: nowrap; }
.chart-keys .key::before {
    content: ""; display: inline-block; width: 1.1rem; height: 0; vertical-align: .22em;
    margin-right: .4rem; border-top: 2px solid currentColor;
}
.key-a { color: var(--accent-deep); }
.key-b { color: var(--band-5); }
.key-b::before { border-top-style: dashed; }

/*  The place and offence charts key their own two lines the same way: same colour, different
    weight, so the key shows the difference it is naming.  */
.key-avg { color: var(--accent-deep); }
.key-raw { color: var(--accent); opacity: .6; }
.key-raw::before { border-top-width: 1px; }

/*  A year is twelve columns. A line through twelve points invites a reader to see a trend in
    what is almost entirely seasonality.  */
.chart .bar-col { fill: var(--accent); opacity: .82; }
.chart .bar-gap { fill: var(--ink-3); opacity: .07; }

/*  Rank numbers are furniture, not data: they sit back so the place names carry the table.  */
.rank-cell { color: var(--ink-3); font-variant-numeric: tabular-nums; width: 3rem; }
.muted { color: var(--ink-3); }
.compare-cell a { font-size: .8125rem; }

/*  330 rows. The head has to stay put or the reader loses which column is which halfway down.

    AND IT HAS TO NOT BE INSIDE A SCROLL CONTAINER, which for a long time it was. position: sticky
    resolves against the nearest scrolling ancestor, and .scroll-x is one (overflow-x: auto makes
    overflow-y compute to auto too - you cannot have one axis clipped and the other visible). So
    the header scrolled away with the table and pinned to nothing, on the single table on this
    site long enough for that rule to be the reason it exists.

    Wide enough for the nine columns to fit, the wrapper stops being a scroll container and the
    header sticks to the viewport as intended. Narrower, being able to reach the columns at all
    matters more than keeping their names on screen.  */
@media (min-width: 64rem) {
    .ranking-scroll { overflow: visible; }
}

/*  border-collapse: separate is the second half of it, and the less obvious half.

    With collapsed borders the cells do not own their edges - the table draws them - so a cell
    cannot be offset from its row, and position: sticky on a th is ignored outright. Separated
    borders with zero spacing look identical here, because every rule in this table is a bottom
    border on a cell and there are no adjacent edges left to merge.  */
.ranking-table { border-collapse: separate; border-spacing: 0; }

/*  The sticky element is the THEAD, not the cells inside it.

    A th is display: table-cell, and engines disagree about whether a table-internal box can be
    stuck at all - some ignore position: sticky on it outright, which is what was happening here:
    a plain sticky div in the same place pinned perfectly while the header row scrolled away.
    thead is honoured far more widely. The cells keep the background and the rule, because a
    background on a table-header-group is the next thing engines disagree about.  */
/*  NOT STICKY, AND IT CANNOT BE FROM HERE.

    This was position: sticky with top: calc(var(--header-h) - 1px), and it hid the first row of
    every table it was on. Every one of these tables sits inside .scroll-x, and overflow-x: auto
    makes that element a scroll container. A sticky offset resolves against the nearest scrolling
    ancestor, so "51px from the top" meant 51px from the top of the TABLE rather than from the top
    of the window: the header parked itself 51px down its own table and covered the row underneath
    it. On the ranked pages that row is number one, which is the row the page exists to show.

    It was doing this on five pages. Sticking to the viewport is not available while the table is
    inside a horizontal scroller, and the scroller is what stops wide tables breaking the page on
    a phone, so the header stays put and keeps only its rule and background.  */
.ranking-table thead {
    position: static;
}

.ranking-table thead th {
    background: var(--paper); box-shadow: 0 1px 0 var(--line);
}

/*  Fifteen years as a strip of links rather than a list: they are one dimension of the same
    thing, and stacking them would read as fifteen separate topics.  */
.year-lead { margin: 1.6rem 0 .5rem; }
.year-strip { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-wrap: wrap; gap: .35rem; }
.year-strip li { margin: 0; }
.year-strip a, .year-strip span {
    display: block; padding: .3rem .6rem; font-size: .82rem; font-weight: 600;
    font-variant-numeric: tabular-nums; text-decoration: none;
    border: 1px solid var(--line-2); color: var(--accent);
}
.year-strip a:hover { background: var(--accent-soft); border-color: var(--accent); }
.year-strip span { color: var(--ink); background: var(--paper-2); border-color: var(--ink-3); }

/*  The comparison picker. A plain GET form, so it works with the browser's own controls and
    nothing has to load before it does.  */
.picker { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .9rem; margin: 1.6rem 0 .5rem; }
.picker-field { display: flex; flex-direction: column; gap: .3rem; }
.picker label {
    font-size: .68rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--ink-3);
}
.picker select {
    font: inherit; font-size: .95rem; padding: .5rem .6rem; min-width: 14rem;
    background: #fff; color: var(--ink); border: 1px solid var(--line-2);
}
.picker select:focus, .picker button:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.picker button {
    font: inherit; font-size: .9rem; font-weight: 600; padding: .55rem 1.2rem; cursor: pointer;
    background: var(--ink); color: var(--paper); border: 1px solid var(--ink);
}
.picker button:hover { background: var(--accent-deep); border-color: var(--accent-deep); }

/*  A comparison leads with two figures of equal weight. Making one of them the lead figure, as
    every other page does, would answer the question before the reader had asked it.  */
.compare-figures { grid-template-columns: 1fr 1fr; }
.compare-figures .figure-lead::before { content: none; }
.figure-kicker {
    display: block; margin-bottom: .35rem;
    font-size: .68rem; font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--ink-2);
}

@media (max-width: 64rem) {
    .map-split { display: block; }
    .map-side { position: static; margin-bottom: 1.2rem; }
    .map-side .map-canvas { height: 380px; }
    .two-col { grid-template-columns: 1fr; }
    .area-list.columns { columns: 3 10rem; }
}

@media (max-width: 44rem) {
    main { padding: 0 0 3.2rem; }
    .wrap { padding: 0 1.1rem; }

    /*  ------------------------------------------------------------- no JavaScript ----

        The floor the drawer is built on, and a real arrangement rather than a degraded one: the
        nav stays in the flow under the brand, wrapping, every link visible and reachable.

        It used to be a horizontally scrolling lane with the scrollbar hidden, which at 375px left
        Methodology and About off-screen with nothing at all to suggest they were there - no
        scrollbar, no fade, no chevron. They were simply absent to anyone who did not happen to
        swipe a strip of dark bar.

        padding-top, NOT the padding shorthand. This element carries .wrap as well, and the
        shorthand wiped out .wrap's 1.1rem gutter, which is why the brand and the links ended up
        hard against the edge of the screen.  */
    .site-header { position: static; }
    .header-inner { display: block; height: auto; padding-top: .75rem; }

    .brand { font-size: .95rem; }

    .site-nav {
        gap: 0 1.15rem; flex-wrap: wrap;
        margin-top: .6rem; border-top: 1px solid rgba(255, 255, 255, .12);
    }
    .site-nav a { font-size: .8rem; padding: .55rem 0; border-bottom-width: 2px; }

    /*  The header is out of the flow here, so the jump nav is what pins, and it pins to the top
        of the viewport. Every sticky offset and scroll-margin on the page reads this token, so
        they stay honest.  */
    :root { --header-h: 0rem; }

    /*  ------------------------------------------------------------ with JavaScript ----

        One row again - brand on the left, Menu on the right - with the links in a panel that
        comes in from the right over a scrim. The header can afford to stay sticky at one row, and
        should: on a 15,000px place page the way out needs to be reachable from wherever you are.  */
    :root.js { --header-h: 3.25rem; }
    .js .site-header { position: sticky; }
    .js .header-inner {
        display: flex; align-items: center; justify-content: space-between;
        gap: .75rem; height: var(--header-h); padding-top: 0;
    }

    /*  See nav.js: the header is a stacking context, so it is lifted as a whole rather than the
        panel inside it trying to out-rank a scrim it cannot reach.  */
    .js.nav-open .site-header { z-index: 900; }

    /*  The scroll lock lives here, inside the media query, and not as an inline style set by the
        script. The page behind a panel that covers it should not scroll under the reader's thumb;
        equally, a lock that script has to remember to release is a lock that outlives the panel
        the first time the release does not run. Leaving the width takes the rule with it.  */
    .js.nav-open, .js.nav-open body { overflow: hidden; }

    .js .nav-toggle {
        display: inline-flex; align-items: center; gap: .55rem;
        background: transparent; border: 1px solid rgba(255, 255, 255, .25);
        color: #fff; font: inherit; font-size: .7rem; font-weight: 600;
        letter-spacing: var(--track-label); text-transform: uppercase;
        padding: .45rem .72rem; cursor: pointer;
    }
    .js .nav-toggle:hover { border-color: rgba(255, 255, 255, .5); }

    .nav-bars { position: relative; display: block; width: .95rem; height: 1px; background: currentColor; }
    .nav-bars::before, .nav-bars::after {
        content: ""; position: absolute; left: 0; width: 100%; height: 1px; background: currentColor;
    }
    .nav-bars::before { top: -5px; }
    .nav-bars::after  { top: 5px; }

    /*  The panel.

        visibility, not transform alone: a panel parked off-screen is still in the tab order, and
        tabbing into links behind a scrim that nobody can see is the usual way a drawer fails for
        anyone not using a mouse. It flips instantly on the way in and waits for the slide on the
        way out, which is what the two transition declarations are for.  */
    .js .site-nav {
        position: fixed; top: 0; right: 0; bottom: 0;
        z-index: 800; width: min(80vw, 19rem);
        display: flex; flex-direction: column; align-items: stretch; gap: 0;
        margin: 0; padding: 1rem 1.3rem 1.6rem;
        background: var(--chrome);
        border-top: 0; border-left: 1px solid rgba(255, 255, 255, .12);
        overflow-y: auto; overscroll-behavior: contain;
        transform: translateX(100%); visibility: hidden;
        transition: transform .22s ease, visibility 0s linear .22s;
    }
    .js .site-nav.open {
        transform: none; visibility: visible;
        transition: transform .22s ease, visibility 0s;
    }

    .js .nav-head {
        display: flex; align-items: center; justify-content: space-between;
        margin-bottom: .35rem;
    }
    .js .nav-title {
        margin: 0; font-size: .68rem; font-weight: 600;
        letter-spacing: var(--track-label); text-transform: uppercase; color: var(--chrome-2);
    }
    .js .nav-close {
        background: transparent; border: 0; color: var(--chrome-2);
        font-size: 1.6rem; line-height: 1; padding: .1rem .35rem;
        margin-right: -.35rem; cursor: pointer;
    }
    .js .nav-close:hover { color: #fff; }

    .js .site-nav a {
        font-size: 1.02rem; font-weight: 500; padding: .95rem 0;
        color: #d9d3c8; border-bottom: 1px solid rgba(255, 255, 255, .1);
    }
    .js .site-nav a:hover { color: #fff; }
    .js .site-nav a.here {
        color: #fff; border-bottom-color: rgba(255, 255, 255, .1);
        box-shadow: inset 3px 0 0 var(--band-5); padding-left: .8rem;
    }

    /*  Pushed to the bottom of the panel by the auto margin: the same stamp the footer carries,
        where it answers "how current is this" without the reader leaving the menu.  */
    .js .nav-stamp {
        display: block; margin: auto 0 0; padding-top: 1.4rem;
        font-size: .72rem; color: var(--chrome-2);
    }

    .js .nav-scrim {
        display: block; position: fixed; top: 0; right: 0; bottom: 0; left: 0;
        z-index: 780; background: rgba(18, 15, 12, .58);
        opacity: 0; visibility: hidden;
        transition: opacity .22s ease, visibility 0s linear .22s;
    }
    .js .nav-scrim.open {
        opacity: 1; visibility: visible;
        transition: opacity .22s ease, visibility 0s;
    }

    .masthead { padding-top: 1.4rem; }

    /*  The fade that says the trail runs past the edge. Narrow screens only: this is the width
        where a five-deep trail actually overflows.  */
    .breadcrumb ol {
        -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 1.5rem), transparent);
                mask-image: linear-gradient(90deg, #000 calc(100% - 1.5rem), transparent);
    }

    .figures { grid-template-columns: 1fr 1fr; border-bottom: 0; }
    .figure { border-bottom: 1px solid var(--line); padding: .8rem 1rem .9rem 0; }
    .figure + .figure { padding-left: 1rem; }
    .figure:nth-child(odd) { border-left: 0; padding-left: 0; }
    .figure-value { font-size: 1.85rem; }

    main section[id] { margin-top: 2.6rem; padding-top: 1.2rem; }
    .area-list.columns { columns: 2 9rem; }
    .data-table { font-size: .85rem; }
    .data-table th, .data-table td { padding: .45rem .5rem; }
    .scroll-x > .data-table { min-width: 30rem; }

    /*  A fifth column repeating the word "Compare" down a 375px screen is most of the table's
        width for one link. The links stay in the markup, so they are still crawled and still
        carry the internal linking; a phone gets the single link under the table instead.  */
    .compare-head, .compare-cell { display: none; }
    .mast-stamp { display: none; }
    .picker { flex-direction: column; align-items: stretch; }
    .picker select, .picker button { width: 100%; min-width: 0; }
    /*  Unpinned on a phone, where a pinned row eats a third of the screen and lands on top of
        the first result. This rule named `thead th` for years and did nothing: the sticky was
        moved up to the thead itself when engines turned out to disagree about sticky table
        cells, and the override was never moved with it.  */
    /*  position is static everywhere now, see the note by the rule itself. The shadow still
        comes off here: on a phone the header rule reads as a smudge under the column names.  */
    .ranking-table thead th { box-shadow: none; }
    .compare-figures { grid-template-columns: 1fr 1fr; }
    .chart, .scale { overflow-x: auto; }
    .chart svg { min-width: 30rem; }
    .scale svg { min-width: 22rem; }
    .map-canvas { height: 340px; }
    /*  The national map keeps more height on a phone than a panel map does: it is the reason
        the page exists, and England and Wales in a 340px box is a thumbnail.  */
    /*  A phone is taller than it is wide, so the country's own proportions are close enough to
        the column's already: a fixed height reads better than an aspect ratio that would make
        the map a third of a screen tall.  */
    .map-national { aspect-ratio: auto; height: min(70vh, 560px); max-height: none; }
    .consent-inner { padding-top: 1.75rem; }
    .consent-title { font-size: 1.15rem; }
    .consent-text { font-size: .875rem; }
    .consent-actions { flex-direction: column-reverse; align-items: stretch; }
    .consent button { width: 100%; }
    .footer-grid { grid-template-columns: 1fr 1fr; gap: 1.8rem 2rem; }
    .footer-id { grid-column: 1 / -1; }
    /*  On a phone the offence list takes the full width and splits there instead, so it does not
        become a column of one word per line.  */
    .footer-col-split { grid-column: 1 / -1; columns: 2 7rem; }
}

/*  The panel still opens and closes; it just stops sliding.  */
@media (prefers-reduced-motion: reduce) {
    .site-nav, .nav-scrim { transition: none !important; }
}

/*  ==================================================================== share  */

.share {
    margin: 2.6rem 0 0; padding-top: 1.2rem;
    border-top: 1px solid var(--line);
}
.share-title {
    font-size: .6875rem; font-weight: 600; letter-spacing: .12em;
    text-transform: uppercase; color: var(--ink-3); margin: 0 0 .7rem;
}
.share-row { display: flex; flex-wrap: wrap; gap: .5rem; }

/*  One shape for links and buttons alike. The row mixes anchors (the platforms, which must work
    without JavaScript) with buttons (copy and print, which cannot), and a reader should not be
    able to tell which is which. Each network's glyph sits in its own colour, which is where most
    of the recognition lives; the text stays in the site's ink.  */
.share-btn {
    font: inherit; font-size: .85rem; font-weight: 500;
    padding: .45rem .85rem .45rem .7rem; cursor: pointer; white-space: nowrap;
    color: var(--ink); background: var(--paper); border: 1px solid var(--line-2);
    text-decoration: none; display: inline-flex; align-items: center; gap: .45rem;
    border-radius: 999px;
}
.share-btn svg { width: 1.05rem; height: 1.05rem; flex: 0 0 auto; }
.share-btn:hover { border-color: var(--ink-3); color: var(--ink); text-decoration: none; }
.share-btn:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }

/*  The brand colours, on the glyphs alone. A row of fully-flooded brand buttons is a row of
    adverts; a neutral pill with a coloured mark reads as "this site, offering routes out".  */
.share-facebook svg { color: #1877f2; }
.share-whatsapp svg { color: #25d366; }
.share-nextdoor svg { color: #2c6e49; }
.share-reddit svg   { color: #ff4500; }
.share-x svg        { color: #0f1419; }
.share-email svg, .share-print svg, .share-copy svg { color: var(--ink-3); }

.share-native { border-color: var(--accent); color: var(--accent-deep); }
.share-copy.is-done { border-color: var(--accent); color: var(--accent-deep); }

@media (max-width: 640px) {
    .share-btn { font-size: .8rem; padding: .4rem .7rem .4rem .6rem; }
}

/*  ------------------------------------------------------------------ table filter  */

.table-filter { margin: .9rem 0 .6rem; }
.table-filter-input {
    font: inherit; font-size: .95rem; width: 100%; max-width: 26rem;
    padding: .55rem .8rem; color: var(--ink);
    background: var(--paper); border: 1px solid var(--line-2);
}
.table-filter-input:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
.table-filter-empty { font-size: .9rem; color: var(--ink-2); margin: .8rem 0; }

/*  ------------------------------------------------------------------ share modal

    The row of networks now lives in here, opened by one button, because eight buttons at the foot
    of every page was eight decisions offered to a reader who had asked for none of them. A dialog
    also gets the focus trap, the Escape key and the backdrop from the browser rather than from
    hand-written script.  */
.share-actions { display: flex; flex-wrap: wrap; gap: .5rem; }

/*  Share is the primary action and now looks like one. It sat in the same grey pill as "Print
    or save as PDF", which asked the reader to choose between two equal-looking options when the
    two are not equal: one is the thing the page wants, the other is a utility. Filled accent for
    the ask, the plain pill for everything else.  */
.share-open {
    background: var(--accent); border-color: var(--accent); color: #fff;
    padding-left: .9rem; padding-right: 1.15rem; font-weight: 600;
}
.share-open svg { color: #fff; }
.share-open:hover { background: var(--accent-deep); border-color: var(--accent-deep); color: #fff; }

/*  The prompt is a question now, so it reads as a line of text rather than a section label.  */
.share-title {
    font-size: 1.02rem; font-weight: 500; letter-spacing: 0; text-transform: none;
    color: var(--ink); margin: 0 0 .85rem; max-width: 34rem; line-height: 1.45;
}

.share-modal {
    border: 1px solid var(--line-2); border-radius: 3px;
    padding: 1.4rem 1.5rem 1.5rem; max-width: 30rem; width: calc(100% - 2rem);
    background: var(--paper); color: var(--ink);
    box-shadow: 0 18px 50px rgba(0, 0, 0, .28);
}
.share-modal::backdrop { background: rgba(20, 18, 16, .5); }

.share-modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.share-modal-head h2 { font-size: 1.15rem; margin: 0; line-height: 1.25; max-width: 22rem; }

.share-modal-close {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2rem; height: 2rem; flex: 0 0 auto; margin: -.3rem -.4rem 0 0;
    background: none; border: 0; cursor: pointer; color: var(--ink-3);
}
.share-modal-close svg { width: 1.1rem; height: 1.1rem; }
.share-modal-close:hover { color: var(--ink); }
.share-modal-close:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }

/*  The sentence that will actually be sent. Quoted rather than paraphrased, because the point is
    that the share carries the figure and not just an address.  */
.share-modal-preview {
    font-size: .85rem; line-height: 1.5; color: var(--ink-2);
    margin: .7rem 0 1.1rem; padding-left: .8rem;
    border-left: 2px solid var(--line-2);
}

.share-modal .share-row { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.share-modal .share-btn { justify-content: flex-start; width: 100%; }

@media (max-width: 420px) {
    .share-modal .share-row { grid-template-columns: 1fr; }
}

/*  ------------------------------------------------------------------ sticky share, phones

    Shown when scrolling stops, hidden the moment it resumes, and offered three times at most.
    Built and driven by share.js; a phone with JavaScript off never gets one, which is fine
    because the foot block is still there.

    Treated as a distinct surface rather than more page: an accent rule along the top edge, a
    lifted shadow, and a filled button that is the only thing in it competing for a thumb. The
    first version was paper on paper with a hairline, which read as the page continuing and gave
    the eye no reason to stop.  */
.share-sticky {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 1200;
    display: none; align-items: center; gap: .9rem;
    padding: .75rem 1rem calc(.75rem + env(safe-area-inset-bottom, 0px));
    background: var(--paper);
    border-top: 3px solid var(--accent);
    box-shadow: 0 -10px 30px rgba(0, 0, 0, .18);
    transform: translateY(115%); transition: transform .24s cubic-bezier(.2, .8, .3, 1);
}

/*  The figure, not a label. It is the reason to press the button, so it gets the reading weight
    and the button gets the colour.  */
.share-sticky-label {
    font-size: .9rem; line-height: 1.35; color: var(--ink);
    flex: 1 1 auto; min-width: 0;
}

/*  A full-size target. 44px is the smallest thing a thumb hits reliably, and this is the one
    control on the bar.  */
.share-open-lg {
    flex: 0 0 auto; min-height: 44px;
    padding: .6rem 1.25rem .6rem 1rem; font-size: .95rem;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .16);
}
.share-open-lg svg { width: 1.15rem; height: 1.15rem; }

/*  Under the consent scrim (2000) and sheet (2010) on purpose: a first-time reader settles the
    legal choice before this site asks them for anything.  */
@media (max-width: 640px) {
    .share-sticky { display: flex; }
    .share-sticky.is-up { transform: translateY(0); }
}

/*  Very narrow phones: the prompt gets two lines rather than squeezing the button.  */
@media (max-width: 360px) {
    .share-sticky { gap: .6rem; padding-left: .75rem; padding-right: .75rem; }
    .share-sticky-label { font-size: .85rem; }
    .share-open-lg { padding-left: .85rem; padding-right: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
    .share-sticky { transition: none; }
}
}

/*  ==================================================================== print  */

/*  WHAT A PRINTED PAGE IS FOR HERE. People print and PDF these to put a figure in a report, a
    planning objection, a council paper or a listing pack. That means the printout has to say
    where it came from and when, because a table of crime rates with no source and no date is
    worthless to whoever reads it next, and the reader who printed it will not add either.  */
@media print {
    /*  Anything that is navigation, interaction or decoration. None of it can be used on paper
        and all of it costs pages.  */
    .site-nav, .nav-scrim, .site-header, .jump, .share, .share-sticky, .share-modal,
    .consent, .consent-scrim,
    .search-box, .site-footer nav, .footer-prefer, .skip,
    details.disclose summary, .map-shell, .map-rail, .map-legend, .map-detail,
    .bar-cell, .heat-grid, button, .picker { display: none !important; }

    /*  Charts are the exception: an SVG carries the shape of the data and prints perfectly.  */
    .chart, .chart svg { display: block; max-width: 100%; }

    html, body { background: #fff; color: #000; font-size: 10.5pt; }
    .wrap { max-width: none; padding: 0; }
    a { color: #000; text-decoration: underline; }

    /*  The masthead becomes the printout's letterhead.  */
    .masthead { background: #fff !important; color: #000 !important; padding: 0 0 .5rem; }
    .masthead h1 { font-size: 20pt; }
    .masthead a, .standfirst, .figure-value, .figure-label { color: #000 !important; }
    .figures { border: 0; gap: .6rem 1.4rem; }

    /*  Tables are the reason the page is being printed. Repeat the head on every sheet and stop
        rows splitting across a page break, or a 100-row ranking becomes unreadable on paper.  */
    table { border-collapse: collapse; width: 100%; page-break-inside: auto; }
    thead { display: table-header-group; }
    tr { page-break-inside: avoid; }
    .data-table th, .data-table td {
        border-bottom: 1px solid #ccc; padding: 3pt 5pt; font-size: 9pt;
    }
    .data-table tr.is-peak > th, .data-table tr.is-peak > td { background: #f0f0f0 !important; }
    .scroll-x { overflow: visible !important; }

    h2, h3 { page-break-after: avoid; }
    .notice, .callout { border: 1px solid #999; padding: 6pt; page-break-inside: avoid; }

    /*  The provenance, added by the stylesheet so it cannot be forgotten and cannot be edited out
        of the page by a reader who did not mean to. Content is set from the canonical URL and the
        data stamp the footer already carries.  */
    .site-footer { background: #fff !important; color: #000 !important;
                   border-top: 1px solid #000; padding-top: 6pt; }
    .footer-id { display: block; }
    .footer-brand .brand-mark { display: none; }
    .footer-brand, .footer-tag, .footer-stamp { color: #000 !important; font-size: 9pt; }
    .footer-legal { font-size: 8pt; color: #333 !important; }
    .footer-legal a::after { content: " (" attr(href) ")"; font-size: 7.5pt; }

    @page { margin: 14mm 12mm; }
}

/*  A callout that is context rather than the finding. Same shape as .callout, without the accent
    rule that says "this is the sentence to read".  */
.callout-quiet {
    font-size: 1rem; font-weight: 400; color: var(--ink-2);
    border-left: 0; padding-left: 0;
}
.callout-quiet::before { content: none; }


/*  ============================================================
    Stop and search by time of day
    ============================================================  */

/*  The hourly chart reuses the shared .chart frame (grid, baseline, axis type) and only adds the
    bars, so it inherits any later change to chart styling rather than drifting from it.  */
.chart-hours .hour-bar { fill: var(--accent); opacity: .55; }
.chart-hours .hour-bar-peak { opacity: 1; }

/*  Weekday by hour. A table rather than a grid of divs because it IS tabular: the row and column
    headers are the axes, and a screen reader reading "Saturday, 02:00" is reading the chart.  */
.heat-grid { border-collapse: collapse; width: 100%; min-width: 34rem; margin: .9rem 0 0; }
.heat-grid th {
    font-size: .78rem; font-weight: 600; color: var(--ink-3);
    text-align: right; padding: 0 .45rem 0 0; white-space: nowrap;
}
.heat-grid thead th { text-align: center; padding: 0 0 .3rem; font-variant-numeric: tabular-nums; }
.heat-grid tbody th { width: 2.6rem; }
.heat-cell {
    /*  --heat is 0 to 1, set per cell from the server. Alpha stops short of 1 so the busiest cell
        still reads as a shade of the page rather than a block of ink.  */
    background: rgba(var(--accent-rgb), calc(var(--heat, 0) * .92));
    border: 1px solid var(--paper);
    height: 1.5rem;
}

/*  The peak row in the hourly table, matching the emphasised bar in the chart above it.  */
.data-table tr.is-peak > th,
.data-table tr.is-peak > td { background: var(--accent-soft); }

/*  The national curve laid over an area's bars. Deliberately a thin dark line rather than a
    second set of bars: it is a reference, not a rival series, and two bar sets at these widths
    turn into a moire.  */
.chart-hours .hour-national {
    fill: none; stroke: var(--ink); stroke-width: 1.6;
    stroke-linejoin: round; stroke-linecap: round; opacity: .55;
}

.chart-key {
    font-size: .85rem; color: var(--ink-3); margin: .5rem 0 0;
    display: flex; flex-wrap: wrap; align-items: center; gap: .4rem;
}
.chart-key .key-bar {
    width: .8rem; height: .8rem; background: var(--accent); opacity: .55;
    display: inline-block; flex: 0 0 auto;
}
.chart-key .key-line {
    width: 1.1rem; height: 0; border-top: 2px solid var(--ink); opacity: .55;
    display: inline-block; flex: 0 0 auto; margin-left: .5rem;
}

@media (max-width: 640px) {
    .heat-grid { min-width: 30rem; }
    .heat-cell { height: 1.25rem; }
}
