/* ==========================================================================
   pp-tokens.css — shared design tokens
   ==========================================================================

   ONE source, consumed by both:
     - the WordPress theme (pivotalpath.com)
     - Vision's prerenderer (index pages, methodology, answer pages)

   ★ TWO COPIES, ONE PER SYSTEM, AT THE SAME PATH (revised 2026-09-14).

   This header used to read ONE COPY, ONE HOME, and forbade duplication
   outright: Vision hosted the file, both systems linked it as an absolute URL,
   and a second copy was described as a fork that defeats the purpose. That was
   right while one host served everything. It is now wrong, and following it
   would reintroduce the failure it was written to prevent.

   What changed: www.pivotalpath.com serves this file itself. Each system now
   holds its own copy and links it RELATIVELY --

     <link rel="stylesheet" href="/assets/pp-tokens.css">

   -- so a page's design comes from the host that served the page. The path is
   identical on both, which is what makes the two copies interchangeable.

   WHY THE RULE INVERTED. A single hosted copy means every page on BOTH
   properties depends on ONE host being up and correct. That is not a shared
   source, it is a cross-domain runtime dependency, and it failed exactly that
   way twice: the /a/ pages shipped an absolute link to a host that did not
   serve the file and rendered unstyled; later the same link pointed at a host
   running an older build, so the pages loaded a palette that had been replaced.
   Neither 404s visibly. Both look like "the theme overrode our CSS".

   THE COST, STATED PLAINLY: two copies CAN drift, and drift here is invisible
   -- two properties that are meant to be indistinguishable slowly stop being
   so, with nothing erroring. That is a real regression against the old
   arrangement, and it is accepted only because it is MECHANICALLY CHECKED.

   THE SYNC REQUIREMENT. The two served copies must be BYTE-IDENTICAL.
     - vision/public/assets/pp-tokens.css is the EDITABLE source. Change it here.
     - The WordPress copy is a mirror. Never edit it directly; copy this file
       over it, whole.
     - server/scripts/check_tokens_sync.py fetches both served URLs and fails if
       they differ by a single byte. It is a deploy gate, so a change that lands
       on one property and not the other cannot ship quietly.

   The seam between systems sits at LOGIN, not at the platform boundary.
   A visitor moving from /about/ to /hedge-fund-index/composite must see no
   change in typography, spacing, colour, header or navigation.

   Rules:
     - Public pages use these tokens only. No local colour or type values.
     - Application UI (behind login) may extend, never redefine.
     - Add a token rather than hardcoding a value in one system.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1 · Type
   -------------------------------------------------------------------------- */
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400&family=Archivo:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');

:root {
  /* ★ NEWSREADER, not Instrument Serif (2026-09-14). Newsreader is what Vision
     actually ships on its public pages and what has been approved; Instrument
     Serif was this file's own choice and never matched. Vision loads it with
     optical sizing, which the import above preserves. */
  --pp-font-display: "Newsreader", Georgia, "Times New Roman", serif;
  --pp-font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  --pp-font-body: Archivo, system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Scale. Fluid where a heading must hold at both ends. */
  /* ★ CEILING 66px, WAS 72px (2026-09-17). The CLAMP stays: it holds at narrow
     widths where the Lab's flat 66px literal does not, so only the ceiling was
     wrong. */
  --pp-size-hero:      clamp(40px, 6.2vw, 66px);
  --pp-size-page:      clamp(36px, 5.4vw, 60px);
  --pp-size-section:   clamp(28px, 4vw, 42px);
  --pp-size-sub:       27px;
  --pp-size-lede:      19px;
  /* ★ 16px, WAS 16.5px (2026-09-17). The Lab's base, adopted for the same
     reason as the measure and the CTA. Half a pixel sounds like nothing and
     is inherited by every element that does not set its own size, so it was
     the reason the lede and the nav line-heights differed even where their
     font sizes already agreed. */
  --pp-size-body:      16px;
  --pp-size-ui:        15px;
  --pp-size-small:     14px;
  --pp-size-caption:   12.5px;
  --pp-size-label:     12px;

  --pp-lh-tight:  1.05;
  --pp-lh-head:   1.12;
  /* ★ 1.6, THE LAB'S (2026-09-17). These two were the SECOND inherited value
     the two systems disagreed about - the first was the base font-size - and
     between them they accounted for every remaining line-height difference
     across the estate, in elements whose font sizes already agreed. */
  --pp-lh-body:   1.6;
  --pp-lh-lede:   1.6;

  --pp-track-display: -0.02em;   /* display type needs negative tracking */
  --pp-track-label:    0.09em;   /* uppercase labels need positive */
}

/* --------------------------------------------------------------------------
   2 · Colour — VISION'S PUBLIC PALETTE, adopted verbatim 2026-09-14

   Every value below is lifted from the `.pp-app` block in
   src/pages/lab/PivotalPathPrototype.jsx, which is what Vision actually serves
   on its public pages. The NAMES here are semantic and load-bearing — 32 uses
   of --pp-accent alone across the prerenderer and the chrome — so the values
   changed and the names did not.

   ★★ global.css IS A DIFFERENT DESIGN SYSTEM. DO NOT RECONCILE AGAINST IT.
   src/styles/global.css is the SIGNED-IN APPLICATION's palette: its --blue is
   #1a4d80, its --gold #e3a330, its --navy-header #243a60. Those are not these.
   This file previously carried #243a60 as --pp-brand, --pp-panel and
   --pp-theme-color, taken from the application via the <meta name="theme-color">
   tag — a plausible-looking route to the wrong colour, since the app and the
   public pages are different surfaces that happen to share a company. Vision's
   public navy is #0b1620. Anyone reconciling these later must take the public
   values from PivotalPathPrototype.jsx, never from global.css.

   Three of these were INLINE LITERALS in Vision and never variables: the
   stats-band grey, the ticker green, and the two hero gradient stops. They are
   named here because a value used once is still a decision, and an unnamed one
   is the value that silently diverges.
   -------------------------------------------------------------------------- */
:root {
  /* ink ramp — Vision --text/--ink, --ink2, --muted */
  --pp-ink:        #0b1620;   /* primary text, and the dark ground */
  --pp-ink-2:      #13202e;   /* secondary ink */
  --pp-ink-3:      #637080;   /* labels, captions, de-emphasis (Vision --muted) */

  /* paper — Vision --paper is the PAGE ground; --white is the card surface */
  --pp-ground:     #faf9f6;   /* warm off-white page ground */
  --pp-surface:    #ffffff;   /* cards and panels sitting on the ground */
  --pp-wash:       #f1efe9;   /* stats band. WAS INLINE in Vision, never a var */

  --pp-rule:       #d8d4c9;   /* structural rules (Vision --line2) */
  --pp-rule-fine:  #e7e4dd;   /* within-component rules (Vision --line) */

  --pp-accent:     #1a46c0;   /* links, emphasis, active state (Vision --blue) */
  --pp-accent-strong: #1140b8;/* hover/pressed (Vision --blue2) */

  /* The dark surface: hero and footer. Vision paints it as a GRADIENT, not a
     flat fill — a flat #0b1620 is visibly not the same hero. */
  --pp-brand:      #0b1620;
  --pp-panel:      #0b1620;
  --pp-panel-from: #1c2e50;   /* gradient stop 1. WAS INLINE */
  --pp-panel-to:   #0b1620;   /* gradient stop 2. WAS INLINE */
  --pp-panel-grad: radial-gradient(ellipse 120% 80% at 60% -10%,
                     var(--pp-panel-from) 0%, var(--pp-panel-to) 65%);
  --pp-panel-ink:  #eef2f8;   /* text on dark */
  --pp-panel-mute: #8a98ac;   /* de-emphasised text on dark */
  --pp-panel-rule: rgba(255,255,255,.10);
  --pp-panel-border:#48597E;  /* ghost button on dark */

  /* status. Vision uses a DIFFERENT green on dark: #0f7a4d has too little
     contrast against the navy, so the ticker uses #5fd08c. Both are kept. */
  --pp-positive:   #0f7a4d;
  --pp-positive-on-dark: #5fd08c;   /* ticker figures. WAS INLINE */
  --pp-negative:   #b9202e;
  --pp-negative-bg:#F6E9E6;
  --pp-caution:    #b08d57;   /* the FREE & PUBLIC tag (Vision --gold) */
  --pp-caution-bg: #f7f3ec;   /* its ground (Vision --gold-bg) */
  --pp-focus:      #1a46c0;   /* focus ring follows the accent */

  --pp-theme-color:#0b1620;   /* <meta name="theme-color">. Was #243a60, which
                                 came from the APPLICATION, not from here. */
}

/* --------------------------------------------------------------------------
   2b · The values Vision's public pages use INLINE, named (2026-09-14)

   Added because the /a/ pages are now a PORT of those pages rather than a
   reimplementation of a description of them, and a port cannot be faithful
   while the values it needs have no names. Every one below is lifted from the
   CSS string in src/pages/lab/PivotalPathPrototype.jsx, where it appears as a
   literal inside a rule — Vision's `.pp-app` block names only the 14 colours,
   and these are the rest of what its components actually use.

   This file's own rule says: add a token rather than hardcoding a value in one
   system. That is what this is. The alternative was to hardcode a shadow and a
   radius into the /a/ page stylesheet and let the two properties drift on
   exactly the values nobody would think to compare.

   ★ --pp-radius STAYS 0. It is used by the existing pp-* components and is a
   deliberate choice for them; Vision's cards and buttons are rounded and get
   their own names. Two radii that mean different things need two tokens, not
   one token with a changed value.
   -------------------------------------------------------------------------- */
:root {
  /* radii, as Vision sets them per component */
  --pp-radius-card:  4px;     /* .card, .cap */
  --pp-radius-ui:    2px;     /* .btn, .tag */
  --pp-radius-icon:  3px;     /* .capicon plate */

  /* elevation. Vision lifts a card on hover; flat cards on a warm ground read
     as boxes drawn on the page rather than objects sitting on it. */
  --pp-shadow-card:  0 1px 2px rgba(11,22,32,.04), 0 3px 8px rgba(11,22,32,.04);
  --pp-shadow-cap:   0 1px 3px rgba(11,22,32,.05);
  --pp-shadow-lift:  0 4px 16px rgba(11,22,32,.09);

  /* the primary button is a GRADIENT with a coloured shadow, not a flat fill */
  --pp-accent-grad:        linear-gradient(160deg, #2456d8 0%, #1a46c0 100%);
  --pp-accent-grad-hover:  linear-gradient(160deg, #1c4bc4 0%, #1438a8 100%);
  --pp-shadow-accent:        0 1px 3px rgba(26,70,192,.35);
  --pp-shadow-accent-strong: 0 2px 8px rgba(26,70,192,.40);

  /* accent tints: the icon plate, and the row-hover wash */
  --pp-accent-plate: #eef2fb;
  --pp-accent-wash:  #f0f4ff;

  /* the APPROVED stats-band palette (2026-09-25) -- blue plate, navy mono
     figures, ink labels. Was two treatments: the 39 literal index pages'
     own page-local CSS already had this (composite.html's .strip, second
     declaration wins over a first, dark one in the same block), while
     .pp-panel -- the hub, the answer pages (correlation/downside/alpha),
     performance, data and coverage -- used the dark near-black panel
     instead. "One band treatment across the site, not two": these three
     tokens are that one treatment, and .pp-panel below now uses them. */
  --pp-band-bg:     #d6e1f6;
  --pp-band-border: #bccbeb;
  --pp-band-figure: #0b1f4d;

  /* on the dark panel Vision uses a LIGHTER accent and a lighter mute than the
     ones used on paper — #8fb0ff and #aab6c6, neither of which is --pp-accent
     or --pp-panel-mute. */
  --pp-accent-on-dark:     #8fb0ff;
  --pp-panel-ink-2:        #aab6c6;
  --pp-panel-btn-ink:      #eaf0ff;
  --pp-panel-rule-strong:  rgba(255,255,255,.28);

  /* the status chip's ground (Vision's .tag) */
  --pp-positive-bg:  #eef6f0;
}

/* --------------------------------------------------------------------------
   3 · Space and measure
   -------------------------------------------------------------------------- */
:root {
  /* ★ 1080px, THE LAB'S MEASURE (2026-09-17). Was 1240px. The corporate
     pages and the Lab's home view are the same design tuned twice - the Lab
     as a component with literal values, this as tokens - and where they
     disagreed the Lab wins, because the Lab is the design that was approved.
     160px narrower, and the estate now shares one measure. */
  --pp-wrap:        1080px;   /* corporate pages */
  /* BLOCK B24 item 2: was 940px, centred independently of --pp-wrap's
     1080px -- two different max-widths, each centred on its own,
     produced two different left edges (943/1080 differ, so the
     margin each auto-centring produces differs too). One content
     column: same value as --pp-wrap, so every page sharing the same
     viewport shares the same left edge regardless of which wrap
     class it uses. Reading measure, where wanted, is now the job of
     each paragraph's own max-width (many already carry one), not
     the section wrapper's. */
  --pp-wrap-read:    1080px;
  --pp-gutter:        32px;

  --pp-space-1:  4px;
  --pp-space-2:  8px;
  --pp-space-3:  14px;
  --pp-space-4:  22px;
  --pp-space-5:  34px;
  --pp-space-6:  52px;
  --pp-space-7:  88px;
  --pp-space-edge: 64px;  /* spacing rule v3: BOTH sides of a colour edge (_shared_css.SPACE_EDGE_PX) */
  --pp-space-trail: 32px; /* BLOCK A26 3b: link row -> footnote, one trailing block */

  --pp-measure:      68ch;    /* body copy */
  --pp-measure-lede: 60ch;
  --pp-measure-head: 22ch;

  --pp-header-h:     72px;
  --pp-border:       1px;
  --pp-border-heavy: 2px;
  --pp-radius:       0;       /* deliberate: nothing is rounded */
}

/* --------------------------------------------------------------------------
   4 · Base

   Applied by both systems. WordPress themes will need these scoped or
   !important-free overrides checked against the active theme.
   -------------------------------------------------------------------------- */
/* BLOCK B28 item 2: B27's fix scoped this to ".pp, body.pp" -- a
   plain, unscoped rule instead, so it applies regardless of which
   class (if any) the consuming page's <body> carries. */
body { margin: 0; }
/* BLOCK A13 item 7: THE body-font declaration for both plans -- one
   declaration, one place. Plan B: <body class="pp">. Plan A: WordPress owns
   <body>, so the push wraps the content in #pp-a-scope and _scope_css()
   maps .pp-scope-root onto that wrapper. Before this, font-family sat in
   the rule below, which Plan A scopes to "#pp-a-scope .pp" -- a descendant
   selector with nothing to match -- so the theme's Roboto won the cascade. */
/* BLOCK A22 item A2: three INHERITED properties, not one. Measured on the
   /coverage/ lede at 1440: font-family, colour and page background all
   diverged on Plan A (theme Roboto / 0,0,0 / 255,255,255). font-size and
   line-height are set per element and carry, so they stay on .pp only. */
.pp, body.pp, .pp-scope-root {
  font-family: var(--pp-font-body);
  color: var(--pp-ink);
  background: var(--pp-ground);
}
.pp, body.pp {
  font-size: var(--pp-size-body);
  line-height: var(--pp-lh-body);
  -webkit-font-smoothing: antialiased;
}
/* BLOCK A11 item 1: THE fix, at the source. .pp-link only ever declared
   its own border-bottom (the underline) -- it never set colour, and no
   base rule here ever set one either. The Batch-13 fix ("no link on
   this page has a colour of its own") was applied as a literal
   a{color:var(--pp-accent)} INSIDE only 4 of 16 pub-*.html templates'
   own <style> blocks (hub, alpha, correlation, downside) instead of
   here, in the one shared stylesheet every pp_chrome page links --
   methodology, data, index-composite, performance*, returns-by-year,
   and the flat literal_index_page/methodology families never got it,
   so their anchors (bare and .pp-link alike) fell through to the
   browser's own default link blue. One rule, lowest specificity, so
   every existing class-based override (.pp-cta, .crumbs a, .lock a,
   chrome nav/footer) still wins exactly as it already did. */
a { color: var(--pp-accent); }

/* BLOCK B28 item 1: was margin-inline/padding-inline (logical
   properties). corp-page.css's own .wrap -- confirmed correct at
   205 -- uses the physical margin/padding shorthand instead; this
   rule's logical form was the one thing that differed between the
   two, otherwise-identical boxes. Switched to match exactly. */
/* BLOCK B29 item 1: the actual 32px -- this file carries no universal
   box-sizing reset (corp-page.css's own *{box-sizing:border-box} never
   reached these two, since they're not in that file), so content-box
   made max-width exclude the 64px of padding: a 1144px box, content
   inset only by the auto-margin (173), not the padding too. Scoped to
   these two rather than a universal reset here, since this file is
   also the WordPress theme's shared copy and a bare `*` rule risks
   touching elements this file has never governed. */
.pp-wrap      { box-sizing: border-box; max-width: var(--pp-wrap);      margin: 0 auto; padding: 0 var(--pp-gutter); }
.pp-wrap-read { box-sizing: border-box; max-width: var(--pp-wrap-read); margin: 0 auto; padding: 0 var(--pp-gutter); }
/* BLOCK A9 item 1b: a section's bottom padding is meant to be the ONLY
   gap before its own tint/rule ends -- whatever element happens to sit
   last inside the wrap still carries its own margin-bottom, which stacks
   on top of that padding. The amount then varies by page depending on
   which element is last (34px on the hub's .fam, 16/13px elsewhere).
   The padding owns that space, not the content. */
.pp-wrap > :last-child, .pp-wrap-read > :last-child { margin-bottom: 0; }
.pp-wrap > :first-child, .pp-wrap-read > :first-child { margin-top: 0; }
/* The direct-child rule above doesn't reach a list's own last <li> --
   .pp-list li carries its own margin-bottom (--pp-space-2), which still
   stacked on top of the section's padding when a <ul> was the section's
   last element (found on the "Related questions" wash band). */
.pp-wrap > :last-child li:last-child,
.pp-wrap-read > :last-child li:last-child { margin-bottom: 0; }

/* Figures. Tabular numerals so columns align and digits are comparable. */
.pp-num { font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }

/* --------------------------------------------------------------------------
   5 · Headings
   -------------------------------------------------------------------------- */
.pp-h-hero, .pp-h-page, .pp-h-section, .pp-h-sub {
  font-family: var(--pp-font-display);
  font-weight: 400;
  letter-spacing: var(--pp-track-display);
  margin: 0 0 var(--pp-space-4);
}
/* The hero tracks tighter than the other display sizes: -0.025em against the
   -0.02em the rest share. Display type needs more negative tracking the larger
   it is set, and the hero is the largest thing on the estate. */
.pp-h-hero    { font-size: var(--pp-size-hero);    line-height: var(--pp-lh-tight); max-width: var(--pp-measure-head); letter-spacing: -0.025em; }
.pp-h-page    { font-size: var(--pp-size-page);    line-height: var(--pp-lh-tight); max-width: var(--pp-measure-head); }
.pp-h-section { font-size: var(--pp-size-section); line-height: var(--pp-lh-head); }
.pp-h-sub     { font-size: var(--pp-size-sub);     line-height: var(--pp-lh-head); }

/* ★ THE EYEBROW IS ITS OWN DIVERGENCE (2026-09-17): 12.5px/600 at .09em here
   against the Lab's 12px/500 at .13em, three properties none of which followed
   from anything else. Taken to the Lab's.

   --pp-size-label (12px) rather than a literal, since that token exists and is
   exactly this size. The tracking is written out: --pp-track-label is .09em
   and is shared with the footer headings, which are 12px/700 and not the same
   control - a token moved to suit one user is how the next reader finds a
   value that means two things. */
.pp-eyebrow {
  font-size: var(--pp-size-label);
  font-weight: 500;
  letter-spacing: 0.13em;
  line-height: 1;
  text-transform: uppercase;
  color: var(--pp-accent);
  margin: 0 0 var(--pp-space-4);
}

.pp-lede {
  font-size: var(--pp-size-lede);
  line-height: var(--pp-lh-lede);
  color: var(--pp-ink-2);
  max-width: var(--pp-measure-lede);
  margin: 0 0 var(--pp-space-6);
}

.pp-body { font-size: var(--pp-size-body); color: var(--pp-ink-2); max-width: var(--pp-measure); margin: 0 0 var(--pp-space-3); }

/* --------------------------------------------------------------------------
   6 · Header and footer

   These must be markup-identical across systems. If the nav changes in one,
   it changes in both — that is the strongest single signal of "same site".
   -------------------------------------------------------------------------- */
.pp-header {
  border-bottom: var(--pp-border) solid var(--pp-rule);
  position: sticky; top: 0; z-index: 20;
  /* ★ THE PAPER GROUND, NOT WHITE (2026-09-14). This was rgba(255,255,255,.95)
     -- a hardcoded literal, and the only white on a page whose ground is
     #faf9f6, which made the header read as a separate object sitting on the
     page rather than part of it. There is no --pp-paper token; the paper ground
     is --pp-ground, and that is what this now uses. The translucency goes with
     it: it existed to let content show through on scroll, and at a 1.5%
     difference from the ground it bought nothing but the mismatch. */
  background: var(--pp-ground);
}
.pp-header-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--pp-space-5);
  height: var(--pp-header-h);
  max-width: var(--pp-wrap); margin-inline: auto; padding-inline: var(--pp-gutter);
}
.pp-logo { font-family: var(--pp-font-display); font-size: 26px; letter-spacing: -0.01em; text-decoration: none; color: var(--pp-ink); }
/* The brand mark's two halves differ in COLOUR only -- "Pivotal" ink, "Path"
   accent. The markup comes from pp_chrome (_LOGO); this is the rule that makes
   it visible, and it lives here rather than in a page-local block so BOTH
   properties get it. Without it the span is inert and the mark renders flat. */
.pp-logo .lg-2 { color: var(--pp-accent); font-weight: inherit; }
.pp-nav { display: flex; gap: 30px; font-size: 14.5px; font-weight: 500; }
.pp-nav a { color: var(--pp-ink-2); text-decoration: none; }
.pp-nav a:hover { color: var(--pp-ink); }
.pp-nav a[aria-current="page"] { color: var(--pp-ink); }

/* item K5 (2026-09-25): dark navy, matching /a/'s footer exactly -- the
   two had diverged because .pp-footer never set a background at all (it
   inherited the page's off-white ground), an omission rather than a
   deliberate light-footer design. Colours below are the same hex values
   STANDARD_A_CHROME_CSS already uses for /a/'s footer (push_project_a.py),
   not reinvented, so the two stay one treatment rather than two dark
   footers that happen to differ. */
.pp-footer { background: var(--pp-ink); color: #eef2f8; padding: var(--pp-space-edge) 0 var(--pp-space-5); font-size: var(--pp-size-small); }
.pp-footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--pp-space-5); padding-bottom: var(--pp-space-5); border-bottom: var(--pp-border) solid #26313f; }
.pp-footer h4 { font-size: var(--pp-size-label); font-weight: 700; letter-spacing: var(--pp-track-label); text-transform: uppercase; color: #8a98ac; margin: 0 0 var(--pp-space-3); }
.pp-footer a { display: block; color: #c3cede; text-decoration: none; padding: 5px 0; }
.pp-footer a:hover { color: #fff; }
.pp-footer .pp-logo { color: #eef2f8; }
.pp-footer .pp-logo .lg-2 { color: #8fb0ff; }
/* item G2 (2026-09-25): the audience label beside each Products link. */
.pp-footer a .pl { display: inline; font-size: 11.5px; color: #54607a; margin-left: 5px; }
.pp-footer a:hover .pl { color: #8a98ac; }

/* --------------------------------------------------------------------------
   7 · Actions
   -------------------------------------------------------------------------- */
.pp-cta {
  display: inline-block; white-space: nowrap; cursor: pointer;
  /* ★ A NAV-BAR BUTTON, NOT A STANDALONE CALL TO ACTION (2026-09-17). Was
     14px/600 with 10px 20px padding, rendering 44px tall - the proportions of
     a button that owns its own line. In a header it sat 10px taller than the
     Lab's, which is the same control in the same place. The Lab's values:
     13.5px/500, 9px 15px, 34px rendered. */
  font: inherit; font-size: 13.5px; font-weight: 500;
  /* line-height 1, like the Lab's .btn. Without it the control inherits the
     body's 1.55 and stands 41px rather than 34px - the padding was matched and
     the button was still the wrong height. */
  line-height: 1;
  padding: 9px 15px;
  /* ★ ACCENT, NOT NEAR-BLACK (2026-09-14). The one action in the header should
     read as the accent colour the rest of the site uses for action, and #fff
     was a literal where --pp-surface is the token. */
  border: var(--pp-border) solid var(--pp-accent);
  background: var(--pp-accent); color: var(--pp-surface); text-decoration: none;
  border-radius: var(--pp-radius);
}
.pp-cta:hover { opacity: .85; }
.pp-cta--ghost { background: none; color: var(--pp-accent); }
.pp-cta:focus-visible { outline: 2px solid var(--pp-focus); outline-offset: 2px; }

.pp-link {
  font-weight: 600; text-decoration: none;
  border-bottom: var(--pp-border-heavy) solid var(--pp-accent);
  padding-bottom: 2px; cursor: pointer;
}

/* --------------------------------------------------------------------------
   8 · Data display

   Figures are the product. These are used identically on corporate pages and
   on generated index pages.
   -------------------------------------------------------------------------- */
/* BLOCK B10 item 2: flex, not grid auto-fit. auto-fit's minmax(165px,1fr)
   created implicit empty tracks whenever the wrap was wider than
   cell-count * 165px -- a stray trailing 0-width column, and cells that
   sized off an arbitrary minimum rather than the wrap's actual width.
   flex:1 on every .pp-fig makes the column count exactly the number of
   children, always, at every viewport, with no minmax threshold to force
   an early wrap. */
.pp-figs { display: flex; }
.pp-fig  { flex: 1; padding-inline: 26px; border-left: var(--pp-border) solid var(--pp-band-border); }
.pp-fig:first-child { padding-left: 0; border-left: 0; }
.pp-fig-v { font-size: 26px; font-weight: 500; line-height: var(--pp-lh-tight); letter-spacing: -0.025em; font-variant-numeric: tabular-nums; font-family: var(--pp-font-mono); color: var(--pp-band-figure); }
.pp-fig-k { font-size: var(--pp-size-label); font-weight: 500; letter-spacing: 0.13em; text-transform: uppercase; color: var(--pp-ink-3); margin-top: 7px; line-height: 1.35; }

/* B4/E1: the approved band, not the dark panel this used to be. Applied via
   the SAME class every consuming page already carries (.pp-panel), so the
   hub, correlation/downside/alpha, performance, data and coverage all pick
   it up from one change. */
.pp-panel { background: var(--pp-band-bg); color: var(--pp-ink); border-top: 1px solid var(--pp-band-border); border-bottom: 1px solid var(--pp-band-border); }

/* BLOCK B15: the figure-band caption -- hub, correlation/downside/alpha,
   data and coverage all carry one now, read from the data, never typed. */
.strip-note { font-size: var(--pp-size-caption); color: var(--pp-ink-3); margin: var(--pp-space-4) 0 0; border-top: var(--pp-border) solid var(--pp-rule); padding-top: var(--pp-space-3); }

.pp-table { width: 100%; border-collapse: collapse; font-size: 14.5px; margin: var(--pp-space-4) 0 var(--pp-space-3); }
.pp-table th, .pp-table td { text-align: right; padding: 10px 8px; border-bottom: var(--pp-border) solid var(--pp-rule-fine); white-space: nowrap; }
.pp-table th:first-child, .pp-table td:first-child { text-align: left; white-space: normal; }
.pp-table th { font-size: 11.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--pp-ink-3); border-bottom: var(--pp-border) solid var(--pp-rule); }
.pp-table caption { caption-side: bottom; text-align: left; font-size: var(--pp-size-caption); color: var(--pp-ink-3); padding-top: 10px; line-height: 1.45; }
.pp-table tr.pp-hl td { background: var(--pp-wash); font-weight: 600; }
.pp-table td.pp-neg { color: var(--pp-negative); }

.pp-note {
  font-size: var(--pp-size-small); color: var(--pp-ink-3);
  border-left: var(--pp-border-heavy) solid var(--pp-rule);
  padding-left: var(--pp-space-3); margin: var(--pp-space-4) 0;
  max-width: 64ch;
}

/* Attribution block closing every generated page. BLOCK B11 item 4: no
   margin-top -- the section immediately before this always supplies the
   page's one section-gap value itself (padding-block on .pp-section/
   .pp-panel), so a second, independent margin here compounded into a
   52px+88px gap. padding-top stays: a small visual buffer between the
   border and its own text, not a section-level gap. */
/* BLOCK A18 item 4: max-width lived on .pp-source itself, so the
   border-top (the rule) stopped at 70ch too -- 475px short of the
   content column on /coverage/, reading as a broken divider. The RULE
   now spans the full column; only the TEXT keeps the reading measure,
   via the inner span every render_source_note()/source_note() caller
   already wraps its text in. */
.pp-source {
  padding-top: var(--pp-space-4);
  border-top: var(--pp-border) solid var(--pp-rule);
  font-size: 13.5px; color: var(--pp-ink-3);
}
.pp-source > span { max-width: 70ch; display: block; }


/* --------------------------------------------------------------------------
   8b · Additions requested by the prerenderer
   -------------------------------------------------------------------------- */

/* Table overflow. .pp-table sets nowrap, so a wide reference table must scroll
   within its own container rather than dragging the page sideways on a phone. */
.pp-tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: var(--pp-space-4) 0 var(--pp-space-3); }
.pp-tablewrap > .pp-table { margin: 0; }

/* Display equations. Monospace is justified here: alignment and character
   distinction carry meaning in a formula. */
.pp-eq {
  font-family: ui-monospace, "SFMono-Regular", "IBM Plex Mono", Menlo, monospace;
  font-size: 14px; line-height: 1.7;
  background: var(--pp-wash);
  border-left: var(--pp-border-heavy) solid var(--pp-rule);
  padding: var(--pp-space-3) var(--pp-space-4);
  margin: var(--pp-space-3) 0 var(--pp-space-4);
  overflow-x: auto; white-space: pre;
}
.pp-eq-n { float: right; color: var(--pp-ink-3); font-size: 13px; }

/* Definition lists — Issue/Solution pairs in the methodology. */
.pp-dl { margin: 0 0 var(--pp-space-4); border-left: var(--pp-border-heavy) solid var(--pp-rule); padding-left: var(--pp-space-4); max-width: var(--pp-measure); }
.pp-dl dt { font-weight: 600; font-size: 15.5px; margin-top: var(--pp-space-3); color: var(--pp-ink); }
.pp-dl dt:first-child { margin-top: 0; }
.pp-dl dd { margin: var(--pp-space-1) 0 0; font-size: 15.5px; color: var(--pp-ink-2); }

/* Body lists, outside boxes. */
.pp-list { margin: 0 0 var(--pp-space-3); padding-left: var(--pp-space-4); max-width: var(--pp-measure); }
.pp-list li { font-size: var(--pp-size-body); color: var(--pp-ink-2); margin-bottom: var(--pp-space-2); line-height: var(--pp-lh-body); }
.pp-list li::marker { color: var(--pp-ink-3); }
.pp-list--tight li { margin-bottom: var(--pp-space-1); }

/* Negative figures. Meaning, never decoration. */
.pp-neg { color: var(--pp-negative); }

/* --------------------------------------------------------------------------
   9 · Sections and boxes
   -------------------------------------------------------------------------- */
/* BLOCK A2 item 1: was padding-block (symmetric top+bottom), so two
   adjacent .pp-section boxes doubled to 176px at their shared boundary
   -- each contributing its own 88px. top:0/bottom:88 is the corporate
   family's own chain (section.s in corp-page.css); the one section per
   page that follows something OTHER than a .pp-section (a hero or a
   .pp-panel, both effectively 0 at that edge) gets its top explicitly
   restored at the call site, not by widening this rule back to symmetric. */
.pp-section       { padding-top: 0; padding-bottom: var(--pp-space-7); }
/* BLOCK A2 item 1: the one exception -- a .pp-section that follows
   something other than another .pp-section (a hero, or a .pp-panel
   band with 0 bottom padding by design) needs its own top restored,
   at the same value and the same responsive step as the base rule's
   bottom, not a value invented for this. */
.pp-section-top-restore { padding-top: var(--pp-space-edge) !important; }
/* BLOCK A4 item 1: two hub boundaries read as holes at the full 88px
   step -- tightened to the existing --pp-space-6 (52px) tight token,
   the same one .pp-section--tight already uses, not a new value. */
.pp-section-bottom-tight { padding-bottom: var(--pp-space-6); }
/* BLOCK A8 item 1: a background makes padding VISIBLE AREA, not
   whitespace -- .pp-section--wash inherited the base .pp-section
   0/88 chain, so content sat flush on the tint's top edge with a full
   88px of empty colour beneath. Symmetric regardless of viewport. */
.pp-section--wash { background: var(--pp-wash); padding-top: var(--pp-space-edge) !important; padding-bottom: var(--pp-space-edge) !important; }
/* BLOCK A13 item 2c (corrected rule): every boundary totals one gap,
   owned by exactly one side. .pp-section--wash already owns both its
   own edges unconditionally (88 top, 88 bottom via the base rule); an
   untinted .pp-section immediately before one must give up its own
   bottom, or the two stack to 176 -- the gap the coverage/hub pages
   showed before this. */
/* BLOCK A13 item 4 (spacing rule v3): at a colour edge BOTH sides pad --pp-space-edge.
   BLOCK A22 E1: a figure panel (.pp-panel with no heading or body text) is NOT a band --
   it keeps its own --pp-space-5 and is out of the edge rule; only .pp-panel.close is. */
/* .pp-section--tight is used on its own (no .pp-section class), so it is listed too. */
.pp-section:not(.pp-section--wash):has(+ .pp-section--wash),
.pp-section:not(.pp-section--wash):has(+ .pp-panel.close),
.pp-section:not(.pp-section--wash):has(+ .pp-footer),
.pp-section:not(.pp-section--wash):has(+ footer),
.pp-section--tight:has(+ .pp-section--wash),
.pp-section--tight:has(+ .pp-panel.close),
.pp-section--tight:has(+ .pp-footer),
.pp-section--tight:has(+ footer) { padding-bottom: var(--pp-space-edge) !important; }
/* BLOCK A26 3a: the last section before the footer pads --pp-space-edge on BOTH plans. pp_chrome
   pages end in <footer class="pp-footer">; Plan A pushes the same sections under the corp-family
   <footer>, which the .pp-footer selectors above never matched (88px + the footer's 64 = 152).
   The footer's own padding is untouched. */
/* BLOCK A26 3b: a one-line link row (.rel) and the source footnote (.pp-source) are TRAILING MATTER,
   not two content sections. The row keeps --pp-space-trail below it instead of the section's full
   88; the footnote section already pads 0 above, and pads --pp-space-edge below (3a). */
.pp-section:has(.rel):has(+ .pp-section .pp-source) { padding-bottom: var(--pp-space-trail) !important; }
.pp-section--wash + .pp-section:not(.pp-section--wash),
.pp-panel.close + .pp-section:not(.pp-section--wash),
.pp-section--wash + .pp-section--tight,
.pp-panel.close + .pp-section--tight { padding-top: var(--pp-space-edge) !important; }
.pp-section--tight{ padding-block: var(--pp-space-6); }

/* item H7 (2026-09-25): rounded corners + a card shadow on the grid as a
   whole, matching corp-page.css's .card treatment -- the shared-border
   grid trick (background=rule colour, gap=border width) read as flatter
   and more table-like than a corp page's individually-elevated cards. */
.pp-boxes { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--pp-border); background: var(--pp-rule); border: var(--pp-border) solid var(--pp-rule); border-radius: 4px; overflow: hidden; box-shadow: 0 1px 2px rgba(11,22,32,.045); }
.pp-boxes--2 { grid-template-columns: repeat(2, 1fr); }
.pp-box { background: var(--pp-ground); padding: var(--pp-space-5) 30px 30px; display: flex; flex-direction: column; }
.pp-section--wash .pp-box { background: var(--pp-wash); }
.pp-box ul { margin: 0 0 var(--pp-space-4); padding: 0; list-style: none; flex: 1; }
.pp-box li { font-size: var(--pp-size-ui); color: var(--pp-ink-2); padding: 9px 0; border-top: var(--pp-border) solid var(--pp-rule-fine); line-height: 1.4; }
.pp-box li:first-child { border-top: 0; padding-top: 0; }

/* --------------------------------------------------------------------------
   10 · Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .pp-boxes, .pp-boxes--2 { grid-template-columns: 1fr; }
  .pp-footer-grid { grid-template-columns: 1fr 1fr; }
  .pp-nav { display: none; }
  .pp-figs { flex-direction: column; }
  .pp-fig { padding-left: 0; border-left: 0; border-top: var(--pp-border) solid var(--pp-band-border); padding-top: 17px; margin-top: 17px; }
  .pp-fig:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
}
/* BLOCK A22 item B1: the phone menu. .pp-nav used to vanish at 900px with
   nothing to open it. The toggle is the same checkbox+label markup the corp
   family carries (_shared_css.NAV_TOGGLE_HTML), CSS-only, no script. */
.pp-header .nt, .pp-header .nt-box { display: none; }
@media (max-width: 900px) {
  .pp-header .nt-box { display: block; position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
  .pp-header .nt {
    display: block; flex: none; order: 2; margin-left: auto; box-sizing: content-box; width: 24px; height: 15px;
    padding: 14px 8px; cursor: pointer; background-origin: content-box; background-clip: content-box;
    background-repeat: no-repeat;
    background-image: linear-gradient(var(--pp-ink), var(--pp-ink)), linear-gradient(var(--pp-ink), var(--pp-ink)), linear-gradient(var(--pp-ink), var(--pp-ink));
    background-size: 24px 2px, 24px 2px, 16px 2px; background-position: 0 0, 0 6.5px, 8px 13px;
  }
  .pp-header .nt-box:focus-visible + .nt { outline: 2px solid var(--pp-accent); }
  .pp-header-row { gap: var(--pp-space-3); }
  .pp-header .pp-cta { order: 3; }
  .pp-header .nt-box:checked ~ .pp-nav {
    display: flex; flex-direction: column; gap: 0; position: absolute; left: 0; right: 0; top: 100%;
    padding: 6px var(--pp-gutter) 14px; background: var(--pp-ground);
    border-bottom: var(--pp-border) solid var(--pp-rule); z-index: 30;
  }
  .pp-header .nt-box:checked ~ .pp-nav a { padding: 13px 0; border-top: var(--pp-border) solid var(--pp-rule-fine); }
  .pp-header .nt-box:checked ~ .pp-nav a:first-child { border-top: 0; }
}
/* BLOCK A22 item B2: ONE content left edge at phone width -- see
   _shared_css.MOBILE_EDGE_CSS. A boxed container bleeds to the viewport edge
   and pads its inside by the gutter, so its text sits on the gutter edge. */
@media (max-width: 560px) {
  .pp-boxes { margin-inline: calc(-1 * var(--pp-gutter)); border-inline-width: 0; border-radius: 0; }
  .pp-box { padding-inline: var(--pp-gutter); }
}
@media (max-width: 720px) {
  .pp-table { font-size: 13px; }
  .pp-table th, .pp-table td { padding: 8px 5px; }
  /* BLOCK B24 item 8 / B28 item 5: section padding never had a
     responsive step -- 88px top+bottom at 375 same as at 1440.
     Halved at this breakpoint (already in use above for the table). */
  .pp-section        { padding-top: 0; padding-bottom: var(--pp-space-6); }
  .pp-section--tight { padding-block: 34px; }
  .pp-section-bottom-tight { padding-bottom: 34px; }
  :root { --pp-space-edge: 40px; }
  :root { --pp-space-trail: 24px; }
}

/* --------------------------------------------------------------------------
   11 · Ownership

   Owner: [UNASSIGNED — see O6]

   Served from: <host>/assets/pp-tokens.css  (host: server/scripts/site.json)
                (Vision hosts it: vision/public/assets/pp-tokens.css; Caddy
                rewrites the host-root /assets/pp-tokens.css to the container
                copy under /vision/. See ops/caddy-root-seo.snippet.)
                The WordPress theme links this absolute URL.

   Changing a value here changes both properties. Any change should be checked
   against a corporate page and a generated index page side by side before it
   ships.
   -------------------------------------------------------------------------- */
