/* ── DEFINE landing v3: structure and motion ─────────────────────────────────────────
 * Two things the ported mock-up did not have, and could not get from a design file:
 * a way to tell where you are in a 9,900px argument, and any sense of movement.
 *
 * THE RAIL. Eight numbered stops down the left edge, one per section, the current one
 * lit. It is an index, not decoration: the page makes a long argument in eight moves,
 * and a reader who cannot see the shape of it reads the first two and leaves. Hidden
 * below 1200px, where there is no margin to put it in and the header nav does the job.
 *
 * THE MOTION. Sections rise a few pixels and fade as they arrive. Deliberately small:
 * 14px and 520ms. The point is to make the page feel like it is being laid down in
 * front of you rather than to be noticed. Anything larger reads as a template.
 *
 * Nothing here animates opacity from 0 without a guarantee of arrival. If the observer
 * never fires - no JS, an old browser, a reduced-motion preference - the content is
 * simply present, because .is-in is only ever ADDED and the un-observed default is
 * visible. A reveal that hides content it might never show is a broken page.
 */

/* ── Scroll progress ─────────────────────────────────────────────────────────────────
 * A hairline under the sticky header. It answers "how much of this is there" without
 * costing a single row of vertical space. */
.v3-progress {
    position: fixed; left: 0; top: 0; height: 2px; width: 100%;
    z-index: 300; pointer-events: none;
    background: linear-gradient(90deg, #8032D8, #C6A5F5);
    transform: scaleX(0); transform-origin: 0 50%;
    transition: transform .12s linear;
}

/* ── The section rail ────────────────────────────────────────────────────────────────
 * Fixed to the viewport, vertically centred, in the left margin. */
.v3-rail {
    position: fixed; left: 22px; top: 50%; transform: translateY(-50%);
    z-index: 120; display: grid; gap: 2px;
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
}
.v3-rail a {
    position: relative;                    /* the label hangs off this, see below */
    display: block; width: 26px;
    padding: 5px 0; text-decoration: none; color: inherit;
}
.v3-rail .n {
    display: block; font-size: 10px; letter-spacing: .1em; text-align: right;
    color: rgba(11, 11, 12, .36);
    transition: color .22s ease;
}
.v3-rail .t {
    /* ABSOLUTE, not just transparent. At opacity:0 the labels still take their width,
       which made the rail about 180px wide and put it straight through the headline at
       every size below 1700px. Out of flow, the rail is only ever as wide as "08". */
    position: absolute; left: 34px; top: 50%; transform: translateY(-50%) translateX(-4px);
    font-size: 11px; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap;
    /* Eight labels always on is a second navigation competing with the header, so they
       appear on hover, on focus, and for the current stop only. */
    color: rgba(11, 11, 12, .72);
    opacity: 0; pointer-events: none;
    /* A backdrop, because at 1400px the label reaches the first character of the
       headline. Measured: 12px of clearance for the NUMBER, none for the label once it
       opens. Rather than push the rail off screen for the sake of a hover state, the
       label carries enough background to stay readable if it lands on the copy. */
    background: rgba(255, 255, 255, .92); padding: 4px 8px; border-radius: 3px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
    transition: opacity .22s ease, transform .22s ease;
}
.v3-rail a:hover .t, .v3-rail a:focus-visible .t, .v3-rail a.is-on .t { opacity: 1; transform: translateY(-50%) }
.v3-rail a:focus-visible { outline: 2px solid #8032D8; outline-offset: 2px }
.v3-rail a.is-on .n { color: #8032D8 }
.v3-rail .bar {
    grid-column: 1; justify-self: end; width: 14px; height: 1px; background: currentColor;
    opacity: .28; transition: width .22s ease, opacity .22s ease;
}

/* Over a dark section the rail has to invert, or it disappears into the black. The
   observer sets data-over on <html> from the section under the rail's own midpoint. */
html[data-over="dark"] .v3-rail .n { color: rgba(255, 255, 255, .42) }
html[data-over="dark"] .v3-rail .t { color: rgba(255, 255, 255, .92); background: rgba(11, 11, 12, .88); box-shadow: none }
html[data-over="dark"] .v3-rail a.is-on .n { color: #C6A5F5 }

/* The rail lives in the page margin, and below about 1400px there is no margin left:
   the 1280px container meets the viewport edge and the numbers land on the copy. Measured,
   not guessed - at 1360 the gap between the rail and the first character is 18px. */
/* Two breakpoints, because the rail and its labels need different amounts of room.
   Measured at 1440: the NUMBER clears the headline by 32px, but the label opens 110px
   to the right and lands on the lede - a backdrop did not save it, it just put a pill
   on top of the copy. So below 1600 the rail is numbers only and the name is carried
   by the title attribute; the header nav is still marking the section either way. */
@media (max-width: 1599px) {
    .v3-rail a:hover .t, .v3-rail a:focus-visible .t, .v3-rail a.is-on .t { opacity: 0 }
}
@media (max-width: 1399px) { .v3-rail { display: none } }

/* ── Section reveal ──────────────────────────────────────────────────────────────────
 * Applied by JS to the blocks inside each section, so a section arrives in two or three
 * pieces rather than as one slab. */
.v3-rise {
    opacity: 0; transform: translate3d(0, 14px, 0);
    transition: opacity .52s cubic-bezier(.22, .61, .36, 1), transform .52s cubic-bezier(.22, .61, .36, 1);
    will-change: opacity, transform;
}
.v3-rise.is-in { opacity: 1; transform: none }
/* Three steps is enough to read as a sequence; more and the last item is late. */
.v3-rise[data-step="1"] { transition-delay: .07s }
.v3-rise[data-step="2"] { transition-delay: .14s }
.v3-rise[data-step="3"] { transition-delay: .21s }

/* The header nav marks the section you are in, so the two navigations agree. */
.dnav .nav a.v3-here { color: #8032D8 !important; }
html[data-theme="dark"] .dnav .nav a.v3-here { color: #C6A5F5 !important; }

/* ── Motion off ──────────────────────────────────────────────────────────────────────
 * Everything above is an enhancement. With motion reduced the content is present, the
 * rail still tracks, and the progress bar still fills - it just does not slide. */
@media (prefers-reduced-motion: reduce) {
    .v3-rise { opacity: 1; transform: none; transition: none }
    .v3-progress { transition: none }
    .v3-rail a .t { transition: none }
}

/* ── The abatement table ─────────────────────────────────────────────────────────────
 * "Risk 2036", "Abatement cost" and "Exposure removed" were printed on all five rows:
 * fifteen labels doing the work of three, and the eye has to re-read the label before
 * every number instead of scanning the column. Kept on the first row, where they read
 * as a header, and dropped on the rest.
 *
 * Suppressed rather than deleted, so the markup still carries the name next to each
 * value: a screen reader goes down the rows one at a time and does not have a header
 * row to refer back to. .v3-sr keeps them in the accessibility tree only.
 */
/* span:not(:first-child) matters: the row's FIRST span holds the rank number and
   the asset name, and its first child is "02".."05". Without the guard the rank
   numbers vanished from every row but the first. */
.v3-rank-row:not([data-row="0"]) > span:not(:first-child) > span:first-child {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
/* The first row's labels are now a header, so they get a little more air beneath. */
.v3-rank-row[data-row="0"] > span:not(:first-child) > span:first-child { margin-bottom: 4px }

/* ── Justified body copy ─────────────────────────────────────────────────────────────
 * Justification without hyphenation is the classic way to wreck a text block: the
 * browser can only stretch word spaces, so a long word on a narrow measure opens
 * rivers of white down the paragraph. `hyphens: auto` gives it somewhere else to put
 * the slack, and the two belong together.
 *
 * Scoped to running prose only. Headings stay ragged-right - a justified two-line
 * headline at 42px pulls its words to the edges and reads as a mistake - and so do the
 * mono labels, the buttons and anything in a narrow column, where justification has
 * almost no words per line to work with and does the most damage.
 */
#hero p, #observe p, #mispricing p, #meaning p, #predict p,
#engine p, #prioritise p, #who p, #recognitions p, #demo p,
.definePages footer:not(.ins-legal-footer) p,
#who .v3-tabpanel span, #demo .v3-wrap span,
.v3-justify {
    text-align: justify;
    hyphens: auto;
    -webkit-hyphens: auto;
    /* Never break a word that would leave fewer than three characters stranded. */
    hyphenate-limit-chars: 7 3 3;
}

/* The exceptions, in the same order the eye meets them: the lede under a headline is
   short enough that justifying it just spaces out two lines; a caption is a single
   line; and anything mono is set on a grid where stretched spaces are obvious. */
#observe figcaption, .scene-cap, .v3-rank-row p,
[style*="IBM Plex Mono"] {
    text-align: inherit; hyphens: manual; -webkit-hyphens: manual;
}

/* The exception was drawn at 760px, which gave up justification on every tablet and
   large phone on the way down to the one measure that actually cannot take it. A 600px
   column holds about 76 characters and a 430px phone about 48, both of which justify
   cleanly with hyphens on. It is the smallest screens, around 34 characters a line,
   where the browser runs out of places to put the slack and the block fills with rivers
   and stranded hyphens. So the exception moves to where the problem starts. */
@media (max-width: 419px) {
    #hero p, #observe p, #mispricing p, #meaning p, #predict p,
    #engine p, #prioritise p, #who p, #recognitions p, #demo p,
    .definePages footer:not(.ins-legal-footer) p,
    #who .v3-tabpanel span, #demo .v3-wrap span,
    .v3-justify { text-align: left; hyphens: manual; -webkit-hyphens: manual }
}

/* ── The plan over the cover ─────────────────────────────────────────────────────────
 * .scene-plan and .pl-* live in define-viz.css, which this page does not load - it
 * needs one figure's worth of that stylesheet, not the whole of it. Without these the
 * SVG laid out in normal flow BELOW the hero at its full 1600x900, with black labels
 * on a black page: present in the DOM, invisible on the screen, and pushing the page
 * down by 900px. Scoped to .v3-cover-plan so it cannot collide with the sector pages.
 */
/* The works is drawn centred in its own frame, which puts the furnaces under the
   headline. Shifting it is one transform on the frame BOTH layers share, so the raster
   and the plan move together and stay in register whatever the hero's aspect turns out
   to be - the earlier attempt shifted them with object-position and preserveAspectRatio
   separately, and the two came apart as soon as the hero got wider than the canvas.
   The scale covers the space the translate would otherwise open on the left. */
.v3-cover-frame { transform: scale(1.06) translateX(8%); transform-origin: center }
@media (max-width: 1099px) { .v3-cover-frame { transform: none } }

.v3-cover-plan {
    position: absolute; inset: 0; width: 100%; height: 100%;
    display: block; pointer-events: none; z-index: 1;
    /* The works is drawn across a frame WIDER than the hero and both layers align left,
       which slides the furnaces and the mills out from under the copy column and into
       the open right-hand half. What stays on the left is the stockyard and the coke
       battery, so the plan fades out there rather than labelling things through the
       headline. The fade is on the overlay only: the raster underneath runs full bleed. */
    /* The plan names only four units now (see data-labels on the element), so there is
       nothing left to hide on the copy side and no label for a gradient to cut in half.
       One soft mask remains, and only to keep the faint parcel OUTLINES off the
       headline: they are texture there rather than information. */
    /* One mask, and only to keep the faint parcel outlines off the headline. The
       vertical mask that used to be here existed to dodge the risk-grid card; the card
       is gone, so the plan has the whole right-hand side and both furnaces can be
       named. */
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, transparent 26%, #000 44%, #000 100%);
            mask-image: linear-gradient(90deg, transparent 0%, transparent 26%, #000 44%, #000 100%);
}
.v3-cover-plan .pl-box {
    fill: none; stroke: rgba(255, 255, 255, .42); stroke-width: 1.25;
}
.v3-cover-plan .pl-box.is-thin { stroke: rgba(255, 255, 255, .26); stroke-dasharray: 7 5 }
.v3-cover-plan .pl-key { fill: none; stroke: #ffffff; stroke-width: 1.75 }
.v3-cover-plan .pl-t {
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 15px; letter-spacing: .07em;
    fill: rgba(255, 255, 255, .82);
    /* paint-order puts the halo BEHIND the glyph rather than over it, which is what
       keeps a mono label legible over a raster that is bright in places. */
    paint-order: stroke; stroke: rgba(11, 11, 12, .72); stroke-width: 4;
}
/* The two furnaces are the point of showing a plan at all: they are named, and they
   are the hottest thing in the frame. They get full white and a heavier halo. */
.v3-cover-plan .pl-t.is-key { fill: #ffffff; stroke-width: 5 }

/* Below the rail's own breakpoint the hero is short and the crop tight enough that the
   labels start landing on the headline. The raster carries the frame on its own there. */
@media (max-width: 1099px) { .v3-cover-plan { display: none } }

/* ── The film, full bleed ────────────────────────────────────────────────────────────
 * /sim1 is the command-centre application, not a picture of one. Inside the page's
 * 1280px reading column it was running at 1232px wide on any screen, however big, and
 * its own type ramp is driven by viewport WIDTH (--u: clamp(15px, 0.92vw, 34px)), so a
 * narrow frame makes everything in it small regardless of the screen it is on.
 *
 * Breaking out of the column is the whole fix. 100vw would be right except for the
 * scrollbar, which vw ignores: on a desktop with a classic scrollbar the frame ends up
 * about 15px wider than the page and brings back the horizontal overflow the wrapper
 * used to hide. Measuring the SECTION instead is exact - it is already the full page
 * width - so the margin is pulled from the column's own edge outwards.
 */
/* The film is a direct child of the section now rather than a child of the 1280px
   reading column. Forcing the column's cap off instead freed every OTHER child too,
   and the copy went to 1872px at 1920 - a 300-character measure. Moving one element
   out of the column is the fix; widening the column is not.
   Its caption travels with it, or the credit line floats under nothing. */
.v3-film, .v3-film + p {
    width: min(100%, 1760px);
    margin-inline: auto;
}

/* ── The four headline figures ───────────────────────────────────────────────────────
 * They were on repeat(auto-fit, minmax(min(100%, 320px), 1fr)). Four 320px tracks plus
 * three 32px gaps need 1376px, and the container is 1280, so auto-fit gave three and
 * dropped the fourth onto a row of its own. The four are one claim in four parts, and
 * three-and-one reads as a layout accident.
 *
 * Explicit counts instead of a minimum width, because the count is the design decision:
 * one row on a laptop, two on a tablet, four on a phone held upright. A phone on its
 * side keeps two, which is what the width there affords.
 */
.v3-stats { grid-template-columns: repeat(4, minmax(0, 1fr)) }
@media (max-width: 1023px) { .v3-stats { grid-template-columns: repeat(2, minmax(0, 1fr)) } }
@media (max-width: 640px) and (orientation: portrait) { .v3-stats { grid-template-columns: minmax(0, 1fr) } }

/* ── The film's own box ──────────────────────────────────────────────────────────────
 * The section used to be a full-bleed slab of #0B0B0C, which cut a hard black band
 * across a light page for the length of one figure. The dark belongs to the
 * instrument, not to the page around it: the section is light like its neighbours and
 * the black is confined to the frame, with a radius and a shadow so it reads as a
 * screen set into the page rather than a hole cut out of it.
 */
.v3-film {
    border-radius: 18px;
    border-color: rgba(11, 11, 12, .14) !important;
    box-shadow: 0 2px 6px rgba(11, 11, 12, .06), 0 30px 70px -30px rgba(11, 11, 12, .35);
}
/* The caption sat on black in white; on a light section it takes the page's own
   quiet ink. */
.v3-film + p { color: rgb(120, 120, 126) !important }

/* ── The footer ──────────────────────────────────────────────────────────────────────
 * Three link columns were on repeat(auto-fit, minmax(min(100%, 200px), 1fr)) against a
 * 1280px box, which resolved to `400px 400px 400px 0px 0px`: three tracks far wider
 * than the four or five short links in them, plus two phantom zero-width tracks. The
 * links ended up marooned on the left of enormous columns.
 *
 * Named tracks instead. The brand block keeps the left, and the three lists sit where
 * a reader expects a sitemap: pushed right, sized to their content, not to a third of
 * the page each.
 */
/* The previous pass gave the three lists `max-content` tracks under
   `justify-content: space-between`. In a 1280px box those tracks come out around 120px
   each, so roughly 900px of free space was divided into two gaps: three thin lists
   marooned at the far left, the middle and the far right, with voids between them. The
   fix is not a smaller gap, it is a different shape.

   A masthead. The brand block holds the left, the sitemap holds the right as three even
   columns, and the two sit on ONE row instead of stacking a wordmark above a lonely
   strip of links. That is the arrangement a reader has seen on every site they have
   ever scrolled to the bottom of, which is the whole reason it works. */
.v3-foot-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    gap: clamp(36px, 5vw, 88px);
    align-items: start;
}
.v3-foot-brand p { max-width: 54ch }
.v3-foot-cols {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: clamp(20px, 2.4vw, 40px);
    row-gap: 36px;
}
/* One row cannot hold a paragraph and a sitemap much below a laptop. Below that the
   brand goes back on top, and the three lists keep their even columns. */
@media (max-width: 1040px) {
    .v3-foot-grid { grid-template-columns: minmax(0, 1fr); gap: 48px }
    .v3-foot-brand p { max-width: 62ch }
}
@media (max-width: 520px) {
    .v3-foot-cols { grid-template-columns: repeat(2, minmax(0, 1fr)) }
}

/* 12px grey links on a dark ground were the smallest type on the page and the hardest
   to hit. 13px, a taller line box, and a hover that actually acknowledges the pointer. */
.v3-foot-cols a, .v3-foot-cols span { font-size: 13px; line-height: 1.5 }
.v3-foot-cols a { transition: color .18s ease }
.v3-foot-cols a:hover, .v3-foot-cols a:focus-visible { color: rgb(255, 255, 255) !important }
.v3-foot-cols > div > div:last-child { gap: 11px !important }

/* The legal bar was sitting directly on the last link, and the previous fix reached for
   the footer's padding-bottom - which lands BELOW the bar, not above it. So the crowding
   stayed and a hundred pixels of empty dark appeared under the copyright line instead.
   The gap belongs to the bar. It takes it itself. */
.definePages footer:not(.ins-legal-footer) { padding-bottom: 0 !important }
.definePages footer:not(.ins-legal-footer) .ins-legal-footer {
    margin-top: clamp(40px, 5vw, 64px) !important;
    /* The rest of the page's dark run ends here. Inheriting the light bar's own white
       is correct; what it must not do is sit on a hard edge, so it keeps its rule. */
    border-top-color: rgba(255, 255, 255, .14);
}

/* ── The aerosol card ────────────────────────────────────────────────────────────────
 * Same layering as the cover: a procedural raster with a vector plan registered over it.
 * The two share a viewBox aspect (720x960) so `slice` crops both identically and the
 * labels cannot drift off the thing they name.
 *
 * Its own type scale, not the cover's. The cover's plan draws into a 1600-wide viewBox
 * at roughly 1600 screen pixels, so 15px in user units is 15px on the glass. This one
 * draws into a 720-wide viewBox inside a card about a third of a page across, so the
 * same 15px would arrive at around 7. Sized in user units to land near 12px at the
 * card's real width instead.
 */
.v3-aero-plan { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none }
.v3-aero-plan .pl-box { fill: none; stroke: rgba(255, 255, 255, .36); stroke-width: 2 }
.v3-aero-plan .pl-box.is-thin { stroke: rgba(255, 255, 255, .30); stroke-dasharray: 10 7 }
.v3-aero-plan .pl-key { fill: none; stroke: #ffffff; stroke-width: 3 }
.v3-aero-plan .pl-ring { fill: none; stroke: rgba(255, 255, 255, .55); stroke-width: 2; stroke-dasharray: 9 7 }
.v3-aero-plan .pl-arrow { stroke: #ffffff; stroke-width: 3; stroke-linecap: round }
.v3-aero-plan .pl-head { fill: #ffffff }
.v3-aero-plan .pl-t {
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 24px; letter-spacing: .07em;
    fill: rgba(255, 255, 255, .94);
    /* The plume runs from a saturated white core to near-black clean air, so a caption
       crosses the whole ramp. paint-order puts the halo behind the glyph, which is the
       only way a single ink stays legible over both ends of it. */
    stroke: rgba(11, 11, 12, .72); stroke-width: 5; paint-order: stroke fill;
}
.v3-aero-plan .pl-t.is-key { fill: #ffffff }

/* ── The simulator chart ─────────────────────────────────────────────────────────────
 * The plot shipped as a bare 100x40 viewBox stretched edge to edge: a curve with no
 * scale, no units and nothing to say what a value of 52 was 52 OF. The stretch is worth
 * keeping - it is what lets the forecast fill the panel at any width - so the numbers
 * go around the plot in HTML rather than inside it, where preserveAspectRatio="none"
 * would squash every glyph by whatever ratio the panel happened to be.
 *
 * Both halves position off one function, yFrac() in define-landing-v3.js, so a tick
 * label and the gridline it names cannot come apart.
 */
.v3-chart {
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr) 62px;
    grid-template-rows: clamp(168px, 23vw, 220px) auto;
    column-gap: 10px;
}
.v3-chart-y { position: relative; grid-column: 1; grid-row: 1 }
.v3-chart-y span {
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 10px; color: rgba(255, 255, 255, .55);
}
.v3-chart-plot {
    position: relative; grid-column: 2; grid-row: 1;
    border-left: 1px solid rgba(255, 255, 255, .25);
    border-bottom: 1px solid rgba(255, 255, 255, .25);
}
.v3-chart-plot svg { display: block; width: 100%; height: 100% }

/* The band names are the units. "52" means nothing; "52, Elevated, and here is where
   Elevated starts" is the whole reading. They sit at each band's midpoint, against the
   dashed thresholds the plot draws at 35, 50 and 68.
   In their OWN COLUMN, not inside the plot: laid over it they landed on the curve, and
   a key that obscures the thing it is explaining is worse than no key. */
.v3-chart-key { position: relative; grid-column: 3; grid-row: 1; pointer-events: none }
.v3-chart-key span {
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
    color: rgba(255, 255, 255, .45); white-space: nowrap;
}
/* The scrubber moves a vertical rule across the plot and nothing said which month it
   had landed on. */
.v3-chart-hmark {
    position: absolute; top: 6px; transform: translateX(-50%);
    background: rgba(128, 50, 216, .95); color: #fff;
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 9px; letter-spacing: .12em; padding: 3px 7px; border-radius: 2px;
    white-space: nowrap; pointer-events: none;
}
/* ABSOLUTE, not space-between. Flex spreads whatever labels are left evenly, so the
   moment a phone drops one the remaining years slide to positions that are not their
   own: with three left, 2034 ends up hard against the right edge where 2036 belongs,
   and the axis quietly lies about where the forecast ends. Each year is pinned to its
   own fraction of the horizon instead, so dropping any of them moves none of the rest.
   The two at the ends align inward so they cannot hang off the plot. */
.v3-chart-x {
    position: relative; grid-column: 2; grid-row: 2; height: 13px;
    margin-top: 9px;
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: 10px; color: rgba(255, 255, 255, .55);
}
.v3-chart-x span { position: absolute; top: 0; transform: translateX(-50%) }
.v3-chart-x span:nth-child(1) { left: 0; transform: none }
.v3-chart-x span:nth-child(2) { left: 20% }
.v3-chart-x span:nth-child(3) { left: 40% }
.v3-chart-x span:nth-child(4) { left: 60% }
.v3-chart-x span:nth-child(5) { left: 80% }
.v3-chart-x span:nth-child(6) { left: 100%; transform: translateX(-100%) }

/* Six year labels will not fit across a phone. Every other one still carries the scale,
   and the last one stays: an axis that stops at 2034 on a forecast that runs to 2036 is
   worse than a sparse one. */
@media (max-width: 560px) {
    .v3-chart-x span:nth-child(2), .v3-chart-x span:nth-child(4) { display: none }
    /* Narrower again and 2034 crowds 2036 at the right-hand end. Three labels: the start,
       the middle and the end, which is all a scale of this size can carry. */
    @media (max-width: 480px) { .v3-chart-x span:nth-child(5) { display: none } }
    /* The band key needs 62px it does not have on a phone. The dashed thresholds stay,
       and the numeric scale on the left carries the reading on its own. */
    .v3-chart { grid-template-columns: 26px minmax(0, 1fr) }
    .v3-chart-key { display: none }
}

/* Four hazards, two by two. */
.v3-haz { grid-template-columns: repeat(2, minmax(0, 1fr)) }
@media (max-width: 420px) { .v3-haz { grid-template-columns: minmax(0, 1fr) } }

/* ── The simulator's two halves ──────────────────────────────────────────────────────
 * Both grids were on `auto-fit, minmax(...)`, which is the right tool when the items
 * are interchangeable and the wrong one here. The reading half and the chart half went
 * side by side at any width over about 620px, so on a tablet the plot got roughly two
 * hundred pixels to carry a ten year axis, five tick labels and a band key - and the
 * years on the axis ran into each other. The four tiles under it wrapped three and one.
 *
 * A chart with a scale needs a width, not a fraction. Below a laptop the halves stack
 * and the plot takes the whole panel, which is more readable than two cramped columns.
 */
.v3-sim-split { grid-template-columns: repeat(2, minmax(0, 1fr)) }
.v3-sim-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)) }
@media (max-width: 1180px) {
    .v3-sim-split { grid-template-columns: minmax(0, 1fr) }
    .v3-sim-split > div:first-child {
        border-right: 0 !important;
        border-bottom: 1px solid rgba(255, 255, 255, .16);
    }
    .v3-sim-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)) }
    .v3-sim-tiles > div:nth-child(2) { border-right: 0 !important }
    .v3-sim-tiles > div:nth-child(-n+2) { border-bottom: 1px solid rgba(255, 255, 255, .16) }
}
@media (max-width: 620px) {
    .v3-sim-tiles { grid-template-columns: minmax(0, 1fr) }
    .v3-sim-tiles > div { border-right: 0 !important }
    .v3-sim-tiles > div:not(:last-child) { border-bottom: 1px solid rgba(255, 255, 255, .16) }
}

/* ════════════════════════════════════════════════════════════════════════════════════
 * RESPONSIVE PASS
 * Everything below is about the widths this page was not designed at. The layout came
 * from a desktop mock-up and holds from roughly a laptop up; a phone, a tablet and a
 * television each broke it in a different way, and each needs a different answer rather
 * than one more breakpoint on the same idea.
 * ════════════════════════════════════════════════════════════════════════════════════ */

/* ── The cover, on a phone ───────────────────────────────────────────────────────────
 * The hero scrim is a 100deg gradient: nearly opaque on the left where the headline
 * sits, thinning to a third across to the right where the raster is meant to show
 * through. That works because on a laptop the text occupies the left half and the
 * picture has the right half to itself.
 *
 * On a phone the text occupies ALL of it, so the gradient's dark end covers the whole
 * column and the only raster anyone ever saw was a purple slither down the right edge.
 * The page's cover image was, in effect, invisible on the device most people open it on.
 *
 * So on a narrow screen the cover stops being a backdrop and becomes a BAND: a fixed
 * height at the top of the hero with the type below it, which is how a phone hero is
 * built everywhere. The band's own shape matters as much as its presence - the frame
 * cropped to 390x800 shows a 22% vertical slice of a 16:9 raster and reads as abstract
 * noise; cropped to 390x400 it shows nearly half the works, which reads as a works.
 */
@media (max-width: 899px) {
    #hero { padding-top: 0 !important }
    .v3-cover-frame {
        top: 0; bottom: auto;
        height: clamp(230px, 42vw + 110px, 380px);
    }
    .v3-hero-scrim {
        background: linear-gradient(180deg,
            rgba(11, 11, 12, .18) 0%,
            rgba(11, 11, 12, .32) 46%,
            rgba(11, 11, 12, .86) 74%,
            rgb(11, 11, 12) 88%) !important;
    }
    /* The type starts below the band. clamp() has to agree with the frame height above:
       the band, plus the distance the gradient needs to reach full opacity. */
    #hero > .v3-wrap { padding-top: clamp(196px, 42vw + 76px, 346px) }

    /* The plan labels were hidden below 1099px because on a squeezed full-bleed cover
       they landed on the headline. In a band with no type over it they have the frame
       to themselves again, and they are the thing that says this is a measurement and
       not a stock photograph. Sized for the band's own scale: the SVG is a 1600-unit
       viewBox drawn at about 390 real pixels, so 15 units would arrive at about 7. */
    /* The band ends on a ruled line otherwise: the frame stops, the hero's own black
       begins, and the seam runs straight across the screen. Masked on the RASTER layers
       only - put it on the frame and it takes the plan's captions with it, and BF-2 sits
       low enough in the crop to be the one it fades. */
    .v3-cover-frame > img, .v3-cover-frame > canvas {
        -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 66%, transparent 99%);
        mask-image: linear-gradient(180deg, #000 0%, #000 66%, transparent 99%);
    }

    .v3-cover-plan { display: block }
    /* `slice` crops the 1600-unit viewBox to about 1170 units of it at this width, so the
       two captions furthest from the centre fall outside the visible frame and arrive cut
       in half by its edge. The works keeps its outlines; it is the text that goes. */
    .v3-cover-plan .pl-t[data-l="HOT STRIP MILL"],
    .v3-cover-plan .pl-t[data-l="COKE BATTERY"],
    .v3-cover-plan .pl-t[data-l="ORE AND COAL STOCKYARD"] { display: none }
    .v3-cover-plan .pl-t { font-size: 34px; stroke-width: 9 }
    .v3-cover-plan .pl-t.is-key { stroke-width: 10 }
    .v3-cover-plan .pl-box { stroke-width: 3 }
    .v3-cover-plan .pl-key { stroke-width: 4 }
}

/* ── Chip strips ─────────────────────────────────────────────────────────────────────
 * Five audiences and six assets, each a button wide enough to hold a phrase. Wrapped,
 * that is five rows on a phone for the audiences and six for the assets: the reader
 * scrolls a screen and a half of buttons, taps one, and the panel it controls is now
 * off the bottom of the screen. The control and its result were never visible at once,
 * which is the whole complaint.
 *
 * One row that scrolls sideways, with snap. The panel stays directly under the strip at
 * every width, so tapping a chip changes something the reader can see. The strip bleeds
 * to both screen edges because a scroller that stops short of the edge does not look
 * like a scroller - the half-chip cut off by the edge is the affordance.
 */
@media (max-width: 899px) {
    .v3-chipstrip {
        flex-wrap: nowrap !important;
        overflow-x: auto;
        overscroll-behavior-x: contain;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        /* Out to the section's 24px gutter and back again, so the first chip still
           lines up with the headline above it. */
        margin-left: -24px; margin-right: -24px;
        padding-left: 24px; padding-right: 24px;
        scroll-padding-left: 24px;
    }
    .v3-chipstrip::-webkit-scrollbar { display: none }
    .v3-chipstrip > button {
        flex: 0 0 auto;
        white-space: nowrap;
    }
    /* NO scroll-snap. It was here first and it was actively wrong: after keepInView()
       writes scrollLeft to bring a chip fully inside the strip, snap re-settles the
       scroller on its nearest snap point and undoes exactly that, so the chips needing
       the smallest correction - the ones in the middle of the strip - were the ones
       reliably left half off the edge. Snap buys very little on a row of chips and costs
       the one behaviour this needs, which is landing where it was told to. */
    /* The asset chips carry a second line of spec under the name, which cannot be
       nowrap or it doubles the chip's width for no gain. */
    .v3-chipstrip.is-assets > button > span { white-space: normal }
    .v3-chipstrip.is-assets > button { max-width: 74vw }
    /* The simulator's strip bleeds to the panel's padding, not the section's. */
    .v3-slab-in .v3-chipstrip {
        margin-left: calc(-1 * clamp(22px, 3.4vw, 56px));
        margin-right: calc(-1 * clamp(22px, 3.4vw, 56px));
        padding-left: clamp(22px, 3.4vw, 56px);
        padding-right: clamp(22px, 3.4vw, 56px);
        scroll-padding-left: clamp(22px, 3.4vw, 56px);
    }
}
/* Above the strip breakpoint both sets lay out as rows of equal chips. The asset picker
   was on auto-fit, which put six items into five columns plus one stranded underneath
   at 1920 and 2560 - the same wrap-and-strand fault the stats strip and the hazard bars
   both had. Counted columns, not fitted ones. */
@media (min-width: 900px) {
    .v3-chipstrip.is-assets { display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)) }
}
@media (min-width: 1180px) {
    .v3-chipstrip.is-assets { grid-template-columns: repeat(6, minmax(0, 1fr)) }
}

/* ── The film, on a phone ────────────────────────────────────────────────────────────
 * The observation film is a mapbox globe with a dossier panel, a caption rail and a
 * progress bar, and its whole type ramp is derived from viewport WIDTH. In a 16:9 frame
 * on a 390px screen that is 342 by 192 real pixels: the dossier is wider than the
 * frame, the captions are a few pixels tall, and the thing a reader sees is a smear.
 *
 * It is not a layout to fix at that size, it is a layout that needs a screen. So on a
 * phone the frame shows the still and the invitation that were already sitting under
 * the iframe for the case where the frame is refused, and tapping it opens the film
 * full-bleed on its own page where it has the width it was built for. The iframe itself
 * is REMOVED rather than hidden, in define-landing-v3.js - display:none still loads it,
 * and a mapbox scene fetched to be hidden is somebody's data and battery.
 */
@media (max-width: 767px) {
    /* The ratio is an inline style on the element, so the override has to say so. */
    .v3-film { aspect-ratio: 4 / 3 !important }
    .v3-film > a > img { opacity: .72 !important }
    .v3-film > a > span { font-size: 11px !important; white-space: normal !important; text-align: center; max-width: 78% }
}

/* ── Very wide screens ───────────────────────────────────────────────────────────────
 * Every section is measured to 1280px, which is right on a laptop and is a ribbon down
 * the middle of a television. The measure grows, but not without limit and not as fast
 * as the screen: prose past about 100 characters a line stops being readable however
 * much room there is, so the sections that are mostly type stay where they are and the
 * ones that are mostly figure - the simulator, the tables, the card grids - get the
 * room. 1480 at 1700px up, which is about 92 characters on this page's body size.
 */
@media (min-width: 1700px) {
    .v3-wrap { max-width: 1480px !important }
}
@media (min-width: 2200px) {
    .v3-wrap { max-width: 1640px !important }
}
