/* ===================================================
   SEO Platform — Component Overrides
   Adapter layer for third-party stacks (DaisyUI, HTMX, Alpine)
   and project shell utilities (TailAdmin sidebar, theme bootstrap).

   Contract:
   - design-system.css  → proprietary tokens + .ds-* classes
   - component-overrides.css (this file) → everything else that has to
     live globally: DaisyUI shape overrides, HTMX runtime classes,
     Alpine [x-cloak], pre-Alpine theme icon visibility, sidebar layout,
     #_tip global tooltip element.

   Migrated from inline <style> in base.html (see seo-3mq).
   Loaded in <head> AFTER design-system.css so .ds-* tokens resolve.
   =================================================== */

/* ─── Alpine bootstrap ─── */
[x-cloak] { display: none !important; }

/* ─── Pre-Alpine theme icon visibility ───
   Default = light (corporate): sun hidden, moon shown.
   data-theme is set synchronously by IIFE before Alpine, so this is
   correct on first paint. */
.theme-icon-sun { display: none; }
[data-theme="business"] .theme-icon-sun { display: block; }
[data-theme="business"] .theme-icon-moon { display: none; }

/* ─── Theme transition guard ───
   Suppress all transitions during theme swap to avoid color flash. */
.theme-switching,
.theme-switching *,
.theme-switching *::before,
.theme-switching *::after {
    transition-duration: 0s !important;
}

/* ─── HTMX indicator ─── */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-block; }
.htmx-request.htmx-indicator { display: inline-block; }

/* ─── Sidebar navigation (TailAdmin) ───
   Colors derived from DaisyUI base-content / primary.
   Light/dark collapse into single rules; --bc / --p carry the
   per-theme contrast. */
.ta-menu-section {
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 0 0.75rem;
    margin-bottom: 0.375rem;
    /* seo-2pu3: was /0.5 (3.32 light / 4.13 dark — sub-AA). Hierarchy is carried by
       uppercase + weight 600 + letter-spacing + 11px, NOT by low contrast. */
    color: var(--text-muted-aa);
}

.ta-menu-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    /* 0.5rem (rounded-lg) — unified shell-control radius (seo-h4wn). All other
       shell focusables (theme_toggle, sidebar_buttons, user-menu trigger +
       items, brand logo, super-mode badge) use rounded-lg. The original 6px
       was a TailAdmin-port leftover, not a documented hierarchy intent. */
    border-radius: 0.5rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: oklch(var(--bc) / 0.7);
    transition: background 0.15s ease, color 0.15s ease;
    text-decoration: none;
}
.ta-menu-item:hover {
    background: oklch(var(--bc) / 0.08);
    color: oklch(var(--bc));
}
.ta-menu-item.active {
    background: oklch(var(--p) / 0.08);
    color: oklch(var(--p));
    font-weight: 600;
}
[data-theme="corporate"] .ta-menu-item.active {
    /* Darken primary on light surface to keep WCAG AA contrast (≥4.5:1).
       --p (64% 0.12 264) against primary/0.08 over white is too light as text;
       lowering L to 0.49 (seo-oeub) restores AA. Counterpart of the dark-theme
       lift to oklch(0.73 0.12 264). Chroma calmed 0.186→0.12 by seo-qzm; L+hue
       unchanged so the AA rationale holds (reverified L1 machine). */
    color: oklch(0.49 0.12 264);
}
[data-theme="business"] .ta-menu-item.active {
    /* Lighten primary on dark surface to keep WCAG AA contrast (≥4.5:1).
       Derived from --p (64% 0.12 264) by raising lightness to ~73%. */
    background: oklch(var(--p) / 0.15);
    color: oklch(73% 0.12 264);
}
.ta-menu-item svg { flex-shrink: 0; }

/* ─── Sidebar geometry ─── */
.sidebar-desktop { width: 260px; overflow: hidden; }
/* Collapsed state — managed by class on <html>, applied synchronously
   via IIFE before Alpine to avoid a layout jump. */
html.sidebar-is-collapsed .sidebar-desktop { width: 72px; }
html.sidebar-is-collapsed .sidebar-desktop .ta-menu-text,
html.sidebar-is-collapsed .sidebar-desktop .ta-menu-section,
html.sidebar-is-collapsed .sidebar-desktop .ta-logo-text,
html.sidebar-is-collapsed .sidebar-desktop .ta-version { display: none; }
html.sidebar-is-collapsed .sidebar-desktop .ta-menu-item {
    justify-content: center;
    padding: 0.5rem;
}
/* Animation only after Alpine init (toggled via x-init), so first paint
   doesn't visibly transition from default to saved width. */
.sidebar-animated { transition: width 0.2s ease; }

.sidebar-badge {
    font-size: 0.6rem;
    background: oklch(var(--bc) / 0.07);
    color: oklch(var(--bc) / 0.35);
    padding: 0.1rem 0.4rem;
    border-radius: 0.25rem;
    font-weight: 500;
}

/* ─── Modernize component overrides — card ───
   Light: Modernize float; dark: none (card surface == body surface). */
[data-theme="corporate"] .card {
    box-shadow: rgba(145,158,171,0.2) 0px 0px 2px 0px, rgba(145,158,171,0.12) 0px 12px 24px -4px;
}
[data-theme="business"] .card {
    box-shadow: none;
}

/* ─── seo-mp3n: business .ds-mc-card edge definition ───
   After seo-ojuq surface tokens unification, --surface-card === --surface-body
   in business (both navy oklch(var(--b1))). Card boundary невидим без отдельной
   visual cue. Shadow alone (см. --shadow-dark-md в design-system.css) даёт
   subtle drop, но edge всё ещё ambiguous. 1px subtle navy border (using existing
   --surface-popover-border token, не новый) даёт clear card outline без
   breaking surface symmetry. Scoped к business — corporate cards already have
   strong drop shadow from Modernize palette. */
[data-theme="business"] .ds-mc-card {
    border: 1px solid var(--surface-popover-border);
}

/* ─── Modernize component overrides — table ───
   Reuse the popover-border token for both themes. */
[data-theme="corporate"] .table tr,
[data-theme="business"]  .table tr { border-bottom-color: var(--surface-popover-border); }

/* ─── Cell link group hover ───
   Highlight cell text when hovering the named-group parent. */
.group\/proj:hover .proj-text,
.group\/user:hover .user-text { color: oklch(var(--p)); transition: color 0.15s; }

/* ─── Modernize component shape overrides (D19) ─── */
.btn { border-radius: 0.375rem; font-weight: 600; }
.input, .select, .textarea { border-radius: 0.375rem; }
.modal-box { border-radius: 1rem; }

/* seo-s0aw: the at-rest border of an outlined form control on the b1 card surface.
   daisyui paints `.input-bordered`/`.select-bordered`/`.textarea-bordered` at
   `oklch(var(--bc) / .2)` — live canvas-composited 1.52:1 on the white card, 1.75:1 on
   the business one, below the 3:1 non-text floor (WCAG 1.4.11) in BOTH themes. In dark
   the control's own fill is byte-identical to the card behind it (rgb(28,37,54)), so
   that hairline is the ONLY thing marking the field: the bead's «поле почти невидимо».
   Same compliance-floor policy and the same measured alphas as `.ds-ctrl-border`
   (design-system.css, seo-6zb) — this is that boundary, on the atom instead of a class,
   so no callsite changes: corporate /0.47 = 3.06:1, business /0.40 = 3.16:1. Do not
   raise without a design decision; do not lower below 3:1.

   Specificity contract — BOTH halves stay at (0,1,0), the same as daisyui's own
   `.input-bordered`; they win over it only because this file loads after
   daisyui.min.css. That is why the dark half is wrapped in `:where()`, and the reason
   is the POPOVER rule, not focus: `.ds-surface-popover :is(.input, .textarea, .select)`
   is (0,2,0) but lives in design-system.css, which loads EARLIER — so it can only win
   on specificity. A bare `[data-theme="business"]` prefix here would be (0,2,0), tie
   with it, and win on file order, dropping every dark popover control from its
   surface-measured 0.42 to 0.40 = 2.98:1 on --b2 (mutation-verified: that single edit
   reddens tests/browser/shared_ui/test_popover_input_border_contrast.py[dark]).
   Focus is safe either way: `.input:focus` & co. are (0,2,0) AND sit below this block
   in this same file, so they outrank the light half on specificity and a hypothetical
   (0,2,0) dark half on order — the browser pin asserts the --p border in both themes.
   daisyui `.input:disabled` is (0,2,0) — a pseudo-class counts in the CLASS column, not
   the element one — so an inactive control keeps its --b2 border by specificity alone,
   with no dependence on file order (1.4.11 exempts it; same ruling as
   `.ds-ctrl-border:disabled`). `.ds-ctrl-border` is
   (0,1,0) too and loads earlier, but no element carries both (that class marks outlined
   BUTTONS and the page-size select wrapper), and the corporate alphas are identical.

   Supersedes the page-scoped patch of this same defect on `#kw-filters`
   (design-system.css, seo-jrp1) — removed with this change: its light /0.40 measured
   2.51:1 live, i.e. it was BELOW this floor and would now have lowered the border
   back under 3:1 on /tasks/{id}. Pins: tests/test_design_system_form_control_border.py
   + tests/browser/shared_ui/test_form_control_border_contrast.py. */
.input-bordered,
.select-bordered,
.textarea-bordered {
    border-color: oklch(var(--bc) / 0.47);
}
:where([data-theme="business"]) .input-bordered,
:where([data-theme="business"]) .select-bordered,
:where([data-theme="business"]) .textarea-bordered {
    border-color: oklch(var(--bc) / 0.4);
}

/* Checkbox: Modernize style — DaisyUI-semantic border unchecked,
   primary fill checked. Unchecked border /0.5 (was /0.2) — WCAG 1.4.11
   interactive boundary ≥3:1 on both canonical surfaces (seo-74f: /0.2
   measured 1.52:1 on white / 1.75:1 on business b1; /0.5 = 3.35:1 /
   4.13:1, single alpha clears both themes). Checked border/fill are
   pinned below (seo-60gh), not left to the DaisyUI --p rule. */
.checkbox {
    border-radius: 0.25rem;
    border-width: 1.5px;
    border-color: oklch(var(--bc) / 0.5);
}
/* seo-60gh (ensemble): the white ✓ belongs to the PRIMARY fill only. Declared on
   `.checkbox` it also painted the ✓ of a plain `.checkbox`, whose daisyui fill is
   --bc — near-white in dark — so the tick vanished (1.27:1 on /tasks/new freq-type
   boxes). Plain checkboxes are not a project idiom: every checkbox is
   checkbox-primary (task_new / admin defaults were the 7 strays, fixed in the same
   change); this scoping keeps a future stray from going white-on-white. */
.checkbox-primary {
    --chkfg: white;
}
.checkbox:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: 2px;
    box-shadow: none;
}
/* seo-60gh: the checked fill of .checkbox-primary came from the raw --p (L 0.64 →
   rgb(103,138,213)), LIGHTER than the btn-primary fill the overrides pin to 0.52 —
   a white ✓ on it measured 3.40:1 in both themes (non-text floor 3:1 passed, AA
   4.5 not), and the ✓ is the only state channel of the control. Pin the fill to the
   same 0.52 L as the primary button (white on it = 5.58:1 measured, seo-qzm) — one primary
   ink for «pressed/selected» across buttons and checkboxes, both themes. */
.checkbox-primary:checked,
.checkbox-primary[aria-checked="true"] {
    /* daisyui paints the checked box with background-color from --p DIRECTLY (not
       via --chkbg, which only draws the ✓ cut-outs) — both must move together. */
    --chkbg: oklch(0.52 0.12 264);
    background-color: oklch(0.52 0.12 264);
    border-color: oklch(0.52 0.12 264);
}
/* dark: the 0.52 fill alone measured 2.57:1 against the task form's card surface
   (≈2.75:1 nominal on bare --b1) — the 1.4.11 boundary floor
   (3:1) that seo-74f set for the UNCHECKED border would regress on the checked box.
   Same answer as btn-primary in business: a lighter 0.60 border draws the edge
   (3.85:1 on --b1 / 3.33:1 on --b2); since seo-9z0m the business fill is 0.56 like the
   button's (3.27:1 on --b1, ✓ 4.70:1) — light keeps 0.52 / 5.58:1. */
[data-theme="business"] .checkbox-primary:checked,
[data-theme="business"] .checkbox-primary[aria-checked="true"] {
    /* seo-9z0m: one primary ink — the business fill follows the button to 0.56
       (3.27:1 on --b1, the white ✓ 4.70:1); the 0.60 edge stays. */
    --chkbg: oklch(0.56 0.12 264);
    background-color: oklch(0.56 0.12 264);
    border-color: oklch(0.60 0.12 264);
}

/* Form-controls focus — Modernize pattern (seo-bxfo).
   DaisyUI v5 default sets outline 2px oklch(--bc / 0.2) + offset 2px on
   .input:focus and .input:focus-within — the offset gap reads as a
   "thick pale halo" while the outline itself is near-invisible. Modernize
   Free uses border-color recolor with no ring (forms.html:
   focus:border-blue-600 focus:ring-0).

   :focus + :focus-visible cover real <input>.input — receives focus via
   both mouse and keyboard. :focus-within covers the <div class="input">
   wrapper case (search bar in data_table.html — wrapper holds chips +
   a child <input>, fires :focus-within from the child without ever
   getting :focus itself, so without this the DaisyUI halo stays as a
   visible "double border" outside the 1px wrapper border).

   seo-s0aw: the ring is BACK, but FLUSH — `outline-offset: 0`, the
   `.ds-focus-ring-flush` idiom already in this file. seo-bxfo's finding stands and is
   what offset 0 preserves: daisyui's ring was near-invisible (bc/0.2) AND held two
   pixels off the edge, so it read as a detached pale halo. What changed is the REST
   state. While rest was daisyui's bc/0.2 hairline, recolouring that one pixel to --p was
   a big step; now that seo-s0aw lifted rest to the 1.4.11 floor (0.47 / 0.40), the
   recolour moves the SAME pixel from 3.06:1 to 3.41:1 in light and 3.16:1 to 4.51:1 in
   dark — only 1.11:1 and 1.43:1 BETWEEN the two states, i.e. a hue swap at nearly equal
   luminance, identical for mouse and keyboard, with hover a no-op. WCAG 2.4.13 counts
   the pixels the indicator ADDS: 2px of --p laid on the bare card surface = 3.41:1 light
   / 4.51:1 dark. Offset 0 keeps the ring touching the (also --p) border, so the two read
   as ONE thick primary edge with no grey gap — never two concentric rings.
   `box-shadow: none` still kills daisyui's own focus shadow.
   Consequence for the DS vocabulary: form controls now DO grow an outline, so the
   «border-recolor, never an outline» wording of seo-bxfo is retired. The
   NON_OUTLINE_ALLOWLIST entries in test_focus_audit.py and scripts/visual_machine_layer.py
   stay LOAD-BEARING, just for a new reason: the shared audit requires
   `outline-offset ∈ {2px, -2px}` and these controls are the one family drawing at
   offset 0, so without the allowlist every field on every page would read as a
   violation. Whether to teach the audit about offset 0 instead is seo-5dyu.
   A callsite must NOT pin its own outline utility on such a control: `focus:outline-none`
   (Tailwind = a 2px TRANSPARENT outline at offset 2, and the Play CDN injects it after
   this file) silently replaced this ring with nothing on the kw-add textarea, and
   `.ds-focus-ring` (offset 2, later in this file) drew a SECOND concentric ring around
   four admin selects — both removed with this change. One owner per state.
   Pins: test_form_focus_modernize.py (shape) + test_form_control_border_contrast.py
   (measured, both themes) + tests/test_design_system_form_control_border.py (source). */
.input:focus,
.input:focus-visible,
.input:focus-within,
.select:focus,
.select:focus-visible,
.select:focus-within,
.textarea:focus,
.textarea:focus-visible,
.textarea:focus-within {
    outline: 2px solid oklch(var(--p));
    outline-offset: 0;
    border-color: oklch(var(--p));
    box-shadow: none;
}

/* Focus ring flush with element edge — outline-offset:0 (no gap, no overshoot).
   Use for buttons embedded inside a bordered container (e.g. clear-search ×
   inside .input.input-bordered) so the ring stays WITHIN the parent border. */
.ds-focus-ring-flush:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: 0;
    border-radius: var(--radius-control);
}

/* Shell focus indicators — keyboard-only (seo-2ph8 follow-up to seo-bxfo).
   Sidebar nav links + opt-in .ds-focus-ring class for shell icon-buttons,
   logo, and standalone interactive elements without DaisyUI focus styles.
   :focus-visible only (not :focus) — mouse clicks shouldn't draw the ring,
   matches Modernize/WCAG 2.4.7 standard. */
.ta-menu-item:focus-visible,
.ds-focus-ring:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: 2px;
}

/* Inset variant — dropdown rows where neighbours are touching. Positive
   offset visually leaks into adjacent row, negative pulls the ring inside
   the row geometry. */
.ds-focus-ring-inset:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: -2px;
}

/* DaisyUI .btn focus — unify with the rest of the form/shell focus contract
   (primary outline). DaisyUI v5 default uses `outline: 2px solid currentColor`
   which for .btn-outline без semantic reads as a thick base-content ring
   doubling the existing border, and for .btn-error/.btn-warning splits the
   keyboard affordance into per-variant colours. Semantic signal is already
   carried by fill/text — focus indicator stays primary for parity with
   .input/.select/.textarea, .ds-tr-cell-link, .ds-icon-btn. (seo-3n0o) */
.btn:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: 2px;
}

/* ─── seo-2pu3: WCAG AA on button / control LABELS ───
   The brand --p (L0.64) yields white-on-fill only ~3.45:1 and --p-as-text ~3.1–3.45:1
   on dark surfaces — both below AA for the label. Fixed at the COMPONENT level, NOT on
   the --p/--er/--wa tokens, so links / active states / borders keep the brand hue+L.
   Filled buttons darken the FILL in LIGHT (specificity: bare `.btn-primary` = 1 class,
   loads after daisyui → wins; `.btn-outline.btn-primary` = 2 classes → its transparent
   fill is untouched); in BUSINESS the primary fill is 0.56 since seo-9z0m (see that
   block below — the dark surface bounds the fill from both sides). Primary-as-TEXT controls (outline buttons, active pagination) lift the text
   in dark, mirroring the existing .ds-tab-active / .ta-menu-item.active dark lift.
   Values verified ≥4.5:1 via visual_machine_layer.py both themes.
   seo-oeub (the follow-up polish): deepened the primary — filled button fill 0.57→0.52
   (hover 0.52→0.47; white label AA 4.63→5.69) and the LIGHT primary-as-text canon
   0.53→0.49 (deeper, less violet on white, more AA margin). The DARK canon stays 0.73/0.75:
   dark primary-text sits on primary-tinted surfaces already at the AA floor (L0.73 on the
   double-tint tab-counter = 4.34, L0.68 = 3.68 FAIL), so it CANNOT darken. Hence the
   per-theme asymmetry: light deepens, dark holds. */
/* Filled buttons — darken the FILL. :not(.btn-outline) so outline variants (which
   daisyui paints transparent via the 1-class `.btn-outline`) are NOT filled by this
   same-specificity-but-later rule. */
/* :not(.ds-cta-disabled) — the disabled-CTA muted style (--bc/0.06 fill, design-system.css)
   is 1-class; without this exclusion the 2-class rule below would repaint a disabled CTA with
   the full primary fill (+ its muted label → sub-AA). Disabled controls keep their muted look
   and are WCAG-exempt (detector skips [disabled]/[aria-disabled]).
   seo-qzm: all accent FILLS + primary-as-text below calmed chroma 0.186/0.163/0.158→0.12
   (L+hue preserved → AA ratios essentially unchanged, reverified L1 both themes).
   Error fill (btn-error) kept loud — alarm semantics. */
.btn-primary:not(.btn-outline):not(.ds-cta-disabled) { background-color: oklch(0.52 0.12 264); border-color: oklch(0.52 0.12 264); }
.btn-primary:not(.btn-outline):not(.ds-cta-disabled):hover { background-color: oklch(0.47 0.12 264); border-color: oklch(0.47 0.12 264); }
.btn-error:not(.btn-outline):not(.ds-cta-disabled) { background-color: oklch(0.577 0.223 27.3); border-color: oklch(0.577 0.223 27.3); }
.btn-error:not(.btn-outline):not(.ds-cta-disabled):hover { background-color: oklch(0.527 0.223 27.3); border-color: oklch(0.527 0.223 27.3); }
.btn-warning:not(.btn-outline):not(.ds-cta-disabled) { background-color: oklch(0.60 0.12 70); border-color: oklch(0.60 0.12 70); }
/* warning hover LIGHTENS (not darkens): the label is BLACK, so a darker amber would
   drop below AA (Codex R2: 0.55 → 4.21:1). Brighter amber keeps black ≥4.5 + reads as hover. */
.btn-warning:not(.btn-outline):not(.ds-cta-disabled):hover { background-color: oklch(0.66 0.12 70); border-color: oklch(0.66 0.12 70); }

/* seo-i792 — filled primary/error have no visible BOUNDARY on the dark surfaces.
   A filled .btn carries no ring or shadow, so its fill IS its whole boundary, and the fill
   is what has to clear the 3:1 non-text floor of WCAG 1.4.11 against the surface behind it.
   Measured (canvas pixel readback, not token math — computing from the oklch tokens gives
   4.51:1 and is WRONG):
       primary        2.75:1 on --b1 · 2.38:1 on --b2   (0.52 fill; 3.27 / 2.82 since seo-9z0m lifted it to 0.56)
       primary:hover  2.21:1 · 1.91:1   ← hover DARKENED the fill, so it was the worst case
                                          (business hover no longer darkens — seo-9z0m below)
       error          3.16:1 · 2.73:1
       error:hover    2.63:1 · 2.28:1
   Nothing mechanical sees this: the contrast axes score the white LABEL on the button
   (5.58:1, passes) and the chroma-harmony check whitelists .btn-primary outright.
   Fix is border-colour only — .btn already carries a 1px border and box-sizing: border-box,
   so geometry does not move, the fill and the label stay exactly as they were, and the
   accent does not get louder (seo-qzm's calmed chroma is preserved: same C and H, +L on the
   1px edge alone).
   Scope is the whole class, checked rather than assumed: warning clears it unaided
   (3.80:1 / 3.29:1 dark, 4.04:1 light) and light theme's worst case is warning:hover at
   3.17:1 — both left untouched. Disabled variants are WCAG-exempt and keep their muted look. */
/* seo-9z0m: the edge alone was the whole presence of the primary CTA in business — the
   0.52 fill sat at 2.75:1 on the card, under the 3:1 non-text floor, blue on dark blue,
   and hover DARKENED it further (2.21:1). The fill itself is lifted to 0.56/0.12/264:
   3.27:1 on --b1, 2.82:1 on --b2 (the 0.60 edge still draws 3.33:1 there), white label
   4.70:1. Canvas readback, business surfaces: at chroma 0.12 the fill can only live
   ABOVE 0.54 and BELOW 0.58 — at 0.54 the body sits on the 3:1 floor (2.995), at 0.58
   the white label drops under AA 4.5 (4.34; 0.60 = 3.99) — so the hover cannot move the
   fill either way: the dark hover keeps the 0.56 fill and brightens the EDGE to 0.73
   (6.35:1 on --b1, 5.49:1 on --b2, 1.94:1 against the fill), doubled to a 2px ring by an
   inset shadow of the same tone — a 1px ring at 1.94:1 against its own fill was the whole
   hover cue and the dark lens of the seo-9z0m ensemble read it as marginal — the same
   «hover = louder, never towards the surface» rule the row-action family follows
   (seo-np5b). Light theme untouched (5.58:1 fill on white, hover darkens to 0.47).
   The outline variant's hover/active FILL (`.btn-outline.btn-primary`, 0.47 in both
   themes, further below) is the same class in business — 2.21:1 on --b1 — and gets the
   same 0.56 fill + 0.73 ring there. */
[data-theme="business"] .btn-primary:not(.btn-outline):not(.ds-cta-disabled) {
    background-color: oklch(0.56 0.12 264);
    border-color: oklch(0.60 0.12 264);   /* 3.85:1 on --b1 · 3.33:1 on --b2 */
}
[data-theme="business"] .btn-primary:not(.btn-outline):not(.ds-cta-disabled):hover,
[data-theme="business"] .btn-outline.btn-primary:not(.ds-cta-disabled):hover,
[data-theme="business"] .btn-outline.btn-primary:not(.ds-cta-disabled):active {
    background-color: oklch(0.56 0.12 264);
    border-color: oklch(0.73 0.12 264);   /* 6.35:1 on --b1 · 5.49:1 on --b2 · 1.94:1 vs the fill */
    box-shadow: inset 0 0 0 1px oklch(0.73 0.12 264);   /* + the border = a 2px ring */
}
[data-theme="business"] .btn-error:not(.btn-outline):not(.ds-cta-disabled),
[data-theme="business"] .btn-error:not(.btn-outline):not(.ds-cta-disabled):hover {
    border-color: oklch(0.62 0.223 27.3); /* 3.79:1 on --b1 · 3.28:1 on --b2 */
}

/* Outline/text primary controls = primary AS TEXT on a surface → lift per-theme
   (parity with .ta-menu-item.active: darken on light, lighten on dark). */
[data-theme="corporate"] .btn-outline.btn-primary { color: oklch(0.49 0.12 264); border-color: oklch(0.49 0.12 264); }
[data-theme="business"]  .btn-outline.btn-primary { color: oklch(0.73 0.12 264); border-color: oklch(0.73 0.12 264); }
/* On :hover/:active the outline-primary fills in (daisyui outline→filled affordance) with the
   UN-darkened --p, but our at-rest `color` (0.49/0.73, 2-class) beats daisyui's --pc → dark
   text on light-primary fill ≈1.2–1.8, sub-AA. Invisible to the at-rest `contrast` axis;
   caught by `interactive-contrast` (seo-vpyg). Fix: drive hover/active to the SAME darkened
   fill + white as a filled .btn-primary:hover (measured 5.69 both themes) instead of letting
   the light --p through. :not(.ds-cta-disabled) keeps a disabled outline-CTA muted. */
.btn-outline.btn-primary:not(.ds-cta-disabled):hover,
.btn-outline.btn-primary:not(.ds-cta-disabled):active {
  background-color: oklch(0.47 0.12 264);
  border-color: oklch(0.47 0.12 264);
  color: #fff;
}
[data-theme="corporate"] .ds-page-active { color: oklch(0.49 0.12 264); }
[data-theme="business"]  .ds-page-active { color: oklch(0.73 0.12 264); }

/* ─── seo-bma: WCAG AA on daisyUI alert fills ───
   daisyui `.alert` paints `background-color:var(--alert-bg)`; `.alert-error` /
   `.alert-warning` set `--alert-bg` to `oklch(var(--er)/1)` / `oklch(var(--wa)/1)`
   (no color-mix). White-on-error (--erc) measured 3.79:1 and dark-on-warning
   (--wac) 4.3:1 corporate — both sub-AA. Darken the error fill / lighten the
   corporate warning fill to the matching filled-button levels (.btn-error :273 /
   .btn-warning :275) → white-on-error 4.85:1, dark-on-warning ≥4.5:1. The semantic
   --er/--wa tokens stay untouched, so btn/badge/ds-stat-tile/ds-row-action/
   ds-filter-chip keep the brand hue+L. business alert-warning (9.5:1) and
   alert-success (6.8:1) already pass → left at daisyui default. */
.alert-error { background-color: oklch(0.577 0.223 27.3); }
[data-theme="corporate"] .alert-warning { background-color: oklch(0.60 0.12 70); }

/* UI-Kit TOC nav links (seo-oeub) — opaque per-theme primary, SAME canon as
   .ds-page-active / .ds-tab-active. Replaces a kit-only text-primary/80 whose 80%
   alpha made the rendered link lightness background-dependent (flipped between
   themes) — a style with no match on the real site. Opaque → stable + AA both
   themes (light 0.49 ≈ 6.5:1 on white, dark 0.73 ≈ 5.2:1 on navy). */
[data-theme="corporate"] .ds-toc-nav a { color: oklch(0.49 0.12 264); }
[data-theme="business"]  .ds-toc-nav a { color: oklch(0.73 0.12 264); }

/* Shell breadcrumb anchors — container lives in layouts/{admin,dashboard}.html.
   Canonical pages render breadcrumb as <span> (not focusable), anchors only
   appear on out-of-scope pages, but the container itself is chain-in-scope.
   border-radius matches .btn / .input shape token. (seo-3n0o) */
.ds-breadcrumb-bar a:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: 2px;
    border-radius: 0.375rem;
}

/* ─── Global tooltip element (#_tip) ───
   Triggered by data-tip / data-tip-collapsed / title fallback (tooltip.js).
   Surface and text colors come from .ds-tooltip-surface (design-system.css).
   Light: dark surface against light page = strong contrast.
   Dark: surface elevated to slate-700 + brighter border + deeper shadow,
   because slate-800 fill blends into the sidebar and the dark drop-shadow
   disappears on a dark page. */
#_tip {
    position: fixed;
    z-index: 9999;
    pointer-events: none;
    padding: 6px 12px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
    font-family: 'IBM Plex Sans', sans-serif;
    transform: translate(-50%, -100%);
    box-shadow: 0 8px 24px rgba(0,0,0,.35), 0 2px 6px rgba(0,0,0,.2);
    /* seo-rtke: backdrop-filter: blur(6px) removed — both --tooltip-surface
       tokens are OPAQUE rgb() fills, so the blur had zero visible effect while
       still promoting a compositor layer (a known source of stale-position
       flashes when a reused fixed element is teleported). */
}
