/* The whole stylesheet, served rather than re-sent inside every page.
 *
 * It lived in `base.html` as 197 lines of inline `<style>`, which cost two things. Every
 * response carried it again, uncacheable — and `style-src` had to allow `'unsafe-inline'`,
 * which is the allowance that makes a content security policy stop being one. Served from
 * `/static`, both go away (D-024, stage 2).
 *
 * ============================================================================
 * NEWFANGLED 2026. The tokens below are the brand's, verbatim from
 * `record/source/skills/newfangled-brand-system/design-system/tokens.md`.
 * `spec/STYLE-GUIDE.md` records the four places this diverges and why.
 *
 * Four things to know before editing:
 *
 *   1. CORNERS ARE SQUARE. "Newfangled = sharp corners." Cards, panels, inputs,
 *      tables, notes — all `0`. Buttons, pills and badges are fully round. Sharp
 *      surfaces with round controls is the brand's signature, not an oversight.
 *
 *   2. CALLOUTS HAVE NO BORDERS. None, on any side. The brand forbids it
 *      absolutely: a single-sided rule "reads as a generic CMS pattern — the
 *      kind of treatment WordPress themes ship with by default". The 10% tints
 *      exist to mark a region without chrome.
 *
 *   3. TYPE IS SMALLER THAN THE BRAND SPECIFIES. Its body is 20px, written for
 *      a report read once. This is a tool somebody scans against a clock, so
 *      the dashboard bridge's compact end is used instead — 16px body, 14px
 *      Roboto tables. A deliberate divergence, recorded in the style guide.
 *
 *   4. RED MEANS REFUSED. The brand fixes no `danger = red` and says of it:
 *      "use sparingly — when red appears, it should mean it." Orange carries
 *      attention, red carries refusal, green carries confirmation, and every
 *      count on every page stays neutral.
 * ============================================================================ */

/* Self-hosted, not from Google — see `static/fonts/README.md`. The CSP has no
 * `font-src`, and `photos.py` already refuses to hot-link for the same privacy
 * reason: a request to a third party says which page an analyst has open.
 * Both are variable fonts, one file per family covering every weight. */
@font-face {
  font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 300 600;
  font-display: swap; src: url('/static/fonts/IBMPlexSans-var.woff2') format('woff2');
}
@font-face {
  font-family: 'Roboto'; font-style: normal; font-weight: 400 600;
  font-display: swap; src: url('/static/fonts/Roboto-var.woff2') format('woff2');
}

    :root {
      /* ---- Newfangled 2026 palette. Theme-invariant: only surfaces flip. ---- */
      --nf-blue:#1A99D4; --nf-blue-dark:#0E7CB0; --nf-blue-hover:#0E81B6;
      /* LinkedIn's own brand blue, and the only thing it is for: the one box on the post page
         that reproduces something of theirs. Not a member of the palette above — nothing else
         in this interface may use it, because its whole job is to say "this part is theirs". */
      --li-blue:#0A66C2;
      /* The reference series in a comparison: the baseline a subject is measured
         against, never an identity of its own. Validated against --nf-blue. */
      --data-reference:#A8A8A8;
      --nf-green:#4DC8A1; --nf-green-dark:#1D9A72;
      --nf-purple:#50246C; --nf-orange:#F36D32; --nf-red:#FF002F;
      --nf-white:#FFFFFF; --nf-off-white:#FAFAFA; --nf-dark-grey:#222222;

      /* 10% tints. The brand's tool for marking a region without chrome — they
       * let the surface show through, so they read correctly on either ground. */
      --blue-10:rgba(26,153,212,.10); --blue-50:rgba(26,153,212,.50);
      --green-10:rgba(77,200,161,.12); --orange-10:rgba(243,109,50,.10);
      --purple-10:rgba(80,36,108,.10); --red-10:rgba(255,0,47,.08);

      /* ---- semantic. The old names are kept as aliases so a component that
       * still says `var(--ink)` keeps working; new work should use these. ---- */
      --surface-page:var(--nf-off-white);     /* never pure white */
      --surface-raised:var(--nf-white);       /* the tier above the page */
      /* The painted band at the top of every page. The brand's dark grey — tried in the
       * brand's purple for an afternoon and put back: it made the masthead the loudest object
       * on a page whose job is to be read, and it left the quoted ground below it (`.artefact`,
       * which is the same paint on purpose) as a second dark box in a second colour.
       *
       * Named rather than used directly even so, because `--nf-dark-grey` is `--text-primary`
       * as well as a surface: repainting the band by changing that token turns every word on
       * every page with it. The token is the thing to change; this is where it is decided. */
      --surface-masthead:var(--nf-dark-grey);
      --surface-sunken:rgba(34,34,34,.04);
      /* The page colour, thinned so the sticky filter bar can frost what passes
         beneath it rather than simply hide it. */
      --surface-veil:rgba(250,250,250,.72);
      /* The ground a quotation sits on. The brand's dark grey — the same paint as the
       * masthead, which is the only other band on a page that is not a card.
       *
       * A neutral tint was too quiet to do the job and a purple one was a third colour for a
       * page that already has enough. Dark needs no new hue: a white LinkedIn card on it is
       * unmistakably a thing pinned up rather than a thing concluded, and the page already
       * reads a painted band as *not a card* because its masthead is one. */
      --text-primary:var(--nf-dark-grey);
      /* .62, the brand's own value — the app had .60, which is nothing to look at and
       * everything to chase when two stylesheets are compared for a living. */
      --text-secondary:rgba(34,34,34,.62);
      --text-muted:rgba(34,34,34,.50);
      --divider:rgba(34,34,34,.10);

      --ink:var(--text-primary); --muted:var(--text-secondary); --faint:var(--text-muted);
      --line:var(--divider); --wash:var(--surface-sunken); --link:var(--nf-blue);
      --warn:#A84517; --warn-bg:var(--orange-10);
      --ok:#14795A; --ok-bg:var(--green-10);
      --bad:#C4002B; --bad-bg:var(--red-10);

      /* ---- the brand's spacing scale, mapped onto the names in use ---- */
      --s1:5px; --s2:10px; --s3:20px; --s4:20px; --s6:30px; --s8:40px;
      --s5:40px; --s7:60px;

      /* Square. See note 1 above. `--radius` is kept and set to 0 so every
       * component that reads it goes square in one edit rather than forty. */
      --radius:0;
      --radius-pill:9999px;
      --radius-full:50%;

      --font-body:'IBM Plex Sans',system-ui,-apple-system,sans-serif;
      --font-utility:'Roboto',system-ui,-apple-system,sans-serif;

      /* The rail's two gaps. Everything in it either names a list or belongs to one. */
      --rail-above-label:10px; --rail-under-label:2px;

      /* ---- the dashboard's own measurements, from `snapshots/nf-dash.css` ----
       * These were referenced by nothing and defined nowhere, so every card was padded on
       * the generic `--s4` and every KPI sized by whatever rule reached it last. The two
       * stylesheets are compared by a test now, so a value edited in one and not the other
       * fails rather than drifts. */
      --card-padding:24px; --card-padding-lg:32px;
      --kpi-value-weight:300; --kpi-label-weight:500;
      --kpi-value-size-hero:64px; --kpi-value-size-standard:40px;
      --kpi-value-size-compact:28px;
      --toolbar-height:60px;

      /* The data family, by the job each colour does rather than by its hue. */
      --color-data-fg:#1A99D4;
      --color-data-bg:rgba(26,153,212,.10);      /* track and gridline */
      --color-data-area:rgba(26,153,212,.08);    /* the fill under a line */
      --color-data-2:#7B4E9C;
      --color-data-3:#1D9A72;
      --color-data-other:rgba(34,34,34,.25);     /* the residual, never a category */
    }
    * { box-sizing: border-box; }
    body { font: 16px/1.55 var(--font-body);
           color: var(--text-primary); margin: 0; background: var(--surface-page);
           -webkit-font-smoothing: antialiased; }
    /* ---- links ----
       One rule set a link's colour — `td a` — so links inside a table were nf-blue and every
       other link in the product was the browser's own `-webkit-link`: a different blue, and
       purple once visited. Nothing failed, and no assertion about page content could see it.

       **There is deliberately no `a:visited` rule, and adding one breaks buttons.** The
       cascade sorts by origin before specificity, so this plain author rule already defeats
       the UA sheet's purple — that is the whole of the fix. An explicit `a:visited` would
       read as belt and braces and is a landmine: at (0,1,1) it outranks every class-only
       colour rule on an anchor — `.btn` is (0,1,0) — so every link-button anybody had
       clicked would repaint itself blue on blue, and only for them.

       The underline is kept and quietened. Colour alone is not enough to mark a link for
       somebody who cannot separate this blue from this grey, and a full-strength rule under
       every company name in a career list is noise — so it sits at half strength until
       hover, when it comes up to meet the text. */
    a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 2px;
        text-decoration-color: var(--blue-50); }
    a:hover { color: var(--nf-blue-hover); text-decoration-color: currentColor; }
    /* Inputs were given a focus ring in this stylesheet and links were not, so a keyboard
       user got whatever the browser felt like — which on some is nothing. */
    a:focus-visible { outline: 2px solid var(--link); outline-offset: 2px;
                      text-decoration-color: currentColor; }

    .shell { max-width: 68rem; margin: 0 auto; padding: 0 var(--s6) var(--s8); }

    /* ================================================================
       LAYOUT — creator's structural contract, not a preference.

       "The content area is a 12-column CSS grid container, not a stack of
       modules." "Side-nav by default — the left rail is the natural home for
       views." Both quoted from `references/dashboard/dashboard.md`, and both
       were being ignored: the portal was top tabs over stacked full-width
       modules, which is what every admin panel looks like. Applying brand
       colour to that changed the colour and nothing else.

       The dimensions below are marked "do not override" in the template. They
       are structure, not brand.
       ================================================================ */
    :root {
      /* One line of running text, about seventy-five characters at 16px. The person page had
         three measures in one column — 749px for the timeline, 664 for the bio, 749 for the
         career — which read as three unrelated blocks, and 1094 in the trigger, roughly a
         hundred and thirty characters a line. */
      --measure: 42rem;
      --side-nav-width: 240px;
      --content-padding-x: 40px;
      --grid-cols: 12;      /* layout — do not override */
      --grid-gap-x: 20px;   /* layout — do not override */
      --grid-gap-y: 20px;   /* layout — do not override */
      --toolbar-height: 60px;
    }

    body[data-nav-position="side"] {
      display: grid;
      grid-template-columns: var(--side-nav-width) 1fr;
      grid-template-areas: "nav body";
      min-height: 100vh;
    }
    /* `align-self: stretch` and not the template's `start`: the template's rail sits on a
       page whose background matches it, so a short rail is invisible. Here it carries a right
       border, and a border that stops two thirds down reads as a broken box. */
    .page-nav { grid-area: nav; display: flex; flex-direction: column;
                padding: var(--s4) var(--s6) var(--s3); position: sticky; top: 0;
                align-self: stretch; height: 100vh; overflow-y: auto;
                border-right: 1px solid var(--divider); background: var(--surface-raised); }
    .page-body { grid-area: body; min-width: 0; }
    .content { padding: var(--s4) var(--content-padding-x) var(--s7); max-width: 78rem; }

    /* The lockup is an object, not the first row of the list under it. At 20px it read as a
       heading for the client name directly beneath; the package asks for half the mark's
       height of clear space on every side, which is 13px at this scale, and that is the floor
       rather than the target. 40px separates the two so the rail reads as a mark and then a
       tree, which is what it is. */
    .page-nav__brand { padding-bottom: var(--s5); }
    .page-nav__brand a { display: block; padding: 0; }
    /* One drawing at one width, and the width is the package's for a 240px rail. It was a
       150px wordmark with the product name set under it in live text at 14px — two objects
       held in a relationship by two stylesheet rules, either of which could drift. The name is
       outlined inside the file now, so it cannot be re-typeset, cannot wait on a webfont, and
       cannot wrap differently at another size.

       `height: auto` against the `width`/`height` attributes on the tag: the attributes give
       the browser the aspect ratio before the file arrives, so the rail does not reflow as it
       paints, and this lets the drawing scale without the two fighting. */
    .brand-lockup { display: block; width: 134px; height: auto; }
    /* Below 110px the wordmark stops being legible, which is why there are two drawings and
       not one scaled further. The compact one is the same lockup with the wordmark removed. */
    .brand-lockup--compact { display: none; }
    /* The root. It sat at the same size and weight as an organization one level below it and
       in lighter ink, so the tree read as inverted — the child looked like the parent. */
    /* One class was not enough to beat `.page-nav a`, so the root rendered in muted ink while
       the organization one level below it rendered near-black — the tree read inverted. */
    /* `flex`, not `block`: the kind mark and the name are one line with a gap, and the name
       has to be able to wrap without the icon wrapping under it. */
    .page-nav a.page-nav__title { display: flex; align-items: center; gap: var(--s2);
                                  padding: 0 10px var(--s3) 20px;
                                  font-size: 16px; font-weight: 600; line-height: 1.3;
                                  color: var(--text-primary); }
    .page-nav a.page-nav__title:hover { color: var(--nf-blue); }
    /* Quieter than muted ink, and it stays quiet on hover. It says what kind of thing this row
       is; it is not part of the name and should not light up as though it were — and at 16px
       beside a 16px semibold name, muted still read as a second object competing with the word.
       `.32` is the stylesheet's own quietest step, already worn by a section with nowhere to go.
       `flex-shrink: 0` so a long client name takes the wrap rather than squashing the mark. */
    .page-nav a.page-nav__title .i { flex: 0 0 auto; color: rgba(34,34,34,.32); }
    .page-nav__heading { font-family: var(--font-utility); font-size: 10px; font-weight: 600;
                         letter-spacing: .1em; text-transform: uppercase;
                         color: var(--text-muted); padding: 0 0 var(--s2) 20px; display: block; }
    /* Two gaps in the whole rail, and two values for them: the space above a section's label,
       and the space between that label and its first item. There were four — 14px above the
       first group and 10px above the other two, because the first one's container carried a
       margin its siblings did not; 2px under one disclosure and 20px under the other. No one
       number was wrong and together they read as a rail somebody assembled rather than drew.

       This is the second: a list hangs `--rail-under-label` below whatever names it, whether
       that is a disclosure's summary or a group heading. */
    .page-nav__links, .page-nav__subjects { display: flex; flex-direction: column; }
    /* A list of bare links hangs `--rail-under-label` below the summary that names it. The
       section list needs none: its first child is a group label, which brings its own space —
       the gap always belongs to the thing below the label, never to the list around it. */
    .page-nav__subjects { margin-top: var(--rail-under-label); }
    .page-nav__rule { height: 1px; background: var(--divider); margin: var(--s2) 0 var(--s2) 20px; }

    /* The marker is a 5px dot revealed at the left of the active link — the
       brand's own navigation marker, and the reason the rail indents by 20px. */
    .page-nav a { position: relative; padding: 2px 10px 2px 20px; font-size: 14px;
                  line-height: 26px; color: var(--text-muted); text-decoration: none; }
    .page-nav a::before { content: ""; position: absolute; left: 0; top: 50%;
                          width: 5px; height: 5px; margin-top: -2.5px;
                          background: var(--nf-blue); border-radius: var(--radius-full);
                          opacity: 0; }
    .page-nav a:hover { color: var(--text-primary); }
    .page-nav a.is-active { color: var(--nf-blue); font-weight: 600; }
    .page-nav a.is-active::before { opacity: 1; }
    .page-nav a.soon { color: rgba(34,34,34,.32); }
    /* A section with nowhere to go. Every one of them below the Overview hangs off an
       organization, so a workspace with none can name them and cannot link them — drawn in
       the same column and the same size as the links beside it, in muted ink, so the shape of
       the product still reads while nothing in it is clickable. */
    .page-nav__waiting { display: block; padding: 2px 10px 2px 20px; font-size: 14px;
                         line-height: 26px; color: rgba(34,34,34,.32); }
    .branch .page-nav__links .page-nav__waiting { padding-left: 19px; }
    .page-nav__subjects a { display: flex; align-items: baseline; gap: var(--s2);
                            line-height: 24px; }
    .page-nav__subjects .kind { margin-left: auto; font-family: var(--font-utility);
                                font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
                                color: var(--text-muted); }
    /* ---- the branches: a tree that closes ----
       Four ranks in a 240px rail, so each one pays for its indent. The workspace and the
       branch headings share a column — 16px semibold against 10px uppercase muted already says
       which is which, twice. Below that it is 20 → 30 → 51px: a subject steps in from its
       branch, and the sections step in again from the subject, with the guide line dropping
       from the subject's first letter. Deeper than that and the text has nowhere to go, which is
       the argument for principals being leaves rather than a fourth level. */
    .branch { border-top: 1px solid var(--divider); }
    .branch > summary { list-style: none; cursor: pointer; display: flex; align-items: center;
                        gap: var(--s2); padding: var(--s2) 10px var(--s2) 20px;
                        font-family: var(--font-utility); font-size: 10px; font-weight: 600;
                        letter-spacing: .1em; text-transform: uppercase;
                        color: var(--text-muted); }
    .branch > summary::-webkit-details-marker { display: none; }
    /* The disclosure arrow, drawn rather than borrowed: the platform triangle sits on the
       baseline of a 10px uppercase label and reads as a typo. */
    .branch > summary::after { content: ""; margin-left: auto; width: 6px; height: 6px;
                               border-right: 1.5px solid currentColor;
                               border-bottom: 1.5px solid currentColor;
                               transform: rotate(-45deg) translate(-1px, -1px);
                               transition: transform .12s ease; }
    .branch[open] > summary::after { transform: rotate(45deg) translate(-2px, -2px); }
    .branch > summary:hover { color: var(--text-primary); }
    /* The branch you are in, which is not the page you are on. It gets weight and ink — you
       can see where you are in the tree — and never the dot, which means "this is the page"
       and was being worn by two rows at once, one of them a link somewhere else. */
    /* Weight is the rank, and it has to say three things rather than two: the leaf you are on
       (600, blue, a dot), the branch you are in (500, primary ink), and everything else (400,
       muted). The branch was 600 and primary, which is the workspace title's own treatment one
       row above it — and where the organisation shares the workspace's name, as the first
       client here does, the two rows were indistinguishable. */
    /* Indented, and qualified so it lands: `.branch__subject` alone is 0-1-0 against the
       0-1-1 of `.page-nav a`, which sets the rail's 20px inset in a four-value shorthand.
       The unqualified version of this rule is a no-op that reads like a fix. */
    /* A block, like every other link in the rail. As an inline it sat in a line box of its
       own 26px against the branch's 24.8px strut, and the whitespace after it in the markup
       carried the difference — two pixels of gap that belonged to no rule and could not be
       tuned out of one. */
    .page-nav a.branch__subject { display: block; padding-left: 30px; font-weight: 400; }
    /* The dot is placed from the rail's edge, not from its own text, so it has to move with
       the row — otherwise it stays in the branch heading's column and marks the gap. */
    .page-nav a.branch__subject::before { left: 10px; }
    .branch__subject.is-open { color: var(--text-primary); font-weight: 500; }

    /* The guide line is what makes a tree a tree, and it starts where the name it descends
       from starts. Both numbers below are 30px for that reason; they are one decision. */
    /* No top margin: the first thing inside is a group label, and the label owns the space
       above it. Two pixels here were added to the label's ten and made the first group in the
       tree sit lower than the two below it — the fourth value doing the second of the rail's
       two jobs. */
    .branch .page-nav__links { margin: 0 0 var(--s3) 30px; padding-left: 11px;
                               border-left: 1px solid var(--divider); }
    /* A section is filed under its mode, not beside it. `Engagement` and `Overview` shared a
       column, so the mode read as a fifth row in the list rather than as the thing holding the
       four under it — and the rail's other heading, one level up, already uses exactly this
       10px-uppercase-muted treatment. Repeating it at a second depth says *a heading* without
       saying *which*, so the step has to carry the rank. */
    .branch .page-nav__group { padding-left: 9px; }
    .branch .page-nav__links a { padding-left: 19px; }
    /* Moved with the label, not left on the guide line. `left` resolves against the anchor's
       own box, which does not move when its padding grows — so the dot would have stayed put
       at 29px and ended up marking the rule instead of the word. */
    .branch .page-nav__links a::before { left: -3px; }
    .branch__empty { padding: 0 10px var(--s2) 20px; font-size: 13px; }

    /* The mode a section belongs to. Not a link and never was: Engagement names what is under
       it, and Publishing and Ad Manager name what is not built yet — `soon` is the whole
       point, so it is drawn as a tag rather than left to the reader to infer from a dead row. */
    /* The first gap: every group label sits `--rail-above-label` below the list before it,
       including the first one, which used to inherit its container's margin on top of its own
       padding and sat four pixels lower than its siblings. No `:first-child` exception — the
       exception was the bug. */
    .page-nav__group { display: flex; align-items: center; gap: var(--s2);
                       margin-top: var(--rail-above-label);
                       padding: 0 10px 2px 20px; font-family: var(--font-utility);
                       font-size: 10px; font-weight: 600; letter-spacing: .08em;
                       text-transform: uppercase; color: var(--text-secondary); }
    /* No `:first-child` exception. The first group used to inherit its container's margin on
       top of its own and sat four pixels lower than its siblings; zeroing it put it six
       pixels higher instead. One value, applied to all of them. */
    .page-nav__group em { font-style: normal; font-size: 9px; letter-spacing: .04em;
                          padding: 1px 6px; border: 1px solid var(--divider);
                          border-radius: var(--radius-full); color: var(--text-muted); }

    .page-nav__controls { margin-top: auto; padding-top: var(--s4); }
    .page-nav__controls form { display: flex; flex-direction: column; gap: var(--s2);
                               align-items: flex-start; }
    .page-nav__controls .who { font-size: 12px; color: var(--text-muted); }

    /* ---- title row: what this page is, and one fact about it ---- */
    .title-row__meta { font-family: var(--font-utility); font-size: 13px;
                       color: var(--text-muted); text-align: right; }

    /* ---- toolbar: the controls that change what the grid shows ----

       **Separated by space, not by rules.** It was fenced top and bottom, and the identity
       strip above it drew a third — three grey lines in the first 200px of every page, each
       one saying only "something ends here", which the gap says on its own. The controls are
       already enclosed objects: pills and segmented groups have their own outlines, so a rule
       around them is a box around boxes.

       The space is asymmetric on purpose. More below than above, because the toolbar belongs
       to the grid it filters rather than to the row above it, and equal gaps would leave it
       floating between the two. */
    .toolbar { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
               min-height: var(--toolbar-height); margin: var(--s2) 0 var(--s5); }
    /* The bar follows the reader down the page and stays.

       It governs every figure below it and there are six screens of figures, so a reader four
       screens down had to return to the top to change the window — and the label saying the
       page *is* narrowed sits beside the control they scrolled past.

       It was briefly built to leave again on the way up. Settled against: a control that comes
       and goes with the direction of a scroll is a control you have to think about, and this
       one is small enough to simply live there.

       `margin-top` becomes `padding-block`, which is not tidying. A sticky element pins its
       border box at `top: 0` and its margin stays above it, transparent — so the page's own
       text runs through the gap between the viewport and the control. Padding is painted with
       the background; margin is not. */
    /* A request is in flight.

       Two things say so, and both last **at least** 150ms — a minimum, not a delay. Holding
       them back until a quarter-second had elapsed was the first version, and on a local
       database where every request finishes in fifty milliseconds it meant nothing ever
       appeared. Inverted: always shown, never for less than long enough to see. `is-working`
       is set in `portal.js` and outlives the swap, so the new page arrives dimmed and fades
       up — that fade *is* the transition.

       1 · A line across the top. Not a spinner over the middle: the page is still there and
       still readable, and a spinner says otherwise. */
    .working { position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 30;
               background: var(--nf-blue); transform: scaleX(0); transform-origin: left;
               opacity: 0; transition: opacity .12s linear; pointer-events: none; }
    body.is-working .working { opacity: 1;
                               animation: working 1.4s cubic-bezier(.2,.8,.3,1) infinite; }
    /* Never reaches the right-hand edge: a bar that completes and keeps going says the thing
       finished when it did not. It runs out of travel instead. */
    @keyframes working {
      0%   { transform: scaleX(0);   opacity: 1; }
      70%  { transform: scaleX(.85); opacity: 1; }
      100% { transform: scaleX(.95); opacity: .5; }
    }

    /* 2 · The figures are not shown while they are being recomputed.

       This is not decoration. Every number on the page belongs to the *previous* window until
       the response lands, and a page that looks unchanged while it is being replaced invites
       the reader to take a figure that is about to move.

       **No transition, in either direction.** They go on the way out and the new ones are
       simply there. A cross-fade puts a blur over the one moment the reader is watching for and
       doubles the time the page spends saying nothing; the 150ms floor in `portal.js` is what
       makes the gap legible, and the CSS does not need to help.

       **`visibility`, never `display`.** The elements keep their space, so the page does not
       collapse and spring back, and the reader's scroll position still means something — which
       matters here more than usual, because a filter deliberately leaves them where they were.

       **Two exemptions, both deliberate.** The toolbar, because it is the control just clicked
       and blanking it makes the click feel like it missed — which is when people click a second
       time; it stays put and stays live, so changing your mind mid-request works. And the
       masthead, because it is the page's identity rather than one of its figures, and a client's
       own name vanishing reads as the page breaking rather than as a number being recomputed.

       The masthead's lede *does* narrow with the toolbar, so this is a real trade and not an
       oversight: one sentence stays on screen for a beat while it is stale, and the page stops
       blinking end to end every time somebody clicks a filter. */
    body.is-working .content > :not(.toolbar):not(.title-row) { visibility: hidden; }

    /* And the new figures say they are new.

       Two hundred milliseconds, and four pixels of rise. The displacement is the whole trick:
       a pure opacity fade reads as a flicker, and a small movement reads as *this arrived*
       rather than *this was already here*. One direction only — a symmetric fade costs the
       out-time as well as the in-time, and doubles how long the page spends saying nothing.

       **Hung off the end of the working state, and that is not stylistic.** Declared on the
       base rule it would begin when the element is inserted — which is while `is-working` still
       hides it, so it would play out invisibly and be over before the reveal. Attached here,
       the selector *starts matching* when the class comes off, and the animation starts then.

       The exclusions are the same two as above, and a test holds them to it: something that
       hides and does not arrive comes back without the others, and something that arrives
       having never left moves for no reason. */
    @keyframes arrive {
      from { opacity: 0; transform: translateY(4px); }
      to   { opacity: 1; transform: none; }
    }
    body:not(.is-working) .content > :not(.toolbar):not(.title-row) {
      animation: arrive .2s ease; }
    /* The one thing here somebody may have asked their machine not to do. */
    @media (prefers-reduced-motion: reduce) {
      body:not(.is-working) .content > :not(.toolbar):not(.title-row) { animation: none; }
    }

    /* 3 · Something in the space where the figures were, when the wait is long enough.

       The line at the top says a request is in flight; this says where the answer is going to
       appear. Fixed rather than placed in the column, because a filter deliberately leaves the
       reader where they were scrolled to — so "in the content" could be two thousand pixels
       above them, and the only place that is reliably in front of them is the viewport.

       Offset by the rail so it centres on the column it belongs to, and not on the window. */
    .loading { position: fixed; top: 38%; left: var(--side-nav-width); right: 0; z-index: 25;
               display: grid; place-items: center; pointer-events: none;
               opacity: 0; transition: opacity .16s ease; }
    /* **A delay, and the opposite of the two above on purpose.** They are a minimum because a
       click has to visibly do something; this waits, because with the arrival animation the
       whole exchange is over in about 350ms and a ring that appears and vanishes inside that
       window is the flicker the minimum exists to prevent. It shows up when the wait is long
       enough to worry about, and on a loopback it never does. Leaving on the way out has no
       delay — the base rule's transition applies, so it goes the moment the answer lands. */
    body.is-working .loading { opacity: 1; transition-delay: .45s; }
    @media (max-width: 46rem) { .loading { left: 0; } }
    /* Thin, small and unhurried. A fast spinner reads as strain; this is a request that will
       be over in a moment and should look like one. */
    .loading::before { content: ""; width: 22px; height: 22px; border-radius: 50%;
                       border: 2px solid rgba(34,34,34,.10);
                       border-top-color: var(--nf-blue);
                       animation: loading .8s linear infinite; }
    @keyframes loading { to { transform: rotate(360deg); } }
    /* Spinning is the one thing here somebody may have asked their machine not to do. The mark
       stays — it still says where the answer is going — and simply holds still. */
    @media (prefers-reduced-motion: reduce) {
      .loading::before { animation: none; border-color: rgba(34,34,34,.28);
                         border-top-color: var(--nf-blue); }
    }

    .toolbar--sticky { position: sticky; top: 0; z-index: 10;
                       margin-top: 0; padding-block: var(--s4);
                       background: var(--surface-veil);
                       backdrop-filter: blur(10px) saturate(1.4);
                       -webkit-backdrop-filter: blur(10px) saturate(1.4);
                       }
    /* The veil is translucent so the blur has something to do. Where there is no blur to be
       had there is also nothing softening what shows through, so the bar goes back to being
       flatly opaque — a see-through control over a page of numbers is worse than a plain one. */
    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
      .toolbar--sticky { background: var(--surface-page); }
    }
    .toolbar__group { display: flex; align-items: center; gap: var(--s2); }
    .toolbar__group--right { margin-left: auto; }
    /* What the toolbar is doing, in words, beside the controls doing it. Present only when
       something is narrowed — a page that says "all time" on every unfiltered view is a label
       nobody reads by the third screen. */
    .toolbar__narrowing { font-family: var(--font-utility); font-size: 12.5px;
                          color: var(--text-muted); }
    .toolbar__narrowing strong { color: var(--text-primary); font-weight: 600; }
    .segmented { display: inline-flex; border: 1px solid var(--divider);
                 border-radius: var(--radius-pill); padding: 3px; gap: 2px;
                 background: var(--surface-raised); }
    .segmented a, .segmented span { font-family: var(--font-utility); font-size: 13px;
                 font-weight: 500; padding: 3px 14px; border-radius: var(--radius-pill);
                 color: var(--text-muted); text-decoration: none; }
    .segmented .on { background: var(--nf-blue); color: var(--nf-white); }

    /* ---- the 12-column grid ---- */
    .dashboard-grid { display: grid; grid-template-columns: repeat(var(--grid-cols), 1fr);
                      column-gap: var(--grid-gap-x); row-gap: var(--grid-gap-y);
                      align-items: start; }
    /* A column that holds more than one panel stacks them at the grid's own gap, so a
       two-panel column and a one-panel column beside it stay on the same rhythm. */
    .stack { display: flex; flex-direction: column; gap: var(--grid-gap-y); }
    .dashboard-grid + .dashboard-grid { margin-top: var(--grid-gap-y); }
    .col-3 { grid-column: span 3; } .col-4 { grid-column: span 4; }
    .col-5 { grid-column: span 5; } .col-6 { grid-column: span 6; }
    .col-7 { grid-column: span 7; } .col-8 { grid-column: span 8; }
    .col-12 { grid-column: span 12; }
    @media (max-width: 62rem) {
      .col-3, .col-4 { grid-column: span 6; }
      .col-5, .col-6, .col-7, .col-8 { grid-column: span 12; }
    }
    @media (max-width: 46rem) {
      body[data-nav-position="side"] { grid-template-columns: 1fr;
                                       grid-template-areas: "nav" "body"; }
      .page-nav { position: static; max-height: none; flex-direction: row;
                  flex-wrap: wrap; align-items: center; gap: var(--s3);
                  border-right: 0; border-bottom: 1px solid var(--divider); }
      /* The rail is a horizontal bar here and the brand has a row to share rather than a
         column to own, so the lockup drops its wordmark and keeps the mark and the name. */
      .brand-lockup--full { display: none; }
      .brand-lockup--compact { display: block; width: 153px; }
      .page-nav__links, .page-nav__subjects { flex-direction: row; flex-wrap: wrap; }
      .page-nav__controls { margin: 0 0 0 auto; padding: 0; }
      .page-nav__rule { display: none; }
      .col-3, .col-4, .col-5, .col-6, .col-7, .col-8 { grid-column: span 12; }
      .content { padding: var(--s3); }
    }

    /* ---- KPI: one number, its label, and what it is next to ---- */
    /* Centred in a card with a floor, which is what makes a row of them read as one band.
       It was `gap: 8px` with the comparison pinned to the bottom by `margin-top: auto`, so a
       stretched card left a hand's width of white between the number and its reading. */
    .kpi { background: var(--surface-raised); border: 1px solid var(--divider);
           padding: var(--card-padding); min-width: 0; display: flex; flex-direction: column;
           justify-content: center; min-height: 140px; position: relative; }
    /* Sentence case. Uppercase here and sentence case on the card headings was the wrong way
       round in both places: the label sits under a 40px numeral and does not need to shout,
       and the card heading is the one carrying no other signal. */
    .kpi__label { font-family: var(--font-utility); font-weight: var(--kpi-label-weight);
                  font-size: 12px; color: var(--text-muted); }
    .kpi__value { font-family: var(--font-body); font-weight: var(--kpi-value-weight);
                  font-size: var(--kpi-value-size-standard); line-height: 1.05;
                  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
                  margin: 6px 0 4px; }
    .kpi__value.compact { font-size: var(--kpi-value-size-compact); }
    /* Primary ink, not muted: the comparison is the half of a KPI that carries the meaning,
       and the tail after it is the part that recedes. */
    .kpi__compare { font-family: var(--font-utility); font-size: 12.5px;
                    color: var(--text-primary); }
    .kpi__compare .quiet { color: var(--text-muted); }

    /* ---- page header: title left, one primary action right ----
       One name. `.head` and `.title-row` were the same idea under two, which was survivable
       while both were a row of text and stopped being survivable the moment the header became
       a painted band — ten pages kept the old name and silently did not get it. */
    .title-row { display: flex; align-items: flex-end; gap: var(--s3);
                 margin: 0 0 var(--s3); padding-bottom: var(--s3); flex-wrap: wrap; }
    .title-row .grow { flex: 1; min-width: 0; }
    h1 { font-size: 30px; font-weight: 300; margin: 0 0 var(--s1); letter-spacing: -.02em;
         line-height: 1.15; text-wrap: balance; }
    h2 { font-size: 19px; font-weight: 500; margin: var(--s8) 0 var(--s3); letter-spacing: -.01em; }
    h3 { font-size: 15px; font-weight: 600; margin: 0 0 var(--s1); }
    .lede { color: var(--muted); margin: 0; font-size: .925rem; max-width: 44rem; }
    .hint { color: var(--muted); font-size: .85rem; font-weight: 400; }
    /* The sentence under a chart. `.hint` is the app's older, quieter version and stays for
       form help; a reading is a finding and is set to be read, with the claim in primary ink
       and the qualification behind it. */
    .read { font-size: 13px; color: var(--text-secondary); margin: 12px 0 0; line-height: 1.5; }
    .read b, .read strong { color: var(--text-primary); }
    .empty { color: var(--muted); font-style: italic; padding: var(--s6) 0; }

    /* ---- buttons ---- */
    /* Fully round against square surfaces — the brand's signature pairing. */
    .btn { display: inline-block; font: inherit; font-size: 15px; font-weight: 500;
           padding: 7px 18px; border: 1px solid var(--nf-blue);
           border-radius: var(--radius-pill); background: var(--nf-blue);
           color: var(--nf-white); cursor: pointer; text-decoration: none; white-space: nowrap; }
    /* **Every hover pins its own colour**, and that is not belt-and-braces. `a:hover` is
       page-wide and more specific than `.btn`, so on hover a button's text came from *there* —
       which on a primary button meant `#0E81B6` ink on a `#0E81B6` ground. The label did not
       fade, it disappeared, at the exact moment the reader was pointing at it to ask what it
       did. Guarded by `tests/test_a_hover_never_hides_the_label.py`. */
    .btn:hover { background: var(--nf-blue-hover); border-color: var(--nf-blue-hover);
                 color: var(--nf-white); }
    .btn.secondary { background: transparent; color: var(--text-primary);
                     border-color: rgba(34,34,34,.20); }
    .btn.secondary:hover { background: var(--surface-sunken); border-color: rgba(34,34,34,.35);
                           color: var(--text-primary); }
    .btn.small { padding: 3px 13px; font-size: 13px; }
    .btn.ghost { background: transparent; border-color: transparent; color: var(--nf-blue); }
    .btn.ghost:hover { background: var(--blue-10); color: var(--nf-blue-dark); }
    /* ---- Google's sign-in button ---- */
    /* Theirs, not ours. Every value here comes from Google's sign-in guidelines and none of
       them is a design decision available to this codebase: the mark as issued, 40px tall, a
       `#747775` hairline, `#1F1F1F` at 14px medium with .25px tracking, and only the wording
       they sanction.

       The pill radius is ours and is also theirs — the guidelines offer a pill variant, and it
       is the one that sits properly in an interface whose signature is round against square.

       `color` is pinned on hover for the same reason every button here now pins it: `a:hover`
       is page-wide and more specific than a block class, so without this the label turns blue
       under the cursor and the button stops being Google's. */
    .gsi { display: inline-flex; align-items: center; gap: 10px; height: 40px;
           padding: 0 12px; border-radius: var(--radius-pill);
           background: #fff; border: 1px solid #747775; color: #1F1F1F;
           font-family: var(--font-utility); font-size: 14px; font-weight: 500;
           letter-spacing: .25px; text-decoration: none;
           transition: background .12s ease, box-shadow .12s ease; }
    .gsi:hover { background: #F8F9FA; color: #1F1F1F; border-color: #747775;
                 box-shadow: 0 1px 2px rgba(60,64,67,.30), 0 1px 3px 1px rgba(60,64,67,.15); }
    .gsi:focus-visible { outline: 2px solid var(--nf-blue); outline-offset: 2px; }
    .gsi__mark { display: grid; place-items: center; width: 20px; height: 20px; flex: none; }
    .gsi__label { display: block; }

    .actions { display: flex; gap: var(--s2); align-items: center; }

    /* ---- tables: dense by default; an analyst scans these against a clock ---- */
    table { border-collapse: collapse; width: 100%; font-family: var(--font-utility);
            font-size: 14px; background: var(--surface-raised); }
    th, td { text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--line);
             vertical-align: top; }
    th { color: var(--text-muted); font-weight: 600; font-size: 11px; letter-spacing: .06em;
         text-transform: uppercase; white-space: nowrap;
         border-bottom-color: var(--divider); }
    tbody tr:hover { background: var(--wash); }
    td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
    td a { color: var(--link); }
    .sub { color: var(--text-muted); font-size: 13px; }
    /* A LinkedIn headline runs to 200 characters. In a table it is context, not content —
       one line, and the whole of it on hover. */
    /* Inside a card the table has a column's width, not the page's: at 22rem the headline
       took the room the last column needed and its own text was clipped mid-word — "did not
       retur". The card is the narrower context, so it gets the narrower cap. */
    .card .clip { max-width: 13rem; }
    .clip { display: block; max-width: 22rem; overflow: hidden; text-overflow: ellipsis;
            white-space: nowrap; }
    /* A table must never push the page sideways: an analyst scanning rows should not have to
       scroll horizontally to see the column that carries the decision. */
    .scroller { overflow-x: auto; }
    table { table-layout: auto; }

    /* ---- forms ---- */
    form.stack { max-width: 34rem; }
    label { display: block; font-weight: 600; font-size: .9rem; margin: var(--s4) 0 var(--s1); }
    input[type=url], input[type=text], input[type=search], textarea, select {
      width: 100%; padding: .45rem .55rem; border: 1px solid var(--line);
      border-radius: 0; font: inherit; font-size: 15px; background: var(--surface-raised); }
    input:focus, textarea:focus, select:focus { outline: 2px solid var(--link);
      outline-offset: -1px; border-color: var(--link); }
    textarea { min-height: 7rem; font-family: ui-monospace, SFMono-Regular, monospace;
      font-size: .78rem; line-height: 1.45; }
    fieldset { border: 1px solid var(--line); border-radius: var(--radius);
               padding: 0 var(--s3) var(--s3); margin: var(--s4) 0 0; }
    legend { font-weight: 600; font-size: .85rem; padding: 0 var(--s1); }
    .checks label { font-weight: 400; display: flex; gap: var(--s2); align-items: center;
                    margin: var(--s2) 0; font-size: .9rem; }
    .checks input { width: auto; }
    .formactions { display: flex; gap: var(--s3); align-items: center; margin-top: var(--s6);
                   padding-top: var(--s4); border-top: 1px solid var(--line); }

    /* ---- notes, counts, cards ---- */
    /* A callout has NO border. None, on any side — the brand forbids it outright,
     * because a single-sided rule "reads as a generic CMS pattern, the kind of
     * treatment WordPress themes ship with by default". The 10% tints exist to
     * mark a region without chrome; that is the whole treatment.
     *
     * Three roles and no more: something to do, something refused, something
     * fine. Red is not a synonym for bad — it means a thing was refused. */
    .note { padding: var(--s2) var(--s3); margin: var(--s4) 0; font-size: 15px;
            color: var(--text-primary); }
    .note.warn { background: var(--orange-10); }
    .note.ok   { background: var(--green-10); }
    .note.bad  { background: var(--red-10); }
    .note strong, .note b { color: var(--text-primary); }
    .counts { display: flex; gap: var(--s6); flex-wrap: wrap; margin: var(--s4) 0; }
    .counts div { min-width: 4.5rem; }
    /* Figures are IBM Plex Sans Light. A number set in Light at size reads as a
     * quantity; the same number in Semibold reads as a headline about itself. */
    .counts .n { font-size: 28px; font-weight: 300; display: block; line-height: 1.1;
                 letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
    .counts .k { color: var(--text-muted); font-family: var(--font-utility);
                 font-size: 11px; font-weight: 600; letter-spacing: .09em;
                 text-transform: uppercase; }
    .cards { display: grid; gap: var(--s3);
             grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
    /* The brand's four card surfaces. Default carries most things; accent and
     * feature are for one statement per page, at most — "paint sparingly". */
    .card { border: 1px solid var(--divider); background: var(--surface-raised);
            padding: var(--card-padding-lg); }
    /* A card's heading is a label, not a title: small, spaced, upper, and recessive, so the
       chart under it is the thing the eye lands on. It had no rule at all and fell through to
       the page's `h3` — sentence case, body font, near-black — which read as a second headline
       competing with the number beside it. */
    .card > h3 { margin: 0 0 14px; font-family: var(--font-utility); font-size: 11px;
                 font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
                 color: var(--text-secondary); }
    /* The qualifier floats to the far edge of the same line. Inline after the heading it read
       as part of it, so "Audience per post new against returning" ran together as one phrase. */
    .card > h3 .sub, .card > h3 .more { float: right; font-weight: 400; letter-spacing: 0;
                                        text-transform: none; font-size: 12px;
                                        color: var(--text-muted); }
    .card h3 a { color: var(--text-primary); }
    .card--accent { background: var(--nf-blue); border-color: var(--nf-blue);
                    color: var(--nf-white); }
    .card--feature { background: var(--nf-dark-grey); border-color: var(--nf-dark-grey);
                     color: var(--nf-white); }
    .card--accent a, .card--feature a { color: var(--nf-white); }
    /* ---- Ad Manager, a mock-up ----
       Five figures and an audience line do not fit the default 15rem column, and wrapping
       them turns a scan into a read. */
    .cards--wide { grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); }
    .card.is-quiet { background: transparent; }
    .card.is-quiet .adv, .card.is-quiet h2 { color: var(--text-muted); }
    .adaud { margin: var(--s3) 0; padding: var(--s2) 0; font-size: .9rem;
             border-top: 1px solid var(--divider); border-bottom: 1px solid var(--divider); }
    /* Auto-fit rather than a fixed count: a draft campaign shows an em dash where four of the
       five numbers would be, and five equal columns of nothing is a worse shape than three. */
    .adfigures { display: grid; gap: var(--s2) var(--s4);
                 grid-template-columns: repeat(auto-fit, minmax(5rem, 1fr)); }
    .adk { display: block; font-family: var(--font-utility); font-size: 10px; font-weight: 600;
           letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); }
    .adv { display: block; font-size: 1.05rem; font-variant-numeric: tabular-nums;
           color: var(--text-primary); }
    /* The distinction the whole screen is arguing for, so it is drawn and not left to the
       wording: an audience the portal keeps current, against one it can only point at. */
    .adfresh { display: inline-flex; align-items: baseline; gap: 5px; font-size: .82rem;
               color: var(--ok); white-space: nowrap; }
    /* `baseline` above and a shrink guard here: the marker was centring against a two-line
       block and floating in the gutter beside it rather than sitting with the words. */
    .adfresh::before { content: ""; width: 5px; height: 5px; flex: 0 0 5px;
                       border-radius: var(--radius-full); background: var(--ok); }
    .facts { display: grid; grid-template-columns: max-content 1fr; gap: var(--s2) var(--s4);
             font-size: .9rem; margin: 0; }
    .facts dt { color: var(--muted); }
    .facts dd { margin: 0; }
    code { font-family: ui-monospace, SFMono-Regular, monospace; font-size: .82em; }
    blockquote { margin: var(--s2) 0; padding-left: var(--s3);
                 border-left: 2px solid var(--line); color: var(--ink); font-size: .9rem; }
    /* Round, like every control. A pill carries a state or a list membership and
     * never a count — counts are neutral text, because a number in a coloured
     * capsule reads as a verdict on itself. */
    .pill { display: inline-flex; align-items: center; gap: 5px;
            font-family: var(--font-utility); font-size: 11px; font-weight: 600;
            letter-spacing: .04em; text-transform: uppercase; padding: 2px 10px;
            /* A rounded end only works on one line: wrapped, the radius draws stadium curves
               around a two-line block and reads as a rendering fault. The column widens
               instead, which is the right thing to give up. */
            white-space: nowrap;
            border-radius: var(--radius-pill); background: var(--surface-sunken);
            border: 0; color: var(--text-secondary); }
    .pill.blue { background: var(--blue-10); color: var(--nf-blue); }
    .pill.new { background: var(--blue-10); color: var(--nf-blue); }
    /* ---- THE BAND ----
       The one section on these five pages you ACT on — every other card reports. So it is
       marked as a surface rather than by shouting: a neutral lift off the page plus a rule down
       its leading edge, which is the whole of the accent.

       **No border, on any side.** It was a `.card` as well as this, so it carried a 1px box AND
       a left edge — and on its own page that gave a masthead saying *Shortlist*, a section label
       saying it again, and a bordered card saying it a third time. Three frames and two headings
       around one table. A band has no outside, so it cannot be inside anything.

       It is also what the brand actually asks for. Callouts carry no borders: a single-sided
       rule "reads as a generic CMS pattern — the kind of treatment WordPress themes ship with by
       default", and the 10% tints exist to mark a region without chrome. The app had been
       breaking its own note at the top of this file.

       `--surface-sunken` and `--nf-blue`, both existing tokens, because they are already rgba
       over whatever they sit on and therefore need no second definition for a dark surface —
       inventing a flat hex here would be the one panel that stops working when the theme does.
       The tint is deliberately below the threshold where it reads as a warning: this is "start
       here", not "something is wrong". */
    .band { background: var(--surface-raised); border-left: 3px solid var(--nf-blue);
            padding: var(--s3) var(--s3) var(--s3) 24px;
            box-shadow: inset 0 0 0 1px var(--line); }
    .band > h3 { margin-top: 0; }
    /* Anything with its own surface sits on a tinted ground here, so the plain ones need one of
       their own or they dissolve into it. A `weak` chip that cannot be seen is a criterion that
       silently stopped being reported. */
    .band .pill:not(.ok):not(.warn):not(.stroke) { background: var(--surface-raised); }

    /* The elevated list. Denser than a report table on purpose: it is read at a glance before
       a call, not studied, and its own first draft was three lines a row because each carried a
       sentence of model prose. Every cell here is a word or a chip. */
    .elevated { margin-top: var(--s4); }
    /* **Not `table-layout: fixed`.** It was, with five percentages, and they were right for the
       two chips a person carried while only people were judged. Judging firms took that to
       eight, `Judged on` needed the room, and `Last captured` kept a fifth of the table while
       its content was clipped to "14 …" — a wide column holding a crushed cell. Let the browser
       size them and constrain the ones that would otherwise run away. */
    .elevated td { padding-block: 7px; vertical-align: middle; }
    .elevated .nowrap { white-space: nowrap; }
    .elevated__judged { width: 34%; }
    .elevated__tier { width: 14px; padding-right: 0; }
    .elevated__fit { width: 70px; }
    .elevated__tags { margin-top: 3px; }
    /* One line each, cut rather than wrapped — a title or a place that runs to two lines doubles
       the height of every row for a detail nobody reads to the end of. `max-width` and not
       `width`, so a short one takes only what it needs. */
    /* One line each, cut rather than wrapped. **Anchored on named columns, not positions.**
       These were `nth-child(2)` and `nth-child(5)`, a tier column was inserted in front of
       them, and every one of them started pointing at the wrong cell — so nothing clipped, the
       post title wrapped to three lines and rows went from 50px to 103px. A position is a
       selector that breaks when a column is added, which is the one thing a table does. */
    .elevated .dv-title,
    .elevated__who .sub,
    /*  The count of somebody's other current employers, beside the chip naming the main one.
        Sits on the same line as the chip rather than under it: it qualifies that name, and a
        line of its own would read as a second employer rather than as "and two more".  */
    .elevated__also { margin-left: 4px; vertical-align: middle; cursor: default; }

    .elevated__where .sub { display: block; max-width: 22ch; overflow: hidden;
                            text-overflow: ellipsis; white-space: nowrap; }
    .elevated__did .sub { display: block; max-width: 24ch; overflow: hidden;
                          text-overflow: ellipsis; white-space: nowrap; }
    .elevated .person { align-items: center; }
    /* The bucket tabs need air above them: hard against the paragraph they read as part of it. */
    .buckets { margin-top: var(--s4); }

    /* The tier, as an edge rather than a word. It carries no text — the score two columns over
       is the same fact stated precisely — so it is here only to let the eye find where one tier
       stops and the next begins. */
    .tierbar { display: block; width: 3px; height: 30px; background: var(--line); }
    .tierbar--strong { background: var(--ok); }
    .tierbar--moderate { background: var(--warn); }
    .tierbar--weak { background: var(--bad); opacity: .5; }

    /* **The score and its coverage, in one cell, always.** 0.80 from three of eight criteria and
       0.80 from seven of eight are not the same claim, and a screen showing the first number
       alone misleads constantly — the same defect as a bounded number under an unbounded
       caption. Tabular figures so a column of them lines up and can be compared without reading
       each one. */
    .score { font-family: var(--font-utility); font-size: 15px; font-weight: 600;
             font-variant-numeric: tabular-nums; }
    .cover { font-family: var(--font-utility); font-size: 11px; color: var(--muted);
             font-variant-numeric: tabular-nums; white-space: nowrap; }
    /* Thin coverage says so in its own colour rather than waiting to be read. Orange carries
       attention here, not refusal: a record answered on two criteria is not wrong, it is a
       number that means less than the one above it. */
    .cover--thin { color: var(--warn); font-weight: 600; }

    /* ---- one criterion, as a chip ----
       Eight of these to a row, in the rubric's declared order. Small enough that they take two
       lines rather than three, which is what set the height of every row in the table. */
    /* **The ranker's sentence, two lines of it.** On a `div` inside the cell and never on the
       `td` itself: a `display`-changing class put straight on a `<td>` took that column out of
       the table's own layout once, and the row went from 50px to 103px with every test green.
       Anchored on its own class rather than on `:nth-child`, for the same reason — a column
       inserted in front of this one would silently point every rule one cell to the left. */
    .elevated__why { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
                     overflow: hidden; margin-bottom: 5px; font-size: 12px; line-height: 1.45;
                     color: var(--text-secondary); }

    .crits { display: flex; flex-wrap: wrap; gap: 3px; align-items: center; }
    .crit { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
            font-family: var(--font-utility); font-size: 9.5px; font-weight: 600;
            letter-spacing: .04em; text-transform: uppercase; padding: 2px 7px 2px 6px;
            border-radius: var(--radius-pill); cursor: default;
            background: rgba(34,34,34,.05); color: var(--faint); }
    .crit__mark { font-size: 7.5px; line-height: 1; }
    /* Its own rule rather than bare text in the chip, so the name cannot inherit the mark's
       size when one of them is next edited. */
    .crit__name { font-size: inherit; }
    .crit--s { background: var(--green-10); color: var(--ok); }
    .crit--m { background: var(--orange-10); color: var(--warn); }
    .crit--w { background: var(--red-10); color: var(--bad); }
    /* Hollow rather than filled, and it keeps its name. "2 of 8" says how many could not be
       answered; a hollow `ownership` says which — and which is the one somebody can do
       something about. */
    .crit--u { background: transparent; box-shadow: inset 0 0 0 1px rgba(34,34,34,.13);
               color: var(--faint); }

    /* ---- the key, and the note behind it ----
       Drawn once under the table rather than per row: the marks and the abbreviations are a
       vocabulary, and a vocabulary is explained where it ends rather than on every use. */
    .legend { display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
              margin-top: var(--s3); padding-top: var(--s2);
              border-top: 1px solid var(--line);
              font-family: var(--font-utility); font-size: 11.5px; color: var(--muted); }
    .legend__k { display: inline-flex; align-items: center; gap: 5px; }
    .legend__k i { font-style: normal; font-size: 8px; }
    .legend__k--s i { color: var(--ok); }
    .legend__k--m i { color: var(--warn); }
    .legend__k--w i { color: var(--bad); }
    .legend__k--u i { color: var(--faint); }
    .legend__said { max-width: 52ch; }
    .legend__help { margin-left: auto; font-family: var(--font-utility); font-size: 11.5px;
                    font-weight: 600; color: var(--nf-blue-dark); background: none; border: 0;
                    padding: 0; cursor: pointer; text-decoration: underline;
                    text-underline-offset: 2px; }
    .legend__help:hover { color: var(--nf-blue); }
    .helpnote { margin-top: var(--s2); background: var(--surface-raised); padding: 16px 18px;
                font-size: 13.5px; color: var(--muted); line-height: 1.5; }
    .helpnote h4 { font-size: 13px; margin: 0 0 8px; color: var(--ink); }
    .helpnote dl { display: grid; grid-template-columns: auto 1fr; gap: 5px 14px; margin: 0; }
    .helpnote dt { font-family: var(--font-utility); font-size: 10px; font-weight: 600;
                   letter-spacing: .05em; text-transform: uppercase; color: var(--faint);
                   padding-top: 2px; white-space: nowrap; }
    .helpnote dd { margin: 0; }
    .helpnote p { margin: 11px 0 0; }

    /* ---- the ledger: everyone who engaged ----
       One line a person, and every column doing work. The list it replaces spent 285px on a
       name and 500px on a company chip and still could not say what anybody had done.

       `band--plain` because this is the long list rather than the thing to act on: it wants the
       same shape as the shortlist above it — a region, not a box — without claiming the same
       urgency, so it keeps the tint and drops the blue edge. */
    .band--plain { border-left-color: var(--line); border-left-width: 1px;
                   padding-left: var(--s3); }
    .ledger td { padding-block: 5px; vertical-align: middle; }
    .ledger tbody tr:hover { background: var(--surface-sunken); }
    .ledger__gut { width: 16px; padding-right: 0; text-align: center;
                   color: var(--nf-blue-dark); font-size: 11px; }
    /* Sized, because seven auto-width columns over 1000px put 220px of nothing between Role
       and Where. The two that can run long are capped and the three counts are as narrow as
       their headings. */
    .ledger__who { width: 24%; }
    .ledger__role { width: 22%; }
    .ledger__where { width: 26%; }
    .ledger .clip { display: block; max-width: 26ch; overflow: hidden;
                    text-overflow: ellipsis; white-space: nowrap; }
    /* The group heading is a row rather than a second table, because it IS a row group — and
       two tables side by side size their columns independently, which is how a grouped list
       ends up with two different sets of column widths. */
    .ledger__group td { padding: var(--s3) 0 6px; border-bottom: 1px solid var(--line);
                        font-family: var(--font-utility); font-size: 10.5px; font-weight: 600;
                        letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
    .ledger tbody tr:first-child.ledger__group td { padding-top: 6px; }
    .ledger__group tr:hover, .ledger tbody tr.ledger__group:hover { background: none; }
    .ledger__head { color: var(--nf-purple); font-size: 12px; letter-spacing: .06em;
                    margin-right: 8px; }
    .ledger__rest { float: right; text-transform: none; letter-spacing: 0; font-weight: 400;
                    font-size: 11.5px; }
    /* A dash rather than a nought, and grey rather than black: this column is mostly empty and
       a column of black noughts reads as data when it is the absence of it. */
    .ledger .is-nought { color: rgba(34,34,34,.22); }
    /* And the few that are not empty carry weight. Only about 7% of engagements have words in
       them, so these are the rows the list exists to surface. */
    .ledger .is-said { font-weight: 600; color: var(--nf-purple); }

    /* ---- the hover card ----
       A chip's own sentence, on demand. It is not a `title`: a native tooltip can only
       concatenate the question, the level and the reason, and the concatenation was what a
       reader met first — "Current role: CEO · Agathon · 1999 · Current role: CEO · Agathon ·
       1999". It is also slow enough that nobody waits for it.

       `pointer-events: none` so the card can never sit between the cursor and the chip that
       opened it, which is how a tooltip starts flickering. */
    .tip { position: fixed; z-index: 80; max-width: 330px; padding: 11px 13px;
           background: var(--surface-masthead); color: var(--nf-white);
           font-family: var(--font-body); font-size: 13px; line-height: 1.45;
           box-shadow: 0 6px 24px rgba(0,0,0,.22); pointer-events: none;
           opacity: 0; transition: opacity .09s; }
    .tip--on { opacity: 1; }
    .tip__q { display: block; margin-bottom: 3px; font-family: var(--font-utility);
              font-size: 10px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
              color: rgba(255,255,255,.6); }
    /* The level's own colour, lifted for a dark ground — the page tokens are mixed for light
       surfaces and go muddy here. */
    .tip__lv { font-weight: 600; }
    .tip__lv--s { color: #7FE3C0; }
    .tip__lv--m { color: #FFB37A; }
    .tip__lv--w { color: #FF8FA8; }
    .tip__lv--u { color: rgba(255,255,255,.62); }
    .tip__why { display: block; margin-top: 5px; color: rgba(255,255,255,.82); }
    .tip__none { display: block; margin-top: 5px; color: rgba(255,255,255,.55); font-style: italic; }

    .pill.ok { background: var(--green-10); color: var(--ok); }
    .pill.warn { background: var(--orange-10); color: var(--warn); }
    .pill.bad { background: var(--red-10); color: var(--bad); }
    .pill.stroke { background: transparent; box-shadow: inset 0 0 0 1px currentColor;
                   color: var(--nf-blue); }
    .pager { display: flex; gap: var(--s3); align-items: center; margin-top: var(--s4);
             font-size: .875rem; color: var(--muted); }

    /* ---- people ----
       A monogram, not a photograph: both photo sources are expiring signed URLs, and storing
       the images would mean holding pictures of third parties under an open retention
       question. The colour is a hash of the person, so it is the same on every screen and
       after every restart — which is what makes a row findable again on a long list. */
    .avatar { position: relative; display: inline-flex; align-items: center;
              justify-content: center; width: 1.75rem; height: 1.75rem; border-radius: 50%;
              flex: 0 0 auto; overflow: hidden;
              font-size: .675rem; font-weight: 600; font-variant: normal;
              background: hsl(var(--hue) 42% 91%); color: hsl(var(--hue) 45% 27%);
              border: 1px solid hsl(var(--hue) 34% 83%); user-select: none; }
    /* Over the initials, not instead of them: an image that fails to load reveals what was
       already underneath, with no gap and no layout shift. */
    .avatar img { position: absolute; inset: 0; width: 100%; height: 100%;
                  object-fit: cover; border-radius: 50%; }
    .avatar.big { width: 3rem; height: 3rem; font-size: 1.05rem; }
    .person { display: flex; gap: var(--s2); align-items: flex-start; }
    .person .grow { flex: 1; min-width: 0; }

    /* ---- a record in two columns: where they are, and where they were ---- */
    .split2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s8);
              align-items: start; }
    .split2 h2 { margin-top: var(--s6); }
    .rolelist { list-style: none; margin: 0; padding: 0; font-size: .9rem; }
    .rolelist li { display: grid; grid-template-columns: 1fr max-content; gap: var(--s2);
                   padding: .35rem 0; border-bottom: 1px solid var(--line); }
    .rolelist li:last-child { border-bottom: 0; }
    .rolelist .when { color: var(--muted); font-variant-numeric: tabular-nums;
                      font-size: .82rem; white-space: nowrap; }
    /* A section somebody opens when they want to know why, not something they scroll past. */
    details.app-details { border-top: 1px solid var(--line); padding: var(--s3) 0 0;
                          margin-top: var(--s8); }
    details.app-details > summary { cursor: pointer; font-size: .85rem; color: var(--muted);
                                    font-weight: 600; list-style: none; display: flex;
                                    gap: var(--s1); align-items: center; }
    details.app-details > summary::-webkit-details-marker { display: none; }
    details.app-details > summary::before { content: "\25b8"; font-size: .7rem; }
    details.app-details[open] > summary::before { content: "\25be"; }
    .pill.ok { background: var(--ok-bg); color: var(--ok); border-color: transparent; }
    .pill.warn { background: var(--warn-bg); color: var(--warn); border-color: transparent; }
    @media (max-width: 52rem) { .split2 { grid-template-columns: 1fr; gap: var(--s4); } }

    /* ---- capture: form on the left, consequences on the right ----
       The pane is the point of the screen, so it does not scroll away from the textarea being
       pasted into. Below 60rem the two stack and the pane comes first, because on a phone the
       answer matters more than the form that produced it. */
    .split { display: grid; gap: var(--s6); grid-template-columns: minmax(0, 1fr) 22rem;
             align-items: start; margin-top: var(--s4); }
    .split form.stack { max-width: none; margin: 0; }
    /* The consequences panel. Sticky because a reviewer scrolls the form and needs the answer
       to stay in view — it is the whole reason the screen is split. */
    .pane { border: 1px solid var(--divider); padding: var(--s3);
            background: var(--surface-raised); position: sticky; top: var(--s3); }
    .dashboard-grid > form.stack, .col-8 > form.stack { max-width: none; margin: 0; }
    .pane h2 { margin-top: 0; font-size: .95rem; }
    .pane h3 { margin-top: var(--s4); text-transform: capitalize; color: var(--muted);
               font-size: .8rem; letter-spacing: .02em; }
    .pane .note { font-size: .85rem; }
    .pane-post p { margin: 0 0 var(--s1); }
    .pane-actions { display: flex; gap: var(--s2); align-items: center; margin-top: var(--s6);
                    padding-top: var(--s4); border-top: 1px solid var(--line); }
    .pane-actions .btn[disabled] { opacity: .45; cursor: not-allowed; }
    .counts.tight { gap: var(--s4); margin: var(--s2) 0; }
    .counts.tight .n { font-size: 1.15rem; }
    .pill.new { background: var(--ok-bg); color: var(--ok); border-color: #cfe3d8; }
    @media (max-width: 60rem) {
      .split { grid-template-columns: 1fr; }
      .pane { position: static; order: -1; }
    }

    /* ---- toasts: what just changed, then out of the way ----
       Only ever counts, and only ever things that went well. Anything a reviewer needs to act
       on stays on the page — a message that fades is no way to report a broken paste. */
    .toasts { position: fixed; right: var(--s6); bottom: var(--s6); z-index: 20;
              display: flex; flex-direction: column; gap: var(--s2); }
    .toast { background: var(--ink); color: #fff; font-size: .875rem; padding: .5rem .9rem;
             border-radius: var(--radius); box-shadow: 0 2px 10px rgba(0,0,0,.18);
             animation: toast-in .18s ease-out; }
    .toast.going { opacity: 0; transform: translateY(.4rem); transition: opacity .4s, transform .4s; }
    @keyframes toast-in { from { opacity: 0; transform: translateY(.5rem); } }
    @media (prefers-reduced-motion: reduce) {
      .toast { animation: none; } .toast.going { transition: none; }
    }

    /* ================================================================
       Surfaces added with the navigation rework.

       One idea throughout: a page should be readable at three distances.
       From across the room the shape says how much there is; at arm's
       length the headings and numbers say what happened; up close the
       rows say who. Everything below exists to make one of those work.
       ================================================================ */

    /* ---- the tab bar's divider between what is built and what is not ---- */
    .tabs .tabsep { flex: 0 0 1px; align-self: center; height: 1rem;
                    background: var(--line); margin: 0 var(--s3); }
    .tabs a.soon { color: var(--faint); }
    .tabs a.soon::after { content: "soon"; font-size: .625rem; letter-spacing: .04em;
                          text-transform: uppercase; color: var(--faint);
                          border: 1px solid var(--line); border-radius: 3px;
                          padding: 0 .25rem; margin-left: .35rem; vertical-align: 1px; }
    .tabs a.soon[aria-current="page"] { color: var(--ink); }

    /* ---- section subnav: one section, several doors ---- */
    .subnav { display: flex; gap: var(--s1); margin: 0 0 var(--s6);
              border-bottom: 1px solid var(--line); }
    .subnav a { padding: var(--s2) var(--s3); font-size: .875rem; color: var(--muted);
                text-decoration: none; border-bottom: 2px solid transparent;
                margin-bottom: -1px; }
    .subnav a:hover { color: var(--ink); }
    .subnav a[aria-current="page"] { color: var(--text-primary);
                                     border-bottom-color: var(--nf-blue); font-weight: 600; }

    /* ---- stat strip: the three or four numbers a page opens with ----
       Equal columns rather than inline text, because the eye compares
       magnitudes down a column and cannot compare them along a sentence. */
    .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
             border: 1px solid var(--line); border-radius: var(--radius);
             overflow: hidden; background: #fff; }
    .stats .stat { padding: var(--s4) var(--s4); border-right: 1px solid var(--line); }
    .stats .stat:last-child { border-right: 0; }
    .stat .n { font-size: 28px; font-weight: 300; line-height: 1.1; letter-spacing: -.02em;
               font-variant-numeric: tabular-nums; }
    .stat .k { display: block; margin-top: var(--s1); font-family: var(--font-utility);
               font-size: 11px; font-weight: 600; letter-spacing: .09em;
               text-transform: uppercase; color: var(--text-muted); }
    .stat .sub { display: block; margin-top: var(--s1); font-size: .8rem; color: var(--faint); }
    .stat.quiet .n { color: var(--faint); }

    /* ---- sparkline: shape first, numbers second ----
       Bars and not a line: a line implies a continuous quantity, and this
       is a count of events per week, which is discrete. */
    .spark { display: flex; align-items: flex-end; gap: 2px; height: 2.25rem;
             margin-top: var(--s2); }
    /* Neutral throughout. The brand sets trend-up and trend-down to the same
     * colour deliberately — direction is not sentiment, and green-for-up decides
     * for the reader that more engagement is good. Only the current week is
     * coloured, and it marks *now* rather than *well*. */
    .spark i { flex: 1; background: rgba(34,34,34,.14); min-height: 2px; }
    .spark i.on { background: rgba(34,34,34,.42); }
    .spark i.last { background: var(--nf-blue); }
    .sparkcap { font-size: .75rem; color: var(--faint); margin-top: var(--s1); display: block; }
    /* The hero's version: taller, wider gaps, sized to sit beside a 64px numeral. One bar
       per post rather than per week — the unit of time here is the post, because 0 of 238
       reactions carry a timestamp and a chart with days on the bottom is a chart of when
       somebody pasted. */
    .spark--posts { height: 3.25rem; gap: 3px; width: 100%; max-width: 200px; }
    /* One series, so one colour. The weekly sparkline greys everything but the last bar
       because its question is "is it still happening"; this one is a trend in a single
       measure, and greying ten of eleven bars implied a distinction that is not there. */
    .spark--posts i, .spark--posts i.on, .spark--posts i.last { background: var(--color-data-fg); }

    /* ---- panel: a titled box, the unit every section is made of ---- */
    .panel { border: 1px solid var(--line); border-radius: var(--radius);
             background: #fff; margin: var(--s6) 0; }
    .panel > header { display: flex; align-items: baseline; gap: var(--s3);
                      padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line); }
    .panel > header h2 { margin: 0; font-family: var(--font-utility); font-size: 11px;
                         font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
                         color: var(--text-muted); }
    .panel > header .more { margin-left: auto; font-size: .825rem; }
    /* A count or a date beside the panel name — a fact about the panel, not a link out. */
    .panel > header .more.quiet { font-family: var(--font-utility); font-size: 12px; }
    .panel .body { padding: var(--s4); }
    .panel .flush { padding: 0; }
    .panel table { font-size: .875rem; }
    .panel table th:first-child, .panel table td:first-child { padding-left: var(--s4); }
    .panel table th:last-child, .panel table td:last-child { padding-right: var(--s4); }

    /* ---- needs you: the only block that asks for something ----
       Given the one warm colour on the page. If everything is emphasised
       nothing is, so this is the only place the palette raises its voice. */
    /* No border, per the callout rule. The tint alone marks the region. */
    .attention { background: var(--orange-10); padding: var(--s3); margin: var(--s6) 0; }
    .attention h2 { margin: 0 0 var(--s3); font-family: var(--font-utility); font-size: 11px;
                    font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
                    color: var(--warn); }
    .attention ul { list-style: none; margin: 0; padding: 0;
                    display: flex; flex-direction: column; gap: var(--s2); }
    .attention li { display: flex; align-items: baseline; gap: var(--s3); font-size: .9rem; }
    .attention li .grow { flex: 1; min-width: 0; }
    .attention .none { color: var(--ok); font-size: .9rem; margin: 0; }

    /* ---- subject cards: organisations and principals on the overview ---- */
    .subjects { display: grid; gap: var(--s3);
                grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
    .subject { display: block; border: 1px solid var(--line); border-radius: var(--radius);
               padding: var(--s4); text-decoration: none; color: inherit; background: #fff; }
    .subject:hover { border-color: var(--nf-blue); }
    .subject .name { font-weight: 600; display: block; }
    .subject .meta { color: var(--muted); font-size: .825rem; margin-top: var(--s1);
                     display: block; font-variant-numeric: tabular-nums; }
    .subject .spark { height: 1.5rem; margin-top: var(--s3); }

    /* ---- engager rows: a face, a name, a number ---- */
    .engagers { list-style: none; margin: 0; padding: 0; }
    .engagers li { display: flex; align-items: center; gap: var(--s3);
                   padding: var(--s2) var(--s4); border-top: 1px solid var(--line); }
    .engagers li:first-child { border-top: 0; }
    .engagers .who { flex: 1; min-width: 0; }
    .engagers .who a { font-weight: 500; }
    .engagers .where { display: block; color: var(--muted); font-size: .8rem;
                       white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .engagers .count { font-variant-numeric: tabular-nums; color: var(--muted);
                       font-size: .875rem; }

    /* ---- placeholder: says what it will be, and that it is not here ---- */
    .placeholder { display: grid; gap: var(--s6); align-items: start;
                   grid-template-columns: minmax(0, 1fr); border: 1px dashed var(--line);
                   border-radius: var(--radius); padding: var(--s6); background: var(--wash); }
    @media (min-width: 46rem) { .placeholder { grid-template-columns: auto minmax(0, 1fr); } }
    .placeholder .sketch { font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
                           color: var(--faint); white-space: pre; overflow-x: auto; }
    .placeholder .says p { margin: 0 0 var(--s3); color: var(--muted); max-width: 34rem; }
    .placeholder .stated { font-weight: 600; color: var(--ink); }

    /* ================================================================
       Content and Calendar: mock-ups.

       Real markup with invented content, because a wireframe with
       nothing in it cannot be judged. Every one of these pages carries
       the banner below, in the reader's line of sight rather than in a
       tooltip — a screenshot without it becomes a claim.
       ================================================================ */

    .mockup-banner { border: 1px solid #cfd8e3; border-left: 3px solid var(--link);
                     background: #f4f8fc; border-radius: var(--radius);
                     padding: var(--s3) var(--s4); margin: 0 0 var(--s6);
                     font-size: .875rem; color: var(--muted); }
    .mockup-banner strong { color: var(--ink); }

    /* ---- the source recording ---- */
    .source { display: flex; gap: var(--s4); align-items: center; flex-wrap: wrap;
              border: 1px solid var(--line); border-radius: var(--radius);
              padding: var(--s4); background: #fff; }
    .source .grow { flex: 1; min-width: 14rem; }
    .source .asset { margin: 0; font-size: 1.05rem; }
    .source .sub { margin: var(--s1) 0 0; }
    .reel { position: relative; width: 11rem; height: 6rem; border-radius: var(--radius);
            background: var(--ink); display: flex; align-items: center;
            justify-content: center; overflow: hidden; }
    .reel .play { color: #fff; font-size: 1.5rem; opacity: .9; z-index: 1; }
    .reel .wave { position: absolute; inset: auto 0 0; height: 2rem; display: flex;
                  align-items: flex-end; gap: 1px; padding: 0 .25rem; opacity: .35; }
    .reel .wave i { flex: 1; background: #fff; border-radius: 1px; }
    .reel .wave i:nth-child(3n)   { height: 55%; }
    .reel .wave i:nth-child(3n+1) { height: 25%; }
    .reel .wave i:nth-child(3n+2) { height: 85%; }
    .progressline { height: 4px; border-radius: 2px; background: var(--line);
                    margin: var(--s2) 0 0; overflow: hidden; }
    .progressline span { display: block; height: 100%; background: var(--ok); }

    /* ---- the fan-out from one recording to many pieces ---- */
    .lineage { display: flex; flex-direction: column; align-items: center; height: var(--s6); }
    .lineage .drop { width: 1px; height: 100%; background: var(--line); }
    .kinds { display: grid; gap: var(--s3);
             grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
    .kind { border: 1px solid var(--line); border-radius: var(--radius);
            padding: var(--s3) var(--s4); background: #fff; }
    .kind .kn { font-size: 1.35rem; font-variant-numeric: tabular-nums; }
    .kind .kk { display: block; font-size: .75rem; letter-spacing: .04em;
                text-transform: uppercase; color: var(--muted); margin-top: var(--s1); }
    .kind .kbar { display: block; height: 3px; border-radius: 2px; background: var(--line);
                  margin: var(--s2) 0 var(--s1); overflow: hidden; }
    .kind .kbar i { display: block; height: 100%; background: var(--ink); }
    .kind .ksub { font-size: .75rem; color: var(--faint); }

    .piecekind { display: inline-block; font-size: .7rem; letter-spacing: .04em;
                 text-transform: uppercase; color: var(--muted); border: 1px solid var(--line);
                 border-radius: 3px; padding: 0 .3rem; margin-right: var(--s2); }

    /* ---- the calendar: lanes down, days across ----
       A table and not a grid of divs: these are rows with a header column,
       which is what a table is for, and it keeps the lane name readable
       when the days scroll sideways on a narrow screen. */
    table.lanes { border-collapse: separate; border-spacing: 0; table-layout: fixed;
                  min-width: 46rem; }
    table.lanes th, table.lanes td { border-bottom: 1px solid var(--line);
                                     vertical-align: top; padding: var(--s2); }
    table.lanes thead th { font-size: .75rem; letter-spacing: .04em; text-transform: uppercase;
                           color: var(--muted); text-align: left; font-weight: 500; }
    table.lanes th.lane { width: 9rem; text-align: left; background: #fff; }
    table.lanes th.lane .who { display: block; font-weight: 600; font-size: .875rem; }
    table.lanes th.lane .role { display: block; font-size: .75rem; color: var(--faint); }
    .slot { display: block; border-radius: var(--radius); padding: var(--s2);
            border: 1px solid var(--line); background: var(--wash); }
    .slot .slotkind { display: block; font-size: .65rem; letter-spacing: .04em;
                      text-transform: uppercase; color: var(--muted); }
    .slot .slottitle { display: block; font-size: .8rem; line-height: 1.3;
                       margin-top: .1rem; }
    .slot .slottime { display: block; font-size: .7rem; color: var(--faint);
                      margin-top: .15rem; font-variant-numeric: tabular-nums; }
    .slot.scheduled { border-color: #c8d4e2; background: #f2f6fb; }
    .slot.live { border-color: #bcd8c8; background: var(--ok-bg); }
    .slot.draft { border-style: dashed; }

    .legend { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
              font-size: .8rem; color: var(--muted); margin: var(--s3) 0 0; }
    .legend .key { width: .7rem; height: .7rem; border-radius: 2px; display: inline-block;
                   border: 1px solid var(--line); }
    .legend .key.scheduled { border-color: #c8d4e2; background: #f2f6fb; }
    .legend .key.live { border-color: #bcd8c8; background: var(--ok-bg); }
    .legend .key.draft { border-style: dashed; background: var(--wash); }
    /* The chart family's swatch. It was styled as `.key` while every chart macro drew an
       `<i>`, so each legend rendered as a row of bare words — the reader had the names, the
       chart had the colours, and nothing joined them. Square, like every other mark here. */
    .legend i { width: .7rem; height: .7rem; display: inline-block; flex: none;
                margin-right: 6px; vertical-align: -1px; }
    .legend span { display: inline-flex; align-items: center; }

    /* ================================================================
       Components added with the brand. `spec/STYLE-GUIDE.md` says why
       each exists; the short version is on every one below.
       ================================================================ */

    /* Inline icons — see `_icon.html` for why they are not a font. */
    .i { vertical-align: -.15em; flex: none; }

    /* ---- coverage: a proportion, drawn ----
       The most important number in the product is a proportion nothing
       currently shows: how many of the people who engaged are on a list.
       A bar makes "none of them" legible in a way "0 of 63" does not. */
    .meter { display: flex; height: 10px; background: var(--surface-sunken); overflow: hidden; }
    .meter i { display: block; height: 100%; }
    .meterkey { display: flex; gap: var(--s3); flex-wrap: wrap; margin-top: var(--s2);
                font-family: var(--font-utility); font-size: 12px; color: var(--text-secondary); }
    .meterkey span { display: inline-flex; align-items: center; gap: 6px; }
    .meterkey i { width: 9px; height: 9px; display: inline-block; flex: none; }

    /* ---- absence, with a reason ----
       "The model found nothing" and "the model could not be reached" are
       different facts, and writing the second as the first once
       blacklisted forty people permanently. `.gap` is the first —
       nothing to say. `.unreachable` is the second, and it is orange
       because it is work waiting rather than an answer. */
    .gap { color: var(--text-muted); }
    .unreachable { display: inline-flex; align-items: center; gap: 5px;
                   font-family: var(--font-utility); font-size: 11px; font-weight: 600;
                   letter-spacing: .04em; text-transform: uppercase; padding: 2px 10px;
                   border-radius: var(--radius-pill); background: var(--orange-10);
                   color: var(--warn); }

    /* ---- an empty screen is somebody's first ----
       Italic grey text says nothing about what would fill the page or
       how. This says both, and offers the action if there is one. */
    .emptystate { text-align: center; padding: var(--s7) var(--s3); }
    .emptystate .i { color: rgba(34,34,34,.22); }
    .emptystate h3 { margin: var(--s2) 0 var(--s1); font-size: 16px; }
    .emptystate p { color: var(--text-muted); font-size: 14px; max-width: 28rem;
                    margin: 0 auto var(--s3); }

    /* ---- a claim, and who made it ----
       The app decides whose statement about a person wins — the principal,
       then a human, then their headline, then the vendor. Nothing showed
       that, so a company read as a fact when it was the winner of an
       argument. `.claim` carries the winner; `.claim .by` says who said so. */
    .claim { display: inline-flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; }
    .claim .by { font-family: var(--font-utility); font-size: 11px; font-weight: 600;
                 letter-spacing: .04em; text-transform: uppercase; padding: 2px 10px;
                 border-radius: var(--radius-pill); background: var(--blue-10);
                 color: var(--nf-blue); }
    .claim .lost { display: block; width: 100%; margin-top: var(--s2);
                   background: var(--surface-sunken); padding: var(--s2) var(--s3);
                   font-size: 14px; color: var(--text-secondary); }

    /* ---- avatar stack: who, compactly ---- */
    .avstack { display: inline-flex; }
    .avstack .avatar { margin-left: -8px; box-shadow: 0 0 0 2px var(--surface-raised); }
    .avstack .avatar:first-child { margin-left: 0; }
    .avstack .more { background: var(--nf-blue); color: var(--nf-white); }

    /* The subnav sits inside the toolbar on list pages, so it loses its own rule and
       border — the toolbar already draws one. */
    .toolbar .subnav { margin: 0; border-bottom: 0; }
    .toolbar input[type=search] { margin: 0; width: 15rem; }
    .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
                       overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

    /* The capture reconciliation. `.meter` carries the proportions; this only gives it room
       and a taller bar, because it is the thing a reviewer checks before trusting a capture. */
    .reconcile { margin: var(--s3) 0; }
    .reconcile .meter { height: 14px; }

    /* ---- the decision card ----
       Mark's SOP: "deciding without the trigger visible is guesswork." So what they did is on
       the card verbatim rather than clipped into a table cell, and the six lists are one row
       of buttons — this happens sixty times in a thirty-minute session, and a dropdown costs
       two clicks and a read every time. */
    .verdicts { display: flex; flex-direction: column; gap: var(--s3); }
    .verdict { background: var(--surface-raised); border: 1px solid var(--divider); }
    .verdict__body { display: flex; gap: var(--s3); padding: var(--s3); align-items: flex-start; }
    .verdict__body .grow { flex: 1; min-width: 0; }
    .verdict__who { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; }
    .verdict__who a { font-size: 17px; font-weight: 600; }
    .verdict__role { display: block; font-size: 14px; color: var(--text-secondary);
                     margin-top: 2px; }
    .verdict__role .by { font-family: var(--font-utility); font-size: 10px; letter-spacing: .05em;
                         text-transform: uppercase; color: var(--text-muted); margin-left: 6px; }
    .verdict__head { display: block; font-size: 13px; color: var(--text-muted); margin-top: 2px; }
    /* The trigger, in a blue-tinted region — the brand's tool for marking a block without
       chrome. No border, per the callout rule. */
    .verdict__why { background: var(--blue-10); padding: var(--s2) var(--s3);
                    margin-top: var(--s2); font-size: 14px; color: var(--text-secondary); }
    .verdict__why strong { color: var(--text-primary); }
    .verdict__why blockquote { margin: var(--s2) 0 0; padding-left: var(--s3);
                               border-left: 2px solid var(--blue-50); font-style: italic;
                               color: var(--text-primary); font-size: 15px; }
    .verdict__choose { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
                       padding: var(--s2) var(--s3); border-top: 1px solid var(--divider); }
    .verdict__choose form { display: contents; }
    .choice { font-family: var(--font-utility); font-size: 13px; font-weight: 500;
              padding: 4px 14px; border-radius: var(--radius-pill);
              border: 1px solid var(--divider); background: var(--surface-raised);
              color: var(--text-secondary); cursor: pointer; }
    .choice:hover { border-color: var(--nf-blue); color: var(--nf-blue); }
    .choice.on { background: var(--nf-blue); border-color: var(--nf-blue); color: var(--nf-white); }
    .choice.dismiss { margin-left: auto; border-color: transparent; color: var(--text-muted); }
    .choice.dismiss:hover { color: var(--text-primary); border-color: var(--divider); }

    /* ---- sign in: the one page with no rail ---- */
    .signin { max-width: 26rem; margin: 12vh auto 0; text-align: left; }
    /* 320px, the package's login width and the top of what the lockup can carry: the
       Newfangled wordmark inside it is a 560x59 raster in an SVG wrapper, so it is pixel-honest
       to about 640px — a 320px slot on a 2x display, exactly here, with no headroom above it.
       A larger login lockup needs a true vector wordmark from the design team first. */
    .signin__mark { display: block; width: 320px; max-width: 100%; height: auto;
                    margin-bottom: var(--s5); }
    .signin h1 { font-size: 26px; }
    .signin .lede { margin-bottom: var(--s4); }
    .signin .hint { margin-top: var(--s4); }
    /* Signed out there is no grid, so the body must not reserve a rail column. */
    body[data-nav-position="side"]:not(:has(.page-nav)) { grid-template-columns: 1fr;
                                                          grid-template-areas: "body"; }

    /* The stack's own small avatar, and the "+N" cap on the end. */
    .avatar.sm { width: 26px; height: 26px; font-size: 10px; }
    .avstack .avatar.sm { margin-left: -7px; }
    .avstack .avatar.sm:first-child { margin-left: 0; }

    /* ---- the person page ----------------------------------------------------------------
       Its own vocabulary, because it is the only screen in the product that is *read* rather
       than scanned: somebody opens it mid-session to decide one thing and then to write to
       this person. Everything here serves one of those two jobs. */

    /* Sentence case, and the distinction carried by family, weight and colour rather than by
       shouting. The brand is explicit: "Never apply uppercase to a Strapline or eyebrow
       text... uppercase straplines feel like generic UI labels and break the editorial
       voice." */
    .quiet { color: var(--text-muted); font-weight: 400; }

    /* The trigger. Same treatment as the decision card's — a blue-tinted region and no
       border, which is the brand's tool for marking a block without chrome. */
    .attention.trigger { background: var(--blue-10); }
    .attention.trigger h2 { color: var(--nf-blue); }
    .trigger__what { margin: 0; font-size: 15px; color: var(--text-secondary); }
    .trigger__what a { font-weight: 600; }
    .trigger blockquote { margin: var(--s2) 0 0; padding-left: var(--s3);
                          border-left: 2px solid var(--blue-50); font-style: italic;
                          color: var(--text-primary); font-size: 16px; }

    /* Context, not measurement. These were four KPI cards, which gave "first seen" the same
       weight as the engagement count and said the two matter equally. */
    .context { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
               padding: 0 0 var(--s4); margin-bottom: var(--s4);
               border-bottom: 1px solid var(--divider);
               font-family: var(--font-utility); font-size: 13px; color: var(--text-secondary); }
    .context b { font-family: var(--font-body); font-weight: 300; font-size: 20px;
                 color: var(--text-primary); font-variant-numeric: tabular-nums;
                 margin-right: 4px; }
    .context .gap { color: var(--text-muted); }

    /* ---- the timeline ----
       A filled marker means we know when it happened. A hollow one means we do not — and the
       row says so in words rather than showing the capture date under a heading that reads
       as an event date. Three quarters of real engagements are the hollow kind. */
    .tl { list-style: none; margin: 0; padding: 0; }
    .tl__item { position: relative; padding: 0 0 var(--s4) var(--s4);
                border-left: 1px solid var(--divider); }
    .tl__item:last-child { border-left-color: transparent; padding-bottom: 0; }
    .tl__item::before { content: ""; position: absolute; left: -5px; top: 5px;
                        width: 9px; height: 9px; border-radius: var(--radius-full);
                        background: var(--nf-blue); box-shadow: 0 0 0 3px var(--surface-page); }
    .tl__item--undated::before { background: var(--surface-page);
                                 border: 1px solid var(--text-muted); }
    .tl__what { margin: 0; font-size: 15px; font-weight: 600; }
    .tl__where { margin: 2px 0 0; font-size: 14px; }
    .tl__when { margin: 2px 0 0; font-family: var(--font-utility); font-size: 12px;
                color: var(--text-secondary); font-variant-numeric: tabular-nums; }
    .tl blockquote { margin: var(--s2) 0 0; font-size: 15px; }

    /* ---- career ----
       Carrying the summary they wrote about the job, which is often the clearest statement
       anywhere of what somebody actually does and was being read past. */
    .career { list-style: none; margin: 0; padding: 0; }
    .career__role { padding: var(--s2) 0; border-bottom: 1px solid var(--divider); }
    .career__role:last-child { border-bottom: 0; }
    .career__head { display: flex; gap: var(--s3); align-items: baseline; margin: 0; }
    .career__head b { font-size: 15px; flex: 1; }
    .career__head .when { font-family: var(--font-utility); font-size: 12px;
                          color: var(--text-muted); white-space: nowrap;
                          font-variant-numeric: tabular-nums; }
    .career__role--now .career__head .when { color: var(--nf-blue); font-weight: 500; }
    .career__where { margin: 2px 0 0; font-size: 14px; }
    .career__said { margin: var(--s1) 0 0; font-size: 14px; color: var(--text-secondary);
                    max-width: 46ch; }
    .claims dd { font-size: .95rem; }

    /* The bio: prose at a reading width. The only thing on the page the person wrote
       themselves, and it was a blockquote under a table. */
    .bio__text { font-size: 17px; line-height: 1.6; margin: 0; }
    .bio__headline { margin: var(--s2) 0 0; font-size: 15px; font-style: italic;
                     color: var(--text-secondary); }

    /* ---- their own posts ----
       LinkedIn's own anatomy, because the principal is about to open LinkedIn and reply and a
       card shaped like the thing they will see there is faster to place than a table row:
       48px round avatar, name, headline, date, the words, media across the full width, the
       original nested inside a repost, and the social-proof row along the bottom.

       The one deliberate departure is the corners. The brand says it outright — "Newfangled =
       sharp corners" — and LinkedIn's are 8px. The avatar stays round because every avatar in
       this product is.

       No outer box. LinkedIn is white cards on a grey feed, which is what `--surface-page`
       already is; the panel that used to wrap these drew a second frame around every one. */
    .feed { display: grid; gap: var(--s3); align-items: start;
            grid-template-columns: repeat(auto-fill, minmax(26rem, 1fr)); }
    .li { background: var(--surface-raised); border: 1px solid var(--divider); }
    .li .avatar.md { width: 48px; height: 48px; font-size: 15px; }
    .li .avatar.xs { width: 32px; height: 32px; font-size: 11px; }

    /* The thin line above a bare repost — ninety-two of the two hundred and ninety-six real
       posts. What follows it is somebody else's post, which is the point of showing it. */
    .li__repost { display: flex; align-items: center; gap: var(--s2); margin: 0;
                  padding: 10px var(--s3); font-size: 12px; color: var(--text-secondary);
                  border-bottom: 1px solid var(--divider); }

    .li__head { display: flex; gap: var(--s2); align-items: flex-start;
                padding: var(--s2) var(--s3) 0; }
    .li__head .grow { flex: 1; min-width: 0; }
    .li__who { margin: 0; font-size: 14px; font-weight: 600; line-height: 1.3; }
    .li__meta { margin: 0; font-size: 12px; color: var(--text-secondary); line-height: 1.3;
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .li__when { margin: 1px 0 0; font-size: 12px; color: var(--text-muted); line-height: 1.3; }
    .li__out { font-size: 13px; color: var(--text-muted); text-decoration: none; }
    .li__out:hover { color: var(--nf-blue); }

    .li__body { padding: var(--s2) var(--s3) var(--s2); }
    .li__text { margin: 0; font-size: 14px; line-height: 1.45; white-space: pre-wrap;
                display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5;
                overflow: hidden; }
    /* The expander is a sibling, so the clamp is lifted from the wrapper. No script: the CSP
       drops inline script silently and this is one boolean. */
    .li__body:has(.li__expand[open]) .li__text { -webkit-line-clamp: unset; }
    .li__expand > summary { cursor: pointer; list-style: none; font-family: var(--font-utility);
                            font-size: 13px; color: var(--text-secondary); margin-top: 2px; }
    .li__expand > summary:hover { color: var(--nf-blue); }
    .li__expand > summary::-webkit-details-marker { display: none; }
    .li__expand .less, .li__expand[open] .more { display: none; }
    .li__expand[open] .less { display: inline; }

    /* Media spans the card, as LinkedIn's does — an inset grey box with its own margins read
       as a note about the post rather than as part of it. */
    .li__card { padding: var(--s2) var(--s3); background: var(--surface-sunken);
                border-top: 1px solid var(--divider); border-bottom: 1px solid var(--divider);
                font-size: 13px; }
    .li__kind { font-family: var(--font-utility); font-size: 11px; color: var(--text-muted);
                display: block; }

    /* The original, inside the card, the way LinkedIn nests it. */
    .li__share { margin: var(--s2) var(--s3); border: 1px solid var(--divider); }
    .li__sharehead { display: flex; gap: var(--s2); align-items: flex-start;
                     padding: var(--s2) var(--s2) 0; }
    .li__sharehead .grow { flex: 1; min-width: 0; }
    /* Bottom *margin*, not padding. `overflow: hidden` clips at the padding edge, so a clamped
       box with padding underneath shows a sliver of the next line through it — here that put
       half a line of the original post on top of the link below it. */
    .li__sharetext { margin: var(--s1) 0 var(--s2); padding: 0 var(--s2); font-size: 13px;
                     color: var(--text-secondary); display: -webkit-box;
                     -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
    .li__sharelink { margin: 0; padding: 0 var(--s2) var(--s2); font-size: 12px; }
    .li__mentions { margin: 0; padding: 0 var(--s3) var(--s2); font-size: 12px; }

    .li__stats { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center;
                 padding: var(--s2) var(--s3); border-top: 1px solid var(--divider);
                 font-family: var(--font-utility); font-size: 12px;
                 color: var(--text-secondary); }
    .li__counts { margin-left: auto; font-variant-numeric: tabular-nums; }
    /* LinkedIn's social proof: the reaction faces overlapping, then the total. Only the ones
       that happened get a disc — Clay returns all six every time, mostly zero, and the name of
       each is on the disc for anyone who wants it. "Insightful" on a technical post says
       something "like" does not. */
    .reacts { display: inline-flex; align-items: center; }
    .react { width: 16px; height: 16px; border-radius: var(--radius-full);
             margin-left: -5px; background: var(--text-muted);
             box-shadow: 0 0 0 1.5px var(--surface-raised); }
    .react:first-child { margin-left: 0; }
    .react--like { background: var(--nf-blue); }
    .react--celebrate { background: var(--nf-green); }
    .react--insightful { background: var(--nf-orange); }
    .react--love { background: var(--nf-red); }
    .react--support { background: var(--nf-purple); }
    .reacts .total { margin-left: var(--s1); font-variant-numeric: tabular-nums; }

    .creds { list-style: none; margin: 0; padding: 0; font-size: 14px; }
    .creds li { padding: var(--s2) 0; border-bottom: 1px solid var(--divider); }
    .creds li:last-child { border-bottom: 0; }
    .creds b { display: block; }
    .creds .sub { font-size: 13px; }
    .creds .when { font-family: var(--font-utility); font-size: 12px; color: var(--text-muted);
                   font-variant-numeric: tabular-nums; margin-left: var(--s2); }

    /* Drawn and not built. A control that looks live and does nothing is worse than a gap, so
       it is shaped like the real one and visibly inert, with the banner beside it saying why. */
    .inert { opacity: .5; cursor: not-allowed; pointer-events: none; }
    .choices { display: flex; flex-wrap: wrap; gap: var(--s1); margin: 0 0 var(--s2); }

    /* Somebody a post named. Not a `.pill`: that component uppercases, and a person's name
       is not a label — it turned "David Mello" into "DAVID MELLO" on every post naming him. */
    .named { color: var(--text-primary); font-weight: 500; }
    .named + .named::before { content: ", "; font-weight: 400; color: var(--text-muted); }
    .tl__said { margin: var(--s1) 0 0; font-size: 13px; color: var(--text-muted);
                font-style: italic; }

    /* The rest of a long history. Forty engagements made a three-thousand-pixel timeline and
       put the whole of the rest of the page below it. Capped, never truncated — the count
       says what is behind the disclosure, and the summary is a node on the line itself so it
       reads as part of the timeline rather than as a control stuck underneath one. */
    .tl__rest { position: relative; }
    .tl__rest > summary { cursor: pointer; list-style: none; position: relative;
                          padding-left: var(--s4); font-family: var(--font-utility);
                          font-size: 13px; color: var(--nf-blue); }
    .tl__rest > summary::-webkit-details-marker { display: none; }
    .tl__rest > summary::before { content: ""; position: absolute; left: -5px; top: 5px;
                                  width: 9px; height: 9px; border-radius: var(--radius-full);
                                  background: var(--surface-page);
                                  border: 1px solid var(--nf-blue);
                                  box-shadow: 0 0 0 3px var(--surface-page); }
    .tl__rest > summary { border-left: 1px solid var(--divider); margin-left: 0; }
    .tl__rest .less, .tl__rest[open] .more { display: none; }
    .tl__rest[open] .less { display: inline; }
    .tl__rest > .tl { margin-top: var(--s2); }

    /* Sections stacked inside one grid column keep the grid's own rhythm, so the two columns
       stay on the same lines rather than drifting apart down the page. */


    /* One measure down the reading column, so the timeline, the bio and the career line up on
       the same right edge instead of each finding its own. It also closes the gap the career
       dates opened: right-aligned in a 749px column they sat a long way from the job title
       they belong to. */
    .col-8 .panel .body > * { max-width: var(--measure); }
    .trigger__what, .trigger blockquote { max-width: var(--measure); }

    /* A panel that is only a heading. The feed underneath is white cards on the page's grey,
       which is what LinkedIn is — a panel's border and white ground drew a second frame
       around every card in it. */
    .panel--bare { background: transparent; border: 0; }
    .panel--bare > header { padding-left: 0; padding-right: 0; }
    .panel--bare > .body { padding-left: 0; padding-right: 0; padding-bottom: 0; }

    /* ---- four treatments, because seven identical white boxes give the eye nothing ----
       The page holds four different kinds of thing and drew them all the same way:

         .panel          a record we keep — the timeline, the roles they hold now
         .panel--quiet   reference beside it — schooling, credentials, which claim won. The
                         rail should not compete with the record it sits next to.
         .panel--action  the one thing there is to do
         .panel--bare    words the person wrote. Never in a box — which is the rule the feed
                         was already following on its own.
    */
    .panel--quiet { background: transparent; border: 0; border-top: 1px solid var(--divider); }
    .panel--quiet > header { padding: var(--s2) 0 var(--s1); border-bottom: 0; }
    .panel--quiet > .body { padding: 0 0 var(--s2); }
    .panel--quiet > .body.flush { padding: 0 0 var(--s2); }
    /* Stacked in a rail they read as one column of reference rather than four objects, so the
       gap between them closes to the width of the rule that separates them. */
    .stack > .panel--quiet + .panel--quiet { margin-top: calc(var(--grid-gap-y) * -0.5) !important; }

    .panel--action { border-top: 2px solid var(--nf-blue); }
    .panel--action > header h2 { color: var(--nf-blue); }

    /* ---- the masthead ------------------------------------------------------------------
       The page read flat because it was: one surface, one weight, nothing lifting off
       anything. The brand's answer to that is not a shadow — `components.md` ships four card
       surfaces and none of them is an elevation. `.card--feature` is the dark one: "Newfangled
       dark-grey surface, white text. Neutral feature with high contrast."

       One painted surface and no more, which is the same file's rule: a card earns its colour
       "by being the one thing that ought to stand out, not by being one of many." */
    /* Every page, not one. A painted band on the person page alone reads as a page that
       wandered in from another product — and the same band on all of them is the app's spine.
       `.card--feature` is the brand's dark surface; here it is chrome rather than content
       theming, which is the same job the brand gives its own footer. */
    .title-row { background: var(--surface-masthead); color: var(--nf-white);
                 /* Less above than beside. The kind pill is an object with its own box, so
                    equal padding all round reads as more space at the top than at the sides. */
                 padding: var(--s6) var(--s5) var(--s6);
                 margin: 0 0 var(--grid-gap-y); row-gap: var(--s3); }
    /* The person page puts two blocks in the header — who they are, and why you are looking at
       them — so each takes a whole row of the flex it already was. Every other page keeps the
       flat title-left-action-right shape unchanged. */
    .title-row__who, .title-row__why { flex: 1 0 100%; }
    .title-row__who { display: flex; gap: var(--s3); align-items: flex-start; }
    .title-row__who .grow { flex: 1; min-width: 0; }
    /* The rhythm inside the band, which was 2px between every part of it. The kind, the name
       and what is true today are three things and read as three; at two pixels they read as
       one block set in three sizes. */
    .title-row h1 { color: var(--nf-white); margin-bottom: var(--s2); }
    .title-row .lede { color: rgba(255,255,255,.72); margin: 0; font-size: 15px;
                       line-height: 1.55; }
    .title-row__pills { margin: var(--s2) 0 0; }
    .title-row a { color: var(--nf-white); text-decoration-color: rgba(255,255,255,.5); }
    .title-row a:hover { color: var(--nf-white); text-decoration-color: currentColor; }
    .title-row code { color: rgba(255,255,255,.85); }
    /* Dark on dark, three times now. "Last captured 10 Sep, 16:32" disappeared from every
       page that had one; the person page's `.quiet` was then fixed under `.title-row__why`
       alone; and the organisation's three findings rendered as one interleaved paragraph,
       because the muted detail after each claim was invisible and a reader saw only the bold
       halves running together. Every muted class the masthead carries is lightened here, and
       a test derives that list from the templates rather than trusting this comment. */
    .title-row .title-row__meta,
    .title-row .quiet,
    .title-row .sub,
    .title-row .hint,
    .title-row .legend,
    .title-row .mockup-banner { color: rgba(255,255,255,.6); }
    .title-row .title-row__meta strong { color: var(--nf-white); }
    /* On a dark ground a tinted pill disappears; the outline carries it instead. */
    .title-row .pill { background: transparent; color: rgba(255,255,255,.85);
                       box-shadow: inset 0 0 0 1px rgba(255,255,255,.28); }
    .title-row .avatar { box-shadow: 0 0 0 2px rgba(255,255,255,.18); }
    .title-row .btn.secondary { color: var(--nf-white); border-color: rgba(255,255,255,.35);
                                background: transparent; }
    .title-row .btn.secondary:hover { background: rgba(255,255,255,.12);
                                      border-color: rgba(255,255,255,.6); }

    /* The trigger, on the dark. A tint of the page's own blue rather than a second box: the
       brand's callout rule forbids a border, and the SOP's rule is that this is never absent. */
    .title-row__why { margin-top: var(--s3); padding: var(--s3);
                     background: rgba(26,153,212,.22);
                     border-left: 3px solid var(--nf-blue); }
    .title-row__why h2 { margin: 0 0 var(--s2); font-family: var(--font-utility); font-size: 11px;
                        font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
                        color: rgba(255,255,255,.8); display: flex; align-items: center;
                        gap: var(--s1); }
    .title-row__why .trigger__what { margin: 0; font-size: 15px; color: rgba(255,255,255,.85);
                                    max-width: var(--measure); }
    .title-row__why a { color: var(--nf-white); text-decoration-color: rgba(255,255,255,.5); }
    .title-row__why a:hover { color: var(--nf-white); }
    .title-row__why .quiet { color: rgba(255,255,255,.6); }
    .title-row__why blockquote { margin: var(--s2) 0 0; padding-left: var(--s3);
                                border-left: 2px solid rgba(255,255,255,.35);
                                color: var(--nf-white); font-size: 16px; font-style: italic;
                                max-width: var(--measure); }

    /* ---- the hero KPI ----
       `dashboard.md` gives the KPI three sizes and says what the biggest is for: "the one
       number that matters most this view." Four cards at one size is the strip with no view in
       it. The chart is the other half — whether somebody is warming up or went quiet after one
       reaction is the question a principal brings to a record, and it is the only form the
       amplifier question takes that we can draw. */
    .col-2 { grid-column: span 2; }
    /* The number and its shape side by side. Stacked, the hero ran to twice the height of the
       three cards beside it and left an L of empty white — and "density is the job"
       (`dashboard.md`). */
    .kpi--hero { padding: var(--card-padding-lg); display: grid; gap: var(--s4);
                 min-height: 188px;
                 grid-template-columns: minmax(0, 1fr) minmax(9rem, 15rem); align-items: center; }
    .kpi--hero .kpi__figure { display: flex; flex-direction: column; }
    .kpi--hero .kpi__value { font-size: var(--kpi-value-size-hero); }
    .kpi__spark { display: flex; flex-direction: column; gap: var(--s1); }
    .kpi__spark .spark { height: 2.75rem; }
    /* Level across the row, so the strip reads as one band rather than four objects. */
    .dashboard-grid > .kpi { align-self: stretch; }
    @media (max-width: 62rem) {
      .col-2 { grid-column: span 6; }
      .kpi--hero { grid-column: span 12; }
    }

    /* What kind of thing this page is about. "Newfangled" is the workspace, an organisation
       inside it, and a company on the accounts list — three pages, one name, and the URL was
       the only thing separating them. The rail already labels a subject by kind in its own
       list; the page that subject opens on did not. */
    /* Above the title, never inside it. Inside the `h1` it becomes part of the heading a
       screen reader announces and is read *after* the name; above it, it is the first thing
       anybody sees, which is the job — "Newfangled" is a workspace, an organisation and a
       company, and the URL was the only thing separating the three. */
    /* ---- the capture widget ----------------------------------------------------------
       Capture, and when it last happened, in the masthead of every page you can capture from.
       Two parts on purpose: the date is a fact about the page and only the button is a
       control, so a reader is never invited to press a date.

       Sized under a page action rather than at it. On three of the four pages this sits below
       the masthead's own meta, and a full-weight button there would outrank the heading. */
    /* The masthead's right-hand column. Three of the four pages that draw the widget already
       have a meta in that slot, so the two stack and stay flush to the banner's edge. */
    .title-row__aside { margin-left: auto; display: flex; flex-direction: column;
                        align-items: flex-end; gap: var(--s2); }
    .title-row__aside .title-row__meta { margin: 0; }

    .capture { display: inline-flex; align-items: center; gap: var(--s2);
               font-family: var(--font-utility); font-size: 12.5px; line-height: 1;
               white-space: nowrap; }
    .capture__when { color: rgba(255,255,255,.6); }
    /* Outlined rather than filled: on a painted band an outline is the brand's own secondary
       button, and a filled one would be the fourth solid object in a row of three. */
    .capture__do { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px;
                   border-radius: var(--radius-pill); color: var(--nf-white);
                   text-decoration: none;
                   box-shadow: inset 0 0 0 1px rgba(255,255,255,.35); }
    .capture__do:hover { background: rgba(255,255,255,.12);
                         box-shadow: inset 0 0 0 1px rgba(255,255,255,.6);
                         color: var(--nf-white); }

    .title-row__kind { display: inline-block; margin: 0 0 var(--s2);
                       font-family: var(--font-utility); font-size: 11px; font-weight: 600;
                       letter-spacing: .09em; text-transform: uppercase;
                       color: rgba(255,255,255,.55); padding: 3px 10px;
                       border-radius: var(--radius-pill);
                       box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); }

    /* ---- a company, wherever one appears --------------------------------------------------
       The same firm shows up in a person's career, in a list and as the subject of its own
       page, and it read as three unrelated things: a link, a table cell and a heading.

       **Square where a person is round.** Every avatar in this product is a circle; a company
       takes the brand's own sharp corners. A reader learns it once. It also marks a system
       object — `.co` is a record you can open, `.co-plain` is a name off somebody's employment
       history that nothing resolved, and the difference should be visible without reading. */
    .comark { display: inline-flex; align-items: center; justify-content: center;
              width: 22px; height: 22px; flex: none; position: relative; overflow: hidden;
              background: hsl(var(--hue) 45% 92%); color: hsl(var(--hue) 45% 32%);
              font-family: var(--font-utility); font-size: 9px; font-weight: 700;
              letter-spacing: .02em; border-radius: var(--radius); }
    .comark img { position: absolute; inset: 0; width: 100%; height: 100%;
                  object-fit: contain; background: var(--surface-raised); padding: 1px; }
    .comark.big { width: 40px; height: 40px; font-size: 13px; }
    .comark.big img { padding: 3px; }

    .co { display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
          padding: 2px 8px 2px 2px; background: var(--surface-sunken);
          text-decoration: none; color: var(--text-primary); vertical-align: -5px; }
    .co:hover { background: var(--blue-10); color: var(--nf-blue); }
    .co__name { font-size: 14px; overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; }
    /* A name nothing resolved to. No box, because there is nothing to open and a box would
       say we hold a record we do not. */
    .co-plain { color: var(--text-secondary); }

    .cocard { display: flex; gap: var(--s3); align-items: flex-start; padding: var(--s3);
              background: var(--surface-raised); border: 1px solid var(--divider);
              text-decoration: none; color: var(--text-primary); }
    .cocard:hover { border-color: var(--nf-blue); }
    .cocard .grow { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
    .cocard__name { font-size: 15px; font-weight: 600; }
    .cocard__what { font-size: 13px; color: var(--text-secondary); }
    .cocard__facts { font-family: var(--font-utility); font-size: 12px;
                     color: var(--text-muted); font-variant-numeric: tabular-nums; }
    /* A target account is the one thing about a company that changes what anybody does. */
    .cocard--target { border-left: 3px solid var(--nf-orange); }

    /* The chip in a dense row — a people list cell, an engagers line. Smaller than the inline
       form and without its tint, so eight of them down a column read as a list of companies
       rather than as eight boxes. */
    .sub .co, .engagers .where .co { padding: 1px 6px 1px 1px; background: transparent;
                                     vertical-align: -4px; }
    .sub .co .comark, .engagers .where .co .comark { width: 18px; height: 18px; font-size: 8px; }
    .sub .co .co__name, .engagers .where .co .co__name { font-size: 13px; }
    .sub .co:hover, .engagers .where .co:hover { background: var(--blue-10); }

    /* A company's own words for what it sells. Not `.pill`, which uppercases — one real
       specialty runs to fifty characters, and shouting a company's own phrasing makes it
       harder to read rather than more important. */
    .plainlist { list-style: none; margin: 0; padding: 0; font-size: 14px; }
    .plainlist li { padding: 3px 0; border-bottom: 1px solid var(--divider); }
    .plainlist li:last-child { border-bottom: 0; }

    /* ---- the read: performance at whatever the page is standing on --------------------
       Every chart here answers "how much", which is magnitude — and a magnitude chart in five
       hues is a rainbow pretending to be information. One hue, the brand's workhorse blue,
       with a neutral for a folded remainder. No legend: a single series is named by its title.

       Horizontal because the labels are long. "Technology, Information and Internet" does not
       fit under a column, and a rotated label is a label nobody reads. */
    /* No padding of its own any more — it sits inside a card that is already padded, and the
       two together inset the bars by fifty-two pixels. */
    .facets { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: var(--s6); }
    .facet h3 { margin: 0 0 var(--s2); font-family: var(--font-utility); font-size: 11px;
                font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
                color: var(--text-secondary); padding-bottom: var(--s1);
                border-bottom: 1px solid var(--divider); }
    .facet .empty { padding: var(--s3) 0; font-size: 13px; }
    @media (max-width: 62rem) { .facets { grid-template-columns: 1fr; gap: var(--s4); } }

    .bars { display: flex; flex-direction: column; gap: 2px;
            font-family: var(--font-utility); font-size: 13px; }
    .bars .row { display: grid; gap: var(--s2); align-items: center;
                 grid-template-columns: minmax(0, 11rem) 1fr minmax(2rem, max-content); }
    .bars .name { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis;
                  white-space: nowrap; }
    .bars .track { background: var(--surface-sunken); height: 16px; }
    /* A span is inline, and height does not apply to an inline box. */
    .bars .fill { display: block; background: var(--nf-blue); height: 100%; }
    .bars .fill.rest { background: rgba(34,34,34,.12); }
    .bars .n { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-primary); }
    .bars .row:hover .name, .bars .row:hover .n { color: var(--text-primary); }
    .bars .row:hover .fill { background: var(--nf-blue-dark); }

    /* How this one did against the others in its context. Green and red, against the brand's
       own rule that direction is not sentiment — its dashboard sets trend-up and trend-down to
       the same neutral because a metric going down can be good. This is the case that rule is
       not about: engagement on your own post measured against your own other posts has one
       settled direction, and there is no ambiguity to protect. */
    .delta { font-family: var(--font-utility); font-size: 12px; font-weight: 600;
             letter-spacing: .02em; padding: 2px 9px; border-radius: var(--radius-pill);
             vertical-align: 12px; margin-left: var(--s2); white-space: nowrap; }
    .delta.up { background: var(--green-10); color: var(--ok); }
    .delta.down { background: var(--red-10); color: var(--bad); }

    /* A post's own words, faded past a screenful rather than cut. A hard clip with no
       affordance reads as a rendering fault, and a three-thousand-character post pushes
       everything under it off the page. */
    .saidwrap { position: relative; }
    .saidwrap--long .said { max-height: 22rem; overflow: hidden; }
    .saidwrap--long:has(.said__more[open]) .said { max-height: none; }
    .saidwrap--long::after { content: ""; position: absolute; left: 0; right: 0; bottom: 2.2rem;
                             height: 5rem; pointer-events: none;
                             background: linear-gradient(to bottom, transparent,
                                         var(--surface-page)); }
    .saidwrap--long:has(.said__more[open])::after { display: none; }
    .said__more > summary { cursor: pointer; list-style: none; margin-top: var(--s2);
                            font-family: var(--font-utility); font-size: 13px;
                            color: var(--nf-blue); }
    .said__more > summary::-webkit-details-marker { display: none; }
    .said__more .less, .said__more[open] .more { display: none; }
    .said__more[open] .less { display: inline; }

    /* The two things a workspace is. `.subject` was written for exactly this page and then
       never used on it — the overview showed organisations and principals as two lists in a
       side panel beside somebody else's numbers. */
    .subject .who { display: flex; align-items: center; gap: var(--s2); }
    .subject .who .name { display: inline; }
    .subject .spark { height: 1.75rem; margin-top: var(--s3); }
    .subject--person .meta + .meta { margin-top: 2px; color: var(--text-muted); }
    /* A card that is not a way in. A principal's page stands in one organisation, so the
       workspace landing — which has not chosen one — names them and does not link. Same box,
       no hover: an affordance on something that does nothing is the half a reader aims at. */
    .subject--flat:hover { border-color: var(--line); }

    /* ---- what the page found ----
       Findings, not description: the masthead says what this page is, and this says what is
       true of it today. Set apart from body copy because a reader scanning for the answer
       should find it before the charts, and because it is the only prose on the page that
       changes with the data. */
    .said { margin: 0 0 var(--s4); padding: var(--s3) 0 0;
            border-top: 1px solid var(--divider); }
    .said__claim { margin: 0 0 var(--s2); font-size: 1.0625rem; line-height: 1.45;
                   color: var(--text-primary); max-width: var(--measure); }
    .said__claim:last-child { margin-bottom: 0; }
    .said__detail { color: var(--text-secondary); font-size: .9375rem; }
    /* Deliberately quieter and italic. "Not enough posts to say" is an absence, and drawing it
       like a finding would make the absence itself the claim. */
    .said__waiting { margin: 0 0 var(--s2); font-size: .9375rem; font-style: italic;
                     color: var(--text-muted); max-width: var(--measure); }

    /* A title LinkedIn manufactured from the post's first line. Italic and trailing ellipsis
       rather than a second column: the reader needs to know they are reading an opening
       sentence, not what to call it. */
    /* A post's title inside a report table. Twelve rows of link-blue is a table shouting;
       primary ink reads as a list of posts and the hover says which one is about to open.
       The underline stays, because this rule takes the colour away and colour alone was
       never enough to mark a link in the first place. */
    .dv-title { color: var(--text-primary); text-decoration-line: underline;
                text-decoration-color: var(--divider); text-underline-offset: 2px; }
    .dv-title:hover { color: var(--nf-blue); text-decoration-color: currentColor; }
    a.excerpt { font-style: italic; color: var(--text-secondary); }
    /* The one place the marker lives. `PostRecord.name` strips LinkedIn's own trailing
       ellipsis off an excerpt so the two cannot both appear — which they did, the moment the
       detection started working. */
    a.excerpt::after { content: " \2026"; font-style: normal; color: var(--text-muted); }
    /* The format column: a fact about the post, not a word to read. Small, spaced and upper,
       so it reads as a tag beside the title rather than as part of the sentence. It had no
       rule at all and rendered as body text. */
    .fmt { font-family: var(--font-utility); font-size: 10.5px; letter-spacing: .04em;
           text-transform: uppercase; color: var(--text-muted); }

    /* ---- against the others ----
       Polarity is its own colour job: two hues and a neutral midpoint. One hue with a minus
       sign in front reads as *less of the same thing*; what a reader needs is *which side of
       the line*, and the line has to be drawn. Blue above, orange below, and the orange is
       the brand's own — the tertiary palette's legitimate home is a chart series. */
    .dv-bar { position: relative; display: inline-block; width: 150px; height: 16px;
              vertical-align: middle; background: var(--color-data-bg); }
    .dv-bar i { position: absolute; top: 0; height: 100%; }
    .dv-bar__mid { left: 50%; width: 1px; background: rgba(34,34,34,.25); z-index: 2; }
    .dv-bar__up { background: var(--nf-blue); }
    .dv-bar__down { background: var(--nf-orange); }
    .dv-bar__n { display: inline-block; width: 52px; text-align: right;
                 font-family: var(--font-utility); font-size: 12px; margin-left: var(--s2);
                 font-variant-numeric: tabular-nums; }
    .dv-bar__n.up { color: var(--text-primary); font-weight: 600; }
    .dv-bar__n.down { color: var(--text-muted); }

    /* A section's name is a rule over it, not a headline beside it. As `<h2>`s in body type
       the page had four things competing with its own title; the mock-up sets them small,
       spaced and upper, which is the same voice as a card heading one level up. */
    .section-label { font-family: var(--font-utility); font-size: 11px; font-weight: 600;
                     letter-spacing: .1em; text-transform: uppercase;
                     color: var(--text-muted); margin: var(--s6) 0 var(--s2); }
    .section-label:first-child { margin-top: 0; }
    /* Who this is, and what to do next, on one line under the headline. It lived in the
       filter bar, which wrapped that bar onto two rows at every width the app renders at and
       left the two actions under the format buttons as though they filtered something. */
    /* The facts that place the firm, sharing the filter row rather than owning a band above
       it. They were an `.identity-strip` — a logo chip, three facts and two actions, on its own
       line under a rule — which put two rows of chrome between the banner and the first figure.
       The logo is gone because the banner above already names the organization, and the
       industry with it: every client of this product is in advertising services.

       `margin-right: auto` on nothing — the group they sit in is already pushed right — so
       they simply precede the two buttons and take the wrap first on a narrow window. */
    /* The post page still draws a strip of its own — different facts, a different row — so
       these stay. The organisation page's is what folded into the toolbar above. */
    .identity-strip { display: flex; align-items: center; gap: var(--s3);
                      padding: var(--s3) 0 0; margin-bottom: var(--grid-gap-y); }
    .identity-strip__facts { font-family: var(--font-utility); font-size: 12.5px;
                             color: var(--text-muted); }
    .identity-strip__do { margin-left: auto; display: flex; gap: var(--s2); }
    /* A filter row inside a card rather than across the page. Same control, tighter space:
       the card is already the enclosure, so this needs less air around it than a toolbar
       standing on the page does. */
    .toolbar--inline { height: auto; padding: 0; margin: 0 0 var(--s3); border: 0; }
    /* A quoted comment inside a table, folded to three lines. One person writing four
       paragraphs turned a fifty-row table into a page of prose and took the column away from
       the reader. Clamped rather than cut: the whole comment stays in the DOM, so the
       browser's own search still finds it and a copy still takes all of it. */
    td blockquote { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
                    overflow: hidden; max-width: 34rem; margin: var(--s1) 0 0;
                    font-size: 13px; color: var(--text-secondary); }

    /* The artefact: a well, not a card.
     *
     * Every other box on a report page is something this product concluded. The post is a
     * reproduction of something that exists on LinkedIn, and drawn as a white card with a
     * divider border — in a row of white cards with divider borders — the only thing telling
     * the two apart was that one of them contains an avatar.
     *
     * Three grounds rather than two: the page, a well sunk into it, and the facsimile inside
     * the well. The nesting is the signal, and it needs no colour to carry it. */
    /* LinkedIn's ground, deliberately. Every other box on this page is something this product
       concluded; this one is a reproduction of something on somebody else's website, and the
       colour is the fastest way to say which is which — before the label is read.

       **The text opacities came up with it, and that is not taste.** The old alphas were set
       against `--nf-dark-grey`, where .72 white is 6.6:1. On `#0A66C2` the same .72 is
       **3.72:1** — under AA for text this size, which is how a colour change quietly becomes an
       accessibility regression. White is 5.69:1 here and .90 is 4.93:1, so the hierarchy is
       carried by size and weight instead, which it mostly was already. */
    .artefact { background: var(--li-blue); padding: var(--s6);
                display: flex; flex-direction: column; gap: var(--s3); }
    .artefact__label { margin: 0; font-family: var(--font-utility); font-size: 11px;
                       font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
                       color: #fff; display: flex; align-items: center; gap: var(--s2); }
    .artefact__note { margin: 0; font-family: var(--font-utility); font-size: 11.5px;
                      color: rgba(255,255,255,.90); }
    /* The mark sits with the label rather than above it: at this size it is a bullet that
       happens to be a logo, and a full row to itself would make it an announcement. */
    .artefact__mark { flex: none; }
    /* The way out. Outlined rather than filled: a solid white button on this ground is the
       loudest thing on the page, and what it opens is somebody else's website. */
    .artefact__out { align-self: flex-start; font-family: var(--font-utility); font-size: 12.5px;
                     font-weight: 600; color: #fff; text-decoration: none;
                     padding: var(--s2) var(--s3); border-radius: 999px;
                     border: 1px solid rgba(255,255,255,.55);
                     transition: background .12s ease, border-color .12s ease; }
    .artefact__out:hover { background: rgba(255,255,255,.14);
                           border-color: rgba(255,255,255,.85); color: var(--nf-white); }
    /* On the well's ground the facsimile keeps its own white and its own thin rule — it is
       what LinkedIn draws, and it should look like it. */
    .artefact .li { background: var(--surface-raised); }
    /* The note under an engagers list. Inside the panel's padding, quiet, and always present
       rather than conditional: "no colleagues were removed" and "colleagues are never shown"
       are the same sentence to a reader, and only one of them can be written in advance. */
    /* A principal, as a card in the strip: who, what they have drawn, and how that sits
       against the firm. */
    .card--subject { padding: var(--card-padding); }
    .card--subject .person { align-items: center; gap: var(--s2); text-decoration: none; }
    .person__nm { display: block; font-size: 14px; font-weight: 600;
                  color: var(--text-primary); }
    .card--subject .person:hover .person__nm { color: var(--nf-blue); }
    .person__sub { display: block; font-family: var(--font-utility); font-size: 11.5px;
                   color: var(--text-muted); margin-top: 2px; }
    .person__standing { display: flex; align-items: center; margin-top: var(--s3); }

    /* ---- audience per post ----
       Bars rather than a line: posts are discrete events at irregular intervals, and a line
       between them draws a quantity moving continuously through weeks when nothing went out.
       Two segments because the finding is the split — on the real workspace the returning
       floor holds steady while the first-time bar collapses, and one bar per post hides it. */
    /* The audience sequence and the histogram were div stacks about 46px and 88px tall with
       no axis between them. Both are gridded SVG now; their old rules are gone rather than
       left to rot, because a stale rule is one somebody restores by accident. */
    .ch-donut { text-align: center; }
    /* The headline proportion, at the size the mock-up draws it: the number is the finding
       and it was set at 26px inside a 132px ring, which read as a thumbnail beside a 64px KPI
       two inches above it. */
    .ch-donut svg { width: 100%; max-width: 300px; height: auto; margin: 0 auto; }
    /* Inside the SVG, so the size is in viewBox units and scales with the ring rather than
       against it. The label and the sub-line moved in there with it for the same reason. */
    .ch-donut__n { font-family: var(--font-body); font-weight: 600; font-size: 44px;
                   fill: var(--text-primary); }
    /* The other principals' shares, under the ring. The scale the headline number is read
       against — 73% alone is a figure, 73% beside 64% and 41% is a ranking. */
    .ch-donut__peers { margin: var(--s2) 0 0; text-align: center;
                       font-family: var(--font-utility); font-size: 12px;
                       color: var(--text-muted); }
    /* A hovered mark lifts and the rest recede, so a dense chart can be read one series at a
       time without a legend doing the work. */
    .ch-seq g:hover rect, .ch-hist g:hover rect { fill: var(--nf-blue-dark); }

    .ch-venn svg { width: 100%; height: auto; display: block; }
    .ch-venn__n { font-family: var(--font-utility); font-size: 13px; font-weight: 600;
                  fill: var(--text-primary); }
    .ch-venn__s { font-family: var(--font-utility); font-size: 10px; fill: var(--text-secondary); }
    .ch-venn__name { font-family: var(--font-utility); font-size: 10px; font-weight: 500; }
    .ch-fmt { display: flex; flex-direction: column; gap: var(--s2);
              font-family: var(--font-utility); font-size: 12px; }
    .ch-fmt__row { display: grid; align-items: center; gap: var(--s2);
                   grid-template-columns: 4rem 1fr 2.2rem 2.2rem; }
    .ch-fmt__k { color: var(--text-secondary); }
    .ch-fmt__track { background: var(--surface-sunken); height: 16px; }
    .ch-fmt__track i { display: block; height: 100%; background: var(--nf-blue); }
    /* Grey where the sample is too thin to be a finding — the bar is still drawn, because
       hiding it would imply video had been compared with nothing. */
    .ch-fmt__track i.thin { background: rgba(34,34,34,.18); }
    .ch-fmt__v { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;
                 color: var(--text-primary); }
    .ch-fmt__n { text-align: right; color: var(--text-muted); }

    /* One audience against another. Subject in the brand blue, the thing it is measured
       against in a recessive grey — a second saturated hue would say the two are peers when
       one of them is the baseline. `#A8A8A8` is the step the palette validator passed against
       the blue (delta-E 15.8 to normal vision, 10.8 under protanopia); it sits under 3:1
       against the surface, which is why every pair carries a printed percentage rather than a
       tooltip alone. */
    .ch-pair { display: flex; flex-direction: column; gap: var(--s2); }
    .ch-pair__rows { display: flex; flex-direction: column; gap: var(--s2);
                     font-family: var(--font-utility); font-size: 12px; }
    .ch-pair__row { display: grid; align-items: center; gap: var(--s2);
                    grid-template-columns: 7rem 1fr; }
    .ch-pair__k { color: var(--text-secondary); text-align: right; }
    .ch-pair__tracks { display: flex; flex-direction: column; gap: 3px; }
    /* Bar and value on one line, so the number belongs to the bar beside it. In a shared
       right-hand column the two numbers sat under each other and a reader had to work out
       which belonged to which bar. */
    .ch-pair__line { display: grid; grid-template-columns: 1fr 2.6rem; align-items: center;
                     gap: var(--s2); }
    .ch-pair__track { display: block; background: var(--color-data-bg); height: 11px; }
    .ch-pair__track i { display: block; height: 100%; }
    .ch-pair__track i.mine { background: var(--nf-blue); }
    .ch-pair__track i.theirs { background: var(--data-reference); }
    .ch-pair__v { font-weight: 600; font-variant-numeric: tabular-nums;
                  color: var(--text-primary); }
    .ch-pair__v2 { font-weight: 400; font-variant-numeric: tabular-nums;
                   color: var(--text-muted); }

    /* The hero KPI is defined once, further up, beside the rest of the KPI anatomy. A second
       block here set `justify-content` (which does nothing on a grid) and a `line-height` that
       fought the one above it — the brand's Light-at-64px voice is carried by
       `--kpi-value-weight` and `--kpi-value-size-hero`, which is the thing not to raise. */

    /* A segmented control of links rather than buttons: each state is a URL, so it is
       bookmarkable, shareable, and works with the back button — which a JavaScript filter is
       not and does not. */
    .segmented a { display: inline-block; padding: 5px 13px; font-family: var(--font-utility);
                   font-size: 12px; color: var(--text-secondary); text-decoration: none; }
    .segmented a.is-on { background: var(--nf-blue); color: var(--nf-white); font-weight: 600; }
    .segmented a:hover:not(.is-on) { background: var(--surface-sunken); }

    /* Chart furniture, shared by every gridded chart. The grid sits behind the data and is
       recessive by construction: a tint of the series colour rather than a grey rule, so it
       never competes with a mark drawn in the same hue. */
    .gridline { stroke: var(--color-data-bg); stroke-width: 1; }
    .ax  { fill: var(--text-muted); font-family: var(--font-utility); font-size: 10.5px; }
    .axs { fill: var(--text-secondary); font-family: var(--font-utility); font-size: 11px;
           font-weight: 500; }
    /* A value written on the mark that carries the finding — never on every point, which is
       the anti-pattern the shapes doctrine names. */
    .val { fill: var(--text-primary); font-family: var(--font-utility); font-size: 11px;
           font-weight: 600; font-variant-numeric: tabular-nums; }
    .ch-seq svg, .ch-area svg, .ch-scatter svg, .ch-hist svg, .ch-venn svg, .ch-donut svg
      { display: block; width: 100%; height: auto; overflow: visible; }

    .ch-dots svg { width: 100%; height: auto; display: block; }

    /* The flow a post's audience takes, and a named breakdown of what they did. */
    .ch-flow svg { width: 100%; height: auto; display: block; overflow: visible; }
    .ch-flow__n { font-family: var(--font-utility); font-size: 13px; font-weight: 600;
                  fill: var(--text-primary); }
    .ch-flow__k { font-family: var(--font-utility); font-size: 10.5px;
                  fill: var(--text-muted); }
    .ch-tally { display: flex; flex-direction: column; gap: var(--s2);
                font-family: var(--font-utility); font-size: 12px; }
    .ch-tally__row { display: grid; align-items: center; gap: var(--s2);
                     grid-template-columns: 9rem 1fr 2.2rem; }
    /*  One line, always. The key is a reaction name on the post page and a post *title* on the
        company page, and a title wrapped in a 5.5rem column ran to five lines — one row as tall
        as five, for a label. The whole of it is on the `title` attribute. */
    .ch-tally__k { color: var(--text-secondary); white-space: nowrap; overflow: hidden;
                   text-overflow: ellipsis; }
    .ch-tally__track { background: var(--surface-sunken); height: 14px; }
    .ch-tally__track i { display: block; height: 100%; background: var(--nf-blue);
                         min-width: 1px; }
    .ch-tally__v { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;
                   color: var(--text-primary); }

    /* The claim, then the rows it was counted from. A number on a page with a client's logo
       needs its evidence reachable, and a separate panel further down is reachable in theory
       and unread in practice. Set in from the chart above it so it reads as support rather
       than as a second finding. */
    .evidence { margin-top: var(--s4); padding-top: var(--s3);
                border-top: 1px solid var(--divider); }
    .evidence > h4 { font-family: var(--font-utility); font-size: 11px; font-weight: 600;
                     letter-spacing: .09em; text-transform: uppercase;
                     color: var(--text-muted); margin: 0 0 var(--s2); }
    /* What became of one person, as a word rather than an empty cell. "Did not return" is a
       fact; a blank is missing data, and the two must not look the same. */
    /* A pill, as the mock-up draws it and as every other marker in the app is. Plain coloured
       text read as a link — and one of the two states was the same blue as the links beside
       it. The chip is the second signal, so the pair is not told apart by colour alone. */
    .proof { font-family: var(--font-utility); font-size: 10px; font-weight: 600;
             letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
             padding: 2px 8px; border-radius: var(--radius-pill);
             background: var(--color-data-bg); color: var(--nf-blue-dark); }
    .proof.none { background: var(--surface-sunken); color: var(--text-muted); }
    /* The footnote a page ends on: what the numbers above cannot mean. */
    .foot { margin: var(--s6) 0 var(--s4); font-size: 13px; color: var(--text-muted);
            max-width: var(--measure); }
    /* A toolbar slot that looks like a control and is not one — the post's own date. Dashed,
       because a solid one reads as a filter that does nothing, and a control that changes no
       number is worse than no control. */
    .toolbar__still { font-family: var(--font-utility); font-size: 12px;
                      color: var(--text-secondary); padding: 4px 10px;
                      border: 1px dashed var(--divider); }
    .toolbar__asof { font-family: var(--font-utility); font-size: 12px;
                     color: var(--text-muted); margin-right: var(--s2); }
    /* A grid nested inside one column of the outer grid — four KPIs filling a `col-7`. It
       needs its own twelve columns; inheriting the page's would make each child span the
       whole width. */
    .dashboard-grid--inner { margin-top: 0; }
    /* The person page's masthead carries a face, so it sits centred rather than baseline. */
    .title-row--subject { align-items: center; }

    /* ============================================================
       The Shortlist — a grid rather than a table.

       **Not a `<table>`, deliberately.** The style guide names three ways a table breaks that
       no test here can see, and two of them bit this component: a positional `nth-child` clip
       rule pointed one cell left the moment a tier column went in front of it, and a
       `display:block` utility on a `<td>` took the cell out of its own row. A grid of divs has
       neither failure mode — every cell is addressed by the class it carries.
       ============================================================ */
    .shortlist { padding: 0; }
    /* The one accent moment on the page. A wash rather than a rule, because the panel is
       already inside a card and a second border around a heading is a third frame. */
    .sl-head { background: var(--blue-10); padding: 18px 24px;
               display: flex; justify-content: space-between; align-items: center; gap: 40px; }
    .sl-head h2 { margin: 0; font-size: 22px; font-weight: 500; letter-spacing: -.01em; }
    .sl-head h2 small { display: block; margin-top: 4px; font-size: 14px; font-weight: 400;
                        color: var(--text-secondary); max-width: 62ch; }
    .sl-count { display: flex; align-items: baseline; gap: 8px;
                font-size: 32px; font-weight: 300; color: var(--nf-blue); }
    .sl-count small { font-family: var(--font-utility); font-size: 13px; font-weight: 400;
                      color: var(--text-muted); }
    .sl-toolbar { padding: 16px 24px 0; }
    /* The count inside a tab. Muted against its own label, and inheriting on the selected one —
       white-on-blue at .7 alpha is still legible, and the rule below keeps it that way. */
    .segmented i { font-style: normal; opacity: .65; margin-left: 5px; }

    .sl-tbl { padding: 0 24px 4px; }
    .sl-row { display: grid; gap: 0 20px; align-items: start; padding: 16px 0 14px;
              border-bottom: 1px solid var(--divider);
              grid-template-columns: 28px minmax(240px, 1.1fr) 250px minmax(200px, 1fr) 170px; }
    /* **`display:grid` beats `[hidden]` and this is why the tabs stopped filtering.** The
       attribute hides an element through the user-agent sheet's `[hidden]{display:none}`, which
       any class setting `display` overrides — the old rows were `<tr>` with no display of their
       own, so the attribute worked and nothing said it was load-bearing. Both halves of the
       filter write the attribute (the server renders the opening bucket, the tabs toggle it),
       so without this the panel renders every bucket at once and the control does nothing
       visible. */
    .sl-row[hidden] { display: none; }
    .sl-row--head { padding: 20px 0 10px; font-family: var(--font-utility); font-size: 12px;
                    font-weight: 500; color: var(--text-muted); }
    .sl-row:last-child { border-bottom: 0; }
    /* The hover reaches past the row's own padding, so a pointer anywhere across the band
       lights the whole line rather than a column of it. */
    .sl-row:not(.sl-row--head) { position: relative; }
    .sl-row:not(.sl-row--head):hover { background: var(--surface-sunken);
              box-shadow: 0 0 0 12px var(--surface-sunken); z-index: 2; }
    .sl-rank { font-family: var(--font-utility); font-size: 13px; color: var(--text-muted);
               padding-top: 10px; text-align: right; }

    .sl-who { display: grid; grid-template-columns: 40px 1fr; gap: 12px; align-items: start; }
    /* The row's own size, not the list default of 1.75rem — a 28px mark in a 40px column reads
       as a mistake, and this row is the widest thing on the page. */
    .sl-who .avatar { width: 40px; height: 40px; font-size: 13px; }
    /* **No underline anywhere in this panel.** The page-wide rule gives every link a
       `--blue-50` underline, which on a row already carrying a name, a firm, a title and a post
       title is four underlines a reader has to look past. The name and the post say they are
       links by going blue on hover; the company chip says it with its ground. */
    .shortlist a { text-decoration: none; }
    .sl-name { font-size: 16px; font-weight: 500; color: var(--text-primary);
               display: inline-block; border-bottom: 1px solid transparent; }
    .sl-name:hover { color: var(--nf-blue); border-bottom-color: var(--nf-blue); }
    /* Smaller than the name above it: it is evidence for the row rather than the row's
       heading, and at the same size the two competed for the eye on every line. */
    .sl-sentence { margin: 3px 0 0; font-size: 13px; line-height: 1.45;
                   color: var(--text-primary); }
    /* *Not yet ranked* is a gap in our work rather than a fact about them, and it reads as one:
       muted and set apart from the sentences that carry evidence. */
    .sl-sentence--none { color: var(--text-muted); font-style: italic; }

    .sl-roles { margin: 0; padding: 4px 0 0; list-style: none; display: grid; gap: 7px;
                min-width: 0; }
    .sl-roles li { display: flex; align-items: center; gap: 8px; min-width: 0;
                   line-height: 20px; }
    .sl-roles li .co { flex: 0 1 auto; max-width: calc(100% - 44px); min-width: 0; }
    .sl-roles li .t { flex: 1 1 auto; min-width: 36px; font-family: var(--font-utility);
                      font-size: 12px; color: var(--text-muted);
                      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .sl-roles li.empty { font-family: var(--font-utility); font-size: 13px;
                         color: var(--text-muted); }
    /* Which of somebody's firms the score is about carries a `title` and no mark of its own.
       A rule down the side of one item in a list of two or three is a second vertical line on a
       row that already has columns — it read as a border somebody had left behind rather than
       as a statement about that firm. The tooltip is enough for a thing that is true of one row
       in four and interesting on none of them until somebody asks. */

    .sl-why { min-width: 0; }
    .sl-act { display: flex; align-items: center; gap: 12px; font-family: var(--font-utility);
              font-size: 13px; color: var(--text-secondary); }
    .sl-act .ct { display: inline-flex; align-items: center; gap: 4px; font-weight: 500;
                  color: var(--text-primary); position: relative; }
    .sl-act .ct .i { color: var(--text-muted); }
    /* A comment is the rare half — about 7% of engagements carry words — so the one that has
       them is the one that is lit, and it is the one that opens. */
    .sl-act .ct.has, .sl-act .ct.has .i { color: var(--nf-blue); }
    .sl-act time { color: var(--text-muted); }
    .sl-post { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
               overflow: hidden; margin-top: 3px; font-size: 14px; line-height: 1.4;
               color: var(--text-primary); }
    a.sl-post:hover { color: var(--nf-blue); }

    .sl-fit { position: relative; padding-top: 2px; outline: 0; }
    .sl-score { font-size: 26px; font-weight: 300; letter-spacing: -.02em; }
    /* Two lengths on one bar: how much of the rubric could be answered, and how much of that
       came back well. The score alone is the defect this exists to prevent — 0.80 from three of
       eight and 0.80 from seven of eight are not the same claim. */
    .sl-bar { position: relative; height: 5px; margin-top: 8px;
              background: var(--surface-sunken); }
    .sl-bar .cov { position: absolute; inset: 0 auto 0 0; background: rgba(34,34,34,.12); }
    .sl-bar .val { position: absolute; inset: 0 auto 0 0; background: var(--nf-blue); }
    .sl-cov { margin-top: 6px; font-family: var(--font-utility); font-size: 12px;
              color: var(--text-muted); }
    .sl-fit.is-thin .sl-cov { color: var(--nf-orange); font-weight: 500; }
    .sl-flag { margin-top: 5px; font-family: var(--font-utility); font-size: 12px;
               color: var(--nf-red); }
    .sl-flag b { font-weight: 500; }

    /* The eight criteria as one object, in the rubric's own order. A hollow square keeps its
       place rather than sorting to the end: sixth and hollow is what says *the sixth question
       could not be answered*. */
    .strip { display: inline-grid; grid-auto-flow: column; gap: 4px; }
    .strip i { display: block; width: 14px; height: 14px; background: var(--nf-green-dark); }
    .strip i[data-s="moderate"] { background: var(--nf-orange); }
    .strip i[data-s="weak"] { background: var(--nf-red); }
    .strip i[data-s="unknown"] { background: transparent;
                                 box-shadow: inset 0 0 0 1.5px rgba(34,34,34,.22); }

    /* The hover panel, shared by the fit tile and the comment quote. Hover *and* focus: a panel
       reachable only by pointer is a panel a keyboard reader does not have. */
    .sl-fit .pop, .sl-act .pop { position: absolute; top: calc(100% + 6px); z-index: 5;
              width: 340px; background: var(--surface-raised);
              border: 1px solid var(--divider); box-shadow: 0 8px 24px rgba(34,34,34,.14);
              padding: 14px 16px 12px; text-align: left;
              opacity: 0; visibility: hidden; transform: translateY(-4px);
              transition: opacity .15s, transform .15s, visibility .15s; }
    .sl-fit .pop { right: 0; }
    .sl-act .pop { left: 0; }
    .sl-fit:hover .pop, .sl-fit:focus .pop, .sl-fit:focus-within .pop,
    .sl-act .ct.has:hover .pop, .sl-act .ct.has:focus .pop { opacity: 1; visibility: visible;
                                                             transform: none; }
    .sl-pop ul { list-style: none; margin: 12px 0 0; padding: 0;
                 font-family: var(--font-utility); font-size: 12.5px; line-height: 1.45;
                 color: var(--text-secondary); }
    .sl-pop li { padding: 4px 0 4px 16px; position: relative;
                 border-top: 1px solid var(--divider); }
    .sl-pop li::before { content: ''; position: absolute; left: 0; top: 9px;
                         width: 8px; height: 8px; background: var(--nf-green-dark); }
    .sl-pop li[data-s="moderate"]::before { background: var(--nf-orange); }
    .sl-pop li[data-s="weak"]::before { background: var(--nf-red); }
    .sl-pop li[data-s="unknown"]::before { background: transparent;
                                           box-shadow: inset 0 0 0 1.5px rgba(34,34,34,.22); }
    .sl-pop li[data-s="unknown"] { color: var(--text-muted); }
    /* **Everything the opener sets has to be undone here.** The panel is a child of the comment
       count, which is `font-weight:500`, blue when it has words, and inside a row set in the
       utility face — so a quotation left to inherit came out bold, blue and in the wrong family,
       which is three things a quotation is not. A hover panel hanging off a styled control
       inherits the control, and the control is styled to be a number. */
    .quotepop { font-family: var(--font-body); font-size: 14px; font-weight: 400;
                line-height: 1.45; color: var(--text-primary); white-space: normal;
                padding-left: 16px; border-left: 3px solid var(--nf-blue); }
    .quotepop span { display: block; }
    .quotepop span + span { margin-top: 8px; padding-top: 8px;
                            border-top: 1px solid var(--divider); }

    .sl-foot { display: flex; justify-content: space-between; align-items: flex-end;
               gap: 24px; flex-wrap: wrap; padding: 14px 24px 18px;
               border-top: 1px solid var(--divider); }
    .sl-foot .hint { margin: 6px 0 0; max-width: 68ch; }
    .sl-legend { display: flex; gap: 16px; align-items: center;
                 font-family: var(--font-utility); font-size: 12px; color: var(--text-muted); }
    .sl-legend i { display: inline-block; width: 10px; height: 10px; vertical-align: -1px;
                   margin-right: 6px; background: var(--nf-green-dark); }
    .sl-legend i[data-s="moderate"] { background: var(--nf-orange); }
    .sl-legend i[data-s="weak"] { background: var(--nf-red); }
    .sl-legend i[data-s="unknown"] { background: transparent;
                                     box-shadow: inset 0 0 0 1.5px rgba(34,34,34,.22); }

    @media (max-width: 1060px) {
      .sl-row { grid-template-columns: 24px 1fr 1fr 130px; }
      .sl-roles { grid-column: 2 / -1; }
      .sl-row--head span:nth-child(4) { display: none; }
    }
    @media (max-width: 700px) {
      .sl-row { grid-template-columns: 1fr; gap: 10px; }
      .sl-rank, .sl-row--head { display: none; }
      .sl-fit .pop { left: 0; right: auto; width: min(340px, 86vw); }
      .sl-head, .sl-toolbar, .sl-tbl, .sl-foot { padding-left: 16px; padding-right: 16px; }
    }

    /* ============================================================
       Everyone who engaged — the census under the shortlist.

       A grid of divs for the reason the Shortlist is one: the style guide's three table traps
       all apply to a row this dense, and a positional selector here would break the first time
       a column was added.
       ============================================================ */
    .everyone { padding: 0; }
    .ev-head { display: flex; justify-content: space-between; align-items: center; gap: 20px;
               padding: 20px 24px 4px; }
    .ev-head h2 { margin: 0; font-size: 22px; font-weight: 500; }
    .ev-head h2 small { font-size: 14px; font-weight: 400; color: var(--text-secondary);
                        margin-left: 8px; }
    .ev-filter { padding: 10px 24px 0; }
    .everyone a { text-decoration: none; }

    .ev-tbl { padding: 0 24px; }
    .ev-row { display: grid; gap: 0 16px; align-items: center; padding: 9px 0;
              border-bottom: 1px solid var(--divider); font-size: 14px; line-height: 1.3;
              grid-template-columns: 28px minmax(170px, .9fr) minmax(280px, 1.4fr) 190px; }
    /* `display:grid` beats the user-agent `[hidden]{display:none}`, so both halves of the
       filter need this or the tabs do nothing visible — the same fault the Shortlist shipped
       for an afternoon. */
    .ev-row[hidden], .ev-group[hidden] { display: none; }
    .ev-row--head { padding: 14px 0 8px; font-family: var(--font-utility); font-size: 12px;
                    font-weight: 500; color: var(--text-muted); }
    .ev-row--head .r { text-align: right; }
    .ev-row:not(.ev-row--head) { position: relative; }
    .ev-row:not(.ev-row--head):hover { background: var(--surface-sunken);
              box-shadow: 0 0 0 12px var(--surface-sunken); z-index: 2; }

    .ev-name { display: flex; align-items: center; gap: 8px; min-width: 0; }
    .ev-name .name { font-weight: 500; white-space: nowrap; overflow: hidden;
                     text-overflow: ellipsis; border-bottom: 1px solid transparent;
                     color: var(--text-primary); }
    .ev-name .name:hover { color: var(--nf-blue); border-bottom-color: var(--nf-blue); }
    .ev-lifted { display: inline-flex; color: var(--nf-blue); flex-shrink: 0; }

    .ev-where { display: flex; align-items: center; gap: 8px; min-width: 0; line-height: 20px; }
    .ev-where .co { flex: 0 1 auto; max-width: 58%; min-width: 0; }
    .ev-where .t { flex: 0 1 auto; min-width: 36px; font-family: var(--font-utility);
                   font-size: 12px; color: var(--text-muted);
                   white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .ev-more { position: relative; flex: 0 0 auto; font-family: var(--font-utility);
               font-size: 11px; font-weight: 500; line-height: 18px; padding: 0 7px;
               border-radius: var(--radius-pill); background: rgba(34,34,34,.08);
               color: var(--text-secondary); cursor: default; outline: 0; }
    /* Text on a coloured ground says what colour it goes, and it goes lighter rather than
       darker — the page-wide `a:hover` once painted a button's own label its own background. */
    .ev-more:hover, .ev-more:focus { background: var(--nf-blue); color: var(--nf-white); }

    .ev-eng { position: relative; display: flex; justify-content: flex-end; align-items: center;
              gap: 14px; font-family: var(--font-utility); font-size: 13px;
              color: var(--text-secondary); outline: 0; padding: 4px 0; }
    .ev-eng .ct { display: inline-flex; align-items: center; gap: 4px; font-weight: 500;
                  color: var(--text-primary); min-width: 34px; }
    .ev-eng .ct .i { color: var(--text-muted); }
    .ev-eng .ct.has, .ev-eng .ct.has .i { color: var(--nf-blue); }
    .ev-eng time { color: var(--text-muted); min-width: 44px; text-align: right; }

    /* The two hover panels. Hover *and* focus, and both openers carry `tabindex` — a panel
       reachable only by pointer is a panel a keyboard reader does not have. */
    .ev-rolepop, .ev-engpop { position: absolute; top: calc(100% + 6px); z-index: 6;
              background: var(--surface-raised); border: 1px solid var(--divider);
              box-shadow: 0 8px 24px rgba(34,34,34,.14); text-align: left;
              opacity: 0; visibility: hidden; transform: translateY(-4px);
              transition: opacity .15s, transform .15s, visibility .15s; }
    /* Reset, for the reason `.quotepop` gives: this hangs off an 11px, weight-500, utility-face
       pill, and a list of employers left to inherit comes out as small bold captions. A panel
       hanging off a styled control inherits the control. */
    .ev-rolepop { left: 0; width: 300px; padding: 12px 14px;
                  font-family: var(--font-body); font-size: 14px; font-weight: 400;
                  line-height: 1.3; color: var(--text-primary); }
    .ev-engpop { right: 0; width: 400px; padding: 4px 14px 8px;
                 font-family: var(--font-utility); font-size: 13px; }
    .ev-more:hover .ev-rolepop, .ev-more:focus .ev-rolepop,
    .ev-eng:hover .ev-engpop, .ev-eng:focus .ev-engpop, .ev-eng:focus-within .ev-engpop {
              opacity: 1; visibility: visible; transform: none; }
    .ev-rolepop .poph { display: block; font-family: var(--font-utility); font-size: 11px;
                        font-weight: 500; color: var(--text-muted); margin-bottom: 8px; }
    .ev-engpop ul { list-style: none; margin: 0; padding: 0; }
    .ev-engpop li { display: grid; grid-template-columns: 18px 1fr; gap: 10px; padding: 9px 0;
                    border-bottom: 1px solid var(--divider); }
    .ev-engpop li:last-child { border-bottom: 0; }
    .ev-engpop li .i { margin-top: 2px; color: var(--text-muted); }
    .ev-engpop li.c .i { color: var(--nf-blue); }
    .ev-engpop .v { color: var(--text-muted); }
    .ev-engpop a { color: var(--text-primary); }
    .ev-engpop a:hover { color: var(--nf-blue); }
    .ev-engpop time { color: var(--text-muted); margin-left: 8px; font-size: 12px; }
    /* Their words, set apart from the row that names the post. The only thing in this panel a
       count could not have told you. */
    .ev-engpop p { margin: 5px 0 0; padding-left: 8px; border-left: 2px solid var(--nf-blue);
                   font-family: var(--font-body); font-size: 13px; line-height: 1.45;
                   color: var(--text-secondary); }

    .ev-group { display: flex; align-items: baseline; gap: 8px; padding: 16px 0 6px;
                font-family: var(--font-utility); font-size: 12px; color: var(--text-muted);
                border-bottom: 1px solid var(--divider); }
    .ev-group b { font-weight: 500; color: var(--text-primary); font-size: 13px; }
    .ev-group span { margin-left: auto; }
    .ev-group--fold { cursor: pointer; list-style: none; padding: 14px 0; }
    .ev-group--fold::-webkit-details-marker { display: none; }
    .ev-fold[open] .ev-group--fold { border-bottom: 0; }
    .ev-foot { display: flex; justify-content: space-between; align-items: baseline; gap: 24px;
               flex-wrap: wrap; padding: 12px 24px 18px; }
    .ev-foot .read { margin: 0; }

    @media (max-width: 700px) {
      .ev-row { grid-template-columns: 28px 1fr 130px; }
      .ev-where { grid-column: 2 / -1; }
      .ev-row--head span:nth-child(3) { display: none; }
      .ev-head, .ev-filter, .ev-tbl, .ev-foot { padding-left: 16px; padding-right: 16px; }
      .ev-engpop { width: min(400px, 86vw); }
    }
