   VISUAL DIGESTIBILITY LAYER (.article-wrap, canonized 2026-06-30)
   Nesyona-palette port of the light system: rose #e11d48 + amber #f59e0b
   on paper. Same component class vocabulary network-wide. Per-article
   bespoke graphics add CONTENT; this provides STYLE + motion + grids.
   ══════════════════════════════════════════════════════════════════════ */
.article-wrap .lc-illus{max-width:560px;margin:1.7rem auto .7rem;display:block}
.article-wrap .lc-illus svg{width:100%;height:auto;display:block;filter:drop-shadow(0 12px 26px rgba(225,29,72,.16))}
.article-wrap h2{position:relative;padding-left:1.05rem}
.article-wrap h2::before{content:"";position:absolute;left:0;top:.18em;bottom:.18em;width:5px;border-radius:3px;background:linear-gradient(180deg,#e11d48,#f59e0b)}
.article-wrap .article-intro,.article-wrap p.lede{font-size:1.14rem;line-height:1.75;color:var(--text)}
.article-wrap .pq{margin:2.2rem 0;padding:.3rem 0 .3rem 1.5rem;border-left:4px solid #e11d48;font-family:var(--display),Georgia,serif;font-weight:700;font-size:clamp(1.2rem,2.5vw,1.6rem);line-height:1.38;color:var(--dark);letter-spacing:-.01em}
.article-wrap .pq .hl{background:linear-gradient(90deg,#e11d48,#f59e0b);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
.article-wrap .pq .src{display:block;margin-top:.55rem;font-family:var(--mono),monospace;font-size:.68rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.article-wrap .lc-split{display:grid;grid-template-columns:1fr 1fr;margin:1.7rem 0;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:#fff}
.article-wrap .lc-split figcaption{grid-column:1/-1;order:2;font-size:.79rem;color:var(--text-light);line-height:1.55;padding:.85rem 1.2rem;background:var(--paper);border-top:1px solid var(--border)}
.article-wrap .lc-split .pane{padding:1.3rem 1.35rem}
.article-wrap .lc-split .a{background:linear-gradient(160deg,#fff1f2,#fff)}
.article-wrap .lc-split .b{background:linear-gradient(160deg,#fffbeb,#fff);border-left:1px solid var(--border)}
.article-wrap .lc-split .ph{font-family:var(--mono),monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;margin:0 0 .2rem;font-weight:700}
.article-wrap .lc-split .a .ph{color:#be123c}
.article-wrap .lc-split .b .ph{color:#b45309}
.article-wrap .lc-split .sub{font-size:.76rem;color:var(--text-light);margin:0 0 .85rem}
.article-wrap .lc-split .chips{display:flex;flex-wrap:wrap;gap:.35rem;margin-bottom:.85rem}
.article-wrap .lc-split .chip{font-family:var(--mono),monospace;font-size:10px;color:var(--text);background:#fff;border:1px solid var(--border);border-radius:5px;padding:.22rem .52rem}
.article-wrap .lc-split .big{font-family:var(--display),Georgia,serif;font-weight:800;font-size:1.16rem;margin:.1rem 0 .55rem;letter-spacing:-.02em}
.article-wrap .lc-split .a .big{color:#be123c}
.article-wrap .lc-split .b .big{color:#b45309}
.article-wrap .lc-split .tags{margin:0;font-size:.84rem;color:var(--text);line-height:1.85}
.article-wrap .lc-split .tags .t{display:flex;align-items:flex-start;gap:.45rem}
.article-wrap .lc-split .tags .t::before{content:"";width:5px;height:5px;border-radius:50%;flex:none;margin-top:.5em}
.article-wrap .lc-split .a .tags .t::before{background:#e11d48}
.article-wrap .lc-split .b .tags .t::before{background:#f59e0b}
.article-wrap .key{display:flex;gap:.9rem;align-items:flex-start;margin:1.8rem 0;padding:1.05rem 1.2rem;background:linear-gradient(135deg,#fff1f2,#fffbeb);border:1px solid #fecdd3;border-radius:13px}
.article-wrap .key .ic{flex:none;width:30px;height:30px;border-radius:9px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#e11d48,#f59e0b);font-family:var(--display),Georgia,serif;font-weight:800;color:#fff;font-size:1rem}
.article-wrap .key .kt{margin:0;color:var(--text);font-size:.94rem;line-height:1.62}
.article-wrap .key .kt b{color:var(--dark)}
.article-wrap .shot{margin:1.8rem 0;border:1px solid var(--border);border-radius:13px;overflow:hidden;background:#fff;box-shadow:0 8px 28px rgba(24,24,27,.08)}
.article-wrap .shot .bar{display:flex;align-items:center;gap:.5rem;padding:.55rem .85rem;background:var(--paper);border-bottom:1px solid var(--border)}
.article-wrap .shot .bar i{width:9px;height:9px;border-radius:50%;background:#d4d4d8;display:inline-block}
.article-wrap .shot .bar .u{margin-left:.6rem;font-family:var(--mono),monospace;font-size:11px;color:var(--muted)}
.article-wrap .shot img{display:block;width:100%;height:auto}
.article-wrap .shot figcaption{font-size:.8rem;color:var(--text-light);line-height:1.5;padding:.7rem .95rem;border-top:1px solid var(--border)}
.article-wrap .crm-dash{display:grid;grid-template-columns:repeat(3,1fr);gap:.8rem;margin:1.9rem 0 .6rem}
.article-wrap .crm-dash .dcard{background:#fff;border:1px solid var(--border);border-radius:14px;padding:1.05rem 1.1rem 1.15rem;box-shadow:0 8px 24px rgba(24,24,27,.06);display:flex;flex-direction:column}
.article-wrap .crm-dash .dh{font-family:var(--mono),monospace;font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin:0 0 .7rem}
.article-wrap .crm-dash .dsub{font-size:.73rem;color:var(--text-light);margin:.7rem 0 0;line-height:1.4}
.article-wrap .gauge{position:relative;width:104px;height:104px;margin:.1rem auto .1rem}
.article-wrap .gauge svg{transform:rotate(-90deg)}
.article-wrap .gauge .ring{fill:none;stroke:#fde7ea;stroke-width:10}
.article-wrap .gauge .prog{fill:none;stroke:url(#gaugeg);stroke-width:10;stroke-linecap:round;stroke-dasharray:283;stroke-dashoffset:283;animation:gFill 1.9s cubic-bezier(.2,.8,.2,1) forwards}
@keyframes gFill{to{stroke-dashoffset:23}}
.article-wrap .gauge .num{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
.article-wrap .gauge .num b{font-family:var(--display),Georgia,serif;font-size:1.75rem;font-weight:800;color:#be123c;line-height:1}
.article-wrap .gauge .num em{font-family:var(--mono),monospace;font-size:7.5px;letter-spacing:.12em;color:var(--muted);text-transform:uppercase;font-style:normal;margin-top:.15rem}
.article-wrap .pbars{display:flex;flex-direction:column;gap:.5rem;padding:.55rem 0;flex:1;justify-content:center}
.article-wrap .pbar{height:13px;border-radius:7px;background:linear-gradient(90deg,#e11d48,#f59e0b);transform-origin:left;transform:scaleX(0);animation:pGrow 1.3s cubic-bezier(.2,.8,.2,1) forwards}
@keyframes pGrow{to{transform:scaleX(1)}}
.article-wrap .echips{display:flex;flex-wrap:wrap;gap:.4rem;padding:.5rem 0;flex:1;align-content:center}
.article-wrap .echip{font-family:var(--mono),monospace;font-size:10px;color:#15803d;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:6px;padding:.26rem .55rem;display:flex;align-items:center;gap:.32rem;opacity:0;animation:eIn .5s ease forwards}
.article-wrap .echip::before{content:"";width:5px;height:5px;border-radius:50%;background:#22c55e;flex:none}
@keyframes eIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.article-wrap .verdict-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.8rem;margin:1.9rem 0}
.article-wrap .vcard{background:#fff;border:1px solid var(--border);border-radius:13px;padding:1.1rem 1.15rem;border-top:3px solid #e11d48;transition:transform .2s,box-shadow .2s}
.article-wrap .vcard:hover{transform:translateY(-4px);box-shadow:0 16px 34px -14px rgba(225,29,72,.4)}
.article-wrap .vcard.top{background:linear-gradient(160deg,#fff1f2,#fff)}
.article-wrap .vcard .vt{font-family:var(--mono),monospace;font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-bottom:.4rem}
.article-wrap .vcard .vn{font-family:var(--display),Georgia,serif;font-weight:800;font-size:1.18rem;color:#be123c;letter-spacing:-.02em;margin-bottom:.35rem}
.article-wrap .vcard .vd{font-size:.85rem;color:var(--text);line-height:1.5}
.article-wrap .reveal{opacity:0;transform:translateY(18px);transition:opacity .6s ease,transform .6s ease}
.article-wrap .reveal.in{opacity:1;transform:none}
@media(prefers-reduced-motion:reduce){.article-wrap .reveal{opacity:1;transform:none;transition:none}}
@media(max-width:760px){.article-wrap .crm-dash,.article-wrap .verdict-grid{grid-template-columns:1fr}}
@media(max-width:680px){.article-wrap .lc-split{grid-template-columns:1fr}.article-wrap .lc-split .b{border-left:0;border-top:1px solid var(--border)}}


/* === RESPONSIVE TABLES (network heal 2026-07-30) === */
/* Wide comparison tables were pushing article pages into horizontal scroll on mobile (measured at
   390px by lattice-core/scripts/render-audit.mjs). Give the table its own scroll container so the
   PAGE never scrolls sideways, while thead/tbody stay display:table so the columns do not collapse
   and the table remains readable. Both rules are required; the first alone destroys the layout. */
table.data-table, table.pricing-table, table.capability-matrix, table.comparison-table,
.article-body table, main table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
table.data-table > thead, table.data-table > tbody,
table.pricing-table > thead, table.pricing-table > tbody,
table.capability-matrix > thead, table.capability-matrix > tbody,
table.comparison-table > thead, table.comparison-table > tbody,
.article-body table > thead, .article-body table > tbody,
main table > thead, main table > tbody {
  display: table;
  width: 100%;
  min-width: max-content;
}


/* === RESPONSIVE TABLES v2 (network heal 2026-07-30) === */
/* Wide comparison tables push article pages into horizontal scroll on mobile (measured at 390px by
   lattice-core/scripts/render-audit.mjs). Give every table its own scroll container so the PAGE
   never scrolls sideways, while thead/tbody stay display:table so the columns do not collapse and
   the table stays readable. BOTH rules are required; the first alone destroys the layout.
   Deliberately generic: the network uses 15+ distinct table class names and an explicit list has
   already been outrun once. */
table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
table > thead, table > tbody, table > tfoot {
  display: table;
  width: 100%;
  min-width: max-content;
}


/* === RESPONSIVE TABLES v3 (network heal 2026-07-30) === */
/* Wide comparison tables push article pages into horizontal scroll on mobile (measured at 390px by
   lattice-core/scripts/render-audit.mjs). Give every table its own scroll container so the PAGE
   never scrolls sideways, while thead/tbody stay display:table so the columns do not collapse and
   the table stays readable. BOTH rules are required; the first alone destroys the layout.
   Deliberately generic: the network uses 15+ distinct table class names and an explicit list has
   already been outrun once. */
table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
table > thead, table > tbody, table > tfoot {
  display: table;
  width: 100%;
  min-width: max-content;
}

/* Form controls must not outgrow their container either. The newsletter capture ships an email
   input with an inline `min-width:200px`, which together with its padding and the Subscribe button
   pushed three nesyona article pages 5px past a 390px viewport. A hard min-width on a flex child is
   the classic cause, and it is inline on the element, so only a max-width guard reaches it. */
input, select, textarea, button, img, video, iframe, pre {
  max-width: 100%;
}


/* === RESPONSIVE TABLES v4 (network heal 2026-07-30) === */
/* Wide comparison tables push article pages into horizontal scroll on mobile (measured at 390px by
   lattice-core/scripts/render-audit.mjs). Give every table its own scroll container so the PAGE
   never scrolls sideways, while thead/tbody stay display:table so the columns do not collapse and
   the table stays readable. BOTH rules are required; the first alone destroys the layout.
   Deliberately generic: the network uses 15+ distinct table class names and an explicit list has
   already been outrun once. */
table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
table > thead, table > tbody, table > tfoot {
  display: table;
  width: 100%;
  min-width: max-content;
}

/* Form controls must not outgrow their container either. The newsletter capture ships an email
   input with an inline `min-width:200px`, which together with its padding and the Subscribe button
   pushed three nesyona article pages 5px past a 390px viewport. A hard min-width on a flex child is
   the classic cause, and it is inline on the element, so only a max-width guard reaches it. */
input, select, textarea, button, img, video, iframe, pre {
  max-width: 100%;
}

/* max-width alone does not reach this one. The email input carries an INLINE `min-width:200px`, and
   an inline style beats a stylesheet rule, so the flex row stays wider than its parent and the page
   keeps scrolling 5px. A min-width floor on a flex child is the standard cause of exactly this, and
   `!important` is the only lever that reaches an inline declaration — used deliberately here and
   scoped to one control rather than sprayed across the sheet. */
form input[type="email"] {
  min-width: 0 !important;
}


/* === RESPONSIVE TABLES v5 (network heal 2026-07-30) === */
/* Wide comparison tables push article pages into horizontal scroll on mobile (measured at 390px by
   lattice-core/scripts/render-audit.mjs). Give every table its own scroll container so the PAGE
   never scrolls sideways, while thead/tbody stay display:table so the columns do not collapse and
   the table stays readable. BOTH rules are required; the first alone destroys the layout.
   Deliberately generic: the network uses 15+ distinct table class names and an explicit list has
   already been outrun once. */
table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
table > thead, table > tbody, table > tfoot {
  display: table;
  width: 100%;
  min-width: max-content;
}

/* Form controls must not outgrow their container either. The newsletter capture ships an email
   input with an inline `min-width:200px`, which together with its padding and the Subscribe button
   pushed three nesyona article pages 5px past a 390px viewport. A hard min-width on a flex child is
   the classic cause, and it is inline on the element, so only a max-width guard reaches it. */
input, select, textarea, button, img, video, iframe, pre {
  max-width: 100%;
}

/* max-width alone does not reach this one. The email input carries an INLINE `min-width:200px`, and
   an inline style beats a stylesheet rule, so the flex row stays wider than its parent and the page
   keeps scrolling 5px. A min-width floor on a flex child is the standard cause of exactly this, and
   `!important` is the only lever that reaches an inline declaration — used deliberately here and
   scoped to one control rather than sprayed across the sheet. */
form input[type="email"] {
  min-width: 0 !important;
}

/* Long unbroken links. On LensPOV the widest element on four pages was an <a> at 547-678px against
   a 390px viewport: raw URLs used as citation link text, which have no spaces to wrap at, so the
   browser lets them run past the edge and the whole page scrolls sideways. The U3 citation campaign
   adds MORE external links, so this gets worse as the network gets better sourced unless it is
   fixed at the stylesheet. */
a, code, kbd, samp {
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ── MOBILE TABLE OVERFLOW FIX (2026-08-13) ─────────────────────────────────────────────────
   .data-table is the network's core content format — 404 articles across 15 properties use it —
   and NOTHING wrapped it in a scroll container. Measured on best-free-ai-image-generators, the
   property's top page at 11,286 citations: 83px of horizontal overflow at mobile width, with the
   widest element reported as tbody.

   Scoped to small screens so desktop table rendering is untouched. Selector is `table.data-table`
   (0,0,1,1) rather than `.data-table` (0,0,1,0) deliberately: articles carry their own inline
   <style> rules for .data-table which would otherwise win on source order. */
@media (max-width: 640px) {
  table.data-table {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
  }
}


/* ==== TOKEN CONTRACT + THE COMPONENTS THIS PROPERTY EMITS AND NEVER DEFINED ====================
   Added 2026-08-25. Measured across all 123 nesyona articles:

     page-local :root token blocks ....... 120 of 123 (98%), duplicating the same values
     pages defining the STANDARD token set . 77  -> 46 pages have a partial or absent palette
     .bluf emitted ....................... 119 pages, defined page-locally on only 75
                                            -> 44 PAGES SHIP AN UNSTYLED BLUF
     .bluf-title emitted ................. 60 pages, defined on 18
     .callout emitted .................... 67 pages

   * THE BLUF IS THE BLOCK AN ANSWER ENGINE IS MOST LIKELY TO LIFT, AND 44 PAGES RENDER IT AS AN
     UNSTYLED DIV. No error, no console warning, no broken layout: it simply looks like body copy,
     which is precisely what it must not look like.

   ⭐ WHY THIS IS SAFE. A page-local <style> block comes AFTER this linked sheet, so the 75 pages
     that define their own .bluf and the 77 that define their own tokens KEEP THEIRS on source
     order. This changes only the pages that currently have nothing. Zero blast radius by
     construction, which is why it can be done in one pass rather than page by page.

   Token values are the unanimous page-local set (77 pages agree, no competing variants).
   Doctrine: LATTICE-RENDER-ENGINEERING-BIBLE §4 (markup without its stylesheet) and §5 (an
   undefined custom property falls back to INHERIT, not to a default). */

:root{
  --accent:#da1c46; --accent-light:#fff1f2; --accent-hover:#be123c;
  --paper:#fafaf9; --dark:#18181b; --text:#3f3f46; --text-light:#71717a;
  --border:#e4e4e7; --cream:#f4f4f5; --muted:#727279;
  --display:'Bricolage Grotesque', Georgia, serif;
  --body:'Outfit', system-ui, sans-serif;
  --mono:'JetBrains Mono', 'Fira Code', monospace;
  --radius:10px; --max-w:1080px;
}

/* BLUF. Token-based variant (16 pages) rather than the hardcoded one (56), because the hardcoded
   version pins #e11d48 which is not this property's accent (#da1c46). Pages keeping their own are
   unaffected either way. */
.bluf{background:var(--accent-light);border:1px solid #fbcfe0;border-left:4px solid var(--accent);
  border-radius:var(--radius);padding:1.1rem 1.35rem;margin:1.75rem 0;color:var(--text)}
.bluf-title{font-family:var(--display);font-weight:700;font-size:.95rem;color:var(--dark);
  margin-bottom:.5rem;letter-spacing:-.01em}
.bluf-list{margin:0;padding-left:1.15rem}
.bluf-list li{margin:.35rem 0;line-height:1.6}
.bluf-list li::marker{color:var(--accent)}

/* Callout: quieter than the BLUF on purpose. The BLUF states the answer; a callout qualifies it. */
.callout{background:var(--cream);border:1px solid var(--border);border-left:3px solid var(--accent);
  border-radius:0 var(--radius) var(--radius) 0;padding:.95rem 1.2rem;margin:1.5rem 0;
  font-size:.94rem;line-height:1.65;color:var(--text)}


/* ==== SECOND PASS, 2026-08-25: components emitted widely and defined only page-locally ==========
   Measured across 123 nesyona articles (emitted / defined page-locally / UNSTYLED):

     .article-meta-bar ....  49 /  27 / **22 UNSTYLED**
     .freshness-pills ..... 111 / 110 /   1
     .pill / .pill-reviewed / .pill-due  ... same 111 / 110 / 1

   The pills are near-universally defined page-locally, which is not a rendering bug but is 110
   copies of the same 6 rules. Promoting them costs nothing (page-local wins on source order for
   every page that already has them) and stops the 111th page repeating the duplication.
   `.article-meta-bar` is the real defect: 22 pages render the byline row unstyled.

   Values are the most common page-local variant in each case: 92-95 pages agree on the pills,
   14 on the meta bar. `.table-scroll` is added because a wide table must scroll inside its own box
   rather than widen the page, and nothing on this property defined it.
   Doctrine: LATTICE-RENDER-ENGINEERING-BIBLE §4. */

.article-meta-bar{display:flex;flex-wrap:wrap;gap:.5rem 1rem;align-items:center;
  font-size:.8125rem;color:var(--text-light);margin-bottom:1.25rem;line-height:1.5}

.freshness-pills{display:flex;flex-wrap:wrap;gap:.5rem;margin:.5rem 0 1rem;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.75rem}
.pill{padding:.2rem .55rem;border-radius:999px;background:#f3f4f6;color:#374151;border:1px solid #e5e7eb}
.pill-reviewed{background:#ecfdf5;color:#047857;border-color:#a7f3d0}
.pill-due{background:#fef3c7;color:#92400e;border-color:#fde68a}

.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:1.5rem 0}
.table-scroll>table{margin:0;min-width:560px}


/* ==== THE CONTAINER THIS SHEET HAS ALWAYS STYLED THE INSIDE OF (added 2026-08-25) ==============
   This stylesheet has rules for `.article-wrap h2`, `.article-wrap .article-intro` and
   `.article-wrap .lc-illus` going back to the 2026-06-30 digestibility layer, and has never once
   declared `.article-wrap` itself. Containment was left to each page's local <style>.

   Measured 2026-08-25: `.article-wrap` is emitted on 109 articles and declared page-locally on 77.
   ** 32 LIVE PAGES RENDER FULL-BLEED **, prose running the entire viewport width with no reading
   column, because the shared sheet styles the inside of a box it never draws.

   ⭐ A SHARED SHEET THAT STYLES DESCENDANTS OF A CONTAINER IT DOES NOT DECLARE IS ASSUMING A
     CONTRACT IT NEVER WROTE DOWN. Every page that happened to carry the local rule looked fine,
     which is exactly why this survived: the sample you check is the sample that works.

   720px is the most common page-local value (40 pages; 760px on 21). Pages with their own rule
   keep it on source order, so this only reaches the 32 that have none.
   Doctrine: LATTICE-RENDER-ENGINEERING-BIBLE §4. */
.article-wrap{max-width:720px;margin:0 auto;padding:0 1.5rem 4rem}


/* ==== ENGINE-INJECTED TEXT NOBODY STYLED (added 2026-08-25) ====================================
   `site_engine.js` injects `<p class="footer-note">` (the affiliate disclaimer) and a
   `.last-tested` byline into every article. Measured on nesyona 2026-08-25:

     .footer-note   defined in styles.css, WHICH ARTICLES DO NOT LINK.  82 of 123 pages unstyled.
                    Measured on a live page: **2.56:1**.
     .last-tested   defined NOWHERE. 0 of 123 pages. Measured: **3.28:1**.

   ⛔ THE AFFILIATE DISCLOSURE WAS THE LEAST LEGIBLE TEXT ON THE PAGE. The FTC Endorsement Guides
     require a disclosure to be clear and conspicuous; 2.56:1 is neither, and it is below WCAG AA
     for normal text (4.5:1). This is not a styling preference, it is the one piece of text on the
     page with a regulatory reason to be readable.

   ★ THE DEFINITION EXISTED AND WAS UNREACHABLE. `.footer-note` is styled in styles.css and articles
     link only nesyona-digest.css, so the rule has been correct and inert. That is a distinct
     variant of BIBLE §4: not markup without a stylesheet, but markup whose stylesheet the page
     does not load. Check the sheet the page LINKS, never the repo.

   ⚠️ site_engine.js is shared across properties. Sweep the other 13 for the same pair. */
.footer-note{font-size:.8125rem;line-height:1.6;color:var(--text);margin:.75rem 0 0}
.last-tested{font-size:.8125rem;line-height:1.55;color:#52525b;margin:.35rem 0 1.1rem}


/* ═══ IX GROUND LAYER · depth, texture and light · v2 2026-08-25 ════════════════════════════════

   v1 shipped washes at ~5% and grain at 3.5%. It MEASURED as present and READ as absent, which is
   the whole lesson: a value you can only find with getComputedStyle is not a design, it is a
   rounding error. Operator, on v1: "I still don't see that in this final product."

   ★ SUBTLETY IS NOT THE SAME AS RESTRAINT. Restraint is a strong idea expressed once. Subtlety in
     the sense of "barely detectable" is just an idea that failed to arrive. If a texture layer
     cannot be seen with the naked eye at 100% zoom, it is costing bytes and buying nothing.

   FOUR LAYERS, painted behind everything, zero HTTP requests, zero layout impact:
     1. GROUND      a warm paper tone with real presence, not near-white
     2. LIGHT       a broad highlight at the top, so the page reads as LIT rather than as filled.
                    This is the layer that does most of the perceptual work and the one most often
                    left out: depth comes from an implied light source, not from more colour.
     3. ATMOSPHERE  large accent washes, now at a strength you can actually see, drifting slowly
     4. GRAIN       inline SVG fractal noise, raised to a level that reads as material

   ⚠️ Contrast re-measured after every change to these values. Body copy sits on the GROUND, not on
   a card, so raising the ground tone directly moves every ratio on the page. --text on this ground
   measures 9.4:1 and the sanity probe reads 19.2 / 1.00.

   ⚠️ The drift carries NO information, which is the only reason it is allowed under the IX rule
   that nothing carrying information may animate. prefers-reduced-motion stops it entirely. */

:root{
  /* ⚠️ #f4f3f0 was tried first and BROKE AA ON TWELVE ELEMENTS: nav links, breadcrumbs, the meta
     bar and every disclosure summary fell from ~4.5 to 4.30-4.47, because body copy and chrome sit
     directly ON the ground rather than on a card. The sanity probe dropping 20.11 -> 18.93 was the
     tell, and it was deployed before it was re-measured.
     ★ THE GROUND IS THE WRONG LEVER FOR DEPTH. Depth comes from the implied light source, the
       washes, the vignette and the grain: layers that sit BEHIND text without changing its
       background. Tone the base only as far as contrast allows, then let the other four layers
       carry it. */
  --ground:#f8f7f4;              /* warm paper, light enough that chrome text still clears AA */
  --surface:#ffffff;             /* cards are now genuinely lighter than the page */
  --wash-a:rgba(218,28,70,.10);  /* brand accent, atmospheric but visible */
  --wash-b:rgba(99,102,241,.085); /* cool counterweight, keeps the page from going pink */
  --wash-c:rgba(245,158,11,.055); /* warm low note in the lower third */
  --lift-1:0 1px 2px rgba(24,24,27,.05), 0 2px 6px -2px rgba(24,24,27,.07);
  --lift-2:0 1px 3px rgba(24,24,27,.06), 0 14px 34px -16px rgba(24,24,27,.24);
  --hairline:rgba(24,24,27,.10);
  /* ⛔ THE MEASURE IS IN rem, NOT ch. `ch` is the width of "0" IN THE ELEMENT'S OWN FONT, so one
     shared value yields a different column per typeface and per element. The same `70ch` on this
     property computed 638px while body fell back to Times and 837px once Outfit applied: a 31%
     swing in reading measure from a change that touched no layout rule. rem is font-independent. */
  --measure:40rem;      /* prose: ~640px, the reading column */
  --measure-tight:34rem;/* deks and captions, deliberately shorter than the prose they introduce */
}

html{background:var(--ground)}
/* ⛔ A TOKEN THAT NOTHING CONSUMES IS NOT A TYPEFACE, IT IS A COMMENT.
   `--body` was declared property-wide and applied by NO rule in any shared sheet. 93 of 125 articles
   re-declared `body{font-family}` in their own page-local <style>; the other 32 inherited the
   BROWSER DEFAULT SERIF and rendered in Times New Roman. Among them: the prompt-engineering cluster
   and `best-prompt-engineering-tools-2026`, the property's highest-growth page. Browsers do not
   download a face nothing references, so `document.fonts.check('16px Outfit')` was false while the
   font link sat in the <head> looking correct. Same shape as `.related-grid`: the usage lived in the
   pages, the declaration lived nowhere. Promoted here 2026-08-25. */
body{background:transparent;position:relative;font-family:var(--body)}

/* LIGHT + ATMOSPHERE. One fixed pseudo-element behind everything. The first gradient is the light
   source and is deliberately the largest and least saturated. */
body::before{
  content:"";position:fixed;inset:-12%;z-index:-2;pointer-events:none;
  background:
    radial-gradient(120% 70% at 50% -18%, rgba(255,255,255,.95), rgba(255,255,255,0) 62%),
    radial-gradient(46rem 33rem at 8% -4%,  var(--wash-a), transparent 60%),
    radial-gradient(42rem 30rem at 96% 6%,  var(--wash-b), transparent 58%),
    radial-gradient(50rem 34rem at 74% 92%, var(--wash-c), transparent 62%),
    radial-gradient(38rem 26rem at 18% 88%, var(--wash-b), transparent 60%);
  animation:ix-drift 42s ease-in-out infinite alternate;
}

/* ⛔ GRAIN REMOVED 2026-08-25, SAME DAY IT SHIPPED. Operator: "this looks like static and is
   actually very visually jarring."

   ★ THE LESSON IS NOT "NOISE IS BAD", IT IS THAT I ESCALATED THE WRONG DIMENSION. v1 was too faint
     to see, so v2 raised the SAME layer until it was visible, and a noise layer strong enough to
     perceive is a noise layer strong enough to read as television static. The dimension that
     actually carried the depth was the LIGHT SOURCE and the vignette; the grain was never doing
     the work I credited it with. **When a subtle effect is not landing, check whether it is the
     right effect before turning it up.**

   VIGNETTE KEPT: it gives the reading column edges to sit inside, and it is invisible as an effect
   while doing visible work, which is the actual definition of restraint. */
body::after{
  content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(125% 85% at 50% 2%, transparent 46%, rgba(24,24,27,.075) 100%);
}

@keyframes ix-drift{
  0%{transform:translate3d(0,0,0) scale(1)}
  50%{transform:translate3d(1.4%,-1%,0) scale(1.05)}
  100%{transform:translate3d(-1.8%,1.4%,0) scale(1.09)}
}
@media (prefers-reduced-motion: reduce){ body::before{animation:none} }

/* ELEVATION. Cards are #fff on a warm ground, which is what makes them read as raised at all. */
.ix-box,.ix-door,.faq-item,.rn-card,.ix-dead,.data-table,.table-scroll,.ix-note{
  background:var(--surface);
}
.ix-box{box-shadow:var(--lift-2);border-color:var(--hairline)}
.ix-door,.faq-item,.ix-note{box-shadow:var(--lift-1);border-color:var(--hairline)}
.ix-dead{background:linear-gradient(180deg,#fff,#f7f6f4);box-shadow:var(--lift-1)}

/* ═══ FAQ + RELATED · the property's own components, finally reachable ══════════════════════════
   Measured 2026-08-25: `.related-grid` is emitted on 116 of 123 articles, `.qa-inline` on 59,
   `.faq` on 55, `.faq-item` on 43, `.rn-card` on 54 - and NOT ONE of them was in this sheet.
   Every page that has them defines them locally; every page that does not renders browser-default
   bullets and disclosure triangles. A page written by someone reading the shared sheet cannot even
   discover that these exist, which is exactly what happened when this page was first written.
   Values are the most common page-local variant (55 to 61 pages agree). */

.faq{margin:1.4rem 0}
.faq-item{border:1px solid var(--hairline);border-radius:10px;overflow:hidden;margin-bottom:.55rem}
.faq-item>summary{cursor:pointer;list-style:none;padding:.95rem 1.15rem;
  font-family:var(--display);font-weight:700;font-size:1rem;line-height:1.4;color:var(--dark);
  display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;transition:background .15s}
.faq-item>summary::-webkit-details-marker{display:none}
.faq-item>summary::after{content:"+";flex:none;font-family:var(--mono);font-weight:400;
  font-size:1.15rem;line-height:1;color:var(--accent)}
.faq-item[open]>summary::after{content:"\2212"}
.faq-item>summary:hover{background:var(--cream)}
.faq-item>div,.faq-item>p{padding:0 1.15rem 1.05rem;margin:0;
  font-size:.94rem;line-height:1.72;color:var(--text)}

.related-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(238px,1fr));gap:.85rem;margin:1.2rem 0}
.rn-card{display:block;padding:1rem 1.15rem;
  border:1px solid var(--hairline);border-left:3px solid var(--accent);border-radius:0 10px 10px 0;
  text-decoration:none;color:inherit;box-shadow:var(--lift-1);
  transition:transform .16s ease, box-shadow .16s ease}
.rn-card:hover{transform:translateY(-2px);box-shadow:var(--lift-2)}
.rn-card .rn-title{font-family:var(--display);font-weight:700;font-size:1rem;line-height:1.35;
  color:var(--dark);margin:0 0 .3rem;display:block}
.rn-card .rn-why{font-size:.85rem;line-height:1.55;color:var(--text);margin:0}
.rn-card .rn-go{display:block;margin-top:.6rem;font-family:var(--mono);font-size:.62rem;
  letter-spacing:.09em;text-transform:uppercase;color:var(--accent)}
@media (prefers-reduced-motion: reduce){ .rn-card{transition:none} .rn-card:hover{transform:none} }

/* Quiet prose sections need a surface too, or the page reads as "cards, cards, then nothing". */
.ix-note{border:1px solid var(--hairline);border-radius:12px;background:var(--surface);
  box-shadow:var(--lift-1);padding:1.25rem 1.4rem;margin:1.4rem 0}
.ix-note h3{margin:0 0 .5rem;font-family:var(--display);font-weight:700;font-size:1.02rem;color:var(--dark)}
.ix-note p{margin:0;font-size:.93rem;line-height:1.72;color:var(--text);max-width:66ch}
.ix-note p + p{margin-top:.7rem}
.ix-note .ix-tag{display:inline-block;margin-bottom:.55rem;font-family:var(--mono);font-size:.62rem;
  letter-spacing:.09em;text-transform:uppercase;color:var(--text);
  background:var(--cream);border:1px solid var(--hairline);border-radius:4px;padding:.18rem .45rem}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   IX COMPONENT SET — promoted from page-local <style> on 2026-08-25.

   ⛔ PROMOTED, NOT COPIED A THIRD TIME. These proved on two pages
   (`ai-presentation-tools-pricing-2026`, `ai-image-generator-free-tier-limits-2026`). A component
   copied into a third page's <style> is how `.related-grid` ended up on 116 pages and in zero
   stylesheets, and how `--body` ended up declared by everything and applied by nothing.
   INFORMATION-DESIGN-STANDARD §7 step 5: promote before writing new ones.

   Every entry answers a named cognitive failure (STANDARD §2). A component that cannot name its
   job gets deleted, not admired — see `.ix-scan`, which lasted four hours.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */

/* Section head + dek. `.ix-h2` opts out of the property's decorative ::before so a data section
   reads as a different KIND of thing from a prose section. */
/* ⛔ SPECIFICITY, MEASURED NOT ASSUMED (§9). This property already styles `.article-wrap h2` (0,1,1)
   with `padding-left:1.05rem` for its decorative gradient bar. A bare `.ix-h2` (0,1,0) LOSES that
   contest, so the first promoted version left every IX heading indented 16.8px with no bar to
   justify it — each section head sitting slightly right of its own dek. Measured on the live page:
   `paddingLeft: "16.8px"`. The `::before` opt-out won only by luck: the property's rule declares
   content/position/width/background but never `display`, so `display:none` faced no competitor.
   Both are written at `.article-wrap h2.ix-h2` now so neither depends on that luck. */
.article-wrap h2.ix-h2, .ix-h2{font-family:var(--display);font-weight:800;font-size:1.45rem;
  color:var(--dark);letter-spacing:-.015em;margin:2.9rem 0 .3rem;line-height:1.25;
  padding-left:0;max-width:var(--measure)}
.article-wrap h2.ix-h2::before, .ix-h2::before{display:none;content:none}
.ix-dek{max-width:var(--measure-tight);font-size:.95rem;line-height:1.6;color:var(--text);margin:0 0 1.2rem}
.ix-src{font-family:var(--mono);font-size:.67rem;letter-spacing:.03em;color:var(--text);opacity:.7;margin:.7rem 0 0}

/* LADDER — position IS the quantity. Answers: serial comparison of parallel values.
   ⛔ ZERO MUST LOOK LIKE ZERO. `.bar.free` draws a tick at the ORIGIN, never a full-width outline:
   if length encodes price, the cheapest plan cannot be the longest bar (STANDARD §4). */
.ix-ladder{margin:.5rem 0 1rem;font-variant-numeric:tabular-nums;max-width:730px}
.ix-row{display:grid;grid-template-columns:172px 1fr 96px;align-items:center;gap:.85rem;padding:.28rem 0}
.ix-row .lbl{text-align:right;font-size:.875rem;font-weight:500;color:var(--dark);line-height:1.22;letter-spacing:-.005em}
.ix-row .lbl i{display:block;font-style:normal;font-weight:400;font-size:.7rem;line-height:1.3;
  margin-top:.12rem;color:var(--text);opacity:.72;font-variant-numeric:tabular-nums}
.ix-row .bar{height:24px;border-radius:4px;background:var(--accent-fill,var(--accent));min-width:3px}
.ix-row .bar.free{background:transparent;border:0;min-width:0;display:flex;align-items:center;gap:.4rem}
.ix-row .bar.free::before{content:"";width:3px;height:24px;border-radius:2px;background:var(--accent-fill,var(--accent))}
.ix-row .bar.free::after{content:"free";font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--accent)}
.ix-row .amt{font-family:var(--mono);font-size:.85rem;font-weight:600;color:var(--dark);
  font-variant-numeric:tabular-nums;text-align:right;padding-right:.15rem;letter-spacing:-.01em}
.ix-row.alt .bar{background:color-mix(in srgb,var(--accent) 55%,transparent)}

/* BOX — two-panel contrast where one side is DRAWN AS UNKNOWN. Answers: an asymmetry stated in
   prose instead of shown. The unknown panel is the highest-performing element in the set, because
   it turns an absence into evidence (STANDARD §4). */
.ix-box{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--hairline);
  border:1px solid var(--hairline);border-radius:12px;overflow:hidden;margin:1.5rem 0;box-shadow:var(--lift-2)}
.ix-box>div{background:var(--surface);padding:1.25rem 1.35rem}
.ix-box h4{margin:0 0 .15rem;font-family:var(--display);font-size:1.02rem;color:var(--dark)}
.ix-box .sub{font-family:var(--mono);font-size:.655rem;letter-spacing:.085em;text-transform:uppercase;
  color:var(--text);opacity:.78;margin-bottom:.9rem}
.ix-box .unit{font-family:var(--display);font-weight:800;font-size:1.55rem;line-height:1;color:var(--dark);
  letter-spacing:-.025em}
.ix-box .eq{margin-top:.75rem;font-family:var(--mono);font-size:.8rem;color:var(--text);line-height:1.9;
  font-variant-numeric:tabular-nums;padding:.55rem .7rem;background:var(--cream);
  border:1px solid var(--hairline);border-radius:7px}
.ix-box .note{margin-top:.8rem;font-size:.82rem;line-height:1.55;color:var(--text)}
.ix-box .q{border-left:3px solid var(--accent);padding-left:.7rem;margin-top:.75rem;font-size:.8rem;
  line-height:1.5;color:var(--text)}
/* The unknown, drawn as unknown. Hatching is GEOMETRY, not texture — the distinction that separates
   this from the grain layer that shipped and was removed the same day (STANDARD §5). */
.ix-box .unknown{background-image:repeating-linear-gradient(45deg,
  var(--hairline) 0 1px, transparent 1px 9px);border-style:dashed}
.ix-box .unknown .unit{color:var(--text);opacity:.85}

/* TRIAGE — N doors, each labelled by the DECIDING ATTRIBUTE. Answers: "it depends", with the
   depends buried in paragraph four. */
.ix-triage{display:grid;grid-template-columns:repeat(auto-fit,minmax(215px,1fr));gap:.9rem;margin:1.5rem 0}
.ix-door{border:1px solid var(--hairline);border-top:3px solid var(--accent);border-radius:10px;
  padding:1.05rem 1.15rem;background:var(--surface);box-shadow:var(--lift-1)}
.ix-door .if{font-family:var(--mono);font-size:.655rem;letter-spacing:.085em;text-transform:uppercase;
  color:var(--text);opacity:.78}
.ix-door .then{font-family:var(--display);font-weight:800;font-size:1.12rem;color:var(--dark);margin:.35rem 0 .1rem}
.ix-door .price{font-family:var(--mono);font-size:.86rem;color:var(--accent);font-weight:600;font-variant-numeric:tabular-nums}
.ix-door p{margin:.55rem 0 0;font-size:.85rem;line-height:1.6;color:var(--text)}

/* MORE — depth on demand. Answers: a page that cannot be scanned AND read by the same reader. */
.ix-more{border:0;border-top:1px solid var(--hairline);margin:1.1rem 0 0;padding:0}
.ix-more>summary{cursor:pointer;list-style:none;padding:.7rem 0 0;font-family:var(--mono);font-size:.7rem;
  letter-spacing:.07em;text-transform:uppercase;color:var(--accent)}
.ix-more>summary::-webkit-details-marker{display:none}
.ix-more>summary::after{content:" +"}
.ix-more[open]>summary::after{content:" \2212"}
.ix-more p{font-size:.94rem;line-height:1.75;color:var(--text);max-width:var(--measure)}

@media (max-width:760px){
  .ix-box{grid-template-columns:1fr}
  .ix-row{grid-template-columns:100px 1fr 76px;gap:.5rem}
  .ix-row .lbl{font-size:.76rem}
  .ix-row .lbl i{font-size:.66rem}
}

/* ── .data-table CELL DEFAULTS — promoted 2026-08-25 ───────────────────────────────────────────
   ⛔ `.data-table` WAS A CONTAINER STYLE WITH NO CELLS. The shared sheet styled `table.data-table`
   and its `> thead / > tbody` (overflow, scroll, mobile) and NEVER styled `th` or `td`. Measured on
   a live page: `padding: 1px`, no borders, no header background, headers centre-aligned by UA
   default. 93 of 127 articles use one of these table classes; 62 of them carry these exact rules in
   their own page-local <style>, which is why the gap was invisible — the pages that work are the
   pages that wrote it out themselves, for the third time this session (see `--body`, `.related-grid`).

   ⚠️ SPECIFICITY IS DELIBERATE AND LOAD-BEARING. Written as `.data-table th` (0,0,1,1), NOT
   `table.data-table th` (0,0,1,2). Those 62 pages declare `.data-table th` in a page-local <style>
   that comes AFTER this linked sheet, so at EQUAL specificity they still win on source order and
   nothing about them changes. A higher-specificity rule here would silently restyle 62 working
   pages to make 31 broken ones work, which is a worse trade than the bug.

   Values are the property's own unanimous standard, lifted verbatim from those 62 pages rather than
   invented: find the reference standard already on the page. */
.data-table th{background:var(--dark);color:#fff;padding:.75rem 1rem;text-align:left;
  font-family:var(--display);font-weight:700;font-size:.8125rem;text-transform:uppercase;letter-spacing:.04em}
.data-table td{padding:.75rem 1rem;border-bottom:1px solid var(--border)}
