/* ============================================================
   OberBuilt Analysis Engine — Design Tokens
   ------------------------------------------------------------
   Single source of truth for the OberBuilt design system as
   consumed by the WordPress plugin. Enqueued globally on every
   plugin surface. All --oae-* tokens MUST resolve here; no
   per-surface fragment may override these values.

   This build is DARK-ONLY — an instrument-cluster aesthetic
   modeled on a modern OEM gauge cluster. There is no light
   theme. Cyan is the primary because it pops on the deep
   steel-ink background the way a multimeter readout does on
   matte plastic. Racing red and caution yellow do their
   signaling job; everything else gets out of their way.
   ============================================================ */

:root {
  /* ---------- Color ---------- */
  --oae-color-bg:       #0B1620;  /* ink — page background */
  --oae-color-surface:  #142231;  /* raised steel — cards, fields, drop zones */
  --oae-color-primary:  #00B8D4;  /* cyan-500 — CTAs, active step, focus */
  --oae-color-ok:       #2BBF7A;  /* green — OK only, never CD */
  --oae-color-warn:     #F2C300;  /* yellow-500 — caution, low confidence */
  --oae-color-danger:   #FF572D;  /* hazard scarlet — destructive, FAIL, errors. See the
                                     "Danger" note below: this value is AA as TEXT on every
                                     dark surface in the system, which #E1352B was not. */
  --oae-color-neutral:  #8092A4;  /* steel — CANNOT_DETERMINE only */
  --oae-color-ok-limited: #8EA4F0; /* periwinkle — OK_LIMITED_COVERAGE ("Looks good, limited
                                     coverage", engine 0.45.0) only. Deliberately neither the
                                     OK green nor the attention amber: the tier is never a full
                                     all-clear. 7.58:1 under --oae-fg-on-ok-limited, 6.69:1 as an
                                     edge on --oae-color-surface, 6.06:1 on surface-alt. */

  /* Foreground text — tuned for AA on bg + surface. */
  --oae-fg-1:           #EAF1F8;  /* primary body text */
  --oae-fg-2:           #B8C5D2;  /* secondary text */
  --oae-fg-3:           #8291A0;  /* muted / labels — lifted from #7E8C9B, which measured
                                     4.25:1 on --oae-color-surface-alt and 4.41:1 on
                                     --oae-color-neutral-bg and failed AA on both. This
                                     value is the minimum same-hue lift that clears 4.5:1
                                     on EVERY background it is painted on (worst case
                                     4.52:1 on surface-alt). */
  --oae-fg-on-primary:  #062029;  /* dark text on cyan for AA */
  --oae-fg-on-danger:   #2B0806;  /* dark text on scarlet for AA — 5.84:1. Was #FFFFFF,
                                     which measured 4.43:1 on the old danger red and
                                     3.16:1 on the current one. Danger was the only status
                                     fill in the system still using light ink; it now
                                     matches --oae-fg-on-primary / -warn / -ok / -neutral,
                                     all of which are dark ink on a saturated fill. */
  --oae-fg-on-warn:     #2C1C00;  /* dark text on yellow for AA */
  --oae-border:         #243648;  /* DECORATIVE ONLY — card edges, table hairlines,
                                     dividers, panel seams. 1.30:1 on --oae-color-surface
                                     and 1.48:1 on --oae-color-bg: correct for a divider
                                     (WCAG puts no floor on purely decorative lines) and
                                     WRONG for anything a user has to find and operate.
                                     Never use this to draw the boundary of a control. */
  --oae-border-strong:  #3A506B;  /* DECORATIVE ONLY — emphasised divider / raised seam.
                                     2.21:1 on bg, 1.95:1 on surface. Also under the floor,
                                     so "just use the strong one" is not the fix either. */

  /* Control boundaries — WCAG 2.1 AA SC 1.4.11 Non-text Contrast (3:1).
     ------------------------------------------------------------------
     QA wave 3, 2026-08-26 (FINDINGS.md F-080). Sixteen distinct dark-world
     controls — selects, inputs, textareas, help toggles, chart chips, the
     vehicle mode cards — drew their ONLY visual boundary in --oae-border or
     --oae-border-strong, at 1.30:1 to 2.21:1. Every one of them has a fill
     identical to the surface behind it, so the border is the sole thing
     saying "this is a control". That is the exact failure SC 1.4.11 names,
     and it is legal exposure under the Accessible Canada Act / AODA, not a
     visual nit. Same defect class as F-079 in the light account world.

     Fixed with a DEDICATED token rather than by raising --oae-border,
     because --oae-border also draws ~140 decorative hairlines across 22
     stylesheets. Raising it to a 3:1 value would turn every card edge and
     table rule into a prominent line and destroy the instrument-cluster
     read — the quiet chrome is the design, and it is not the defect.

     Measured against every ground these controls actually sit on:

       value      #0B1620 bg   #142231 surface   #0F1C29 topbar
       #657D96      4.29:1        3.78:1            4.04:1
       #8AA0B8      6.78:1        5.99:1            6.40:1

     #657D96 clears the 3:1 floor with real margin on all three rather than
     scraping it, so a future surface tint cannot quietly drop a control back
     under. It stays a muted blue-steel — the way an OEM cluster draws a field
     boundary — so the aesthetic survives the fix.

     These are remapped to the light palette on /my-account/* by
     assets/css/oae-wc-account.css; see the bridge block there. Any NEW
     control-boundary rule must use these, never --oae-border. */
  --oae-border-control:        #657D96;  /* resting boundary of an operable control */
  --oae-border-control-strong: #8AA0B8;  /* hover / active / selected boundary */

  /* Tinted backgrounds for status banners (paired w/ status color) */
  --oae-color-warn-bg:    #2E2614;
  --oae-color-danger-bg:  #311816;
  --oae-color-ok-bg:      #112A20;
  --oae-color-neutral-bg: #1A2735;

  /* ---------- Typography — families ---------- */
  --oae-font-body:    "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --oae-font-heading: "Saira", "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --oae-font-mono:    "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* ---------- Typography — sizes (exactly 4, descending) ---------- */
  --oae-text-display: 32px;  /* verdict badge headline, wizard step title */
  --oae-text-heading: 20px;  /* section headings, card titles */
  --oae-text-body:    16px;  /* body, form fields, buttons */
  --oae-text-label:   13px;  /* labels, status pills, micro copy */

  /* ---------- Spacing — all multiples of 4 ---------- */
  --oae-spacing-xs:  4px;
  --oae-spacing-sm:  8px;
  --oae-spacing-md:  16px;
  --oae-spacing-lg:  24px;
  --oae-spacing-xl:  32px;
  --oae-spacing-2xl: 48px;
  --oae-spacing-3xl: 64px;

  /* ---------- Component tokens ---------- */
  --oae-radius:           6px;
  --oae-shadow-card:      0 1px 0 rgba(0, 0, 0, 0.4),
                          0 1px 2px rgba(0, 0, 0, 0.5);
  --oae-touch-target:     44px;
  --oae-progress-height:  6px;
  --oae-chart-height-min: 220px;

  /* ---- PHASE 6.1 ADDITIONS — App-Shell tokens ---- */

  /* Typography — line heights
     Flagged as a gap during 6.1 UI-SPEC verification — no body
     line-height token previously existed. Top bar items use the
     tight variant so the bar height stays compact; content uses
     body. Mono retains tight by convention. */
  --oae-line-height-body:   1.5;
  --oae-line-height-tight:  1.2;

  /* App-shell top bar chrome — one notch elevated above --oae-color-surface.
     The bar must visually separate from page content so it doesn't bleed in. */
  --oae-color-topbar-bg:      #0F1C29;

  /* Top bar shadow — sticky chrome elevation without competing with --oae-shadow-card. */
  --oae-shadow-topbar:        0 1px 0 rgba(0,0,0,0.55), 0 6px 16px rgba(0,0,0,0.35);

  /* Account avatar — primary-adjacent tint without spending the primary budget. */
  --oae-color-avatar-bg:      #163848;
  --oae-color-avatar-fg:      #7CE2F0;  /* cyan-200ish, AA on avatar-bg */

  /* Bell unread badge — reuses primary so notifications match focus rings. */
  --oae-color-badge-unread:   var(--oae-color-primary);
  --oae-color-badge-text:     var(--oae-fg-on-primary);

  /* Mobile drawer scrim — semi-opaque ink. */
  --oae-color-drawer-backdrop: rgba(7, 14, 21, 0.72);

  /* Structural sizing — top bar height and WP admin bar coexistence offset. */
  --oae-topbar-height:        56px;
  --oae-wp-adminbar-height:   32px;   /* desktop / ≥783px */
  --oae-wp-adminbar-height-mobile: 46px; /* mobile / ≤782px (WP hard-coded breakpoint) */

  /* Motion — drawer + dropdown timing. Single source for reduced-motion overrides. */
  --oae-motion-duration:      180ms;
  --oae-motion-ease:          cubic-bezier(0.2, 0.7, 0.3, 1);

  /* ---- PHASE 6.4 ADDITIONS — Flat Vehicle Editor tokens ---- */
  --oae-editor-max-width:     760px;
  --oae-color-actionbar-bg:   #0F1C29;
  --oae-shadow-actionbar:     0 8px 24px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.40);

  /* ============================================================
     TOKENIZATION PASS ADDITIONS
     ------------------------------------------------------------
     Every token below was added because two or more stylesheets
     were already REFERENCING it by name via var(--name, fallback)
     while it did not exist here. Because it did not exist, every
     one of those references silently resolved to its inline
     fallback — and those fallbacks had drifted into a second,
     unrelated palette (a #4f9fff blue primary, a #18222e surface,
     a #e8eef6 body text). That is why History/Reanalysis/Share
     did not look like the rest of the product: they were never
     actually on this design system, they only looked like they
     were. Defining the names here pulls those surfaces back onto
     the real palette. Do not re-add inline fallbacks: tokens.css
     is a hard dependency of every surface stylesheet
     (Assets.php registers 'oae-tokens' in each deps array), so a
     fallback can only ever mask a bug.
     ============================================================ */

  /* ---------- Radius scale ----------
     --oae-radius (6px) remains the default. These two extremes
     existed only as inline fallbacks before. Nothing may sit
     between sm and the 6px default — that middle step
     (--oae-radius-md, referenced by three files) is deliberately
     NOT defined; those references snap to --oae-radius instead so
     the scale stays three-wide. */
  --oae-radius-sm:   4px;    /* inline controls: chips, table-cell inputs, icon buttons */
  --oae-radius-lg:   12px;   /* modal / dialog shells only */
  --oae-radius-pill: 999px;  /* status badges — the pill shape IS part of the status affordance */

  /* Alternate surface — one step above --oae-color-surface. Used for
     table-row hover, in-flight rows, and icon-button hover, i.e.
     transient emphasis that must not read as a new card. Contrast
     vs --oae-color-surface is 1.10:1 (intentionally subtle);
     --oae-fg-1 on it is 12.81:1, --oae-fg-2 is 8.31:1.
     This is the LIGHTEST background in the dark palette, which makes
     it the binding constraint for every foreground token: --oae-fg-3
     (4.52:1) and --oae-color-danger (4.62:1) were both chosen so they
     clear AA *here*, because a row-hover must not push its own text
     below AA. Nothing may be added to this palette that is lighter
     than #1B2A3A without re-deriving fg-3 and danger against it. */
  --oae-color-surface-alt: #1B2A3A;

  /* Primary hover. Previously each surface improvised: some used
     color-mix(in oklab, primary 88%, black), one hardcoded a blue
     #3d8ae8 from the ghost palette. One token, one hover colour.
     7.07:1 against --oae-fg-on-primary. */
  --oae-color-primary-hover: #00A2BA;

  /* Paired AA-safe foregrounds for the two status fills that had none.
     Both reuse the deep-ink value already proven on cyan so solid
     status chips share one ink colour.
       --oae-fg-on-ok      on --oae-color-ok      = 7.09:1
       --oae-fg-on-neutral on --oae-color-neutral = 5.71:1 */
  --oae-fg-on-ok:      #062029;
  --oae-fg-on-neutral: #0B1620;
  --oae-fg-on-ok-limited: #0B1620; /* on --oae-color-ok-limited = 7.58:1 */

  /* ---------- Danger: ONE red, and why it is this one ----------
     The palette previously carried three reds — #E1352B (borders,
     icons, verdict-tier FAIL), #F87171 (error copy) and #D12B22
     (solid fills) — because no single value could satisfy the two
     roles while --oae-fg-on-danger was white. That constraint is
     provable, not a matter of taste:

       text AA on --oae-color-surface-alt needs relative luminance
       L >= 0.2738;  white text AA on a fill needs L <= 0.1833.

     The two windows do not overlap, so with WHITE ink the split was
     forced. Flipping --oae-fg-on-danger to dark ink (see above)
     dissolves it: one light, saturated red now clears AA as text on
     every dark surface AND carries dark ink as a fill, exactly the
     way ok / warn / neutral / primary already work. So danger is one
     token again, and -text / -fill are kept only as aliases so the
     ~10 files already referencing them keep resolving.

     Measured, #FF572D:
        as TEXT on --oae-color-bg          = 5.79:1  AA
        as TEXT on --oae-color-surface     = 5.11:1  AA
        as TEXT on --oae-color-surface-alt = 4.62:1  AA  <- binding
        as TEXT on --oae-color-danger-bg   = 5.23:1  AA
        as TEXT on --oae-color-neutral-bg  = 4.80:1  AA
        under --oae-fg-on-danger (#2B0806) = 5.84:1  AA
     (as a border/icon it clears WCAG 1.4.11's 3:1 everywhere by 1.5x+)

     WHY THIS HUE, and not a paler red. Any red light enough to pass
     AA on this background is also light enough to collapse toward
     --oae-color-ok under deuteranopia — and "a FAIL that reads as an
     OK" is the single worst thing this product can render. That risk
     was measured, not assumed. Simulating each candidate and taking
     the worst-case RGB separation from OK across protanopia /
     deuteranopia / tritanopia:

        #E1352B (old, fails AA)           FAIL-vs-OK  100
        #FF572D (chosen)                  FAIL-vs-OK   92
        #FF5252                           FAIL-vs-OK   50
        #F87171 (old -text value)         FAIL-vs-OK   19   <- unusable

     #F87171 is the important line there: the previous fix moved all
     error COPY onto a red that a deuteranope cannot tell from the OK
     green. #FF572D keeps 92% of the old red's colour-blind margin
     while actually passing AA. It sits at hue 12 deg (scarlet, between
     tomato and orangered) because pulling toward pure red costs
     FAIL-vs-OK separation and pulling past ~20 deg starts reading as
     --oae-color-warn's orange instead of danger.

     Colour is still never the sole channel: FAIL pills carry the word
     FAIL, findings carry data-status, error copy carries a leading
     warning glyph. Do not remove those on the strength of this hue. */
  --oae-color-danger-text: var(--oae-color-danger);  /* alias, kept for back-compat */
  --oae-color-danger-fill: var(--oae-color-danger);  /* alias, kept for back-compat */

  /* Notification-count numeral. A COMPONENT token, deliberately not a fifth
     entry in the type scale — the digits inside a 22px badge circle are sized
     to the circle, the way --oae-progress-height sizes a bar. The four-size
     scale stays four sizes. --oae-text-label (13px) overflows two digits at
     this diameter, which is why the value cannot simply snap to the scale. */
  --oae-badge-numeral-size: 11px;

  /* Narrow-viewport step-down for --oae-text-display. Same class of token as
     --oae-badge-numeral-size above: a COMPONENT value, deliberately not a
     fifth entry in the type scale. The verdict badge headline renders strings
     as long as "CANNOT DETERMINE" in 700-weight Saira; at 32px that wraps to
     three lines inside a 320px-wide badge and stops reading as a headline at
     all. The next size DOWN the scale is --oae-text-heading (20px), which is
     the size of the section headings below it — using it here would flatten
     the verdict, the one thing on the page that must not be flattened. So the
     step-down is real and it is intentional; naming it is what stops it being
     a magic number. It was the only off-scale px font-size left in the
     front-end stylesheets. Used by oae-verdict.css inside its <=640px block. */
  --oae-text-display-compact: 26px;

  /* ---------- Primary alpha tints ----------
     The transparent washes of --oae-color-primary used for row hover,
     active/selected fills and focus glows. These were spelled out as
     literal rgba() in nine places across oae-bell.css and
     oae-chart-viewer.css, which meant the primary hue existed twice in
     the codebase: once as a token, and once as the number 0,184,212
     copied by hand. Retuning the primary would have moved one and not
     the other.

     They are rgba() literals rather than color-mix(), deliberately, and
     that constraint is real: iOS Safari below 16.2 has no color-mix().
     That constraint applies to the FUNCTION, though, not to custom
     properties — var() has been safe since iOS 12 — so naming them
     costs nothing and removes the duplication. Do not "modernise"
     these into color-mix() without checking the iOS floor first.

     Named by ROLE, not by alpha, so a component picks the one that
     matches what it is doing rather than the number it happens to want. */
  --oae-color-primary-wash:   rgba(0, 184, 212, 0.06);  /* list-row hover */
  --oae-color-primary-tint:   rgba(0, 184, 212, 0.08);  /* hover fill, menu item */
  --oae-color-primary-select: rgba(0, 184, 212, 0.12);  /* active / selected */
  --oae-color-primary-ring:   rgba(0, 184, 212, 0.2);   /* slider focus glow */
  --oae-color-primary-scrim:  rgba(0, 184, 212, 0.7);   /* chart brush overlay */

  /* ---------- Remaining alpha values, named ----------
     The last four hand-written rgba() literals in the surface
     stylesheets. Same reasoning as the primary tints above: an
     alpha value that is not named is a colour the palette does
     not know it has. Kept as rgba() rather than color-mix() for
     the iOS < 16.2 floor. */
  --oae-shadow-dropdown:      0 12px 32px rgba(0, 0, 0, 0.45);  /* floating menus, above --oae-shadow-card */
  --oae-color-surface-scrim:  rgba(20, 34, 49, 0.6);            /* --oae-color-surface @ 60%, chart overlays */
  /* Two washes for use ON --oae-color-warn, where neither --oae-fg-* nor a
     surface token is correct: the banner is a saturated yellow and needs a
     darkening, not a tint. Derived from --oae-fg-on-warn's ink, not from
     neutral black, so they stay in the warn family. */
  --oae-wash-on-warn-weak:    rgba(0, 0, 0, 0.06);              /* hover on a warn fill */
  --oae-wash-on-warn:         rgba(0, 0, 0, 0.08);              /* divider on a warn fill */

  /* ============================================================
     ICON TOKENS — the second channel, and why they are tokens
     ------------------------------------------------------------
     Status in this product must never be carried by colour alone.
     Colour is one channel; these are the second. They are stored
     as inline-SVG data URIs so they can be applied with `mask`,
     which makes them inherit `currentColor` — one icon definition
     works on a tinted banner, a solid pill, and a dark surface
     without a per-context recolour, and it cannot drift out of
     sync with the status colour beside it.

     SVG, deliberately, not emoji and not a font glyph. The FAQ
     accordion previously used the browser's own
     `list-style-type: disclosure-closed` triangle — a UA glyph
     that renders at a font-dependent size and is a different
     shape in every engine. Nothing in the system should depend
     on what a UA decides a marker looks like.

     THE SHAPES ARE LOAD-BEARING, not decoration. Each status gets
     a distinct SILHOUETTE, so the set survives greyscale, a
     screenshot, and every form of colour blindness:

       ok            solid ring + check
       warn          triangle + bang
       danger        octagon + cross   (stop-sign outline)
       cannot-determine  DASHED ring + question mark
       not-run       solid ring + single bar

     `cannot-determine` is the one that matters most. It is NOT a
     greyed-out check and NOT a softened warning — either would
     make "we could not tell" read as a weak pass or a mild
     problem, and a false all-clear is the worst output this
     product can produce. The dashed ring says "incomplete
     reading" as a shape, independent of the neutral colour it is
     painted in. Do not swap these mappings per surface: the
     status -> (colour, icon, word) triple is a fixed lookup, so
     that recognition transfers between screens.
     ============================================================ */
  --oae-icon-chevron-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  --oae-icon-ok: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m8.5 12.5 2.5 2.5 4.5-5'/%3E%3C/svg%3E");
  --oae-icon-warn: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E");
  --oae-icon-danger: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2h8l6 6v8l-6 6H8l-6-6V8Z'/%3E%3Cpath d='m15 9-6 6'/%3E%3Cpath d='m9 9 6 6'/%3E%3C/svg%3E");
  --oae-icon-cannot-determine: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9' stroke-dasharray='3.2 3.2'/%3E%3Cpath d='M9.6 9.6a2.5 2.5 0 1 1 3.4 2.4c-.6.3-1 .9-1 1.6v.4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E");
  --oae-icon-not-run: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8 12h8'/%3E%3C/svg%3E");

  /* ---------- Z-index scale (X1-4) ----------
     Added because stacking order across this system was ad hoc — no
     --oae-z-* token existed anywhere despite several surfaces needing one
     (modals, toasts, sticky chrome, dropdowns). FE-15 (audit 2026-10-13)
     retrofitted the page-level layers onto it: top bar = sticky, account
     menu + bell panel = dropdown, drawer + scrim = overlay, bell sheet +
     history delete modal + skip link = modal. Small local stacking
     (z-index 1-10 inside a component) stays numeric. */
  --oae-z-base:     0;
  --oae-z-dropdown: 100;
  --oae-z-sticky:   200;
  --oae-z-overlay:  300;
  --oae-z-modal:    400;
  --oae-z-toast:    500;
}

/* ============================================================
   Defensive theme overrides
   ------------------------------------------------------------
   The OberBuilt design system is dark-only — all text inside an
   OAE surface (any element with a class starting with "oae-")
   must use the dark-palette foreground tokens. Themes like Divi
   apply their own heading colors via selectors such as
   `.entry-content h2` (specificity 0,1,1), which beat the
   partials' own `.oae-foo__title` declarations (0,1,0). The
   compound selectors below (0,2,1) defeat that without needing
   !important and without modifying every partial individually.
   Partials may still override with a more specific selector
   when they intentionally want a non-fg-1 color (e.g. eyebrows
   that use fg-3).
   ============================================================ */
[class^="oae-"] h1,
[class^="oae-"] h2,
[class^="oae-"] h3,
[class^="oae-"] h4,
[class^="oae-"] h5,
[class^="oae-"] h6,
[class*=" oae-"] h1,
[class*=" oae-"] h2,
[class*=" oae-"] h3,
[class*=" oae-"] h4,
[class*=" oae-"] h5,
[class*=" oae-"] h6 {
  color: var(--oae-fg-1);
}

/* ------------------------------------------------------------
   Divi defense, extended past headings.
   ------------------------------------------------------------
   The heading block above was written for `.entry-content h2`.
   Divi restyles three more element classes inside `.entry-content`
   with the same (0,1,1) specificity, and each one has already or
   could plausibly break a plugin surface:

     .entry-content a            → link colour + underline
     .entry-content button       → renders our buttons as plain
                                   unstyled text (documented; this
                                   already cost one debug session)
     .entry-content input/select/textarea → light-theme field chrome
                                   on our dark surfaces

   Same compound-selector technique, still no !important.
   These only assert the properties Divi actually overrides —
   colour, and for buttons the appearance/background/border reset
   that makes a <button> render as a button at all. Everything
   else (padding, radius, sizing) stays with the per-surface rules
   so they can still win without fighting.
   ------------------------------------------------------------ */

/* ------------------------------------------------------------
   DEFAULT LINK COLOUR — a FLOOR, and it must stay one. (D-04)
   ------------------------------------------------------------
   This rule used to read

     [class^="oae-"] a:not([class*="button"]):not([class*="btn"])

   which the comment above claimed was (0,2,1). It was not: `:not()`
   takes the specificity of its ARGUMENT, so each of those two
   `:not([class*=...])` contributed a full class. Real specificity
   was (0,3,1) — higher than every per-surface anchor rule in the
   product. The result was that any <a> painted with a SOLID
   --oae-color-primary background got cyan TEXT on that cyan fill:

     .oae-marketing__cta--primary  (0,1,0)  landing + pricing hero CTA
     .oae-share-cta                (0,2,0)  the public share page's only CTA
     .oae-history__empty-cta       (0,2,0)  history empty state
     .oae-shell__skip-link         (0,1,0)  the accessibility skip link

   All four measured 1.00:1 contrast in Chrome — literally invisible.
   `.oae-topbar__nav-link` / `.oae-legal__toc a` / `.oae-history__pagination a`
   lost their fg-1/fg-2 colour channel the same way.

   THE FIX IS SPECIFICITY, NOT !important. What this rule actually
   has to beat was measured against the live site rather than
   assumed: the ONLY non-plugin rule competing for link colour
   inside an OAE surface is the theme's bare `a { color: … }` at
   (0,0,1). Nothing needs (0,3,1); nothing even needs a class.

   So the scope selector goes inside `:where()` — which contributes
   ZERO specificity by definition and exists for exactly this job —
   and the leading `html` supplies one element unit. That lands this
   rule at (0,0,2):

     beats  a                              (0,0,1)  theme link colour
     loses  .oae-marketing__cta--primary   (0,1,0)  and EVERY other
                                                    class-bearing rule

   (0,0,2) is the smallest value that clears the theme and is
   strictly below the (0,1,0) floor of any component rule, so there
   is no tie to be broken by stylesheet order — a component can
   never lose to this rule again, and no component has to inflate
   its own selector to win. The button/btn exclusions are kept for
   intent but moved inside `:where()` so they cost nothing.

   If you ever need to raise this, raise it to a value that is still
   below (0,1,0), or the invisible-CTA bug comes straight back.
   ------------------------------------------------------------ */
html :where([class^="oae-"], [class*=" oae-"])
  a:where(:not([class*="button"]):not([class*="btn"])) {
  color: var(--oae-color-primary);
}

[class^="oae-"] button,
[class*=" oae-"] button {
  /* The Divi gotcha in one place: without an explicit appearance
     reset a plugin <button> inherits theme chrome (or loses all of
     it and renders as bare text). Per-surface rules supply the real
     background/border; this guarantees a clean slate to paint on. */
  appearance: none;
  -webkit-appearance: none;
  font-family: inherit;
}

[class^="oae-"] input,
[class^="oae-"] select,
[class^="oae-"] textarea,
[class*=" oae-"] input,
[class*=" oae-"] select,
[class*=" oae-"] textarea {
  color: var(--oae-fg-1);
}

/* ------------------------------------------------------------
   Focus-ring floor.
   ------------------------------------------------------------
   Every interactive element inside an OAE surface gets a visible
   focus ring in the primary colour, per the stated intent of
   --oae-color-primary ("CTAs, active step, focus"). This is a
   FLOOR, not a ceiling: any per-surface rule must override it, but
   it means a new control can never ship with no focus indicator at
   all. Keyed on :focus-visible so mouse users never see it.

   It was NOT a floor. The old selector

     [class^="oae-"] :is(a, button, input, select, textarea, [tabindex]):focus-visible

   is (0,3,0), not the (0,1,1) the comment claimed — `[class^=…]`
   is a class unit, `:focus-visible` is a class unit, and `:is()`
   takes the MAX specificity of its arguments, which here is
   `[tabindex]`, another class unit. Every per-surface focus rule in
   the product is `.some-class:focus-visible` = (0,2,0), so they all
   lost, e.g.

     .oae-shell__skip-link:focus-visible { outline-color: --oae-color-bg }
        → overridden to cyan, i.e. a cyan ring on the cyan skip-link fill
     .oae-topbar__nav-link:focus-visible { outline-offset: -2px }
        → overridden to +2px, so the inset nav ring rendered outset

   Rebuilt as a real floor at (0,1,2): `html` gives one element
   unit, `:where()` zeroes the scope, `:is()` now maxes at an
   element because `[tabindex]` is wrapped in its own `:where()`
   (still matched, contributes nothing), and `:focus-visible`
   supplies the single class unit. (0,1,2) < (0,2,0), so ANY
   component `:focus-visible` rule now wins outright — no tie, no
   dependence on stylesheet order.
   ------------------------------------------------------------ */
html :where([class^="oae-"], [class*=" oae-"])
  :is(a, button, input, select, textarea, :where([tabindex])):focus-visible {
  /* LONGHANDS, not `outline: 2px solid var(--oae-color-primary)`.
     A shorthand containing var() cannot be parsed at parse time, so it is
     stored whole as a "pending-substitution value" and resolved late. Its
     longhand components are not independently readable until then, which
     makes the declaration harder to reason about, impossible to inspect via
     CSSOM (`rule.style.outlineColor` reads back empty), and dependent on
     correct late substitution for the COLOUR specifically — width and style
     are literals and survive regardless.

     That matters here because a focus ring that silently loses only its
     colour still looks like a focus ring, so the failure is invisible in
     review: you get a ring in the UA's default accent instead of the
     product's cyan, and nothing errors. Measuring this on the live
     dashboard produced exactly that shape — width 2px and offset 2px as
     specified, with outline-color reading as the OS accent blue rather
     than --oae-color-primary — while an otherwise identical rule written
     with an explicit colour rendered cyan. Splitting into longhands removes
     the pending-substitution path entirely, costs nothing, and makes each
     component independently overridable by a component rule.

     (The observation could not be reproduced on demand — driving real
     keyboard focus is unreliable in a background tab — so this is written
     as hardening, not as a confirmed root-cause fix. It is correct either
     way; do not fold it back into a shorthand.) */
  outline-width: 2px;
  outline-style: solid;
  outline-color: var(--oae-color-primary);
  outline-offset: 2px;
}

/* ------------------------------------------------------------
   [hidden] floor. The third time this bug has been fixed.
   ------------------------------------------------------------
   `hidden` is a semantic promise: the element is not relevant yet.
   Browsers keep that promise with a USER-AGENT rule
   (`[hidden] { display: none }`), and a user-agent rule loses to
   any author rule that sets `display`, at any specificity. So the
   promise holds only until some stylesheet — ours or the theme's —
   sets a display value that happens to match.

   It has not held. Twice already:
     oae-template-editor.css:154  preview overlay
     commit 5b5185b               both sensor-declare surfaces
   and again here, found live on the verdict page:

     section.oae-verdict__owner[hidden]
       display: block, visibility: visible, width 912px

   It measured 0px tall only because it happened to be empty, and
   it still consumed two 48px flex gaps in `.oae-verdict` — 96px of
   dead space in the middle of the page. Had it been populated it
   would have rendered in full, and that section is `data-oae-block
   ="owner"` upload metadata, i.e. the kind of content whose whole
   reason for being hidden is that it is not always the viewer's to
   see. A layout bug and a disclosure risk from the same root.

   Fixing it per-occurrence has now failed three times, so this is a
   floor instead. Scoped to OAE surfaces so it cannot affect theme
   or WP-admin markup. Verified live to need NO !important: with the
   attribute selector present the computed value flips block -> none,
   which means nothing in the cascade actually wants these elements
   visible — the UA rule was simply being displaced. Deliberately
   kept at (0,2,0) so a component that genuinely needs to override
   `hidden` still can, with a class + attribute of its own.
   ------------------------------------------------------------ */
[class^="oae-"][hidden],
[class*=" oae-"][hidden] {
  display: none;
}

/* ------------------------------------------------------------
   Motion floor.
   ------------------------------------------------------------
   A single reduced-motion backstop for the whole plugin. Individual
   stylesheets still declare their own prefers-reduced-motion blocks
   where they need finer control (e.g. keeping a border-colour change
   while dropping a transform), but this guarantees that a surface
   which forgets to declare one cannot animate against the user's
   stated OS preference. Not `none` — 0.01ms keeps transitionend /
   animationend handlers firing, which several surfaces rely on to
   advance state.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  [class^="oae-"],
  [class^="oae-"] *,
  [class*=" oae-"],
  [class*=" oae-"] * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
