/* NOP design system — see docs/superpowers/specs/2026-09-15-nop-design-system-kadence-build-design.md
   Sections: 1 tokens · 2 Kadence chrome · 3 components · 4 LearnPress + posts · 5 motion */

/* ---------- 1. TOKENS ---------- */
@property --nop-bg        { syntax: "<color>"; inherits: true; initial-value: #0c0c0e; }
@property --nop-bg-deep   { syntax: "<color>"; inherits: true; initial-value: #1a1a1f; }
@property --nop-fg        { syntax: "<color>"; inherits: true; initial-value: #efe9df; }
@property --nop-fg-dim    { syntax: "<color>"; inherits: true; initial-value: #b8b2a6; }
@property --nop-rule      { syntax: "<color>"; inherits: true; initial-value: #2a2a31; }
@property --nop-accent    { syntax: "<color>"; inherits: true; initial-value: #ff8a2a; }
@property --nop-on-accent { syntax: "<color>"; inherits: true; initial-value: #0c0c0e; }
@property --nop-surface   { syntax: "<color>"; inherits: true; initial-value: #1a1a1f; }

:root {
  --nop-font-display: "Big Shoulders Display", Impact, "Arial Narrow Bold", sans-serif;
  --nop-font-body: "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif;
  --nop-font-mono: "IBM Plex Mono", Consolas, "Courier New", monospace;
  --nop-font-serif: "Fraunces", Georgia, "Times New Roman", serif;
  --nop-text-2xs: .64rem; --nop-text-xs: .72rem; --nop-text-sm: .84rem; --nop-text-base: 1rem;
  --nop-text-md: 1.125rem; --nop-text-lg: 1.375rem; --nop-text-display: clamp(2.75rem, 6.4vw, 7.4rem);
  --nop-track-mono: .16em; --nop-track-wordmark: .12em;
  --nop-space-1: .25rem; --nop-space-2: .5rem; --nop-space-3: .75rem; --nop-space-4: 1rem;
  --nop-space-5: 1.5rem; --nop-space-6: 2rem; --nop-space-7: 3rem; --nop-space-8: 4rem;
  --nop-gutter: clamp(1.5rem, 5vw, 5rem); --nop-grid-gap: clamp(1rem, 2vw, 2rem);
  --nop-frame-max: 1760px; --nop-wide: 1280px; --nop-content: 800px;
  --nop-letterbox-h: 5vh; --nop-utility-h: 2.25rem;
  --nop-ease-out: cubic-bezier(.22, 1, .36, 1); --nop-ease-shutter: cubic-bezier(.77, 0, .175, 1);
  --nop-band-text: #efe9df; /* text colour inside permanently dark bands, both modes */
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --nop-bg: #0c0c0e; --nop-bg-deep: #1a1a1f; --nop-fg: #efe9df; --nop-fg-dim: #b8b2a6;
  --nop-rule: #2a2a31; --nop-letterbox: #0c0c0e; --nop-on-accent: #0c0c0e;
  --nop-band: #1a1a1f; --nop-band-raised: #242429; --nop-surface: #1a1a1f;
  --nop-fg-30: rgba(239,233,223,.30); --nop-fg-18: rgba(239,233,223,.18); --nop-fg-12: rgba(239,233,223,.12); --nop-fg-08: rgba(239,233,223,.08);
  --nop-bg-90: rgba(12,12,14,.90); --nop-bg-60: rgba(12,12,14,.60); --nop-bg-40: rgba(12,12,14,.40);
  --nop-glow-alpha: 42%; --nop-still-opacity: 1;
  --nop-tile: #ffffff;
}
:root[data-theme="light"] {
  color-scheme: light;
  --nop-bg: #f6f1e8; --nop-bg-deep: #ece4d6; --nop-fg: #17181c; --nop-fg-dim: #3b3c42;
  --nop-rule: #8a857c; --nop-letterbox: #17181c; --nop-on-accent: #f6f1e8;
  --nop-band: #17181c; --nop-band-raised: #26272d; --nop-surface: #ece4d6;
  --nop-fg-30: rgba(23,24,28,.30); --nop-fg-18: rgba(23,24,28,.18); --nop-fg-12: rgba(23,24,28,.12); --nop-fg-08: rgba(23,24,28,.08);
  --nop-bg-90: rgba(246,241,232,.90); --nop-bg-60: rgba(246,241,232,.60); --nop-bg-40: rgba(246,241,232,.40);
  --nop-glow-alpha: 30%; --nop-still-opacity: .55;
  --nop-tile: #ffffff;
}
:root[data-theme="dark"][data-site="studio"]      { --nop-accent: #ff8a2a; --nop-accent-text: #ff8a2a; }
:root[data-theme="dark"][data-site="consulting"]  { --nop-accent: #4fb3bf; --nop-accent-text: #4fb3bf; }
:root[data-theme="dark"][data-site="training"]    { --nop-accent: #f2c14e; --nop-accent-text: #f2c14e; }
:root[data-theme="light"][data-site="studio"]     { --nop-accent: #b04a12; --nop-accent-text: #c5561b; }
:root[data-theme="light"][data-site="consulting"] { --nop-accent: #1f6f7a; --nop-accent-text: #1f6f7a; }
:root[data-theme="light"][data-site="training"]   { --nop-accent: #8a6200; --nop-accent-text: #8a6200; }
:root { --nop-accent-soft: color-mix(in srgb, var(--nop-accent) 30%, transparent); --nop-glow: color-mix(in srgb, var(--nop-accent) var(--nop-glow-alpha), transparent); }
/* Kadence's inline customizer CSS colours links ("a{color:var(--global-palette-highlight)}"),
   headings, buttons, form fields, the mobile drawer and every ".has-theme-paletteN-color" utility
   with var(--global-palette1..9), which its own ":root" block sets to the FIXED hex values of the
   kadence_global_palette option — the light-mode set, never mode-aware (the option stays as it is:
   it drives the editor's colour picker). Measured on Training /courses/ in light mode: an inline
   link outside .entry-content computed #f2c14e, the DARK-mode amber, on paper. Re-point every slot
   at our tokens here, on html[data-site] — the same element as :root but (0,1,1) vs (0,1,0), so it
   wins regardless of stylesheet order — following Kadence's own slot semantics (1-2 accent and its
   hover, 3-4 strongest/body text, 5-6 muted text, 7 subtle surface, 8-9 page/content ground; read
   off global/content/header/footer.min.css + both sites' inline CSS). Every Kadence-generated rule
   then follows the theme for free. Kadence's derived variables resolve through the slots too, but
   two carry a different meaning than their slot: "highlight" is link TEXT (→ the text-safe accent,
   not the fill accent) and "btn" is button TEXT on an accent fill (→ on-accent, not the page
   ground), so those are re-pointed explicitly. --global-palette9rgb stays 255,255,255: it is only
   used inside an rgba() for the "thumbnail behind title" post layout, which no page uses.
   Consequence for editors: a block styled with the ".has-theme-palette3-background-color
   .has-theme-palette9-color" pair (ink on white in the picker) now reads fg-on-bg — the SAME
   contrast in both modes, but a "dark band" intent flips to a light band in dark mode. No rebuilt
   page uses those classes (the design system's bands are .nop-band--deep); prefer them. */
html[data-site] {
  --global-palette1: var(--nop-accent); --global-palette2: var(--nop-accent);
  --global-palette3: var(--nop-fg); --global-palette4: var(--nop-fg);
  --global-palette5: var(--nop-fg-dim); --global-palette6: var(--nop-fg-dim);
  --global-palette7: var(--nop-bg-deep); --global-palette8: var(--nop-bg); --global-palette9: var(--nop-bg);
  --global-palette-highlight: var(--nop-accent-text); --global-palette-highlight-alt: var(--nop-accent-text);
  --global-palette-btn: var(--nop-on-accent); --global-palette-btn-hover: var(--nop-on-accent);
}

/* ---------- 2. KADENCE CHROME ---------- */
html[data-site] body { background: var(--nop-bg); color: var(--nop-fg); font-family: var(--nop-font-body); }
/* Kadence's own inline customizer CSS hardcodes ".site{background:var(--global-palette9)}"
   (the theme's fixed light-mode white), which sits on the .site.wp-site-blocks wrapper
   between body and every unstyled section — invisible in the preview shell (no real
   Kadence markup) but on the live DOM it paints plain .nop-section blocks solid white
   regardless of theme. Reassert our theme-reactive background at the same wrapper. */
html[data-site] .site { background: var(--nop-bg); }
html[data-site] .site-header, html[data-site] .site-main-header-wrap, html[data-site] .site-header-row { background: var(--nop-bg); color: var(--nop-fg); border: 0; box-shadow: none; }
html[data-site] .site-header-row-container-inner, html[data-site] .site-footer-row-container-inner, .nop-utility__inner, .nop-footer-columns__inner { max-width: var(--nop-frame-max); padding-inline: var(--nop-gutter); }
/* utility bar */
.nop-utility { background: var(--nop-bg); border-bottom: 1px solid var(--nop-rule); font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; }
.nop-utility__inner { display: flex; justify-content: space-between; align-items: center; min-height: var(--nop-utility-h); margin-inline: auto; gap: var(--nop-space-4); }
.nop-utility__sites { display: flex; gap: var(--nop-space-5); }
.nop-utility__site { color: var(--nop-fg-dim); text-decoration: none; position: relative; padding-left: .9em; }
.nop-utility__site::before { content: ""; position: absolute; left: 0; top: 50%; width: .4em; height: .4em; border-radius: 50%; background: var(--nop-rule); transform: translateY(-50%); }
.nop-utility__site.is-current { color: var(--nop-accent); } .nop-utility__site.is-current::before { background: var(--nop-accent); }
.nop-utility__site:hover { color: var(--nop-fg); }
.nop-utility__meta { display: flex; align-items: center; gap: var(--nop-space-5); color: var(--nop-fg-dim); }
.nop-utility__lang, .nop-utility__place { white-space: nowrap; }
.nop-toggle { display: inline-flex; align-items: center; gap: .5em; background: transparent; border: 1px solid var(--nop-rule); color: var(--nop-fg); padding: .35em .7em; font: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; border-radius: 2px; }
.nop-toggle:hover { border-color: var(--nop-accent); } .nop-toggle:focus-visible { outline: 2px solid var(--nop-accent); outline-offset: 2px; }
.nop-toggle__icon { width: 1em; height: 1em; }
:root[data-theme="dark"] .nop-toggle__icon--moon, :root[data-theme="light"] .nop-toggle__icon--sun { display: none; }
/* ≤440px: the row is 24px-gapped mono caps; with EN / ES held on one line it no longer fits
   beside three site links + the toggle at 400px, so tighten the gaps and let the meta group
   drop to its own right-aligned row rather than clip the toggle off the viewport edge. */
@media (max-width: 440px) {
  .nop-toggle__label, .nop-utility__place { display: none; }
  .nop-utility__inner { flex-wrap: wrap; gap: var(--nop-space-2) var(--nop-space-3); padding-block: var(--nop-space-2); }
  .nop-utility__sites { gap: var(--nop-space-3); }
  .nop-utility__meta { gap: var(--nop-space-3); margin-left: auto; }
}
/* brand (replaces Kadence's site branding output) */
.nop-brand { display: inline-flex; align-items: center; gap: .7rem; text-decoration: none; color: var(--nop-fg); }
.nop-brand__mark { width: 2.4rem; height: 2.4rem; flex: none; }
.nop-brand__wordmark { font: 700 1.4rem/1 var(--nop-font-display); letter-spacing: var(--nop-track-wordmark); text-transform: uppercase; }
.nop-brand__descriptor { font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: .14em; color: var(--nop-accent); padding-left: .7rem; margin-left: .2rem; border-left: 1px solid var(--nop-fg-18); }
@media (max-width: 480px) { .nop-brand__mark { width: 1.9rem; height: 1.9rem; } .nop-brand__wordmark { font-size: 1.05rem; } }
/* primary nav */
html[data-site] .primary-menu-container > ul > li > a { font: 500 var(--nop-text-sm)/1 var(--nop-font-body); color: var(--nop-fg); padding: .9rem .75rem; position: relative; }
html[data-site] .primary-menu-container > ul > li > a::after { content: ""; position: absolute; left: .75rem; right: .75rem; bottom: .45rem; height: 2px; background: var(--nop-accent); transform: scaleX(0); transform-origin: left; transition: transform .2s ease; }
html[data-site] .primary-menu-container > ul > li > a:hover::after, html[data-site] .primary-menu-container > ul > li.current-menu-item > a::after, html[data-site] .primary-menu-container > ul > li.current-menu-ancestor > a::after { transform: scaleX(1); }
/* Kadence's own inline customizer CSS hardcodes the current-page nav link to
   --global-palette3 (dark ink) via a higher-specificity selector
   (".main-navigation .primary-menu-container > ul > li.menu-item.current-menu-item > a"),
   which goes invisible against our dark-mode header — match its selector shape so our
   theme-reactive token wins instead. */
html[data-site] .main-navigation .primary-menu-container > ul > li.menu-item.current-menu-item > a, html[data-site] .main-navigation .primary-menu-container > ul > li.menu-item.current-menu-ancestor > a { color: var(--nop-fg); }
/* Kadence's inline customizer CSS also restates the link PADDING, colour and (when a
   primary_navigation_typography mod exists) font at
   ".main-navigation .primary-menu-container > ul > li.menu-item > a" (0,3,3) — emitted from the
   primary_navigation_spacing / vertical_spacing / color mods even at their defaults — which beats
   the plain rule above (0,2,4). Measured live on BOTH sites: the links computed Kadence's padding
   (3.2px/10.4px from Training's stale 0.2em/1.3em mods, 8.06px from Studio's defaults), never our
   .9rem/.75rem, so the ::after underline (bottom: .45rem, sized for OUR padding) landed inside the
   letters and read as a strike-through, and on non-transparent-header pages the links took
   palette5 (fixed grey). Repeat font, padding and colour at Kadence's selector shape. */
html[data-site] .main-navigation .primary-menu-container > ul > li.menu-item > a { font: 500 var(--nop-text-sm)/1 var(--nop-font-body); padding: .9rem .75rem; color: var(--nop-fg); }
html[data-site] .main-navigation .primary-menu-container > ul > li.menu-item > a:hover { color: var(--nop-accent); }
/* Training's front page (and other pages with the "_kad_post_transparent" post meta) get Kadence's
   body.transparent-header/mobile-transparent-header classes, which activate a further inline-CSS
   block hardcoding the primary nav links and the mobile hamburger to --global-palette3 (dark ink)
   via selectors more specific than the plain ones above — invisible against our dark-mode header.
   Studio never sets that post meta, so it never hit this block; match Kadence's selector shape
   here too (same fix idiom as the current-menu-item rule above). */
html[data-site] body.transparent-header .header-navigation .header-menu-container > ul > li.menu-item > a { color: var(--nop-fg); }
html[data-site] body.transparent-header .header-navigation .header-menu-container > ul > li.menu-item > a:hover { color: var(--nop-accent); }
html[data-site] body.mobile-transparent-header .mobile-toggle-open-container .menu-toggle-open { color: var(--nop-fg); }
html[data-site] .primary-menu-container ul ul { background: var(--nop-bg-deep); border: 1px solid var(--nop-rule); box-shadow: none; }
html[data-site] .primary-menu-container ul ul a { color: var(--nop-fg); font-size: var(--nop-text-sm); }
html[data-site] .primary-menu-container ul ul a:hover { color: var(--nop-accent); background: transparent; }
/* Kadence's inline customizer CSS paints the dropdown at ".header-navigation
   .header-menu-container ul ul.sub-menu" (0,3,2) / "… ul ul li.menu-item > a" (0,4,4), beating the
   two plain rules above, as a palette3 surface with palette8 text on Studio (palette9/palette3 on
   Training) — a "dark surface, light text" pair that only worked because the slots were fixed hex.
   With the slots re-pointed at the tokens (section 1) that pair would INVERT against the page in
   dark mode, so pin the dropdown to the page ground at Kadence's own selector shapes instead. */
html[data-site] .header-navigation .header-menu-container ul ul.sub-menu, html[data-site] .header-navigation .header-menu-container ul ul.submenu { background: var(--nop-bg-deep); border: 1px solid var(--nop-rule); box-shadow: none; }
html[data-site] .header-navigation .header-menu-container ul ul li.menu-item > a { color: var(--nop-fg); font-size: var(--nop-text-sm); }
html[data-site] .header-navigation .header-menu-container ul ul li.menu-item > a:hover, html[data-site] .header-navigation .header-menu-container ul ul li.menu-item.current-menu-item > a { color: var(--nop-accent); background: transparent; }
html[data-site] .menu-toggle-open, html[data-site] .menu-toggle-close { color: var(--nop-fg); }
/* Same for the mobile drawer: header.min.css hardcodes ".drawer-inner{background:#090c10;color:#fff}"
   (a header_popup_background mod swaps in palette3) and the inline CSS colours its links
   ".mobile-navigation ul li > a{color:var(--global-palette8)}" — with palette8 now the page ground,
   dark-mode drawer links would compute --nop-bg on a fixed near-black. Pin the whole drawer to the
   page ground (surface, links, current item, close toggle) at Kadence's selector shapes; the
   trigger's own colour/background mods are cleared with the other stale Training mods. */
html[data-site] #mobile-drawer .drawer-inner, html[data-site] .popup-drawer .drawer-inner { background: var(--nop-bg-deep); color: var(--nop-fg); }
html[data-site] .mobile-navigation ul li > a, html[data-site] .mobile-navigation ul li.menu-item-has-children > .drawer-nav-drop-wrap, html[data-site] #mobile-drawer .drawer-header .drawer-toggle, html[data-site] #mobile-drawer .drawer-header .drawer-toggle:hover { color: var(--nop-fg); }
html[data-site] .mobile-navigation ul li > a:hover, html[data-site] .mobile-navigation ul li.current-menu-item > a, html[data-site] .mobile-navigation ul li.current-menu-item.menu-item-has-children > .drawer-nav-drop-wrap { color: var(--nop-accent); }
html[data-site] .mobile-toggle-open-container .menu-toggle-open, html[data-site] .mobile-toggle-open-container .menu-toggle-open:focus, html[data-site] .mobile-toggle-open-container .menu-toggle-open:hover { color: var(--nop-fg); background: transparent; }
/* buttons — header CTA + content buttons */
html[data-site] .header-button, html[data-site] .wp-block-button__link, html[data-site] .wp-element-button, .nop-btn { display: inline-block; font: 600 var(--nop-text-xs)/1 var(--nop-font-mono); letter-spacing: .14em; text-transform: uppercase; text-decoration: none; border-radius: 2px; padding: .95em 1.4em; transition: transform .2s ease, filter .2s ease, background-color .2s ease, border-color .2s ease; }
html[data-site] .wp-block-button__link, html[data-site] .wp-element-button, .nop-btn--primary { background: var(--nop-accent); color: var(--nop-on-accent); border: 1px solid var(--nop-accent); }
html[data-site] .header-button, html[data-site] .is-style-outline .wp-block-button__link, .nop-btn--outline { background: transparent; color: var(--nop-fg); border: 1px solid var(--nop-fg-30); }
html[data-site] .header-button:hover, .nop-btn--outline:hover { border-color: var(--nop-accent); color: var(--nop-fg); }
html[data-site] .wp-block-button__link:hover, .nop-btn--primary:hover { filter: brightness(1.08); transform: translateY(-1px); }
/* Kadence emits an ID-scoped rule ("#main-header .header-button") whenever a site carries
   header_button_color/header_button_border theme mods — Training did (pre-design-system values:
   white text and a style-less, so 0-width, border — invisible against our light-mode paper header)
   until the "remove" list in kadence-mods.training.json cleared them. The ID-scoped restatement
   stays as the guard for any site (Consulting next) whose mods have not been cleared, or where a
   customizer edit sets them again: tokens only, so it is harmless where Kadence emits nothing. */
html[data-site] #main-header .header-button { color: var(--nop-fg); border: 1px solid var(--nop-fg-30); background: transparent; }
html[data-site] #main-header .header-button:hover { color: var(--nop-fg); border-color: var(--nop-accent); background: transparent; }
/* page title strip (inner pages) */
html[data-site] .entry-hero.page-hero-section, html[data-site] .entry-hero.page-hero-section .entry-header { background: var(--nop-bg); border-bottom: 1px solid var(--nop-rule); min-height: 0; }
html[data-site] .entry-hero-container-inner { padding-block: var(--nop-space-6); }
html[data-site] .entry-hero .entry-title { font: 700 clamp(2rem, 4vw, 3.4rem)/.95 var(--nop-font-display); letter-spacing: .005em; color: var(--nop-fg); margin: 0; }
.nop-title-strip__eyebrow { display: block; font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; color: var(--nop-accent); margin-bottom: var(--nop-space-3); }
.nop-title-strip__deck { font-size: var(--nop-text-md); color: var(--nop-fg-dim); max-width: 60ch; margin: var(--nop-space-3) 0 0; }
/* content */
html[data-site] .content-area { margin-top: 0; margin-bottom: 0; }
html[data-site] .entry-content { line-height: 1.55; }
html[data-site] .entry-content a:not(.wp-block-button__link, .wp-element-button, .nop-btn, .nop-card a, .nop-chips a) { color: var(--nop-accent-text); text-decoration-thickness: 1px; text-underline-offset: 3px; }
/* hero, result band and CTA (.alignfull) must span the full viewport edge to edge; .nop-section
   stays centred at --nop-wide. push_page.sh sets _kad_post_layout=fullwidth so .entry-content
   already spans the full clientWidth with no side padding — Kadence's own .alignfull rule
   (content.min.css) instead breaks out via negative margins keyed to a "--global-vw" custom
   property that only compensates HALF the scrollbar width, overflowing past clientWidth by half
   the scrollbar's width on pages with a vertical scrollbar. Since the parent is already
   edge-to-edge here, skip the viewport-vw breakout entirely and just fill the parent. */
html[data-site] .entry-content > .alignfull, html[data-site] .single-content > .alignfull { max-width: none; width: 100%; margin-left: 0; margin-right: 0; }
/* Kadence's own inline customizer CSS hardcodes bare "h1,h2,h3{color:var(--global-palette3)}"
   and "h4,h5,h6{color:var(--global-palette4/5)}" — the theme's fixed light-mode ink colours,
   not theme-reactive. Our rule never declared color, so that fixed ink won on the live DOM
   (an omission, not a specificity loss): every plain section heading — including the CTA
   heading — rendered near-invisible in dark mode. Assert the theme-reactive token here. */
html[data-site] :is(h1, h2, h3):not([class*="nop-"]) { font-family: var(--nop-font-display); font-weight: 700; letter-spacing: .005em; line-height: .95; color: var(--nop-fg); }
html[data-site] h4, html[data-site] h5, html[data-site] h6 { font-family: var(--nop-font-body); font-weight: 600; color: var(--nop-fg); }
.headline-accent { font-family: var(--nop-font-serif); font-style: italic; font-weight: 400; font-variation-settings: "opsz" 144; font-size: 1.1em; letter-spacing: -.02em; line-height: 0; position: relative; top: -.03em; padding-inline: .04em; }
/* footer */
.nop-footer-columns { background: var(--nop-bg); border-top: 1px solid var(--nop-rule); }
.nop-footer-columns__inner { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--nop-space-6); margin-inline: auto; padding-block: var(--nop-space-8); }
.nop-footer-columns__label { font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; color: var(--nop-fg-dim); margin: 0 0 var(--nop-space-4); }
.nop-footer-columns ul { list-style: none; margin: 0; padding: 0; } .nop-footer-columns li { margin: 0 0 .5rem; }
.nop-footer-columns a { color: var(--nop-fg); text-decoration: none; font-size: var(--nop-text-sm); } .nop-footer-columns a:hover { color: var(--nop-accent); }
@media (max-width: 800px) { .nop-footer-columns__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px) { .nop-footer-columns__inner { grid-template-columns: 1fr; } }
html[data-site] .site-footer, html[data-site] .site-bottom-footer-wrap { background: var(--nop-bg); color: var(--nop-fg-dim); border-top: 1px solid var(--nop-rule); }
/* Kadence's own inline customizer CSS hardcodes
   ".site-bottom-footer-wrap .site-footer-row-container-inner{background:var(--global-palette8)}"
   (the theme's fixed light "cream" surface, --global-palette8) on the footer's bottom row —
   invisible in the preview shell (no real Kadence markup) but on the live DOM it paints a
   cream strip across the very bottom of every page regardless of theme (confirmed on Training's
   home page in dark mode: the row's inner container computed rgb(246,241,232) while --nop-bg
   resolved to rgb(12,12,14)). Match Kadence's selector shape for all three footer rows (top/
   middle ship the same generated rule when configured) so our theme-reactive token wins by
   specificity alone, no !important. */
html[data-site] .site-bottom-footer-wrap .site-footer-row-container-inner, html[data-site] .site-top-footer-wrap .site-footer-row-container-inner, html[data-site] .site-middle-footer-wrap .site-footer-row-container-inner { background: var(--nop-bg); }
html[data-site] .site-footer .footer-html { font: 400 var(--nop-text-2xs)/1.6 var(--nop-font-mono); letter-spacing: .08em; }
html[data-site] .site-footer a { color: var(--nop-fg); } html[data-site] .site-footer a:hover { color: var(--nop-accent); }
/* front-page header (Kadence adds body.transparent-header): Kadence's own theme.css always
   sets #masthead{background:#fff} unconditionally, so the "transparent" wrapper background we
   used to set here never actually let the hero photo show through — it only fought with the
   theme-aware background/color from the ".site-header" rule above, going invisible in dark
   mode (light text on a white masthead). Kadence's .transparent-header/.mobile-transparent-header
   #masthead rule also sets position:absolute with no top offset, landing at the viewport top and
   overlapping .nop-utility above it (confirmed live: both boxes started at y:0). Since the header
   never actually goes see-through, keep it in normal flow with the same themed background/text as
   every other page — the hero's own min-height calc already reserves the room for it. */
html[data-site] body.transparent-header #masthead, html[data-site] body.mobile-transparent-header #masthead { position: static; left: auto; right: auto; z-index: auto; }

/* ---------- 3. COMPONENTS ---------- */
/* On themes without a theme.json (Kadence), core's "wp_restore_group_inner_container" render
   filter (not the block's save markup — invisible in the standalone preview shell, which never
   runs the real block renderer) wraps every "core/group" block's inner blocks in one shared
   ".wp-block-group__inner-container" div. For a single-column block that's invisible, but for
   every CSS Grid/flex component whose columns are themselves group blocks — the plate/portrait
   hero body, the case-study story rows, the biography/contact split — it collapses the grid to
   one auto-placed item and the "second column" just stacks underneath the first. Un-wrap it for
   EVERY nop-* group (not a per-component list, so a new component can't silently miss it) so the
   real column elements become direct grid children again. */
html[data-site] .wp-block-group[class*="nop-"] > .wp-block-group__inner-container { display: contents; }
.nop-eyebrow { display: inline-flex; align-items: center; gap: .8em; font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; color: var(--nop-accent); }
.nop-eyebrow::before { content: ""; width: 2rem; height: 1px; background: var(--nop-accent); }
.nop-hero { position: relative; isolation: isolate; overflow: hidden; background: var(--nop-bg); color: var(--nop-fg); min-height: calc(100vh - var(--nop-utility-h) - 5.5rem); display: grid; grid-template-rows: var(--nop-letterbox-h) 1fr auto var(--nop-letterbox-h); grid-template-columns: minmax(0, 1fr); }
.nop-hero__bar { background: var(--nop-letterbox); }
.nop-hero__still { position: absolute; inset: var(--nop-letterbox-h) 0; z-index: -2; background-size: cover; background-position: 62% 30%; background-repeat: no-repeat; opacity: var(--nop-still-opacity); }
/* the Home hero's still (OurAmericas poster) is a tall stacked collage; zoom + reposition
   just this image so the canyon band (~25%-37% of its height) fills the frame instead of
   the generic "cover" crop, which would show the vertical title lettering. Scoped to this
   upload so other pages' "still" images keep the generic cover/position above. */
.nop-hero__still[style*="OurAmericas-NewPoster-small"] { background-size: auto 600vh; background-position: -68vh -152vh; }
/* the Realty landing's key art is a square "For Sale" sign graphic; the generic 62%/30%
   cover crop lands on the empty post/chains above the sign. Shift focus down so the
   "THE REALTY" signboard itself fills the frame. Scoped to this upload only. */
.nop-hero__still[style*="TheRealty-logo"] { background-position: 50% 66%; }
.nop-hero__still::after { content: ""; position: absolute; inset: 0; background: linear-gradient(100deg, var(--nop-bg) 0 38%, var(--nop-bg-60) 60%, transparent 100%), linear-gradient(0deg, var(--nop-bg) 0 8%, transparent 45%); }
.nop-hero__glow { position: absolute; z-index: -1; width: 60vmax; height: 60vmax; right: -20vmax; top: -10vmax; border-radius: 50%; background: radial-gradient(closest-side, var(--nop-glow), transparent); pointer-events: none; }
.nop-hero__body { align-self: end; max-width: var(--nop-frame-max); margin-inline: auto; padding: var(--nop-space-8) var(--nop-gutter) var(--nop-space-6); display: grid; gap: var(--nop-space-5); grid-template-columns: minmax(0, 1fr); }
.nop-hero__headline { font: 900 var(--nop-text-display)/.92 var(--nop-font-display); letter-spacing: .005em; margin: 0; color: var(--nop-fg); }
.nop-hero__headline .is-accent { color: var(--nop-accent); }
.nop-hero__deck { font: 400 var(--nop-text-md)/1.55 var(--nop-font-body); color: var(--nop-fg-dim); max-width: 52ch; margin: 0; }
.nop-hero__actions { display: flex; flex-wrap: wrap; gap: var(--nop-space-3); }
.nop-hero--plate .nop-hero__body, .nop-hero--portrait .nop-hero__body { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: end; }
.nop-hero__text, .nop-hero__side { min-width: 0; }
.nop-hero__plate { border: 1px solid var(--nop-rule); background: var(--nop-bg-deep); }
.nop-hero__plate img { display: block; width: 100%; height: auto; max-width: 100%; }
.nop-hero__caption { font: 400 var(--nop-text-2xs)/1.4 var(--nop-font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--nop-fg-dim); text-align: right; margin-top: var(--nop-space-2); }
.nop-hero--portrait .nop-hero__plate { border: 0; background: transparent; -webkit-mask-image: linear-gradient(90deg, transparent, #000 35%); mask-image: linear-gradient(90deg, transparent, #000 35%); }
:root[data-theme="light"] .nop-hero--portrait .nop-hero__plate { -webkit-mask-image: none; mask-image: none; border: 1px solid var(--nop-rule); }
@media (max-width: 900px) { .nop-hero--plate .nop-hero__body, .nop-hero--portrait .nop-hero__body { grid-template-columns: 1fr; } }
.nop-credits { position: relative; z-index: 1; background: var(--nop-bg-90); border-top: 1px solid var(--nop-fg-12); font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--nop-fg-dim); }
.nop-credits__viewport { overflow: hidden; padding: var(--nop-space-4) var(--nop-gutter); display: flex; }
.nop-credits__track { display: flex; gap: var(--nop-space-6); white-space: nowrap; flex: none; }
.nop-credits__item { display: inline-flex; gap: .8em; align-items: baseline; }
.nop-credits__item + .nop-credits__item::before { content: "|"; color: var(--nop-fg-18); margin-right: var(--nop-space-4); }
.nop-credits__index { color: var(--nop-accent); }
.nop-credits--marquee .nop-credits__track { animation: nop-marquee 80s linear infinite; padding-right: var(--nop-space-6); }
.nop-credits--marquee:hover .nop-credits__track { animation-play-state: paused; }
.nop-credits:not(.nop-credits--marquee) .nop-credits__track { flex: 1 1 auto; min-width: 0; white-space: normal; flex-wrap: wrap; row-gap: var(--nop-space-3); }
@media (max-width: 1350px) {
  .nop-credits--marquee .nop-credits__track { animation: none; padding-right: 0; white-space: normal; flex-wrap: wrap; row-gap: var(--nop-space-3); }
  .nop-credits--marquee .nop-credits__viewport { overflow: visible; }
}
@media (max-width: 600px) {
  .nop-credits__track { flex-direction: column; align-items: flex-start; gap: var(--nop-space-2); }
  .nop-credits__item + .nop-credits__item::before { display: none; }
}
.nop-section { max-width: var(--nop-wide); margin-inline: auto; padding: var(--nop-space-8) var(--nop-gutter); }
.nop-spec-sheet { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--nop-space-5); border-top: 1px solid var(--nop-rule); border-bottom: 1px solid var(--nop-rule); padding-block: var(--nop-space-5); margin: 0; }
.nop-spec-sheet dt { font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; color: var(--nop-fg-dim); margin-bottom: var(--nop-space-2); }
.nop-spec-sheet dd { margin: 0; font-size: var(--nop-text-sm); }
@media (max-width: 800px) { .nop-spec-sheet { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.nop-story { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--nop-space-6); padding-block: var(--nop-space-7); border-bottom: 1px solid var(--nop-rule); }
.nop-story__index { font: 500 var(--nop-text-xs)/1 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); color: var(--nop-accent); display: block; margin-bottom: var(--nop-space-3); }
.nop-story h2 { font-size: clamp(1.8rem, 3vw, 2.6rem); margin: 0; }
.nop-story__body { max-width: 65ch; }
@media (max-width: 800px) { .nop-story { grid-template-columns: 1fr; } }
.nop-pull-quote { border-left: 2px solid var(--nop-accent); padding-left: var(--nop-space-5); margin: var(--nop-space-6) 0; }
.nop-pull-quote p { font: italic 400 clamp(1.3rem, 2.2vw, 1.9rem)/1.25 var(--nop-font-serif); font-variation-settings: "opsz" 96; margin: 0 0 var(--nop-space-3); }
.nop-pull-quote cite { font: 500 var(--nop-text-2xs)/1.4 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; font-style: normal; color: var(--nop-fg-dim); }
.nop-band { background: var(--nop-bg-deep); color: var(--nop-fg); }
.nop-band__inner { max-width: var(--nop-wide); margin-inline: auto; padding: var(--nop-space-8) var(--nop-gutter); display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--nop-space-6); }
.nop-band__figure { font: 700 clamp(2.4rem, 5vw, 4.5rem)/1 var(--nop-font-display); }
.nop-band__label { font: 500 var(--nop-text-2xs)/1.4 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; color: var(--nop-fg-dim); margin-top: var(--nop-space-2); }
.nop-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--nop-space-5); list-style: none; margin: 0; padding: 0; }
.nop-cards--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.nop-card { border: 1px solid var(--nop-rule); background: var(--nop-bg); transition: transform .2s ease, border-color .2s ease; display: flex; flex-direction: column; }
.nop-card:hover { transform: translateY(-2px); border-color: var(--nop-accent); }
.nop-card img { display: block; width: 100%; height: auto; aspect-ratio: 2 / 3; object-fit: cover; }
.nop-cards--partners .nop-card img, .nop-cards--square .nop-card img, .nop-cards--product .nop-card img { aspect-ratio: 1; }
.nop-cards--partners .nop-card img { filter: grayscale(1); }
.nop-cards--product .nop-card img { object-fit: contain; background: var(--nop-tile); padding: var(--nop-space-5); }
.nop-card__body { padding: var(--nop-space-4); }
.nop-card__eyebrow { font: 500 var(--nop-text-2xs)/1.4 var(--nop-font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--nop-fg-dim); }
/* Kadence's own inline customizer CSS hardcodes bare "h1,h2,h3{color:var(--global-palette3)}"
   (the theme's fixed light-mode ink) -- the generic rescue rule above deliberately excludes any
   heading carrying a "nop-" class on the assumption those components already set their own color,
   but this one never did for the plain-text case (only its linked-title variant did), so an
   unlinked card title (e.g. a square card with no "url") fell through to that fixed ink and went
   invisible in dark mode. Same fix idiom as .nop-title-strip__title / .nop-hero__headline above. */
.nop-card__title { font: 600 var(--nop-text-md)/1.2 var(--nop-font-body); margin: .3rem 0 0; color: var(--nop-fg); }
.nop-card__title a { color: var(--nop-fg); text-decoration: none; }
.nop-card__text { font-size: var(--nop-text-sm); color: var(--nop-fg-dim); margin: .5rem 0 0; }
@media (max-width: 800px) { .nop-cards, .nop-cards--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px) { .nop-cards, .nop-cards--4 { grid-template-columns: 1fr; } }
.nop-chips { display: flex; flex-wrap: wrap; gap: var(--nop-space-3); list-style: none; margin: 0; padding: 0; }
.nop-chips a, .nop-chips span { display: inline-block; border: 1px solid var(--nop-rule); background: var(--nop-bg); padding: .6em .9em; font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--nop-fg); text-decoration: none; }
.nop-glance { display: grid; gap: var(--nop-space-3); font-size: var(--nop-text-sm); margin: 0; }
.nop-glance dt { font: 500 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: var(--nop-track-mono); text-transform: uppercase; color: var(--nop-fg-dim); }
.nop-glance dd { margin: 0 0 var(--nop-space-2); }
.nop-split { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--nop-space-6); }
@media (max-width: 800px) { .nop-split { grid-template-columns: 1fr; } }
.nop-cta { background: var(--nop-bg-deep); }
.nop-cta__inner { max-width: var(--nop-wide); margin-inline: auto; padding: var(--nop-space-8) var(--nop-gutter); display: grid; gap: var(--nop-space-4); }
.nop-cta h2 { font-size: clamp(2.2rem, 5vw, 4.4rem); margin: 0; }
.nop-prose { max-width: var(--nop-content); }
.nop-hero__headline em, .nop-title-strip__title em, .nop-cta h2 em, .nop-hero__deck em { font-family: var(--nop-font-serif); font-style: italic; font-weight: 400; font-variation-settings: "opsz" 144; font-size: 1.1em; letter-spacing: -.02em; line-height: 0; position: relative; top: -.03em; padding-inline: .04em; }
.nop-hero__deck em { font-size: 1.05em; top: 0; }
.nop-title-strip { max-width: var(--nop-wide); margin-inline: auto; padding: var(--nop-space-7) var(--nop-gutter) var(--nop-space-5); border-bottom: 1px solid var(--nop-rule); }
.nop-title-strip__title { font: 700 clamp(2.4rem, 5vw, 4.4rem)/.95 var(--nop-font-display); margin: 0; color: var(--nop-fg); }
.nop-band__inner--stack { display: grid; grid-template-columns: 1fr; gap: var(--nop-space-4); }
.nop-hero__text { display: grid; gap: var(--nop-space-5); grid-template-columns: minmax(0, 1fr); }

/* ---------- 4. LEARNPRESS + POSTS ---------- */
html[data-site] .lp-archive-courses, html[data-site] .lp-content-area, html[data-site] .learnpress { color: var(--nop-fg); }
html[data-site] .learn-press-courses .course, html[data-site] .lp-course-card { border: 1px solid var(--nop-rule); background: var(--nop-bg); }
html[data-site] .learn-press-courses .course-title, html[data-site] .course-summary .course-title { font-family: var(--nop-font-display); font-weight: 700; }
html[data-site] .lp-button, html[data-site] .learnpress button, html[data-site] .lp-checkout .button { background: var(--nop-accent); color: var(--nop-on-accent); border: 0; font: 600 var(--nop-text-xs)/1 var(--nop-font-mono); letter-spacing: .14em; text-transform: uppercase; border-radius: 2px; }
html[data-site] .lp-user-profile .lp-profile-nav a { color: var(--nop-fg); }
html[data-site] .lp-user-profile .lp-profile-nav .active > a { color: var(--nop-accent); }
html[data-site] .entry-meta, html[data-site] .posted-on { font: 400 var(--nop-text-2xs)/1.4 var(--nop-font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--nop-fg-dim); }
/* LearnPress live-DOM pass (Task 5): the two course single pages, /courses/ and /check-out/ were
   fetched (curl for static markup, Playwright + getComputedStyle for AJAX-rendered course cards,
   the checkout form and the profile "please login" notice) to find every element still carrying
   LearnPress's own hardcoded colour instead of a token. Real class names only — no guessing. */
/* .course-detail-info is LearnPress's own hardcoded-purple (#442e66) "hero" band wrapping the
   title/instructor/meta row on a single course page (verified via computed style, not inline
   style — it's in LP's own stylesheet). Treat it like our other permanently-dark bands: nop-band
   background with nop-band-text foreground, so it reads correctly in both modes instead of a
   fixed purple that only ever half-worked against a fixed-white .course-title. */
html[data-site] .course-detail-info { background: var(--nop-band); }
html[data-site] .course-detail-info, html[data-site] .course-detail-info .course-title, html[data-site] .course-detail-info .course-meta, html[data-site] .course-detail-info a { color: var(--nop-band-text); }
/* .course-sidebar-preview / .course-sidebar-top is LearnPress's own hardcoded solid white price
   card — a glaring white box on the dark-mode page (verified: background-color: rgb(255,255,255)
   regardless of theme). Give it the same card treatment as .nop-card. .course-sidebar-top itself
   ties on specificity with LearnPress's own ".lp-archive-courses .course-summary-sidebar__inner >
   div{background:var(--lp-color-white,#fff)}" (learnpress.min.css, confirmed via the live CSSOM —
   a plain class selector never wins that tie), so repeat its exact selector shape to break the tie
   deterministically rather than rely on stylesheet load order. */
html[data-site] .course-sidebar-preview, html[data-site] .course-sidebar-top { background: var(--nop-bg-deep); color: var(--nop-fg); border: 1px solid var(--nop-rule); }
html[data-site] .lp-archive-courses .course-summary-sidebar__inner > div { background: var(--nop-bg-deep); }
/* The "FEATURED" ribbon uses TWO different LearnPress classes depending on context — .lp-badge /
   .featured-course on the single course page (hardcoded #ff2828) and the unrelated
   .course-featured on an archive card (hardcoded #ff3b30, confirmed a separate rule in
   learnpress.min.css, not a typo of the other) — both hardcoded red; recolour both to the accent. */
html[data-site] .lp-badge, html[data-site] .featured-course, html[data-site] .course-featured { background: var(--nop-accent); color: var(--nop-on-accent); font: 600 var(--nop-text-2xs)/1 var(--nop-font-mono); letter-spacing: .1em; text-transform: uppercase; }
/* .course-nav-tabs (Overview/Curriculum/Instructor) — the active tab's label and its ::after
   underline are LearnPress's own hardcoded gold (#ffb606), which on the light-mode cream
   background (--nop-bg: #f6f1e8) falls well under 4.5:1 contrast; the wrapper's divider is a
   fixed light grey that vanishes on the dark band. Retint both to the accent-text/rule tokens. */
html[data-site] .wrapper-course-nav-tabs { border-bottom-color: var(--nop-rule); }
html[data-site] .course-nav-tabs li.active label { color: var(--nop-accent-text); }
html[data-site] .course-nav-tabs li.active::after { background: var(--nop-accent); }
/* .course-readmore a (the "Buy Now" link on an archive course card) carries no lp-button class,
   so the generic button rule above never reaches it — verified computed style is transparent
   background with a fixed dark-grey (#333) label, close to invisible on the dark-mode card. Give
   it the same accent-button treatment as every other LearnPress button. A plain
   "html[data-site] .course-readmore a" loses on specificity to LearnPress's own
   ".learn-press-courses .course-content .course-readmore a" (three classes beats our two, per the
   live CSSOM), so repeat its exact selector shape rather than rely on load order. */
html[data-site] .learn-press-courses .course-content .course-readmore a, html[data-site] .course-readmore a { display: inline-block; background: var(--nop-accent); color: var(--nop-on-accent); border: 0; font: 600 var(--nop-text-xs)/1 var(--nop-font-mono); letter-spacing: .14em; text-transform: uppercase; border-radius: 2px; padding: .7em 1.1em; text-decoration: none; }
/* .learn-press-message covers every LearnPress notice — "Your cart is currently empty" (.error,
   hardcoded pink/red), "Please login to see your profile content" (.success, hardcoded green),
   and the curriculum-tab's "no items yet" (.info, hardcoded blue) — verified as three different
   fixed LearnPress colour pairs, none theme-reactive. Unify all three into one dark chip using
   the same permanently-dark band-raised/band-text pairing as the rest of the design system,
   rather than trying to preserve three separate un-tokened semantic colours. */
html[data-site] .learn-press-message { background: var(--nop-band-raised); color: var(--nop-band-text); border: 0; border-left: 3px solid var(--nop-accent); border-radius: 0; font: 500 var(--nop-text-sm)/1.5 var(--nop-font-body); padding: var(--nop-space-4); }
html[data-site] .learn-press-message a { color: var(--nop-accent-text); }
/* the /courses/ archive's search box and sort <select> are LearnPress's own plain form controls:
   a fixed white background throughout, and the sort <select> computes WHITE TEXT ON WHITE
   BACKGROUND in dark mode (verified via getComputedStyle) — its options are invisible until
   selected. Token both to match the rest of the page chrome. */
html[data-site] .search-courses input[type="search"], html[data-site] select.courses-order-by { background: var(--nop-bg); color: var(--nop-fg); border: 1px solid var(--nop-rule); }
/* LearnPress's archive template prints its own "<h1>Courses</h1>" (".learn-press-courses-header
   > h1") and a "Home > Courses" breadcrumb (".learn-press-breadcrumb") ABOVE the Courses page
   content, which already carries our .nop-title-strip — two h1s and two titles on /courses/
   (confirmed in the live markup). The h1 is dropped server-side (nop-design-system.php,
   nop_ds_lp_show_archive_title — the DOM then holds ONE h1); the display:none here is the fallback
   should a LearnPress update print it through another path. The breadcrumb is CSS-only, hidden on
   the archive alone (body.post-type-archive-lp_course): the single course page's breadcrumb
   (Home > Courses > category) is that page's only way back up and stays, tokenised by the
   section-1 palette mapping. LearnPress also sizes every h1 inside that header at
   ".lp-archive-courses .learn-press-courses-header h1{margin:0;font-size:calc(… * 1.8)}" (0,2,1),
   which beats the plain .nop-title-strip__title (0,1,0) and TIES an html[data-site] restatement —
   and learnpress.min.css loads after ours, so a tie goes to LearnPress. Measured 28.8px on
   /courses/ where Studio's Work hub computes 70.4px. Restate the strip title at (0,3,1). */
html[data-site] body.post-type-archive-lp_course .lp-archive-courses .learn-press-courses-header > h1, html[data-site] body.post-type-archive-lp_course .lp-archive-courses > .learn-press-breadcrumb { display: none; }
html[data-site] .nop-title-strip .nop-title-strip__title { font: 700 clamp(2.4rem, 5vw, 4.4rem)/.95 var(--nop-font-display); margin: 0; color: var(--nop-fg); }
/* Kadence's own inline customizer CSS hardcodes ".content-bg, body.content-style-unboxed
   .site { background: var(--global-palette9) }" — a fixed white, not theme-reactive (same bug
   pattern as the .site fix above). content-style-unboxed already strips this for every other
   .content-bg surface via ":not(.loop-entry)", but explicitly EXCLUDES .loop-entry cards, so
   native "Similar Posts" / News-archive / category-archive post cards kept a hardcoded white
   card background while their h3.entry-title renders --nop-fg (cream on white — invisible in
   dark mode, low-contrast in light). Verified on the live DOM of a News post, /blog/ and
   /category/film/. .single-content, .entry-content-wrap and .entry-related-inner-content were
   already transparent on all three (merging with the page) and need no override; only
   .loop-entry's own card surface needs a token. */
html[data-site] .content-bg { background: var(--nop-bg); }
html[data-site] .loop-entry, html[data-site] .entry.loop-entry { background: var(--nop-bg-deep); border-color: var(--nop-rule); }
html[data-site] .loop-entry .entry-title, html[data-site] .loop-entry .entry-title a { color: var(--nop-fg); }
html[data-site] .loop-entry .entry-title a:hover { color: var(--nop-accent); }
html[data-site] .loop-entry .entry-meta, html[data-site] .loop-entry .posted-on { color: var(--nop-fg-dim); }
/* Kadence's native "Related Posts" widget (.entry-related, wide layout) also hardcodes a fixed
   light surface background on the section itself (not just .loop-entry), so its own
   .entry-related-title heading -- which correctly picks up our reactive --nop-fg -- goes
   invisible in dark mode (light text on a background that never leaves light mode). Token it
   the same way as .loop-entry, which already matches this exact fixed value in light mode. */
html[data-site] .entry-related { background: var(--nop-bg-deep); }
html[data-site] body.single-post .entry-content { max-width: var(--nop-content); margin-inline: auto; }
/* ---------- 5. MOTION ---------- */
@keyframes nop-marquee { to { transform: translateX(-100%); } }
@keyframes nop-fade-up { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.nop-hero .nop-eyebrow, .nop-hero__headline, .nop-hero__deck, .nop-hero__actions, .nop-hero__plate { animation: nop-fade-up 1s var(--nop-ease-out) both; }
.nop-hero__headline { animation-delay: .15s; } .nop-hero__deck { animation-delay: .3s; } .nop-hero__actions { animation-delay: .45s; } .nop-hero__plate { animation-delay: .35s; }
html.is-theming, html.is-theming * { transition: background-color .4s ease, color .4s ease, border-color .4s ease, fill .4s ease, stroke .4s ease, opacity .4s ease; }
@media (prefers-reduced-motion: reduce) {
  .nop-hero .nop-eyebrow, .nop-hero__headline, .nop-hero__deck, .nop-hero__actions, .nop-hero__plate, .nop-credits--marquee .nop-credits__track { animation: none; }
  html.is-theming, html.is-theming * { transition: none; }
  /* the hover/press transitions declared in sections 2 and 3 */
  .nop-card, .nop-toggle, .nop-btn, html[data-site] .primary-menu-container > ul > li > a::after, html[data-site] .header-button, html[data-site] .wp-block-button__link, html[data-site] .wp-element-button { transition: none; }
}
