/**
 * Newsdesk chassis — arrangement only.
 *
 * Derived from MH Newsdesk 2.2.3 as a STRUCTURAL REFERENCE. None of its CSS is
 * here. MH's layout is float-based (`.mh-col`, `.mh-1-2`, clearfix `.mh-group`)
 * and its 584-line stylesheet interleaves structure with typography and colour,
 * so porting it would have dragged Newsdesk's *surface* into a theme that has
 * its own. This is CSS Grid, written from scratch. See THIRD_PARTY.md.
 *
 * TWO RULES THIS FILE LIVES BY:
 *
 *   1. No Echo KB selector. Ever. `apparatus.css` legitimately targets
 *      `#eckb-article-content-body` — that is its job — which is exactly why the
 *      chassis needs a separate file: the constraint can only be enforced if it
 *      is file-scoped (plan R8a).
 *   2. Arrangement only. No type, no colour, no rules. Everything inside these
 *      boxes is House Style's, and a chassis page is House Style because
 *      `pu_is_house_surface()` says so.
 *
 * Loaded only on chassis Pages — `pu_apparatus_enqueue_chassis_assets()` returns
 * early otherwise, so this never ships on a knowledge-base page (plan R14).
 *
 * Space tokens come from house-style.css, which is a declared dependency of the
 * `pu-chassis` handle. Nothing here defines a token of its own.
 */

/* `.pu-chassis` is the LAYOUT CONTAINER inside the template. The body carries
   `.pu-chassis-page` instead — an earlier revision used one name for both, which
   put `display: grid` on <body>, made `.hs` a grid item and collapsed the page
   to a single track. Do not merge them.

   ══════════════════════════════════════════════════════════════════════════
   The spread: content column + optional aside.

   `minmax(0, 1fr)` and not `1fr`. A bare `1fr` takes its minimum from content
   min-width, which is the defect that has bitten this theme twice — once
   rendering KB articles 496px wide inside a 320px viewport, once leaving 341px
   of live horizontal overflow between 1024 and 1440.
   ══════════════════════════════════════════════════════════════════════════ */

.pu-chassis {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2xl) var(--space-xl);
    align-items: start;
}

@media (min-width: 1024px) {
    .pu-chassis:not(.pu-chassis--no-aside) {
        grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
    }
}

/* An aside that was never emitted leaves a single-column grid, so the content
   centres instead of hanging beside dead space (plan R7). */
.pu-chassis--no-aside .pu-chassis__main {
    margin-inline: auto;
    width: 100%;
}

.pu-chassis__main,
.pu-chassis__aside {
    min-width: 0;
}

.pu-chassis__main {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2xl);
}

@media (min-width: 1024px) {
    .pu-chassis__aside {
        position: sticky;
        top: var(--space-lg);
        max-height: calc(100vh - (var(--space-lg) * 2));
        overflow-y: auto;
        overscroll-behavior: contain;
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   The split row.

   `auto-fit` rather than a fixed 1fr 1fr: when only one of the two zones holds
   widgets, the template emits one child and it spans the row on its own. The
   grid does the collapsing, so a half-empty split never reads as a missing
   column (plan R7).
   ══════════════════════════════════════════════════════════════════════════ */

.pu-chassis__split {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-xl);
}

@media (min-width: 768px) {
    /* One populated zone spans; the base single-track rule above already does
       that, so nothing is needed here for the solo case. */

    /* Two populated zones: 30 / 70, not 50 / 50.
       Measured on the live front page at a 1185px wrap — both columns 562px,
       and the left one (the KB category tree) needs roughly two-thirds of that
       while the right one (headlines and deks) is tight. An even split spent
       ~200px on nothing and pushed the reading column narrower than it needed
       to be. `minmax(0, …)` on both so a long unbroken category name cannot
       widen its track. */
    .pu-chassis__split--pair {
        grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    }
}

/* 30 / 70 only once the track is wide enough to carry it. At 768 a 3fr left
   column is 202px, and 9 of the 82 links in the KB category tree wrap to two
   lines — measured, not guessed. 2fr/3fr there gives ~270px and they fit. */
@media (min-width: 1024px) {
    .pu-chassis__split--pair {
        grid-template-columns: minmax(0, 3fr) minmax(0, 7fr);
    }
}

.pu-chassis__zone {
    min-width: 0;
}

.pu-chassis__zone > * + * {
    margin-top: var(--space-xl);
}

/* ══════════════════════════════════════════════════════════════════════════
   Prose inside the chassis keeps House Style's measure.

   The chassis decides where the column is; `--measure` decides how wide the
   reading is. Without this the prose would run the full grid track, which on a
   no-aside page is the whole 1220px wrap.
   ══════════════════════════════════════════════════════════════════════════ */

.pu-chassis__prose {
    max-width: var(--measure);
    min-width: 0;
}

.pu-chassis--no-aside .pu-chassis__prose {
    margin-inline: auto;
}

/* Widgets are structural boxes; their contents are House Style's business —
   with one exception that is arrangement, not typography.

   `house-style.css` sets `.hs p { margin: 0 }` deliberately: it zeroes the
   global paragraph margin so the kicker, dek and byline are not pushed around,
   and `.prose` re-establishes rhythm for article bodies. Widget content is
   neither, so without this its paragraphs run together with no gap at all.

   Rhythm only, from house tokens. No face, no size, no colour, no rule — a
   widget title must not inherit `.prose h2`'s scotch rule, which is sized for an
   article section and reads as a slab in a 320px column. */
.pu-chassis__widget {
    min-width: 0;
}

/* Scoped under `.hs` for SPECIFICITY, not decoration. `house-style.css` sets
   `.hs p { margin: 0 }` at (0,1,1); a bare `.pu-chassis__widget > * + *` is
   (0,1,0) and loses regardless of load order, so the rule silently did nothing
   and widget paragraphs ran together. Measured computed margin-top: 0px.
   The chassis only ever renders inside `.hs`, so the scope costs nothing. */
.hs .pu-chassis__widget > * + * {
    margin-top: var(--space-md);
}

.hs .pu-chassis__widget li + li {
    margin-top: var(--space-2xs);
}

.pu-chassis__widget + .pu-chassis__widget {
    margin-top: var(--space-xl);
}

/* Wide content must scroll inside its own box rather than widening the page —
   the failure mode this theme has shipped twice. */
.pu-chassis__zone table,
.pu-chassis__prose table,
.pu-chassis__widget table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
}

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


/* ══════════════════════════════════════════════════════════════════════════
   PU KB Articles and PU Pages widgets — one reading-list form, two sources

   A reading list, not a nav list: the dek is what makes an item worth clicking,
   so it gets the room. Structure only — face, colour and rules are House
   Style's, exactly as with every other box in a zone.
   ══════════════════════════════════════════════════════════════════════════ */

.hs .pu-kb-articles__list,
.hs .pu-pages__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--space-lg);
}

.hs .pu-kb-articles__item,
.hs .pu-pages__item {
    min-width: 0;
}

/* Desk kicker — the eyebrow that names the beat and carries its ink.
 *
 * Only rendered on rows that resolved a desk, so this rule never applies to an
 * unassigned story. The rule after the label is drawn with a border on ::after
 * rather than an <hr>, so it is decoration the screen reader never announces.
 *
 * Both the label and the rule take --ink-2, which the row's [data-desk] has
 * already reassigned. Nothing here names a colour: change a desk ink in
 * house-style.css and this follows.
 */
.hs .pu-kicker {
    display: flex;
    align-items: center;
    gap: 0.5em;
    margin: 0 0 var(--space-2xs);
    font-family: var(--face-utility);
    font-size: var(--t-micro);
    font-weight: 500;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-2);
}

.hs .pu-kicker::after {
    content: "";
    flex: 1;
    height: 0;
    border-top: 1px solid var(--ink-2);
    opacity: 0.45;
}

/* An inked row tints its read-more and its headline hover. Scoped to
   [data-desk] so a deskless row keeps the default ink and is not left with a
   headline that changes colour to nothing on hover. */
.hs [data-desk] .pu-kb-articles__more {
    color: var(--ink-2);
}

.hs [data-desk] .pu-kb-articles__title:hover,
.hs [data-desk] .pu-kb-articles__title:focus-visible {
    color: var(--ink-2);
}

/* The title is the headline of the item — block so the dek starts its own line
   rather than running on from it. */
/* Headlines carry --t-title (body + 12px) and 700, so they are larger AND
   heavier than the prose beneath them. --t-sub is deliberately not used here:
   it drives the dek and .prose h3, which must stay quieter than the headline. */
/* WHY opsz IS PINNED, AND WHY font-weight CANNOT DO THIS JOB.
 *
 * Bodoni Moda is a variable font with an `opsz` axis (6-96, default 11), and CSS
 * applies `font-optical-sizing: auto` by default: the browser sets opsz to the
 * RENDERED pixel size. A headline at --t-title is 33px, so opsz resolves to 33 —
 * the poster master, where a didone's horizontal hairlines are thinnest.
 *
 * Measured on the shipped BodoniModa-var-latin.woff2, em-dash bar height:
 *
 *     opsz  33 (today)  0.0055 em  ->  0.18px at 33px    <- below one device pixel
 *     opsz  11 (default) 0.0190 em ->  0.63px
 *     opsz   9 (here)   0.0258 em  ->  0.85px            <- 4.7x today
 *     opsz   6          0.0360 em  ->  1.19px
 *
 * At 0.18px the dash is not faint, it is sub-pixel: on a 1x display it has no
 * pixel to land in and reads as a gap. That is the reported "looks like a blank
 * space", and it is a rendering fact, not a taste question.
 *
 * `font-weight` is NOT the lever. The same measurement at wght 700 / 800 / 900
 * returns 0.0258 em at every weight: Bodoni's weight axis thickens the stems and
 * leaves the horizontal hairlines alone. Bolding the headline changes nothing.
 *
 * 9 rather than 6: 9 is still inside the text range, so the headline keeps its
 * didone character instead of reading as small-text Bodoni scaled up.
 *
 * This is a MITIGATION, not the fix. 0.85px is legible; it is not emphatic, and
 * no opsz value makes a didone em dash carry a headline. The durable fix is
 * editorial — a colon, whose dot is 0.55 em and solid at any optical size — and
 * it lives in the `patriot-headline-style` skill, not here.
 */
.hs .pu-kb-articles__title,
.hs .pu-pages__title {
    display: block;
    font-family: var(--face-display);
    font-size: var(--t-title);
    font-weight: 700;
    /* Both properties, in this order. `font-optical-sizing` is the standard
       lever; `font-variation-settings` is the one that actually pins the axis in
       engines that ignore `none`. wght is named alongside it so the low-level
       descriptor cannot drop the weight set above. */
    font-optical-sizing: none;
    font-variation-settings: "opsz" 9, "wght" 700;
    line-height: var(--lh-head);
    text-decoration: none;
}

.hs .pu-kb-articles__title:hover,
.hs .pu-kb-articles__title:focus-visible,
.hs .pu-pages__title:hover,
.hs .pu-pages__title:focus-visible {
    text-decoration: underline;
    text-underline-offset: 0.14em;
}

/* The body opening sits below the dek — a summary, then the article beginning
   to speak. Same face and size as the dek so the two read as one block rather
   than two competing voices; the rhythm does the separating.

   Two different gaps, deliberately. Paragraph-to-paragraph is --space-sm; the
   dek-to-body transition is --space-md, so the eye sees where the summary ends
   and the piece starts. At 8px for both — the first version — 250 words across
   eight paragraphs read as one wall with faint seams. */
.hs .pu-kb-articles__body {
    margin: var(--space-sm) 0 0 !important;
    font-size: var(--t-small);
    line-height: var(--lh-note);
    color: var(--ink-soft);
}

.hs .pu-kb-articles__dek + .pu-kb-articles__body {
    margin-top: var(--space-md) !important;
}

.hs .pu-kb-articles__dek,
.hs .pu-pages__dek {
    margin: var(--space-2xs) 0 0 !important;
    font-size: var(--t-small);
    line-height: var(--lh-note);
    color: var(--ink-soft);
}

/* Inline with the dek's last line, not on its own row: the link is the end of
   the sentence the dek started. */
.hs .pu-kb-articles__more,
.hs .pu-pages__more {
    white-space: nowrap;
    font-family: var(--face-utility);
    font-size: var(--t-caption);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-2);
}

/* ══════════════════════════════════════════════════════════════════════════
   KB Category Navigation, House Style skin
   ══════════════════════════════════════════════════════════════════════════

   The tree's structure — disclosure mechanics, indentation, hit areas — lives in
   the parent's components.css and is token-neutral. Only appearance is set here,
   scoped to `.hs`, so Surface A keeps the Editorial Civic treatment and nothing
   in this block can reach a knowledge-base page.

   Sections take the utility face at small caps, which is what makes a closed
   88-row tree read as a masthead index rather than a list of links: the reader
   sees ten beats, not ten link texts. Category and subcategory rows drop to the
   text face, so depth is legible without indentation carrying it alone.
   ────────────────────────────────────────────────────────────────────────── */

.hs .pu-catnav {
    color: var(--ink);
    border-top: var(--rule-thin, 1px) solid var(--rule);
}

.hs .pu-catnav__toggle,
.hs .pu-catnav__list--1 > .pu-catnav__item > .pu-catnav__group > .pu-catnav__summary,
.hs .pu-catnav__list--1 > .pu-catnav__item--leaf > .pu-catnav__link {
    font-family: var(--face-utility);
    font-size: var(--t-caption);
    font-weight: 600;
    letter-spacing: 0.055em;
    text-transform: uppercase;
}

/* A hairline between Sections, none between the rows inside one. The list is
   the index; the Sections are its entries. */
.hs .pu-catnav__list--1 > .pu-catnav__item + .pu-catnav__item {
    border-top: var(--rule-thin, 1px) solid var(--rule);
}

.hs .pu-catnav__list--2 .pu-catnav__summary,
.hs .pu-catnav__list--2 .pu-catnav__link,
.hs .pu-catnav__list--3 .pu-catnav__summary,
.hs .pu-catnav__list--3 .pu-catnav__link {
    font-family: var(--face-text);
    font-size: var(--t-small);
    letter-spacing: 0;
    text-transform: none;
}

.hs .pu-catnav__count {
    font-family: var(--face-utility);
    font-size: var(--t-micro);
    color: var(--ink-soft);
    opacity: 1;
}

.hs .pu-catnav__all {
    font-family: var(--face-utility);
    font-size: var(--t-micro);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-2);
    opacity: 1;
}

.hs .pu-catnav__link:hover .pu-catnav__label,
.hs .pu-catnav__summary:hover .pu-catnav__label {
    color: var(--ink-2);
}

/* An open Section is the one the reader is reading; tint its spine so the eye
   can find the branch again after scrolling past the summary. */
.hs .pu-catnav__group[open] > .pu-catnav__list {
    border-left-color: color-mix(in srgb, var(--ink-2) 40%, transparent);
}
