/* ══════════════════════════════════════════════════════════════════════════
   Browser History Reader — application layer.

   modernist.css is the design system and is the source of truth for colour,
   type, spacing, radius and elevation. It is vendored verbatim from the design
   handoff and must not be edited here.

   This file adds only the page-level patterns the handoff uses repeatedly that
   the component layer does not carry: the page frame, the numbered rows, the
   poster banner, the dropzone and the report charts. Everything below resolves
   through the system's tokens — no literal colours, no font names.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
    /* ---------------------------------------------------------------------
       Vertical rhythm.

       The system's --space-* scale runs 4/8/12/16/24/32 and covers component
       padding. The handoff's page rhythm is a separate 14px series
       (14/28/42/56/70/84/112) used for section padding and stack gaps, so it
       is declared once here rather than as literals in each rule.
       --------------------------------------------------------------------- */
    --rhythm: 14px;
    --rhythm-1: var(--rhythm);
    /* The system scale skips 20px; derive it rather than writing the literal. */
    --space-5: calc(var(--space-4) + var(--space-1));
    --rhythm-2: calc(var(--rhythm) * 2);
    --rhythm-3: calc(var(--rhythm) * 3);
    --rhythm-4: calc(var(--rhythm) * 4);
    --rhythm-5: calc(var(--rhythm) * 5);
    --rhythm-6: calc(var(--rhythm) * 6);
    --rhythm-8: calc(var(--rhythm) * 8);

    /* Type sizes the handoff uses that sit between the system's heading steps. */
    --text-body: 17px;
    --text-prose: 15.5px;
    --text-ui: 14px;
    --text-label: 13px;
    --text-sub: 24px;

    --line-body: 28px;
    --line-tight: 20px;

    /* Page frame. */
    --page-max: 1200px;
    --page-gutter: clamp(20px, 5vw, 72px);

    /* Ink at reduced strength. Derived, never a second grey token. */
    --ink-78: color-mix(in srgb, var(--color-text) 78%, transparent);
    --ink-70: color-mix(in srgb, var(--color-text) 70%, transparent);
    --ink-65: color-mix(in srgb, var(--color-text) 65%, transparent);
    --ink-60: color-mix(in srgb, var(--color-text) 60%, transparent);
}

html {
    scroll-behavior: smooth;
}

body {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--line-body);
    text-wrap: pretty;
    -webkit-font-smoothing: antialiased;
    /* Column flex so the footer's margin-top:auto pins it to the viewport floor
       on short pages without the footer overlapping content on long ones. */
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

main {
    flex: 1;
}

a:hover {
    color: var(--color-accent-700);
}

/*
   Off-screen until focused. Not display:none, which would take it out of the
   tab order and defeat the point.
*/
.skip-link {
    position: absolute;
    left: var(--space-3);
    top: calc(var(--space-3) * -10);
    z-index: 30;
    background: var(--color-bg);
    transition: top 120ms ease-out;
}

.skip-link:focus-visible {
    top: var(--space-3);
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* ── page frame ───────────────────────────────────────────────────────── */

.page {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: 0 var(--page-gutter);
}

/* A section rule that sits flush, unlike the system's .hr which carries margin. */
.rule {
    height: 2px;
    border: 0;
    margin: 0;
    background: var(--color-divider);
}

.section {
    padding: var(--rhythm-6) 0 var(--rhythm-5);
}

.section-tight {
    padding: var(--rhythm-3) 0;
}

.section-hero {
    padding: var(--rhythm-8) 0 var(--rhythm-6);
}

/* Section with a heavier top than bottom, used where a rule follows closely. */
.section-alt {
    padding: var(--rhythm-5) 0 var(--rhythm-6);
}

.section-upload {
    padding: var(--rhythm-6) 0;
}

.hero-upload {
    padding-bottom: var(--rhythm-4);
}

/* The three claims under the hero buttons, spaced apart rather than a list. */
.hero-claims {
    display: flex;
    gap: var(--space-5);
    flex-wrap: wrap;
    line-height: var(--rhythm-1);
    margin-top: var(--rhythm-2);
}

/* ── type ─────────────────────────────────────────────────────────────── */

/*
   Display headings hang the first character's side bearing into the gutter so
   the stem aligns optically with the body text below it. The handoff sets this
   as a negative left margin in em, which scales with the clamp.
*/
.display {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: clamp(42px, 6.2vw, 84px);
    line-height: 1.06;
    letter-spacing: -0.02em;
    margin: 0 0 0 -0.058em;
}

.display-2 {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: clamp(38px, 4.8vw, 64px);
    line-height: 1.06;
    letter-spacing: -0.02em;
    margin: 0 0 0 -0.058em;
}

/* Display headings are set as explicit lines, one span each. */
.display span,
.display-2 span {
    display: block;
}

.section-title {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 32px;
    line-height: 42px;
    letter-spacing: -0.015em;
    margin: 0 0 var(--rhythm-2);
}

/* Heading that sits directly above prose rather than above a block. */
.section-title-flush {
    margin-bottom: 0;
}

.section-title-sm {
    font-size: 25px;
    line-height: 32px;
    margin-bottom: var(--rhythm-2);
}

.prose-narrow {
    max-width: 48ch;
    margin-top: var(--space-5);
}

.sub-title {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: var(--text-sub);
    line-height: var(--line-body);
    letter-spacing: -0.01em;
    margin: 0;
}

/* The uppercase accent label that opens most sections. */
.kicker {
    display: block;
    font-size: var(--text-label);
    line-height: var(--rhythm-1);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-accent-700);
    margin: 0 0 var(--rhythm-1);
}

.kicker-lg {
    margin-bottom: var(--rhythm-3);
}

/* Same treatment in ink rather than accent — used for stat captions. */
.label {
    font-size: var(--text-label);
    line-height: var(--line-tight);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-70);
    margin: 0;
}

.lede {
    font-size: var(--text-body);
    line-height: var(--line-body);
    max-width: 58ch;
    margin: var(--rhythm-3) 0 0;
}

.prose {
    font-size: var(--text-prose);
    line-height: var(--line-body);
    color: var(--ink-78);
    max-width: 52ch;
    margin: 0;
}

.note {
    font-size: var(--text-label);
    line-height: var(--line-tight);
    color: var(--ink-60);
    margin: var(--space-3) 0 0;
}

.num {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 15px;
    line-height: var(--line-body);
    margin: 0;
}

.num-accent {
    color: var(--color-accent);
}

/* ── actions ──────────────────────────────────────────────────────────── */

.actions {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: var(--rhythm-2);
}

.actions-lg {
    margin-top: var(--rhythm-3);
}

/*
   A nav or footer control that is semantically a button but reads as a link.
   Declared once so no page repeats the reset inline.
*/
.linkbutton {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    font: inherit;
    color: inherit;
    text-align: left;
}

.linkbutton:hover {
    color: var(--color-accent);
}

.linkbutton-accent {
    color: var(--color-accent-700);
    letter-spacing: inherit;
    text-transform: inherit;
}

/* ── navigation ───────────────────────────────────────────────────────── */

.nav {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--color-bg);
    padding-inline: max(var(--page-gutter), calc((100% - var(--page-max)) / 2 + var(--page-gutter)));
}

.nav-links {
    display: flex;
    align-items: center;
    gap: var(--space-6);
}

/*
   :not(.btn) matters. The system's `.nav a` rule is more specific than `.btn`,
   so without this exclusion the nav's primary CTA would take the nav's ink
   colour and body font size instead of the button's ground-on-accent and 13px.
*/
.nav-links a:not(.btn),
.nav-links .linkbutton {
    font-family: var(--font-body);
    font-size: var(--text-ui);
    text-decoration: none;
}

/* Belt and braces: beat `.nav a { color: inherit }` on the filled button. */
.nav a.btn-primary {
    color: var(--color-bg);
}

/*
   The "Other sources" nav menu. A <details> rather than a click-tracked widget — it opens and
   closes on its own, and Enter/Space on the summary work without a script. It does
   not close on an outside click or Escape, which a scripted menu would; accepted
   here the same way .step-more accepts it elsewhere in the system.
*/
.nav-menu {
    position: relative;
}

.nav-menu > summary {
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    cursor: pointer;
    font-family: var(--font-body);
    font-size: var(--text-ui);
    color: inherit;
}

.nav-menu > summary::-webkit-details-marker {
    display: none;
}

.nav-menu > summary:hover,
.nav-menu > summary[aria-current='page'] {
    color: var(--color-accent);
}

.nav-menu > summary:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* Same turned-square marker as .step-more, pointing down instead of right so it
   reads as a menu chevron rather than an inline disclosure. */
.nav-menu > summary::after {
    content: "";
    width: 0.4em;
    height: 0.4em;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 140ms ease-out;
}

.nav-menu[open] > summary::after {
    transform: rotate(-135deg);
}

.nav-menu-panel {
    position: absolute;
    top: calc(100% + var(--space-2));
    left: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 12rem;
    padding: var(--space-3);
    background: var(--color-bg);
    border: 1px solid var(--color-divider);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.nav-menu-panel a {
    padding-block: var(--space-1);
}

@media (prefers-reduced-motion: reduce) {
    .nav-menu > summary::after {
        transition: none;
    }
}

/* The brand is a link to home, but must not pick up the accent link colour. */
.nav-brand a {
    color: inherit;
    text-decoration: none;
}

@media (max-width: 720px) {
    .nav {
        flex-wrap: wrap;
        row-gap: var(--space-2);
    }

    .nav-links {
        gap: var(--space-4);
        flex-wrap: wrap;
    }

    /* The nav wraps at this width, so a floating popover would run off the edge
       of a narrow viewport. Let it open in place instead. */
    .nav-menu-panel {
        position: static;
        margin-top: var(--space-2);
        padding: 0 0 0 var(--space-3);
        border: none;
        box-shadow: none;
    }
}

/* ── footer ───────────────────────────────────────────────────────────── */

.site-footer {
    border-top: 2px solid var(--color-divider);
    margin-top: auto;
}

.site-footer .page {
    display: flex;
    gap: var(--rhythm-2);
    flex-wrap: wrap;
    align-items: baseline;
    padding-block: var(--rhythm-3);
    font-size: var(--text-label);
    line-height: var(--line-body);
    color: var(--ink-70);
}

.site-footer .footer-brand {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 15px;
    color: var(--color-text);
    margin-right: auto;
}

/* ── breadcrumb ───────────────────────────────────────────────────────── */

.crumbs {
    display: flex;
    gap: var(--space-2);
    font-size: var(--text-label);
    line-height: var(--rhythm-1);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-60);
    margin: 0 0 var(--rhythm-2);
}

/* ── stat band ────────────────────────────────────────────────────────── */

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--rhythm-3) var(--rhythm-2);
}

.stat-figure {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: clamp(34px, 3.4vw, 48px);
    line-height: 56px;
    color: var(--color-accent);
    margin: 0 0 0 -0.045em;
}

.stat-grid .label {
    margin-top: var(--space-2);
}

/* ── numbered rows ────────────────────────────────────────────────────── */

/*
   The handoff's signature block: a wide number column, a fixed-width heading
   column and a flexible body column, separated by 2px rules. Baseline-aligned
   so the number, the heading and the first line of body share a baseline.
*/
.numbered {
    display: grid;
    grid-template-columns: minmax(64px, 120px) minmax(0, 380px) minmax(0, 1fr);
    gap: var(--rhythm-2) clamp(24px, 4vw, 72px);
    align-items: baseline;
    padding: var(--rhythm-3) 0;
}

.numbered + .numbered {
    border-top: 2px solid var(--color-divider);
}

@media (max-width: 860px) {
    .numbered {
        grid-template-columns: minmax(48px, 64px) minmax(0, 1fr);
    }

    .numbered .prose {
        grid-column: 2;
    }
}

/* ── step cards ───────────────────────────────────────────────────────── */

.step-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--rhythm-2);
}

.step {
    border-top: 2px solid var(--color-divider);
    padding-top: var(--space-5);
}

.step h3 {
    font-size: 20px;
    line-height: var(--line-body);
    margin: 0 0 var(--space-2);
}

.step .num {
    margin-bottom: var(--space-2);
}

.step .prose {
    max-width: none;
}

/* Vertically stacked steps with screenshot slots, on the Takeout guide. */
.step-row {
    border-top: 2px solid var(--color-divider);
    padding: var(--rhythm-2) 0;
}

.step-row:last-child {
    border-bottom: 2px solid var(--color-divider);
}

.step-row .sub-title {
    margin: 0 0 var(--space-3);
}

.step-row .prose {
    max-width: 56ch;
    margin-bottom: var(--space-5);
}

/* ── split layouts ────────────────────────────────────────────────────── */

.split {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: var(--rhythm-2) clamp(24px, 5vw, 96px);
    align-items: start;
}

.split-wide-first {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    gap: var(--rhythm-2) clamp(28px, 5vw, 80px);
}

.split-even {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--rhythm-3) clamp(28px, 5vw, 72px);
}

@media (max-width: 860px) {
    .split,
    .split-wide-first {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ── bordered callout / sidebar ───────────────────────────────────────── */

.callout {
    border: 2px solid var(--color-divider);
    padding: var(--space-6);
}

.callout-title {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 25px;
    line-height: 32px;
    letter-spacing: -0.015em;
    margin: 0 0 var(--space-5);
}

.callout-field {
    margin-bottom: var(--space-5);
}

/* Page heading block, lighter than a hero. */
.section-page-head {
    padding: var(--rhythm-4) 0 var(--rhythm-3);
}

.takeout-body {
    padding: var(--rhythm-4) 0 var(--rhythm-6);
}

/*
   The Takeout walkthrough: one column, read top to bottom, held to a prose
   measure. It was a .split with the upload sticky in the right-hand column,
   which turned a sequence of seven steps into two things happening at once.
   Nothing here is a grid — the order in the markup is the order on the page.
*/
.takeout-flow {
    max-width: 46rem;
}

/*
   Detail on demand: the screenshots and the longer explanations, behind a
   disclosure so the default view is the numbered list.

   The native marker is dropped for a chevron drawn from two borders, because
   the UA triangle cannot be coloured or animated and sits at a different
   indent in every browser. Only explanatory content collapses — the file
   input and the time zone select are never inside one of these.
*/
.step-more {
    margin-top: var(--space-5);
}

.step-more > summary {
    /* Three things suppress the same triangle, and which one does the work
       depends on the browser: list-style for anything following the current
       spec, the ::-webkit-details-marker rule below for older WebKit, and
       taking summary off display:list-item for Blink. */
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    width: fit-content;
    padding: var(--space-2) 0;
    cursor: pointer;
    font-size: var(--text-label);
    line-height: var(--line-tight);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-accent);
}

.step-more > summary::-webkit-details-marker {
    display: none;
}

/* A square with two borders, turned. Pointing right when closed, down when
   open — the same rotation the rest of the system uses for a disclosure. */
.step-more > summary::before {
    content: "";
    width: 0.5em;
    height: 0.5em;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform 140ms ease-out;
}

.step-more[open] > summary::before {
    transform: rotate(45deg);
}

.step-more > summary:hover {
    color: var(--color-accent-700);
}

.step-more > summary:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* The revealed content, whatever it is — a figure, a hint, a paragraph. */
.step-more > :not(summary) {
    margin-top: var(--space-3);
}

@media (prefers-reduced-motion: reduce) {
    .step-more > summary::before {
        transition: none;
    }
}

/*
   The upload now leads the callout, so the time zone field needs the gap that
   used to sit under it. The step-row's own top rule does not apply in here.
*/
.takeout-flow .callout-field {
    margin-top: var(--space-6);
}

/*
   The callout leads the page and the walkthrough follows it, so it needs the
   space a section gets rather than the space a sidebar got. The first
   .step-row draws its own rule underneath this.
*/
.takeout-flow .callout {
    margin-bottom: var(--rhythm-3);
}

/* Prose that fills its container rather than holding a measure. */
.prose-full {
    max-width: none;
    font-size: var(--text-ui);
    line-height: var(--space-6);
}

.sticky-aside {
    position: sticky;
    top: 96px;
}

@media (max-width: 860px) {
    .sticky-aside {
        position: static;
    }
}

/* ── tile grid ────────────────────────────────────────────────────────── */

/*
   Cards butted together with a 2px divider showing through the gap, so the
   grid reads as ruled cells rather than floating cards.
*/
.tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 2px;
    background: var(--color-divider);
}

.tile-grid .card {
    padding: var(--space-5);
    gap: var(--space-2);
}

.tile-grid .card-body {
    font-size: var(--text-ui);
    line-height: var(--space-6);
}

.tile-grid a {
    font-size: var(--text-ui);
}

/* ── poster banner ────────────────────────────────────────────────────── */

/*
   The one place the accent runs as a field. Type stays display-grade and the
   ground inverts, so descendant controls need the ground colour explicitly.
*/
.poster {
    background: var(--color-accent);
    color: var(--color-bg);
}

.poster .page {
    padding-block: var(--rhythm-6);
}

.poster h2 {
    font-size: clamp(34px, 4.2vw, 56px);
    line-height: 1.06;
    letter-spacing: -0.015em;
    margin: 0 0 0 -0.058em;
}

.poster h2 span {
    display: block;
}

.poster .btn-ghost {
    color: var(--color-bg);
    border-color: var(--color-bg);
}

.poster .btn-ghost:hover {
    background: color-mix(in srgb, var(--color-bg) 18%, transparent);
}

.poster .btn-ghost:active {
    background: color-mix(in srgb, var(--color-bg) 28%, transparent);
}

.poster :focus-visible {
    outline-color: var(--color-bg);
}

/* ── dropzone ─────────────────────────────────────────────────────────── */

.dropzone {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: var(--space-3);
    padding: var(--rhythm-3) var(--rhythm-2);
    cursor: pointer;
    border: 2px dashed var(--color-divider);
    background: var(--color-surface);
    position: relative;
}

/* Drag-over tint. Also applied by :focus-within so keyboard users see it. */
.dropzone.is-over,
.dropzone:focus-within {
    border-color: var(--color-accent);
    background: var(--color-accent-100);
}

.dropzone h2 {
    font-size: 25px;
    line-height: 32px;
    letter-spacing: -0.015em;
    margin: 0;
}

.dropzone p {
    font-size: var(--text-prose);
    line-height: var(--line-body);
    margin: 0;
    color: var(--ink-78);
    max-width: 52ch;
}

/*
   The input stays in the layout and focusable — visually hidden rather than
   display:none — so the control is reachable by keyboard and the label's
   :focus-within tint fires.
*/
.dropzone input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.dropzone-compact {
    padding: var(--space-6) var(--space-5);
}

.dropzone-compact h2 {
    font-size: 20px;
    line-height: var(--line-body);
}

.dropzone-chosen {
    border: 2px solid var(--color-divider);
    padding: var(--space-6);
}

.dropzone-chosen-head {
    display: flex;
    gap: var(--space-4);
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
}

.meter-ready {
    margin-top: var(--space-5);
}

.meter-ready > span {
    width: 100%;
}

.note-inline {
    margin: 0;
    align-self: center;
}

.dropzone-filename {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 20px;
    line-height: var(--line-body);
    margin: var(--space-1) 0 0;
    word-break: break-all;
}

/* ── meters and charts ────────────────────────────────────────────────── */

.meter {
    height: 10px;
    background: var(--color-neutral-200);
}

.meter > span {
    display: block;
    height: 10px;
    background: var(--color-accent);
}

.meter-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.meter-row {
    display: flex;
    justify-content: space-between;
    font-size: var(--text-label);
    line-height: var(--line-tight);
}

.meter-row + .meter {
    margin-top: var(--space-1);
}

.meter-value {
    font-variant-numeric: tabular-nums;
    color: var(--ink-60);
}

/*
   Bar charts are plain flex columns with a percentage height — no library and
   no canvas, so they print, scale and respect the tokens.
*/
/* The axis sits beside the plot; the labels below stay aligned with the bars
   because both live inside .chart-plot and share its width. */
.chart-frame {
    display: flex;
    gap: var(--space-3);
}

.chart-plot {
    flex: 1;
    min-width: 0;
}

/* Ticks read top-down: the maximum, the midpoint, then zero. Bar heights are a
   percentage of the tallest bar, so those three line up with the gridlines drawn
   at 100%, 50% and the baseline. */
.chart-yaxis {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: flex-end;
    height: 180px;
    font-size: 10px;
    line-height: 1;
    color: var(--ink-60);
    font-variant-numeric: tabular-nums;

    /* Matches the plot's own margin so the top tick sits level with the top
       gridline rather than with the value labels above the bars. */
    margin-top: var(--space-5);
}

/* Nudged up by half a line so each figure centres on its gridline rather than
   sitting under it. */
.chart-yaxis > span {
    transform: translateY(-50%);
}

.chart-yaxis > span:first-child {
    transform: none;
}

.chart-yaxis > span:last-child {
    transform: translateY(50%);
}

/* The week chart is shorter, so its axis has to be too. A modifier on the frame
   rather than :has() on the chart, so the rule does not depend on selector
   support for something this basic. */
.chart-frame-week .chart-yaxis {
    height: 150px;
}

.chart {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 180px;
    border-bottom: 2px solid var(--color-divider);

    /* Gridlines at the top of the scale and at its midpoint. Drawn as background
       layers so they sit behind the bars without extra elements. */
    background:
        linear-gradient(var(--color-neutral-200), var(--color-neutral-200)) 0 0 / 100% 1px no-repeat,
        linear-gradient(var(--color-neutral-200), var(--color-neutral-200)) 0 50% / 100% 1px no-repeat;

    /* Room for the value printed above the tallest bar, which reaches the top. */
    margin-top: var(--space-5);
}

.chart-col {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    height: 100%;
}

.chart-col > span {
    display: block;
    background: var(--color-accent);

    /* Anchors the value label, which is positioned from the top of the bar so it
       tracks whatever height the bar happens to be. */
    position: relative;
}

/* The figure, printed just above its bar. Absolute rather than a flex sibling:
   as a sibling it would take height from the column and a full-height bar would
   then overflow the plot. */
.chart-value {
    position: absolute;
    bottom: 100%;
    left: -2px;
    right: -2px;
    margin-bottom: 3px;
    text-align: center;
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    color: var(--ink-70);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Twenty-four columns are narrow enough that the figure needs to be smaller and
   allowed to spill over its neighbours' empty space. */
.chart-value-tight {
    font-size: 9px;
    font-weight: 500;
    left: -6px;
    right: -6px;
}

/* Below this the hourly columns are too narrow for a figure of any size. The
   axis, the gridlines and the hover title still give the numbers. */
@media (max-width: 40rem) {
    .chart-value-tight {
        display: none;
    }
}

.chart-week {
    height: 150px;
    gap: 6px;
}

.chart-labels {
    display: flex;
    gap: 2px;
    margin-top: 6px;
}

.chart-week + .chart-labels {
    gap: 6px;
}

.chart-labels > span {
    flex: 1;
    text-align: center;
    font-size: 10px;
    line-height: var(--space-4);
    color: var(--ink-60);
    font-variant-numeric: tabular-nums;
}

.chart-week + .chart-labels > span {
    font-size: 11px;
}

/* ── report ───────────────────────────────────────────────────────────── */

.sample-banner {
    background: var(--color-accent-100);
    border-bottom: 2px solid var(--color-divider);
}

.sample-banner .page {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
    align-items: baseline;
    padding-block: var(--space-3);
}

.sample-banner p {
    font-size: var(--text-ui);
    line-height: var(--space-6);
    margin: 0;
}

.kpi-row {
    display: flex;
    gap: 0 var(--rhythm-3);
    flex-wrap: wrap;
    margin-top: var(--rhythm-2);
    border-top: 2px solid var(--color-divider);
    padding-top: var(--space-5);
}

.section-report-head {
    padding: var(--rhythm-4) 0 var(--rhythm-2);
}

.section-export {
    padding: var(--rhythm-3) 0 var(--rhythm-6);
}

/* Chart and table sections lead with a kicker that needs more air than the default. */
.kicker-chart {
    margin-bottom: var(--space-5);
}

/*
   Fieldsets group the export controls for screen readers, but must not draw the
   browser's default box — the system has no borders of that weight.
*/
.fieldset-plain {
    border: 0;
    margin: 0;
    padding: 0;
    min-width: 0;
}

.export-legend {
    padding: 0;
    margin-bottom: var(--space-3);
}

.kpi-figure {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 25px;
    line-height: 32px;
    margin: 0;
}

.num-cell {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* modernist.css's `.table th { text-align: left }` outranks `.num-cell` alone
   on specificity, so header cells need the extra class to actually align right. */
.table th.num-cell {
    text-align: right;
}

.table .muted-cell {
    color: var(--ink-65);
}

/* ── screenshot slot ──────────────────────────────────────────────────── */

/*
   Placeholder for the walkthrough screenshots that have not been supplied yet.
   Deliberately obvious rather than a subtle grey box, so an unfilled slot is
   not mistaken for a finished page.
*/
.shot {
    display: grid;
    place-items: center;
    width: 100%;
    aspect-ratio: 16 / 6;
    background: var(--color-surface);
    border: 2px dashed var(--color-divider);
    padding: var(--space-4);
}

.shot span {
    font-size: var(--text-label);
    line-height: var(--line-tight);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-60);
    text-align: center;
}

.shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ── export panel ─────────────────────────────────────────────────────── */

.checklist {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/*
   The system's .radio + .dot pair is reused for checkboxes so the two read as
   one family. A checkbox gets a square indicator; only the radius differs.
*/
.checklist .radio .dot {
    border-radius: 0;
}

.export-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--rhythm-2) clamp(28px, 5vw, 72px);
    align-items: start;
}

/* ── column head ──────────────────────────────────────────────────────── */

/*
   A section label with its call to action on the same line. Used to head the
   "At Google" column, so the label announces where the steps happen and the
   button gets you there.
*/
.column-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--rhythm-2);
}

/* The kicker's own bottom margin would double up with the row's. */
.kicker-flush {
    margin-bottom: 0;
}

/* ── guide pages ──────────────────────────────────────────────────────── */

/* The file path being hunted for, set apart from the prose. */
.path-callout {
    border-left: 2px solid var(--color-accent);
    padding: 0 0 0 var(--space-4);
    margin-bottom: var(--rhythm-2);
}

.path-callout p {
    margin: var(--space-2) 0 0;
    overflow-wrap: anywhere;
}

.path-callout code {
    font-size: var(--text-ui);
}

/*
   A stated fact about the page, not an error. Used for "this is a guide only"
   and the contact confirmation.
*/
/* display:flex below beats the hidden attribute's UA rule, so an empty notice would
   sit on the page permanently without this. */
.notice[hidden] {
    display: none;
}

.notice {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    flex-wrap: wrap;
    background: var(--color-accent-100);
    border: 2px solid var(--color-divider);
    padding: var(--space-4);
    margin: var(--rhythm-2) 0;
}

.notice p {
    margin: 0;
    font-size: var(--text-prose);
    line-height: var(--line-body);
    flex: 1;
    min-width: 20ch;
}

/*
   Something did not work. The same block as .notice, with the accent on the edge
   rather than as a fill — loud enough not to be missed, quiet enough not to read
   as a catastrophe when the fix is usually "pick a different file".
*/
.notice-problem {
    background: var(--color-bg);
    border-color: var(--color-accent);
    border-left-width: 6px;
}

.notice-problem p {
    color: var(--color-accent-700);
}

.link-list {
    list-style: none;
    margin: var(--space-3) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    font-size: var(--text-ui);
}

/* A filled screenshot slot: same frame as the placeholder, solid rather than dashed. */
.shot-filled {
    border-style: solid;
    padding: 0;
    aspect-ratio: auto;
    background: var(--color-bg);
}

/*
   Screenshots deliberately keep their colour. The system prints photography in
   black and white via .grayscale, but these are instructional figures where the
   colour is doing work — "the orange button", "the highlighted row" — and
   desaturating them would make the steps harder to follow.
*/
.shot-filled img {
    width: 100%;
    height: auto;
    object-fit: contain;
}

/* Numbered rows without the middle heading column. */
.numbered-compact {
    grid-template-columns: minmax(48px, 64px) minmax(0, 1fr);
    padding: var(--space-5) 0;
}

.numbered-compact .prose {
    max-width: 56ch;
}

/* ── contact form ─────────────────────────────────────────────────────── */

.contact-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* Explanatory text under a form control, quieter than a label. */
.field-hint {
    display: block;
    font-size: var(--text-label);
    line-height: var(--line-tight);
    margin-top: var(--space-2);
    color: var(--ink-60);
}

.field-error {
    display: block;
    font-size: var(--text-label);
    line-height: var(--line-tight);
    margin-top: var(--space-1);
    color: var(--color-accent-700);
}

.validation-summary ul {
    margin: 0 0 var(--space-4);
    padding-left: var(--space-4);
    color: var(--color-accent-700);
    font-size: var(--text-ui);
}

.validation-summary:empty {
    display: none;
}

/* Marks a field the server rejected. */
.input-validation-error {
    border-color: var(--color-accent);
}

/*
   Honeypot. Not display:none — some bots skip hidden fields, and some browsers
   refuse to focus them. Moved off-screen instead, and out of the tab order and
   the accessibility tree via the markup.
*/
.honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* ── data credits ─────────────────────────────────────────────────────── */

/*
   Licence attribution for the classification feeds. Quiet, but permanent —
   the Iffy Index is CC BY 4.0 and the credit is a condition of use, so this
   stays visible in print as well as on screen.
*/
.data-credits {
    padding: var(--rhythm-2) 0 var(--rhythm-4);
}

.data-credits ul {
    list-style: none;
    margin: var(--space-3) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.data-credits li {
    font-size: var(--text-label);
    line-height: var(--line-tight);
    color: var(--ink-60);
}

/* ── print ────────────────────────────────────────────────────────────── */

@media print {
    .nav,
    .site-footer,
    .actions,
    .dropzone,
    .sample-banner {
        display: none;
    }

    .page {
        max-width: none;
        padding: 0;
    }
}

/* ── report tabs ──────────────────────────────────────────────────────────
   The panels ship visible and are hidden by tabs.js, so a failed script
   leaves the whole report readable rather than four headings and nothing.
   That is why there is no rule hiding [role="tabpanel"] here.            */

/* A filled segmented control rather than underlined text. Underlines are quiet
   enough to be taken for decoration, and a reader who does not notice them sees
   a report that appears to have lost most of its content. */
.tablist {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    padding: var(--space-1);
    background: var(--color-surface);
    margin-bottom: var(--rhythm-3);
}

.tab {
    appearance: none;
    background: none;
    border: 0;
    padding: var(--space-3) var(--space-5);
    font: inherit;
    font-size: var(--text-prose);
    font-weight: 600;
    line-height: var(--rhythm-1);
    color: var(--ink-65);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 120ms ease, color 120ms ease;
}

.tab:hover {
    background: var(--color-neutral-100);
    color: var(--color-text);
}

/* Reversed out in the accent, so which panel you are on is unmissable and the
   other three read plainly as things to click. */
.tab[aria-selected="true"],
.tab[aria-selected="true"]:hover {
    background: var(--color-accent);
    color: var(--color-bg);
}

@media (prefers-reduced-motion: reduce) {
    .tab {
        transition: none;
    }
}

/* Four filled tabs do not fit a phone in one row; wrapping keeps them all
   visible rather than hiding some off the side of a scroller. */
@media (max-width: 40rem) {
    .tab {
        flex: 1 1 auto;
        text-align: center;
        padding: var(--space-3) var(--space-3);
        font-size: var(--text-ui);
    }
}

/* Keyboard focus stays visible; the pointer does not need it. */
.tab:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
}

.tabpanel:focus {
    outline: none;
}

/* Explains a section that has nothing to show, in place of an empty table. */
.empty-state {
    padding: var(--space-5);
    background: var(--color-neutral-100);
    border-left: 3px solid var(--color-divider);
    color: var(--ink-70);
    font-size: var(--text-body);
    line-height: var(--line-tight);
    margin: var(--rhythm-2) 0 0;
}

.empty-state code {
    font-size: 0.9em;
}

/* ── upload progress ──────────────────────────────────────────────────── */

.upload-progress {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5);
    background: rgb(0 0 0 / 45%);
}

.upload-progress[hidden] {
    display: none;
}

body.is-uploading {
    overflow: hidden;
}

.upload-progress-card {
    width: min(28rem, 100%);
    padding: var(--space-6);
    background: var(--color-surface);
    box-shadow: 0 10px 40px rgb(0 0 0 / 25%);
}

.upload-progress-title {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: var(--text-sub);
    margin: 0 0 var(--rhythm-2);
}

.progress {
    height: 10px;
    background: var(--color-neutral-200);
    overflow: hidden;
}

.progress > span {
    display: block;
    height: 10px;
    width: 0;
    background: var(--color-accent);
    transition: width 120ms linear;
}

.progress[hidden],
.spinner[hidden] {
    display: none;
}

/* Replaces the bar once the file is with the server. A spinner claims only that
   work is happening, which is all that is known at that point — unlike a bar,
   which implies a measurable position it does not have. */
.spinner {
    width: 28px;
    height: 28px;
    margin: var(--space-2) 0;
    border: 3px solid var(--color-neutral-200);
    border-top-color: var(--color-accent);
    border-radius: 50%;
    animation: spinner-turn 850ms linear infinite;
}

@keyframes spinner-turn {
    to { transform: rotate(360deg); }
}

/* Removed rather than stopped under reduced motion.

   The global reduced-motion rule near the top of this file cancels every
   animation with !important, so anything relying on one here is dead code — the
   spinner would render as a motionless ring, which reads as a stalled page and
   is worse than no indicator. The status text beside it is in an aria-live
   region and says what is happening, so it can carry the phase on its own. */
@media (prefers-reduced-motion: reduce) {
    .spinner {
        display: none;
    }

    .progress > span {
        transition: none;
    }
}

/* ── timeline chart ───────────────────────────────────────────────────────
   Up to a couple of hundred bars, so the gap comes down and the bars are let
   shrink. Value labels are off: there is no room, and the axis plus the hover
   title carry the numbers.                                                */

.chart-timeline {
    gap: 1px;
}

.chart-timeline .chart-col {
    min-width: 0;
}

/* Most labels are deliberately blank — roughly eight ticks across the span —
   so they cannot each claim an equal share of the width or they collapse to
   nothing. Positioned against the bars instead. */
.chart-labels-sparse {
    gap: 1px;
    position: relative;
}

.chart-labels-sparse > span {
    flex: 1;
    min-width: 0;
    text-align: left;
    white-space: nowrap;
    overflow: visible;
}

/* ── devices ──────────────────────────────────────────────────────────────*/

.device-id {
    display: block;
    font-size: 10px;
    line-height: var(--space-4);
    color: var(--ink-60);
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
}

.table-devices .device-share {
    width: 22%;
}

.table-devices .meter {
    margin-top: var(--space-1);
}

/* The share bar is decoration for the number beside it, so it goes rather than
   squeezing the columns that carry the actual figures. */
@media (max-width: 46rem) {
    .table-devices .device-share {
        display: none;
    }
}
