/* @toc Reset */
/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */

*,
::before,
::after {
    box-sizing: border-box;
}

html {
    font-family:
        system-ui,
        'Segoe UI',
        Roboto,
        Helvetica,
        Arial,
        sans-serif,
        'Apple Color Emoji',
        'Segoe UI Emoji';
    line-height: 1.15;
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
}

body {
    margin: 0;
}

b,
strong {
    font-weight: bolder;
}

code,
kbd,
samp,
pre {
    font-family:
        ui-monospace,
        SFMono-Regular,
        Consolas,
        'Liberation Mono',
        Menlo,
        monospace;
    font-size: 1em;
}

small {
    font-size: 80%;
}

sub,
sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
}

sub {
    bottom: -0.25em;
}

sup {
    top: -0.5em;
}

table {
    border-color: currentcolor;
}

button,
input,
optgroup,
select,
textarea {
    font-family: inherit;
    font-size: 100%;
    line-height: 1.15;
    margin: 0;
}

button,
[type='button'],
[type='reset'],
[type='submit'] {
    -webkit-appearance: button;
}

legend {
    padding: 0;
}

progress {
    vertical-align: baseline;
}

summary {
    display: list-item;
}

/* @toc Fonts */
/* @font-face for Geist + Geist Mono. Hand-written for now; TypographyService
 * will generate these by scanning the fonts folder. Files in public/fonts/. */

@font-face {
    font-family: 'Geist';
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    src: url('/fonts/geist/Geist-Regular.ttf') format('truetype');
}

@font-face {
    font-family: 'Geist';
    font-weight: 700;
    font-style: normal;
    font-display: swap;
    src: url('/fonts/geist/Geist-Bold.ttf') format('truetype');
}

@font-face {
    font-family: 'Geist';
    font-weight: 900;
    font-style: normal;
    font-display: swap;
    src: url('/fonts/geist/Geist-Black.ttf') format('truetype');
}

@font-face {
    font-family: 'Geist Mono';
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    src: url('/fonts/geist-mono/GeistMono-Regular.ttf') format('truetype');
}

@font-face {
    font-family: 'Geist Mono';
    font-weight: 700;
    font-style: normal;
    font-display: swap;
    src: url('/fonts/geist-mono/GeistMono-Bold.ttf') format('truetype');
}

/* @toc Palettes */
:root {
    --palette-color-names: red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose;
    --palette-neutral-names: slate gray zinc neutral stone;
}
:root {
    --palette-amber-50: #fffbeb;
    --palette-amber-100: #fef3c7;
    --palette-amber-200: #fde68a;
    --palette-amber-300: #fcd34d;
    --palette-amber-400: #fbbf24;
    --palette-amber-500: #f59e0b;
    --palette-amber-600: #d97706;
    --palette-amber-700: #b45309;
    --palette-amber-800: #92400e;
    --palette-amber-900: #78350f;
    --palette-amber-950: #451a03;
}
:root {
    --palette-blue-50: #eff6ff;
    --palette-blue-100: #dbeafe;
    --palette-blue-200: #bfdbfe;
    --palette-blue-300: #93c5fd;
    --palette-blue-400: #60a5fa;
    --palette-blue-500: #3b82f6;
    --palette-blue-600: #2563eb;
    --palette-blue-700: #1d4ed8;
    --palette-blue-800: #1e40af;
    --palette-blue-900: #1e3a8a;
    --palette-blue-950: #172554;
}
:root {
    --palette-cyan-50: #ecfeff;
    --palette-cyan-100: #cffafe;
    --palette-cyan-200: #a5f3fc;
    --palette-cyan-300: #67e8f9;
    --palette-cyan-400: #22d3ee;
    --palette-cyan-500: #06b6d4;
    --palette-cyan-600: #0891b2;
    --palette-cyan-700: #0e7490;
    --palette-cyan-800: #155e75;
    --palette-cyan-900: #164e63;
    --palette-cyan-950: #083344;
}
:root {
    --palette-emerald-50: #ecfdf5;
    --palette-emerald-100: #d1fae5;
    --palette-emerald-200: #a7f3d0;
    --palette-emerald-300: #6ee7b7;
    --palette-emerald-400: #34d399;
    --palette-emerald-500: #10b981;
    --palette-emerald-600: #059669;
    --palette-emerald-700: #047857;
    --palette-emerald-800: #065f46;
    --palette-emerald-900: #064e3b;
    --palette-emerald-950: #022c22;
}
:root {
    --palette-fuchsia-50: #fdf4ff;
    --palette-fuchsia-100: #fae8ff;
    --palette-fuchsia-200: #f5d0fe;
    --palette-fuchsia-300: #f0abfc;
    --palette-fuchsia-400: #e879f9;
    --palette-fuchsia-500: #d946ef;
    --palette-fuchsia-600: #c026d3;
    --palette-fuchsia-700: #a21caf;
    --palette-fuchsia-800: #86198f;
    --palette-fuchsia-900: #701a75;
    --palette-fuchsia-950: #4a044e;
}
:root {
    --palette-gray-50: #f9fafb;
    --palette-gray-100: #f3f4f6;
    --palette-gray-200: #e5e7eb;
    --palette-gray-300: #d1d5db;
    --palette-gray-400: #9ca3af;
    --palette-gray-500: #6b7280;
    --palette-gray-600: #4b5563;
    --palette-gray-700: #374151;
    --palette-gray-800: #1f2937;
    --palette-gray-900: #111827;
    --palette-gray-950: #030712;
}
:root {
    --palette-green-50: #f0fdf4;
    --palette-green-100: #dcfce7;
    --palette-green-200: #bbf7d0;
    --palette-green-300: #86efac;
    --palette-green-400: #4ade80;
    --palette-green-500: #22c55e;
    --palette-green-600: #16a34a;
    --palette-green-700: #15803d;
    --palette-green-800: #166534;
    --palette-green-900: #14532d;
    --palette-green-950: #052e16;
}
:root {
    --palette-indigo-50: #eef2ff;
    --palette-indigo-100: #e0e7ff;
    --palette-indigo-200: #c7d2fe;
    --palette-indigo-300: #a5b4fc;
    --palette-indigo-400: #818cf8;
    --palette-indigo-500: #6366f1;
    --palette-indigo-600: #4f46e5;
    --palette-indigo-700: #4338ca;
    --palette-indigo-800: #3730a3;
    --palette-indigo-900: #312e81;
    --palette-indigo-950: #1e1b4b;
}
:root {
    --palette-lime-50: #f7fee7;
    --palette-lime-100: #ecfccb;
    --palette-lime-200: #d9f99d;
    --palette-lime-300: #bef264;
    --palette-lime-400: #a3e635;
    --palette-lime-500: #84cc16;
    --palette-lime-600: #65a30d;
    --palette-lime-700: #4d7c0f;
    --palette-lime-800: #3f6212;
    --palette-lime-900: #365314;
    --palette-lime-950: #1a2e05;
}
:root {
    --palette-neutral-50: #fafafa;
    --palette-neutral-100: #f5f5f5;
    --palette-neutral-200: #e5e5e5;
    --palette-neutral-300: #d4d4d4;
    --palette-neutral-400: #a3a3a3;
    --palette-neutral-500: #737373;
    --palette-neutral-600: #525252;
    --palette-neutral-700: #404040;
    --palette-neutral-800: #262626;
    --palette-neutral-900: #171717;
    --palette-neutral-950: #0a0a0a;
}
:root {
    --palette-orange-50: #fff7ed;
    --palette-orange-100: #ffedd5;
    --palette-orange-200: #fed7aa;
    --palette-orange-300: #fdba74;
    --palette-orange-400: #fb923c;
    --palette-orange-500: #f97316;
    --palette-orange-600: #ea580c;
    --palette-orange-700: #c2410c;
    --palette-orange-800: #9a3412;
    --palette-orange-900: #7c2d12;
    --palette-orange-950: #431407;
}
:root {
    --palette-pink-50: #fdf2f8;
    --palette-pink-100: #fce7f3;
    --palette-pink-200: #fbcfe8;
    --palette-pink-300: #f9a8d4;
    --palette-pink-400: #f472b6;
    --palette-pink-500: #ec4899;
    --palette-pink-600: #db2777;
    --palette-pink-700: #be185d;
    --palette-pink-800: #9d174d;
    --palette-pink-900: #831843;
    --palette-pink-950: #500724;
}
:root {
    --palette-purple-50: #faf5ff;
    --palette-purple-100: #f3e8ff;
    --palette-purple-200: #e9d5ff;
    --palette-purple-300: #d8b4fe;
    --palette-purple-400: #c084fc;
    --palette-purple-500: #a855f7;
    --palette-purple-600: #9333ea;
    --palette-purple-700: #7e22ce;
    --palette-purple-800: #6b21a8;
    --palette-purple-900: #581c87;
    --palette-purple-950: #3b0764;
}
:root {
    --palette-red-50: #fef2f2;
    --palette-red-100: #fee2e2;
    --palette-red-200: #fecaca;
    --palette-red-300: #fca5a5;
    --palette-red-400: #f87171;
    --palette-red-500: #ef4444;
    --palette-red-600: #dc2626;
    --palette-red-700: #b91c1c;
    --palette-red-800: #991b1b;
    --palette-red-900: #7f1d1d;
    --palette-red-950: #450a0a;
}
:root {
    --palette-rose-50: #fff1f2;
    --palette-rose-100: #ffe4e6;
    --palette-rose-200: #fecdd3;
    --palette-rose-300: #fda4af;
    --palette-rose-400: #fb7185;
    --palette-rose-500: #f43f5e;
    --palette-rose-600: #e11d48;
    --palette-rose-700: #be123c;
    --palette-rose-800: #9f1239;
    --palette-rose-900: #881337;
    --palette-rose-950: #4c0519;
}
:root {
    --palette-sky-50: #f0f9ff;
    --palette-sky-100: #e0f2fe;
    --palette-sky-200: #bae6fd;
    --palette-sky-300: #7dd3fc;
    --palette-sky-400: #38bdf8;
    --palette-sky-500: #0ea5e9;
    --palette-sky-600: #0284c7;
    --palette-sky-700: #0369a1;
    --palette-sky-800: #075985;
    --palette-sky-900: #0c4a6e;
    --palette-sky-950: #082f49;
}
:root {
    --palette-slate-50: #f8fafc;
    --palette-slate-100: #f1f5f9;
    --palette-slate-200: #e2e8f0;
    --palette-slate-300: #cbd5e1;
    --palette-slate-400: #94a3b8;
    --palette-slate-500: #64748b;
    --palette-slate-600: #475569;
    --palette-slate-700: #334155;
    --palette-slate-800: #1e293b;
    --palette-slate-900: #0f172a;
    --palette-slate-950: #020617;
}
:root {
    --palette-stone-50: #fafaf9;
    --palette-stone-100: #f5f5f4;
    --palette-stone-200: #e7e5e4;
    --palette-stone-300: #d6d3d1;
    --palette-stone-400: #a8a29e;
    --palette-stone-500: #78716c;
    --palette-stone-600: #57534e;
    --palette-stone-700: #44403c;
    --palette-stone-800: #292524;
    --palette-stone-900: #1c1917;
    --palette-stone-950: #0c0a09;
}
:root {
    --palette-teal-50: #f0fdfa;
    --palette-teal-100: #ccfbf1;
    --palette-teal-200: #99f6e4;
    --palette-teal-300: #5eead4;
    --palette-teal-400: #2dd4bf;
    --palette-teal-500: #14b8a6;
    --palette-teal-600: #0d9488;
    --palette-teal-700: #0f766e;
    --palette-teal-800: #115e59;
    --palette-teal-900: #134e4a;
    --palette-teal-950: #042f2e;
}
:root {
    --palette-violet-50: #f5f3ff;
    --palette-violet-100: #ede9fe;
    --palette-violet-200: #ddd6fe;
    --palette-violet-300: #c4b5fd;
    --palette-violet-400: #a78bfa;
    --palette-violet-500: #8b5cf6;
    --palette-violet-600: #7c3aed;
    --palette-violet-700: #6d28d9;
    --palette-violet-800: #5b21b6;
    --palette-violet-900: #4c1d95;
    --palette-violet-950: #2e1065;
}
:root {
    --palette-yellow-50: #fefce8;
    --palette-yellow-100: #fef9c3;
    --palette-yellow-200: #fef08a;
    --palette-yellow-300: #fde047;
    --palette-yellow-400: #facc15;
    --palette-yellow-500: #eab308;
    --palette-yellow-600: #ca8a04;
    --palette-yellow-700: #a16207;
    --palette-yellow-800: #854d0e;
    --palette-yellow-900: #713f12;
    --palette-yellow-950: #422006;
}
:root {
    --palette-zinc-50: #fafafa;
    --palette-zinc-100: #f4f4f5;
    --palette-zinc-200: #e4e4e7;
    --palette-zinc-300: #d4d4d8;
    --palette-zinc-400: #a1a1aa;
    --palette-zinc-500: #71717a;
    --palette-zinc-600: #52525b;
    --palette-zinc-700: #3f3f46;
    --palette-zinc-800: #27272a;
    --palette-zinc-900: #18181b;
    --palette-zinc-950: #09090b;
}
:root[data-primary="amber"] {
    --primary-50: #fffbeb;
    --primary-100: #fef3c7;
    --primary-200: #fde68a;
    --primary-300: #fcd34d;
    --primary-400: #fbbf24;
    --primary-500: #f59e0b;
    --primary-600: #d97706;
    --primary-700: #b45309;
    --primary-800: #92400e;
    --primary-900: #78350f;
    --primary-950: #451a03;
}
:root[data-primary="blue"] {
    --primary-50: #eff6ff;
    --primary-100: #dbeafe;
    --primary-200: #bfdbfe;
    --primary-300: #93c5fd;
    --primary-400: #60a5fa;
    --primary-500: #3b82f6;
    --primary-600: #2563eb;
    --primary-700: #1d4ed8;
    --primary-800: #1e40af;
    --primary-900: #1e3a8a;
    --primary-950: #172554;
}
:root[data-primary="cyan"] {
    --primary-50: #ecfeff;
    --primary-100: #cffafe;
    --primary-200: #a5f3fc;
    --primary-300: #67e8f9;
    --primary-400: #22d3ee;
    --primary-500: #06b6d4;
    --primary-600: #0891b2;
    --primary-700: #0e7490;
    --primary-800: #155e75;
    --primary-900: #164e63;
    --primary-950: #083344;
}
:root[data-primary="emerald"] {
    --primary-50: #ecfdf5;
    --primary-100: #d1fae5;
    --primary-200: #a7f3d0;
    --primary-300: #6ee7b7;
    --primary-400: #34d399;
    --primary-500: #10b981;
    --primary-600: #059669;
    --primary-700: #047857;
    --primary-800: #065f46;
    --primary-900: #064e3b;
    --primary-950: #022c22;
}
:root[data-primary="fuchsia"] {
    --primary-50: #fdf4ff;
    --primary-100: #fae8ff;
    --primary-200: #f5d0fe;
    --primary-300: #f0abfc;
    --primary-400: #e879f9;
    --primary-500: #d946ef;
    --primary-600: #c026d3;
    --primary-700: #a21caf;
    --primary-800: #86198f;
    --primary-900: #701a75;
    --primary-950: #4a044e;
}
:root[data-neutral="gray"] {
    --neutral-50: #f9fafb;
    --neutral-100: #f3f4f6;
    --neutral-200: #e5e7eb;
    --neutral-300: #d1d5db;
    --neutral-400: #9ca3af;
    --neutral-500: #6b7280;
    --neutral-600: #4b5563;
    --neutral-700: #374151;
    --neutral-800: #1f2937;
    --neutral-900: #111827;
    --neutral-950: #030712;
}
:root[data-primary="green"] {
    --primary-50: #f0fdf4;
    --primary-100: #dcfce7;
    --primary-200: #bbf7d0;
    --primary-300: #86efac;
    --primary-400: #4ade80;
    --primary-500: #22c55e;
    --primary-600: #16a34a;
    --primary-700: #15803d;
    --primary-800: #166534;
    --primary-900: #14532d;
    --primary-950: #052e16;
}
:root[data-primary="indigo"] {
    --primary-50: #eef2ff;
    --primary-100: #e0e7ff;
    --primary-200: #c7d2fe;
    --primary-300: #a5b4fc;
    --primary-400: #818cf8;
    --primary-500: #6366f1;
    --primary-600: #4f46e5;
    --primary-700: #4338ca;
    --primary-800: #3730a3;
    --primary-900: #312e81;
    --primary-950: #1e1b4b;
}
:root[data-primary="lime"] {
    --primary-50: #f7fee7;
    --primary-100: #ecfccb;
    --primary-200: #d9f99d;
    --primary-300: #bef264;
    --primary-400: #a3e635;
    --primary-500: #84cc16;
    --primary-600: #65a30d;
    --primary-700: #4d7c0f;
    --primary-800: #3f6212;
    --primary-900: #365314;
    --primary-950: #1a2e05;
}
:root[data-neutral="neutral"] {
    --neutral-50: #fafafa;
    --neutral-100: #f5f5f5;
    --neutral-200: #e5e5e5;
    --neutral-300: #d4d4d4;
    --neutral-400: #a3a3a3;
    --neutral-500: #737373;
    --neutral-600: #525252;
    --neutral-700: #404040;
    --neutral-800: #262626;
    --neutral-900: #171717;
    --neutral-950: #0a0a0a;
}
:root[data-primary="orange"] {
    --primary-50: #fff7ed;
    --primary-100: #ffedd5;
    --primary-200: #fed7aa;
    --primary-300: #fdba74;
    --primary-400: #fb923c;
    --primary-500: #f97316;
    --primary-600: #ea580c;
    --primary-700: #c2410c;
    --primary-800: #9a3412;
    --primary-900: #7c2d12;
    --primary-950: #431407;
}
:root[data-primary="pink"] {
    --primary-50: #fdf2f8;
    --primary-100: #fce7f3;
    --primary-200: #fbcfe8;
    --primary-300: #f9a8d4;
    --primary-400: #f472b6;
    --primary-500: #ec4899;
    --primary-600: #db2777;
    --primary-700: #be185d;
    --primary-800: #9d174d;
    --primary-900: #831843;
    --primary-950: #500724;
}
:root[data-primary="purple"] {
    --primary-50: #faf5ff;
    --primary-100: #f3e8ff;
    --primary-200: #e9d5ff;
    --primary-300: #d8b4fe;
    --primary-400: #c084fc;
    --primary-500: #a855f7;
    --primary-600: #9333ea;
    --primary-700: #7e22ce;
    --primary-800: #6b21a8;
    --primary-900: #581c87;
    --primary-950: #3b0764;
}
:root[data-primary="red"] {
    --primary-50: #fef2f2;
    --primary-100: #fee2e2;
    --primary-200: #fecaca;
    --primary-300: #fca5a5;
    --primary-400: #f87171;
    --primary-500: #ef4444;
    --primary-600: #dc2626;
    --primary-700: #b91c1c;
    --primary-800: #991b1b;
    --primary-900: #7f1d1d;
    --primary-950: #450a0a;
}
:root[data-primary="rose"] {
    --primary-50: #fff1f2;
    --primary-100: #ffe4e6;
    --primary-200: #fecdd3;
    --primary-300: #fda4af;
    --primary-400: #fb7185;
    --primary-500: #f43f5e;
    --primary-600: #e11d48;
    --primary-700: #be123c;
    --primary-800: #9f1239;
    --primary-900: #881337;
    --primary-950: #4c0519;
}
:root[data-primary="sky"] {
    --primary-50: #f0f9ff;
    --primary-100: #e0f2fe;
    --primary-200: #bae6fd;
    --primary-300: #7dd3fc;
    --primary-400: #38bdf8;
    --primary-500: #0ea5e9;
    --primary-600: #0284c7;
    --primary-700: #0369a1;
    --primary-800: #075985;
    --primary-900: #0c4a6e;
    --primary-950: #082f49;
}
:root[data-neutral="slate"] {
    --neutral-50: #f8fafc;
    --neutral-100: #f1f5f9;
    --neutral-200: #e2e8f0;
    --neutral-300: #cbd5e1;
    --neutral-400: #94a3b8;
    --neutral-500: #64748b;
    --neutral-600: #475569;
    --neutral-700: #334155;
    --neutral-800: #1e293b;
    --neutral-900: #0f172a;
    --neutral-950: #020617;
}
:root[data-neutral="stone"] {
    --neutral-50: #fafaf9;
    --neutral-100: #f5f5f4;
    --neutral-200: #e7e5e4;
    --neutral-300: #d6d3d1;
    --neutral-400: #a8a29e;
    --neutral-500: #78716c;
    --neutral-600: #57534e;
    --neutral-700: #44403c;
    --neutral-800: #292524;
    --neutral-900: #1c1917;
    --neutral-950: #0c0a09;
}
:root[data-primary="teal"] {
    --primary-50: #f0fdfa;
    --primary-100: #ccfbf1;
    --primary-200: #99f6e4;
    --primary-300: #5eead4;
    --primary-400: #2dd4bf;
    --primary-500: #14b8a6;
    --primary-600: #0d9488;
    --primary-700: #0f766e;
    --primary-800: #115e59;
    --primary-900: #134e4a;
    --primary-950: #042f2e;
}
:root[data-primary="violet"] {
    --primary-50: #f5f3ff;
    --primary-100: #ede9fe;
    --primary-200: #ddd6fe;
    --primary-300: #c4b5fd;
    --primary-400: #a78bfa;
    --primary-500: #8b5cf6;
    --primary-600: #7c3aed;
    --primary-700: #6d28d9;
    --primary-800: #5b21b6;
    --primary-900: #4c1d95;
    --primary-950: #2e1065;
}
:root[data-primary="yellow"] {
    --primary-50: #fefce8;
    --primary-100: #fef9c3;
    --primary-200: #fef08a;
    --primary-300: #fde047;
    --primary-400: #facc15;
    --primary-500: #eab308;
    --primary-600: #ca8a04;
    --primary-700: #a16207;
    --primary-800: #854d0e;
    --primary-900: #713f12;
    --primary-950: #422006;
}
:root[data-neutral="zinc"] {
    --neutral-50: #fafafa;
    --neutral-100: #f4f4f5;
    --neutral-200: #e4e4e7;
    --neutral-300: #d4d4d8;
    --neutral-400: #a1a1aa;
    --neutral-500: #71717a;
    --neutral-600: #52525b;
    --neutral-700: #3f3f46;
    --neutral-800: #27272a;
    --neutral-900: #18181b;
    --neutral-950: #09090b;
}
/* @toc Utilities */
/* Utilities — shared CSS variables that don't belong to a palette, to typography,
 * to a component, to a page, or to reset: the structural scales. Theme-neutral
 * primitives, cribbed pared-down from leadthink's utilities. A catch-all by
 * definition, but a justified one — every var here is a reusable primitive a
 * component, global, or the typography config can space / round / size / elevate
 * to. */

:root {
    /* @toc Spacing — 4px base; padding, margin, gap. Tailwind's rungs: the integer ladder is complete 1–12
     * (13/15 don't exist in Tailwind), then 14/16/20/24. This is the ONE structural scale — width/height read
     * it too (no separate ladders), and config facets can name `space-*` bare (CssService::mapValue). */
    --space-0: 0;
    --space-px: 1px;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-7: 28px;
    --space-8: 32px;
    --space-9: 36px;
    --space-10: 40px;
    --space-11: 44px;
    --space-12: 48px;
    --space-14: 56px;
    --space-16: 64px;
    --space-20: 80px;
    --space-24: 96px;

    /* @toc Type scale — rungs the typography config maps elements to. */
    --text-2xs: 0.625rem;
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.25rem;
    --text-2xl: 1.5rem;
    --text-3xl: 1.875rem;
    --text-4xl: 2.25rem;
    --text-5xl: 3rem;

    /* @toc Border radius — rungs global / components round to. */
    --radius-sm: 6px;
    --radius: 8px;
    --radius-md: 10px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    --radius-2xl: 22px;
    --radius-full: 9999px;

    /* @toc Max width — containers / prose. */
    --max-w-sm: 384px;
    --max-w-md: 448px;
    --max-w-lg: 512px;
    --max-w-xl: 576px;
    --max-w-2xl: 672px;
    --max-w-3xl: 768px;
    --max-w-4xl: 896px;
    --max-w-prose: 65ch;

    /* @toc Shadows — NOT here: elevation reads differently on a dark surface than a light one, so the
     * --shadow-* set lives in config/theme.php (light/dark), remapped per data-theme like --surface/--text.
     * Same names (var(--shadow-md) …), Tailwind's two-layer values, higher alpha in dark. */

    /* @toc Opacity — ZERO consumers today (kept: the scale is tiny and cheap). First real use adopts it;
     * if none arrives it's a clean prune. Recorded here so its status isn't a mystery. */
    --opacity-0: 0;
    --opacity-25: 0.25;
    --opacity-50: 0.5;
    --opacity-75: 0.75;
    --opacity-100: 1;

    /* @toc Z-index — stacking ladder. */
    --z-10: 10;
    --z-20: 20;
    --z-30: 30;
    --z-40: 40;
    --z-50: 50;
    --z-overlay: 100;
    --z-modal: 200;
    --z-popover: 300;
    --z-tooltip: 400;

    /* @toc Transitions */
    --transition-fast: 150ms ease-in-out;
    --transition-default: 300ms ease-in-out;
    --transition-slow: 500ms ease-in-out;
}

/* @toc Typography */
:root {
    --font-sans: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
    --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Consolas, monospace;
    --h1-size: var(--text-3xl);
    --h1-weight: 500;
    --h1-leading: 1.2;
    --h2-size: var(--text-2xl);
    --h2-weight: 500;
    --h2-leading: 1.25;
    --h3-size: var(--text-xl);
    --h3-weight: 500;
    --h3-leading: 1.3;
    --h4-size: var(--text-lg);
    --h4-weight: 500;
    --h4-leading: 1.35;
    --h5-size: var(--text-base);
    --h5-weight: 600;
    --h5-leading: 1.4;
    --h6-size: var(--text-sm);
    --h6-weight: 600;
    --h6-leading: 1.4;
    --body-size: var(--text-base);
    --body-weight: 400;
    --body-leading: 1.6;
}
/* Typography — applies the config-driven type system to the base elements. The
 * families and per-element size/weight/line-height come from
 * config/css/typography.php, emitted as --font-* and --<el>-* by
 * TypographyService::vars(); this file just wires those vars onto the elements
 * and adds the structural bits the config doesn't carry (heading margins, links,
 * mono). The raw --text-* scale lives in utilities.css. Font files: fonts.css. */

body {
    font-family: var(--font-sans);
    font-size: var(--body-size);
    line-height: var(--body-leading);
}

h1,
h2,
h3,
h4,
h5,
h6 {
    margin: 0 0 0.4em;
    font-family: var(--font-sans);
}

h1 {
    font-size: var(--h1-size);
    font-weight: var(--h1-weight);
    line-height: var(--h1-leading);
}

h2 {
    font-size: var(--h2-size);
    font-weight: var(--h2-weight);
    line-height: var(--h2-leading);
}

h3 {
    font-size: var(--h3-size);
    font-weight: var(--h3-weight);
    line-height: var(--h3-leading);
}

h4 {
    font-size: var(--h4-size);
    font-weight: var(--h4-weight);
    line-height: var(--h4-leading);
}

h5 {
    font-size: var(--h5-size);
    font-weight: var(--h5-weight);
    line-height: var(--h5-leading);
}

h6 {
    font-size: var(--h6-size);
    font-weight: var(--h6-weight);
    line-height: var(--h6-leading);
}

p {
    margin: 0 0 0.8em;
    line-height: var(--body-leading);
}

a {
    color: var(--link);
    text-decoration: underline;
    text-underline-offset: 2px;
    transition: opacity var(--transition-fast);
}

a:hover {
    opacity: 0.8;
}

/* @toc Animations */
/* Animations — reusable, swappable entrance animations a component can opt into
 * (a card fading / rising / sliding / zooming in). Apply a class; the keyframes +
 * sane defaults are below, with duration/easing exposed as --anim-* so a component
 * can override per use. Cribbed pared-down from leadthink's animation set,
 * entrance-only. Honours prefers-reduced-motion. */

:root {
    --anim-duration: 500ms;
    --anim-easing: cubic-bezier(0.16, 1, 0.3, 1);
}

.anim-fade-in {
    animation: anim-fade-in var(--anim-duration) var(--anim-easing) both;
}

.anim-rise {
    animation: anim-rise var(--anim-duration) var(--anim-easing) both;
}

.anim-scale-in {
    animation: anim-scale-in var(--anim-duration) var(--anim-easing) both;
}

.anim-slide-in {
    animation: anim-slide-in var(--anim-duration) var(--anim-easing) both;
}

.anim-zoom-in {
    animation: anim-zoom-in var(--anim-duration) var(--anim-easing) both;
}

@keyframes anim-fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

@keyframes anim-rise {
    from {
        opacity: 0;
        transform: translateY(16px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes anim-scale-in {
    from {
        opacity: 0;
        transform: scale(0.96);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes anim-slide-in {
    from {
        opacity: 0;
        transform: translateX(24px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes anim-zoom-in {
    from {
        opacity: 0;
        transform: scale(0.8) translateY(12px);
    }
    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .anim-fade-in,
    .anim-rise,
    .anim-scale-in,
    .anim-slide-in,
    .anim-zoom-in {
        animation: none;
    }
}

/* Beyond the entrance classes: neutralise ALL motion for users who ask for it -
 * component transitions (tabs indicator, drawer, tooltip, swatches, chevron) and
 * the login shake alike. The !important duration override even beats the inline
 * animation login.js sets, so no per-component JS gate is needed (the one exception
 * is JS scrollIntoView, which takes a behavior arg dev-tools.js checks itself). */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* @toc Globals */
:root {
    --global-border-radius: var(--radius-lg);
    --global-border-width: 1px;
    --global-border-style: solid;
}
/* @toc Theme + component vars */
:root {
    --button-radius: 10px;
    --button-padding-y: 9px;
    --button-padding-x: 18px;
    --button-font-size: var(--text-sm);
    --button-font-weight: 500;
    --button-primary-bg: var(--primary-600);
    --button-primary-bg-hover: var(--primary-700);
    --button-primary-bg-active: var(--primary-800);
    --button-primary-fg: var(--on-primary);
    --button-focus-ring: var(--primary);
    --button-ghost-fg: var(--text);
    --button-ghost-border: var(--line);
    --button-ghost-border-hover: var(--text-muted);
    --button-ghost-bg-hover: var(--surface-raised);
    --button-ghost-bg-active: var(--line);
    --input-radius: 8px;
    --input-padding-y: 10px;
    --input-padding-x: 12px;
    --input-font-size: var(--text-sm);
    --input-label-size: var(--text-sm);
    --input-label-color: var(--text-muted);
    --input-bg: var(--surface);
    --input-fg: var(--text);
    --input-border: var(--neutral-500);
    --input-focus-border: var(--primary);
    --card-radius: 14px;
    --card-padding: 20px;
    --card-bg: var(--surface);
    --card-fg: var(--text);
    --card-media-bg: var(--surface-raised);
    --card-desc-color: var(--text-muted);
    --table-head-text: var(--text-muted);
    --table-border: var(--line);
    --table-row-hover: var(--surface-raised);
    --table-positive: var(--success);
    --table-negative: var(--danger);
    --login-radius: 14px;
    --login-padding: 28px;
    --login-bg: var(--surface);
    --login-fg: var(--text);
    --login-border: var(--line);
    --login-error-color: var(--primary-600);
    --theme-picker-radius: 8px;
    --sidebar-nav-width: 240px;
    --code-block-bg: var(--palette-slate-950);
    --code-block-fg: var(--palette-slate-300);
    --code-block-comment: var(--palette-slate-400);
    --code-block-string: var(--palette-sky-300);
    --code-block-keyword: var(--palette-red-400);
    --code-block-number: var(--palette-blue-400);
    --code-block-property: var(--palette-blue-400);
    --code-block-operator: var(--palette-red-400);
    --code-block-punctuation: var(--palette-slate-400);
    --code-block-tag: var(--palette-emerald-300);
    --code-block-attr: var(--palette-sky-400);
    --code-block-blade: var(--palette-violet-300);
    --tabs-tab-text: var(--text-muted);
    --tabs-tab-text-hover: var(--text);
    --tabs-tab-text-active: var(--text);
    --tabs-tab-hover-bg: var(--neutral-200);
    --tabs-line: var(--line);
    --tabs-accent: var(--primary);
    --tabs-pill-track: var(--neutral-200);
    --tabs-pill-active: var(--neutral-50);
    --flow-node-bg: var(--neutral-50);
    --flow-node-border: var(--neutral-200);
    --flow-node-text: var(--neutral-900);
    --flow-note-text: var(--neutral-500);
    --flow-edge: var(--neutral-400);
    --flow-accent-bg: var(--primary-600);
    --flow-accent-text: var(--neutral-50);
    --flowchart-bg: var(--neutral-100);
    --flowchart-node-bg: var(--neutral-50);
    --flowchart-node-border: var(--neutral-300);
    --flowchart-node-text: var(--neutral-900);
    --flowchart-note-text: var(--neutral-500);
    --flowchart-edge: var(--neutral-400);
    --flowchart-edge-label: var(--neutral-500);
    --flowchart-accent-bg: var(--primary-600);
    --flowchart-accent-text: var(--on-primary);
    --tooltip-bg: var(--neutral-900);
    --tooltip-text: var(--neutral-50);
    --dev-tools-surface: var(--palette-slate-950);
    --dev-tools-surface-raised: var(--palette-slate-900);
    --dev-tools-surface-hover: var(--palette-slate-800);
    --dev-tools-border: var(--palette-slate-800);
    --dev-tools-border-strong: var(--palette-slate-700);
    --dev-tools-backdrop: rgba(2, 6, 23, 0.6);
    --dev-tools-text: var(--palette-slate-100);
    --dev-tools-text-muted: var(--palette-slate-400);
    --dev-tools-text-faint: var(--palette-slate-600);
    --dev-tools-accent: var(--primary);
    --dev-tools-accent-bg: var(--palette-slate-800);
    --dev-tools-pill-solid-text: var(--palette-slate-950);
    --dev-tools-pill-muted-text: var(--palette-slate-300);
    --dev-tools-pill-muted-bg: var(--palette-slate-800);
    --dev-tools-pill-all-text: var(--palette-slate-300);
    --dev-tools-pill-all-bg: var(--palette-slate-800);
    --dev-tools-pill-all-solid-bg: var(--palette-slate-500);
    --dev-tools-pill-info-text: var(--palette-emerald-400);
    --dev-tools-pill-info-bg: var(--palette-emerald-950);
    --dev-tools-pill-info-solid-bg: var(--palette-emerald-500);
    --dev-tools-pill-warn-text: var(--palette-amber-400);
    --dev-tools-pill-warn-bg: var(--palette-amber-950);
    --dev-tools-pill-warn-solid-bg: var(--palette-amber-500);
    --dev-tools-pill-error-text: var(--palette-red-400);
    --dev-tools-pill-error-bg: var(--palette-red-950);
    --dev-tools-pill-error-solid-bg: var(--palette-red-500);
    --dev-tools-pill-time-text: var(--palette-sky-400);
    --dev-tools-pill-time-bg: var(--palette-sky-950);
    --dev-tools-pill-time-solid-bg: var(--palette-sky-500);
    --financials-ledger-head-text: var(--text-muted);
    --financials-ledger-border: var(--line);
    --financials-ledger-row-hover: var(--surface-raised);
    --financials-ledger-amount-positive: var(--success);
    --financials-ledger-amount-negative: var(--danger);
    --financials-ledger-chip-empty: var(--neutral-400);
    --financials-pnl-income: var(--success);
    --financials-pnl-expense: var(--danger);
    --financials-pnl-tile-bg: var(--surface);
    --planner-board-gap: 14px;
    --planner-board-list-width: 286px;
    --planner-board-list-bg: var(--surface);
    --planner-board-card-bg: var(--surface-raised);
    --planner-board-card-border: var(--line);
    --textarea-radius: 8px;
    --textarea-padding-y: 10px;
    --textarea-padding-x: 12px;
    --textarea-font-size: var(--text-sm);
    --textarea-line-height: 1.5;
    --textarea-label-size: var(--text-sm);
    --textarea-label-color: var(--text-muted);
    --textarea-bg: var(--surface);
    --textarea-fg: var(--text);
    --textarea-border: var(--neutral-500);
    --textarea-focus-border: var(--primary);
    --textarea-resize: vertical;
    --textarea-min-height: 80px;
    --select-radius: 8px;
    --select-padding-y: 10px;
    --select-padding-x: 12px;
    --select-font-size: var(--text-sm);
    --select-label-size: var(--text-sm);
    --select-label-color: var(--text-muted);
    --select-bg: var(--surface);
    --select-fg: var(--text);
    --select-border: var(--neutral-500);
    --select-focus-border: var(--primary);
    --select-chevron: var(--text-muted);
    --select-chevron-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23000'%3E%3Cpath fill-rule='evenodd' d='M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z' clip-rule='evenodd' /%3E%3C/svg%3E");
    --checkbox-size: 18px;
    --checkbox-radius: 5px;
    --checkbox-gap: 10px;
    --checkbox-font-size: var(--text-sm);
    --checkbox-dot-size: 8px;
    --checkbox-bg: var(--surface);
    --checkbox-border: var(--line);
    --checkbox-border-hover: var(--text-muted);
    --checkbox-check: var(--on-primary);
    --checkbox-checked-bg: var(--primary);
    --checkbox-label-color: var(--text);
    --checkbox-focus-ring: var(--primary);
    --switch-gap: 10px;
    --switch-label-size: var(--text-sm);
    --switch-label-color: var(--text);
    --switch-width: 40px;
    --switch-height: 22px;
    --switch-knob-size: 16px;
    --switch-knob-gap: 2px;
    --switch-track-bg: var(--surface-raised);
    --switch-track-border: var(--line);
    --switch-track-bg-on: var(--primary);
    --switch-knob-bg: var(--text-muted);
    --switch-knob-bg-on: var(--on-primary);
    --switch-focus-ring: var(--primary);
    --slider-label-color: var(--text-muted);
    --slider-label-size: var(--text-sm);
    --slider-value-color: var(--text);
    --slider-value-size: var(--text-sm);
    --slider-track-height: 6px;
    --slider-track-radius: 999px;
    --slider-track-bg: var(--line);
    --slider-fill: var(--primary);
    --slider-thumb-size: 18px;
    --slider-thumb-bg: var(--primary);
    --slider-thumb-border: var(--surface);
    --slider-thumb-border-width: 2px;
    --slider-focus-ring: var(--primary);
    --slider-focus-ring-width: 3px;
    --fieldset-padding: 16px;
    --fieldset-radius: 8px;
    --fieldset-gap: 4px;
    --fieldset-border: var(--line);
    --fieldset-bg: var(--surface);
    --fieldset-focus-border: var(--primary);
    --fieldset-legend-color: var(--text);
    --fieldset-legend-size: var(--text-sm);
    --fieldset-legend-weight: 500;
    --fieldset-legend-padding-x: 6px;
    --link-color: var(--link);
    --link-muted-color: var(--text-muted);
    --link-muted-color-hover: var(--text);
    --link-focus-ring: var(--primary);
    --link-radius: 2px;
    --link-underline-thickness: 1px;
    --link-underline-offset: 2px;
    --link-icon-size: 0.9em;
    --progress-height: 8px;
    --progress-width: 100%;
    --progress-radius: var(--radius-full);
    --progress-track: var(--surface-raised);
    --progress-value: var(--primary);
    --progress-focus-ring: var(--primary);
    --figure-border: var(--line);
    --figure-bg: var(--surface-raised);
    --figure-radius: 0;
    --figure-radius-rounded: 12px;
    --figure-caption-color: var(--text-muted);
    --figure-caption-size: var(--text-sm);
    --figure-caption-gap: 8px;
    --figure-caption-align: start;
    --details-radius: var(--radius);
    --details-border: var(--line);
    --details-bg: var(--surface-raised);
    --details-fg: var(--text);
    --details-padding-y: 12px;
    --details-padding-x: 14px;
    --details-summary-size: var(--text-sm);
    --details-summary-hover: var(--surface);
    --details-marker-color: var(--text-muted);
    --details-focus-ring: var(--primary);
    --details-body-color: var(--text-muted);
    --details-body-size: var(--text-sm);
    --dialog-width: 440px;
    --dialog-radius: 14px;
    --dialog-padding: 20px;
    --dialog-bg: var(--surface-raised);
    --dialog-fg: var(--text);
    --dialog-border: var(--line);
    --dialog-title-size: var(--text-lg);
    --dialog-shadow: var(--shadow-2xl);
    --dialog-backdrop: rgba(0, 0, 0, 0.55);
    --dialog-close-color: var(--text-muted);
    --dialog-close-bg-hover: var(--line);
    --dialog-focus-ring: var(--primary);
    --breadcrumb-font-size: var(--text-sm);
    --breadcrumb-gap: 8px;
    --breadcrumb-link: var(--text-muted);
    --breadcrumb-link-hover: var(--text);
    --breadcrumb-current: var(--text);
    --breadcrumb-current-weight: 500;
    --breadcrumb-separator: var(--line);
    --breadcrumb-radius: var(--radius-sm);
    --dropdown-surface: var(--neutral-50);
    --dropdown-line: var(--line);
    --dropdown-radius: 12px;
    --dropdown-min-width: 12rem;
    --dropdown-shadow: var(--shadow-lg);
    --dropdown-item-text: var(--text);
    --dropdown-item-text-muted: var(--text-muted);
    --dropdown-item-hover-bg: var(--neutral-200);
    --dropdown-danger-text: var(--danger);
    --tree-indent: 16px;
    --tree-row-height: 28px;
    --tree-radius: 6px;
    --tree-row-hover-bg: var(--neutral-200);
    --tree-row-selected-bg: var(--neutral-200);
    --tree-icon: var(--text-muted);
    --tree-twist: var(--text-muted);
    --tree-status-new: var(--info);
    --tree-status-updated: var(--text-muted);
    --tree-status-deleted: var(--danger);
    --footer-padding: 2rem 1rem;
    --footer-gap: 1rem;
}
:root[data-theme="light"] {
    --page-bg: var(--neutral-100);
    --surface: #ffffff;
    --surface-raised: var(--neutral-50);
    --text: var(--neutral-900);
    --text-muted: var(--neutral-600);
    --paper: #ffffff;
    --ink: #000000;
    --success: var(--palette-green-700);
    --success-surface: var(--palette-green-100);
    --danger: var(--palette-red-700);
    --danger-surface: var(--palette-red-100);
    --warning: var(--palette-amber-700);
    --warning-surface: var(--palette-amber-100);
    --info: var(--palette-blue-700);
    --info-surface: var(--palette-blue-100);
    --text-danger: var(--danger);
    --line: var(--neutral-200);
    --line-strong: var(--neutral-300);
    --primary: var(--primary-500);
    --on-primary: var(--neutral-50);
    --link: var(--primary-700);
    --scrollbar-thumb: var(--neutral-300);
    --scrollbar-thumb-hover: var(--neutral-400);
    --scrollbar-track: transparent;
    --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
    --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
    --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
    --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
    --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
    --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
    --card-border: var(--neutral-200);
}
:root[data-theme="dark"] {
    --page-bg: var(--neutral-950);
    --surface: var(--neutral-900);
    --surface-raised: var(--neutral-800);
    --text: var(--neutral-50);
    --text-muted: var(--neutral-400);
    --paper: #ffffff;
    --ink: #000000;
    --success: var(--palette-green-400);
    --success-surface: var(--palette-green-950);
    --danger: var(--palette-red-400);
    --danger-surface: var(--palette-red-950);
    --warning: var(--palette-amber-400);
    --warning-surface: var(--palette-amber-950);
    --info: var(--palette-blue-400);
    --info-surface: var(--palette-blue-950);
    --text-danger: var(--danger);
    --line: var(--neutral-800);
    --line-strong: var(--neutral-700);
    --primary: var(--primary-500);
    --on-primary: var(--neutral-50);
    --link: var(--primary-400);
    --scrollbar-thumb: var(--neutral-700);
    --scrollbar-thumb-hover: var(--neutral-600);
    --scrollbar-track: transparent;
    --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
    --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.5);
    --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.5);
    --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.6), 0 4px 6px -4px rgb(0 0 0 / 0.6);
    --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.6), 0 8px 10px -6px rgb(0 0 0 / 0.6);
    --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.7);
    --card-border: var(--neutral-800);
    --tabs-tab-hover-bg: var(--neutral-800);
    --tabs-pill-track: var(--neutral-900);
    --tabs-pill-active: var(--neutral-800);
    --flow-node-bg: var(--neutral-900);
    --flow-node-border: var(--neutral-700);
    --flow-node-text: var(--neutral-50);
    --flow-note-text: var(--neutral-400);
    --flow-edge: var(--neutral-600);
    --flowchart-bg: var(--neutral-950);
    --flowchart-node-bg: var(--neutral-900);
    --flowchart-node-border: var(--neutral-700);
    --flowchart-node-text: var(--neutral-50);
    --flowchart-note-text: var(--neutral-400);
    --flowchart-edge: var(--neutral-600);
    --flowchart-edge-label: var(--neutral-400);
    --tooltip-bg: var(--neutral-100);
    --tooltip-text: var(--neutral-900);
    --financials-ledger-chip-empty: var(--neutral-600);
    --dropdown-surface: var(--neutral-900);
    --dropdown-item-hover-bg: var(--neutral-800);
    --tree-row-hover-bg: var(--neutral-800);
    --tree-row-selected-bg: var(--neutral-800);
}
/* @toc Base */
/*
 * Base — the document skeleton (html/body as a flex column) and the one generic
 * text helper. Layout moved to its proper homes: the public shell is app-page.css,
 * the authed shell is app-shell.css, and page-specific styles live in pages/.
 */

html,
body {
    padding: 0;
}

body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--page-bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
}

/* Themed scrollbars — ONE base treatment for every overflow container. scrollbar-color inherits, but
 * scrollbar-width does NOT — so both go on * to reach every scroller (thin + themed everywhere, not
 * just the root). Modern engines use these standard properties (MDN-anchored); the ::-webkit-scrollbar
 * fallback below gives older Chromium/Safari the rounded thumb + hover polish. Ramp-tokened
 * (config/theme.php) → flips per theme + tenant. Opt a scroller OUT with scrollbar-width: none (see
 * tabs' nav and the dev-tools chip rows). */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
}

::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
    border-radius: 999px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--scrollbar-thumb-hover);
}

.muted {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* Keyboard focus — one consistent ring on every focusable control. :where() keeps
 * it zero-specificity, so a component can still draw its own (e.g. .button) without
 * fighting this. A real outline (not box-shadow) so it survives forced-colors mode.
 * tabindex="-1" targets (skip-link's main, modal panels) are programmatic focus,
 * not keyboard stops, so they're excluded. */
:where(a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])):focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* Suppress the browser-default outline on non-keyboard focus (mouse / programmatic) -
 * Safari and some Chromium builds draw a stray blue ring there, e.g. tracing the
 * scrollable sidebar list against its divider. The keyboard :focus-visible ring above
 * is untouched, so this only removes the default, never the intentional one. */
:focus:not(:focus-visible) {
    outline: none;
}

/* Visually hidden, still read by assistive tech — for labels and aria-live regions. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* A keyboard key, styled as a physical keycap — the Key column of the shared keyboard-table
   partial (/docs + /accessibility). Ramp tokens only, so it flips per theme. */
kbd {
    display: inline-block;
    padding: 1px 7px;
    font-size: 0.85em;
    line-height: 1.6;
    color: var(--text);
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: 5px;
    box-shadow: 0 1px 0 var(--line);
}

/* The em-dash placeholder for a component with no keyboard interaction (e.g. a presentational callout). */
.kbd-none {
    color: var(--text-muted);
}

/* @toc App header bar */
/* App header bar — the page-title strip at the top of the app-shell content
 * area, above the scrollable main (a sibling of app-shell-main inside
 * app-shell-wrap, so it stays put while main scrolls). Title left, optional
 * subtitle under it, optional actions slot right. Horizontal padding matches
 * app-shell-main so the title aligns with page content.
 *
 * Cribbed pared-down from leadthink's app-header-bar. Used by
 * /resources/views/components/app-shell.blade.php.
 */

.app-header-bar {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    min-height: 60px;
    padding: 12px 32px;
    background: var(--page-bg);
    border-bottom: 1px solid var(--line);
}

.app-header-bar-content {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.app-header-bar-title {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--text);
}

.app-header-bar-subtitle {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.app-header-bar-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

@media (max-width: 768px) {
    .app-header-bar {
        min-height: 0;
        padding: 14px 16px;
    }

    .app-header-bar-title {
        font-size: var(--text-lg);
    }
}

/* @toc App page */
/* Public shell layout — a topbar (logo / login row, right-aligned) above a
 * centered main. The shell for the public pages (landing, login); the authed app
 * uses app-shell.css instead. Used by /resources/views/components/app-page.blade.php. */

.topbar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: 18px 24px;
}

.topbar-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
}

.topbar-nav form {
    margin: 0;
}

.main {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: safe center;
    padding: 24px;
}

@media (max-width: 768px) {
    .topbar {
        padding: 14px 16px;
    }

    .main {
        padding: 20px 16px;
    }
}

/* @toc App shell */
/* App shell — sidebar (left) + scrollable main (right) on desktop. On mobile
 * (<= 768px) the sidebar becomes an off-canvas drawer: a sticky top bar with a
 * hamburger (toggled by sidebar-nav.js -> body.nav-open) slides it in over a
 * backdrop, and the page scrolls naturally instead of locking main. On
 * <body class="app-shell">. */

/* Skip-to-content link — the first focusable element on every shell. Parked
 * off-screen until focused, then pinned top-left. Shared by app-shell and app-page
 * (one global bundle), targets the <main id="main-content">. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: var(--z-overlay);
}

.skip-link:focus {
    left: 8px;
    top: 8px;
    padding: 8px 12px;
    background: var(--page-bg);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

body.app-shell {
    flex-direction: row;
    height: 100vh;
    overflow: hidden;
}

.app-shell-wrap {
    flex: 1;
    min-width: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.app-shell-main {
    flex: 1;
    overflow-y: auto;
    padding: 28px 32px;
}

/* Mobile-only chrome — hidden until the drawer breakpoint. */
.app-shell-bar,
.app-shell-backdrop {
    display: none;
}

@media (max-width: 768px) {
    body.app-shell {
        flex-direction: column;
        height: auto;
        min-height: 100vh;
        /* overflow-x guard: the drawer is parked off the left edge (translateX
         * -100%), so clamp horizontal scroll while leaving the page scrollable. */
        overflow-x: hidden;
        overflow-y: visible;
    }

    /* Lock background scroll while the drawer is open so content can't bleed
     * behind the backdrop. */
    body.app-shell.nav-open {
        overflow: hidden;
    }

    .app-shell-bar {
        display: flex;
        align-items: center;
        gap: 12px;
        position: sticky;
        top: 0;
        z-index: 30;
        padding: 10px 14px;
        background: var(--page-bg);
        border-bottom: 1px solid var(--line);
    }

    .app-shell-menu {
        appearance: none;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 38px;
        height: 38px;
        padding: 0;
        border: 1px solid var(--line);
        border-radius: 8px;
        background: transparent;
        color: var(--text);
        cursor: pointer;
    }

    .app-shell-menu svg {
        width: 20px;
        height: 20px;
    }

    .app-shell-brand {
        display: inline-flex;
        line-height: 0;
    }

    .app-shell-brand .brand-logo svg {
        width: 30px;
    }

    .sidebar-nav {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        width: 264px;
        max-width: 82vw;
        transform: translateX(-100%);
        /* Off-canvas: hidden from tab order / AT until opened (visibility flips
         * after the slide-out so the close still animates), like the backdrop. */
        visibility: hidden;
        transition: transform 220ms ease, visibility 0s linear 220ms;
        z-index: 50;
    }

    body.nav-open .sidebar-nav {
        transform: translateX(0);
        visibility: visible;
        transition: transform 220ms ease;
    }

    .app-shell-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 40;
        background: rgba(0, 0, 0, 0.5);
        opacity: 0;
        visibility: hidden;
        transition: opacity 220ms ease, visibility 0s linear 220ms;
    }

    body.nav-open .app-shell-backdrop {
        opacity: 1;
        visibility: visible;
        transition: opacity 220ms ease;
    }

    .app-shell-wrap {
        height: auto;
        overflow: visible;
    }

    .app-shell-main {
        padding: 20px 16px;
    }
}

/* @toc Brand logo */
/* Brand logo — sizes the inline tokenized SVG injected by MediaService. */

.brand-logo svg {
    display: block;
    width: 200px;
    max-width: 100%;
    height: auto;
}

/* @toc Breadcrumb */
/* Breadcrumb. A horizontal trail of links ending in the current page. Separators
 * are CSS-only chevrons inserted between items (::before on every item but the
 * first), so they carry no text and never reach the accessibility tree. All
 * --breadcrumb-* tokens come from config/components/breadcrumb.php['css']. */

.breadcrumb {
    font-size: var(--breadcrumb-font-size, var(--text-sm));
}

.breadcrumb-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--breadcrumb-gap, 8px);
    margin: 0;
    padding: 0;
    list-style: none;
}

.breadcrumb-item {
    display: flex;
    align-items: center;
    gap: var(--breadcrumb-gap, 8px);
}

/* The separator: a chevron drawn from a border, rotated 45°. Decorative only —
 * it lives in ::before so it never enters the document text or the a11y tree. */
.breadcrumb-item + .breadcrumb-item::before {
    content: "";
    width: 6px;
    height: 6px;
    border-top: 1.5px solid var(--breadcrumb-separator, var(--line));
    border-right: 1.5px solid var(--breadcrumb-separator, var(--line));
    transform: rotate(45deg);
    flex-shrink: 0;
}

.breadcrumb-link {
    color: var(--breadcrumb-link, var(--text-muted));
    text-decoration: none;
    border-radius: var(--breadcrumb-radius, var(--radius-sm));
    transition: color 0.15s ease;
}

.breadcrumb-link:hover {
    color: var(--breadcrumb-link-hover, var(--text));
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Keyboard focus reuses the global :focus-visible ring (base.css); offset a touch
 * so the rounded ring clears the text. */
.breadcrumb-link:focus-visible {
    outline-offset: 3px;
}

.breadcrumb-current {
    color: var(--breadcrumb-current, var(--text));
    font-weight: var(--breadcrumb-current-weight, 500);
}

/* @toc Button */
/*
 * Button. Base + variants (primary, ghost) + states (hover, active,
 * focus-visible, disabled). Every token is a --button-* var emitted from
 * config/components/button.php['css'] — this file references only those plus
 * structural rules, so the config is the component's full theming surface.
 */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: var(--button-padding-y) var(--button-padding-x);
    border: 1px solid transparent;
    border-radius: var(--button-radius);
    font-size: var(--button-font-size);
    font-weight: var(--button-font-weight);
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
}

/* The visible ring is the box-shadow (hugs the rounded border). The transparent
 * outline is a forced-colors fallback: box-shadow is dropped in high-contrast mode,
 * so the outline gets promoted to a system colour and focus stays visible. */
.button:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--button-focus-ring);
}

/* primary */
.button-primary {
    background: var(--button-primary-bg);
    border-color: var(--button-primary-bg);
    color: var(--button-primary-fg);
}

.button-primary:hover {
    background: var(--button-primary-bg-hover);
    border-color: var(--button-primary-bg-hover);
}

.button-primary:active {
    background: var(--button-primary-bg-active);
    border-color: var(--button-primary-bg-active);
}

/* ghost */
.button-ghost {
    background: transparent;
    border-color: var(--button-ghost-border);
    color: var(--button-ghost-fg);
}

.button-ghost:hover {
    border-color: var(--button-ghost-border-hover);
    background: var(--button-ghost-bg-hover);
}

.button-ghost:active {
    background: var(--button-ghost-bg-active);
}

/* disabled / inactive — both <button> and <a> */
.button:disabled,
.button.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* @toc Callout */
/* Callout — a note box whose variant colours the left border (and the optional
 * leading icon); the surface and text are theme tokens, so it adapts to light/dark.
 * Variants pull the categorical status tokens (--success/--warning/--info), so the
 * accent is contrast-vetted and self-flips per theme. View: callout.blade.php. */

.callout {
    display: flex;
    gap: 10px;
    margin: 14px 0;
    padding: 11px 15px;
    border-inline-start: 4px solid var(--callout-accent, var(--line));
    border-radius: var(--radius-sm);
    background: var(--surface-raised);
    color: var(--text);
    font-size: var(--text-sm);
    line-height: 1.55;
}

.callout-icon {
    flex-shrink: 0;
    margin-top: 1px;
    color: var(--callout-accent, var(--text-muted));
}

.callout-icon svg {
    display: block;
    width: 18px;
    height: 18px;
}

.callout-body {
    flex: 1;
    min-width: 0;
}

.callout-body :first-child { margin-top: 0; }
.callout-body :last-child { margin-bottom: 0; }

.callout a {
    color: var(--link);
}

.callout-success { --callout-accent: var(--success); }
.callout-warning { --callout-accent: var(--warning); }
.callout-info { --callout-accent: var(--info); }
.callout-note { --callout-accent: var(--text-muted); }

/* @toc Card */
/* Card. All --card-* tokens come from config/components/card.php['css'];
 * --card-border swaps per theme (css.light/dark). */

.card {
    background: var(--card-bg);
    color: var(--card-fg);
    border: 1px solid var(--card-border);
    border-radius: var(--card-radius);
    overflow: hidden;
}

.card-media {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: var(--card-media-bg);
}

.card-media img {
    display: block;
    width: 84px;
    height: auto;
}

.card-body {
    padding: var(--card-padding);
}

.card-title {
    margin: 0 0 8px;
    font-size: var(--text-base);
    font-weight: 500;
}

.card-desc {
    margin: 0;
    color: var(--card-desc-color);
    font-size: var(--text-sm);
    line-height: 1.5;
}

/* @toc Checkbox */
/*
 * Checkbox / radio. The native <input> is .sr-only (kept for behaviour + a11y);
 * .checkbox-box is the painted control and reacts to the input's state through
 * sibling selectors. Every token is a --checkbox-* var (config/components/
 * checkbox.php['css']) with a global-token fallback, so the config is the full
 * theming surface. Dark-first — colours come from the semantic tokens, which
 * already flip per theme.
 */

.checkbox {
    display: inline-flex;
    align-items: center;
    gap: var(--checkbox-gap, 10px);
    cursor: pointer;
    user-select: none;
    line-height: 1.4;
}

.checkbox-box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: var(--checkbox-size, 18px);
    height: var(--checkbox-size, 18px);
    border: 1px solid var(--checkbox-border, var(--line));
    border-radius: var(--checkbox-radius, 5px);
    background: var(--checkbox-bg, var(--surface));
    color: var(--checkbox-check, var(--on-primary));
    transition: background 0.15s ease, border-color 0.15s ease;
}

/* The radio is the same box, fully round. */
.checkbox-radio .checkbox-box {
    border-radius: 50%;
}

.checkbox-label {
    color: var(--checkbox-label-color, var(--text));
    font-size: var(--checkbox-font-size, var(--text-sm));
}

/* The tick (checkbox) — hidden until the input is checked. */
.checkbox-check {
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: opacity 0.12s ease;
}

/* The dot (radio) — drawn as a pseudo-element so radios need no inline SVG. */
.checkbox-radio .checkbox-box::after {
    content: "";
    width: var(--checkbox-dot-size, 8px);
    height: var(--checkbox-dot-size, 8px);
    border-radius: 50%;
    background: var(--checkbox-check, var(--on-primary));
    transform: scale(0);
    transition: transform 0.12s ease;
}

/* --- Checked: fill the box with the accent, reveal the mark --- */
.checkbox-input:checked ~ .checkbox-box {
    background: var(--checkbox-checked-bg, var(--primary));
    border-color: var(--checkbox-checked-bg, var(--primary));
}

.checkbox-input:checked ~ .checkbox-box .checkbox-check {
    opacity: 1;
}

.checkbox-radio .checkbox-input:checked ~ .checkbox-box::after {
    transform: scale(1);
}

/* --- Hover (only when the row is interactive) --- */
.checkbox:not(.is-disabled):hover .checkbox-box {
    border-color: var(--checkbox-border-hover, var(--text-muted));
}

.checkbox:not(.is-disabled):hover .checkbox-input:checked ~ .checkbox-box {
    border-color: var(--checkbox-checked-bg, var(--primary));
}

/* --- Keyboard focus — the ring rides the painted box, not the hidden input.
 * Transparent outline is the forced-colors fallback (box-shadow is dropped in
 * high-contrast mode, so the outline gets promoted to a system colour). --- */
.checkbox-input:focus-visible ~ .checkbox-box {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--checkbox-focus-ring, var(--primary));
}

/* --- Disabled --- */
.checkbox.is-disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* @toc Code block */
/*
 * Code block — a <pre> tokenised by code-block.js for IDE-style highlighting (no
 * library). Always-dark surface; every colour is a --code-block-* token from
 * config/components/code-block.php. Used by the docs and available as
 * <x-code-block language code>.
 */

.code-block {
    margin: 0;
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--code-block-bg);
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.6;
    color: var(--code-block-fg);
    white-space: pre;
}

/* Token colours — scoped to the code node so BOTH the default block and the file variant get them. */
.code-block-code .tok-comment {
    color: var(--code-block-comment);
    font-style: italic;
}

.code-block-code .tok-string { color: var(--code-block-string); }
.code-block-code .tok-keyword { color: var(--code-block-keyword); }
.code-block-code .tok-number { color: var(--code-block-number); }
.code-block-code .tok-property { color: var(--code-block-property); }
.code-block-code .tok-operator { color: var(--code-block-operator); }
.code-block-code .tok-punctuation { color: var(--code-block-punctuation); }
.code-block-code .tok-tag { color: var(--code-block-tag); }
.code-block-code .tok-attr { color: var(--code-block-attr); }
.code-block-code .tok-blade { color: var(--code-block-blade); }

/* File-sized variant — the card-open Code pane portal: a filename header, a scrollable full-height window,
   and a line-number gutter. Same type metrics as .code-block so the gutter aligns with the code lines. */
.code-block-file {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    border: 1px solid var(--line);
    border-radius: 8px;
    overflow: hidden;
    background: var(--code-block-bg);
}

.code-block-head {
    flex: 0 0 auto;
    padding: 8px 14px;
    border-bottom: 1px solid color-mix(in oklab, var(--code-block-fg) 18%, transparent);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--code-block-fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.code-block-scroll {
    flex: 1 1 auto;
    display: flex;
    align-items: flex-start;
    min-height: 0;
    overflow: auto;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.6;
}

.code-block-gutter {
    flex: 0 0 auto;
    position: sticky;
    inset-inline-start: 0;
    padding-block: 14px;
    padding-inline: 14px 8px;
    text-align: end;
    white-space: pre;
    color: var(--code-block-comment);
    background: var(--code-block-bg);
    user-select: none;
}

.code-block-file .code-block-pre {
    flex: 1 1 auto;
    margin: 0;
    padding-block: 14px;
    padding-inline: 8px 16px;
    background: transparent;
    color: var(--code-block-fg);
    white-space: pre;
}

/* @toc Darbru create */
/* Darbru create widget — the prompt block + the paste/drop JSON form. Theme tokens
 * throughout. View: darbru-create.blade.php; behaviour: darbru-create.js. */

.darbru-create {
    max-width: var(--max-w-3xl);
}

.darbru-create-step {
    margin-bottom: 28px;
}

.darbru-create-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.darbru-create-h {
    margin: 0 0 4px;
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text);
}

.darbru-create-prompt {
    max-height: 320px;
    overflow: auto;
    margin-top: 10px;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Drop zone — a dashed target that also opens the file picker on tap (it wraps a
 * hidden file input), so it works on desktop drag and mobile alike. */
.darbru-drop {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 22px;
    margin-bottom: 12px;
    border: 2px dashed var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    color: var(--text-muted);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.darbru-drop:hover,
.darbru-drop.is-over {
    border-color: var(--primary);
    color: var(--text);
}

.darbru-create-actions {
    margin-top: 12px;
}

/* Type tabs — switch which prompt (essay / theory) is shown + copied. */
.darbru-create-tabs {
    display: flex;
    gap: 6px;
    margin: 10px 0;
}

.darbru-create-tab {
    padding: 6px 16px;
    border: 1px solid var(--line);
    border-radius: var(--radius-full, 999px);
    background: var(--surface-raised);
    color: var(--text-muted);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    cursor: pointer;
}

.darbru-create-tab.is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
}

.darbru-create-prompt[hidden] {
    display: none;
}

/* @toc Data table */
/* DataTable pattern - the chrome around the <x-table> primitive: a bulk-action bar
 * above and a footer (pager) below. The table itself is themed by table.css; this
 * file only lays out the surrounding grid + the injected selection column.
 * Component: app/View/Components/DataTable.php; view: data-table.blade.php. */

.data-table {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.data-table-bulk {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
}

.data-table-footer {
    display: flex;
    flex-direction: column;
}

/* Selection column (added by `selectable`) - keep it to its checkbox width. */
.data-table .table th:first-child,
.data-table .table td.data-table-select {
    width: 1%;
    white-space: nowrap;
}

/* @toc Details */
/*
 * Details — a themed native <details>/<summary> disclosure. The native element
 * supplies the open/close behaviour; this file only themes the chrome. Every
 * --details-* token has a global-token fallback (config/components/details.php
 * is the theming surface). Stack several for an accordion. Dark-first.
 */

.details {
    border: 1px solid var(--details-border, var(--line));
    border-radius: var(--details-radius, var(--radius));
    background: var(--details-bg, var(--surface-raised));
    color: var(--details-fg, var(--text));
    overflow: hidden;
}

/* The summary is the disclosure trigger. list-style:none (+ the WebKit pseudo)
 * drops the UA's default triangle so our chevron is the only marker. */
.details-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: var(--details-padding-y, 12px) var(--details-padding-x, 14px);
    font-size: var(--details-summary-size, var(--text-sm));
    font-weight: 500;
    list-style: none;
    cursor: pointer;
    user-select: none;
}

.details-summary::-webkit-details-marker {
    display: none;
}

.details-summary:hover {
    background: var(--details-summary-hover, var(--surface));
}

/* Keyboard focus ring on the summary (it's the focusable element). Mirrors the
 * other primitives: box-shadow ring + a transparent outline as the
 * forced-colors / high-contrast fallback. */
.details-summary:focus-visible {
    outline: 2px solid transparent;
    outline-offset: -2px;
    box-shadow: inset 0 0 0 2px var(--details-focus-ring, var(--primary));
}

/* The chevron points right when closed and rotates down when [open]. */
.details-marker {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--details-marker-color, var(--text-muted));
    transition: transform 0.15s ease;
}

.details-marker svg {
    display: block;
    width: 16px;
    height: 16px;
}

.details[open] .details-marker {
    transform: rotate(90deg);
}

.details-summary-text {
    flex: 1;
    min-width: 0;
}

.details-body {
    padding: 0 var(--details-padding-x, 14px) var(--details-padding-y, 12px);
    color: var(--details-body-color, var(--text-muted));
    font-size: var(--details-body-size, var(--text-sm));
    line-height: 1.55;
}

.details-body :first-child {
    margin-top: 0;
}

.details-body :last-child {
    margin-bottom: 0;
}

/* @toc Dialog */
/*
 * Dialog. The native <dialog> element does the heavy lifting (centred-in-viewport
 * positioning, focus trap, Escape, top-layer stacking, the ::backdrop pseudo); this
 * file only themes the panel + backdrop. Every token is a --dialog-* var emitted from
 * config/components/dialog.php['css'], each with a global-token fallback so the panel
 * reads on both themes.
 */

.dialog {
    width: min(var(--dialog-width), calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    padding: 0;
    border: 1px solid var(--dialog-border, var(--line));
    border-radius: var(--dialog-radius);
    background: var(--dialog-bg, var(--surface-raised));
    color: var(--dialog-fg, var(--text));
    box-shadow: var(--dialog-shadow);
    /* Open experience (Card-open v2): a fast, smooth entrance the close MIRRORS. The native <dialog>
     * is position:fixed / top-layer, so nothing on the page shifts. Base = the open state; the closed
     * :not([open]) state and @starting-style share the from-state, and the display/overlay
     * allow-discrete transitions keep the element painted through the EXIT so closing reverses the
     * open. Older engines (no @starting-style) fall back to an instant show/hide. */
    opacity: 1;
    transform: none;
    transition:
        opacity 0.18s ease,
        transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
        overlay 0.18s ease allow-discrete,
        display 0.18s ease allow-discrete;
}

/* Closed state = the exit end-state (allow-discrete keeps it painted while it animates out). */
.dialog:not([open]) {
    opacity: 0;
    transform: translateY(6px) scale(0.985);
}

/* Entrance from-state (the first paint, before-open). */
@starting-style {
    .dialog[open] {
        opacity: 0;
        transform: translateY(6px) scale(0.985);
    }
}

/* ::backdrop is the dimming layer the browser paints behind a modal dialog — tint it so the page
 * behind recedes, and fade it with the panel (open + close). */
.dialog::backdrop {
    background: var(--dialog-backdrop);
    opacity: 1;
    transition:
        opacity 0.18s ease,
        overlay 0.18s ease allow-discrete,
        display 0.18s ease allow-discrete;
}

.dialog:not([open])::backdrop {
    opacity: 0;
}

@starting-style {
    .dialog[open]::backdrop {
        opacity: 0;
    }
}

/* Reduced motion: instant swap — the shell appears immediately, no slide / scale / fade. */
@media (prefers-reduced-motion: reduce) {
    .dialog,
    .dialog::backdrop {
        transition: none;
    }

    .dialog:not([open]) {
        transform: none;
    }
}

.dialog-panel {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 64px);
}

.dialog-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: var(--dialog-padding) var(--dialog-padding) 0;
}

.dialog-title {
    margin: 0;
    font-size: var(--dialog-title-size);
    font-weight: 600;
    line-height: 1.3;
    color: var(--dialog-fg, var(--text));
}

/* Close affordance — a borderless icon button in the header. */
.dialog-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    padding: 4px;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--dialog-close-color, var(--text-muted));
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.dialog-close:hover {
    background: var(--dialog-close-bg-hover, var(--line));
    color: var(--dialog-fg, var(--text));
}

.dialog-close svg {
    display: block;
    width: 20px;
    height: 20px;
}

/* Keyboard focus ring — same box-shadow ring as the button, with a transparent
 * outline as the forced-colors / high-contrast fallback. */
.dialog-close:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--dialog-focus-ring, var(--primary));
}

.dialog-body {
    padding: var(--dialog-padding);
    overflow-y: auto;
    font-size: var(--text-sm);
    line-height: 1.6;
}

.dialog-body :first-child {
    margin-top: 0;
}

.dialog-body :last-child {
    margin-bottom: 0;
}

/* @toc Dropdown */
/* Dropdown — the APG menu-button. A relatively-positioned wrapper holds the trigger + an absolutely-
 * positioned flyout; closed = the flyout carries [hidden] (display:none — gone from the AT tree), open =
 * dropdown.js removes it and adds .is-open to the wrapper. Colours are --dropdown-* (config facet):
 * ramp-lawful surface/line, item text via semantic tokens, danger = the destructive token. Behaviour +
 * focus movement: dropdown.js. */

.dropdown {
    position: relative;
    display: inline-block;
}

.dropdown-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    appearance: none;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
    color: var(--text);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    padding: 7px 12px;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.dropdown-trigger:hover {
    background: var(--dropdown-item-hover-bg);
}

.dropdown-trigger-icon,
.dropdown-caret {
    display: inline-flex;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.dropdown-caret {
    color: var(--text-muted);
    transition: transform 0.15s ease;
}

.dropdown.is-open .dropdown-caret {
    transform: rotate(180deg);
}

.dropdown-trigger-icon svg,
.dropdown-caret svg,
.dropdown-item-icon svg,
.dropdown-item-check svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* The flyout */
.dropdown-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: var(--z-popover);
    min-width: var(--dropdown-min-width);
    margin: 0;
    padding: 4px;
    background: var(--dropdown-surface);
    border: 1px solid var(--dropdown-line);
    border-radius: var(--dropdown-radius);
    box-shadow: var(--dropdown-shadow);
}

.dropdown-menu[hidden] {
    display: none;
}

.dropdown[data-align="end"] .dropdown-menu {
    left: auto;
    right: 0;
}

/* Items */
.dropdown-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    box-sizing: border-box;
    appearance: none;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--dropdown-item-text);
    font-family: inherit;
    font-size: var(--text-sm);
    text-align: left;
    text-decoration: none;
    padding: 7px 10px;
    cursor: pointer;
    white-space: nowrap;
}

.dropdown-item:hover,
.dropdown-item:focus-visible {
    background: var(--dropdown-item-hover-bg);
    outline: none;
}

.dropdown-item:focus-visible {
    box-shadow: inset 0 0 0 2px var(--primary);
}

.dropdown-item.is-danger {
    color: var(--dropdown-danger-text);
}

.dropdown-item[aria-disabled="true"] {
    color: var(--dropdown-item-text-muted);
    opacity: 0.55;
    cursor: default;
}

.dropdown-item-icon {
    display: inline-flex;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    color: var(--dropdown-item-text-muted);
}

/* The checkbox/radio tick — reserved space always; shown only when aria-checked. */
.dropdown-item.is-toggle {
    padding-left: 8px;
}

.dropdown-item-check {
    display: inline-flex;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    color: var(--primary);
    visibility: hidden;
}

.dropdown-item[aria-checked="true"] .dropdown-item-check {
    visibility: visible;
}

.dropdown-item-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Groups + derived separators */
.dropdown-group-label {
    margin: 0;
    padding: 6px 10px 2px;
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--dropdown-item-text-muted);
}

.dropdown-sep {
    height: 1px;
    margin: 4px 0;
    background: var(--dropdown-line);
}

/* @toc Fieldset */
/* Fieldset. All --fieldset-* tokens come from config/components/fieldset.php['css'];
 * each var carries the matching global-token fallback so the file reads on its own. */

.fieldset {
    /* Outer spacing rides the --space-* scale (F5): the old 0 0 14px was off-scale and
     * contract-invisible; snapped to the nearest rung (--space-4 = 16px, matching the
     * component's own 16px padding) so a builder reading the contract sees a named token. */
    margin: 0 0 var(--space-4);
    padding: var(--fieldset-padding, 16px);
    border: 1px solid var(--fieldset-border, var(--line));
    border-radius: var(--fieldset-radius, 8px);
    background: var(--fieldset-bg, var(--surface));
    min-inline-size: 0;
}

.fieldset-legend {
    padding: 0 var(--fieldset-legend-padding-x, 6px);
    color: var(--fieldset-legend-color, var(--text));
    font-size: var(--fieldset-legend-size, var(--text-sm));
    font-weight: var(--fieldset-legend-weight, 500);
}

.fieldset-body {
    display: flex;
    flex-direction: column;
    gap: var(--fieldset-gap, 4px);
}

/* Keyboard focus on a control inside the group lifts the border to the focus
 * colour as a grouping cue; the controls keep their own :focus-visible rings. */
.fieldset:focus-within {
    border-color: var(--fieldset-focus-border, var(--primary));
}

/* Native disabled cascades to nested controls; dim the group to match. */
.fieldset:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* @toc Figure */
/* Figure. All --figure-* tokens come from config/components/figure.php['css'];
 * structural rules aside, the config is the component's full theming surface. */

.figure {
    margin: 0;
    display: inline-block;
    max-width: 100%;
}

.figure-img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--figure-border, var(--line));
    border-radius: var(--figure-radius, 0);
    background: var(--figure-bg, var(--surface-raised));
}

/* The rounded variant — opt-in soft corners on the image. */
.figure.is-rounded .figure-img {
    border-radius: var(--figure-radius-rounded, 12px);
}

.figure-caption {
    margin-top: var(--figure-caption-gap, 8px);
    color: var(--figure-caption-color, var(--text-muted));
    font-size: var(--figure-caption-size, var(--text-sm));
    line-height: 1.5;
    text-align: var(--figure-caption-align, start);
}

/* @toc Financials import */
/* Financials import widget - the account field + paste/drop CSV form, and the
 * recent-imports section on the page. Themes off shared tokens (cloned from
 * darbru-create.css). The recent-imports table reuses .fin-table from the ledger.
 * View: financials-import.blade.php; behaviour: financials-import.js. */

.financials-import {
    max-width: var(--max-w-3xl);
}

/* Drop zone - a dashed target that also opens the file picker on tap (it wraps a
 * hidden file input), so desktop drag and mobile tap both work. .is-over matches
 * the class toggled in financials-import.js. */
.fin-import-drop {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 22px;
    margin-bottom: 12px;
    border: 2px dashed var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    color: var(--text-muted);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.fin-import-drop:hover,
.fin-import-drop.is-over {
    border-color: var(--primary);
    color: var(--text);
}

/* Keyboard focus — the file <input> is .sr-only (focusable but off-screen), so its focus ring must ride
 * the visible drop-zone label (WCAG 2.4.7). :focus-visible = keyboard only; the transparent outline is the
 * forced-colors fallback (box-shadow is dropped in high-contrast, so the outline gets promoted). */
.fin-import-drop:has(input:focus-visible) {
    border-color: var(--primary);
    color: var(--text);
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--primary);
}

/* The CSV paste box is the textarea primitive; only override to a monospace face
 * so pasted columns line up (border / bg / focus come from textarea.css). */
.textarea-field.fin-import-textarea {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.5;
}

.fin-import-actions {
    margin-top: 12px;
}

/* Recent imports (page section below the widget) */
.fin-import-recent {
    margin-top: var(--space-10);
}

.fin-import-recent-h {
    margin: 0 0 var(--space-3);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text);
}

.fin-import-undo {
    display: inline;
}

/* @toc Financials ledger */
/* Financials ledger - the filter bar, the bulk-categorize controls, and the per-row
 * cells inside the DataTable pattern. The table chrome, sortable headers, selection
 * column and pager come from the table / data-table / pagination components; this
 * file styles only the Finance-specific bits: filter layout, amount colours, and the
 * category cell + its inline select. Colours from --financials-ledger-* (config).
 * View: financials-ledger.blade.php; JS: financials-ledger.js. */

.financials-ledger {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* Filter bar - a responsive row of primitive controls + apply / clear. */
.fin-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
}

.fin-filters .input,
.fin-filters .select {
    width: 100%;
}

.fin-filter-search {
    flex: 1 1 220px;
    min-width: 180px;
}

.fin-filter {
    flex: 0 1 160px;
}

.fin-filter-actions {
    display: flex;
    gap: var(--space-2);
    margin-left: auto;
}

/* Bulk-categorize bar (the DataTable `bulk` slot): the picker + count + save. */
.data-table-bulk .select {
    flex: 0 1 200px;
}

.fin-bulk-count {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.fin-bulk-save {
    margin-left: auto;
}

/* Amount cell - tabular, green for income / red for expense (right-aligned by the
 * table primitive's table-right class on the cell). */
.fin-cell-amount {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.fin-cell-amount.is-positive {
    color: var(--financials-ledger-amount-positive);
}

.fin-cell-amount.is-negative {
    color: var(--financials-ledger-amount-negative);
}

.fin-cell-desc {
    max-width: 360px;
    white-space: normal;
    color: var(--text);
}

.fin-cell-account {
    color: var(--text-muted);
}

/* Category cell - a colour dot + the inline select primitive. */
.fin-cell-cat {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.fin-cell-cat .select {
    flex: 1;
    min-width: 150px;
}

.fin-cat-dot {
    flex-shrink: 0;
    width: 9px;
    height: 9px;
    border-radius: var(--radius-full);
    background: var(--dot, var(--financials-ledger-chip-empty));
}

/* A per-row select changed since load (client-side) gets a subtle accent so unsaved
 * marks are visible before hitting Save. */
.select-field.fin-cat-select.is-dirty {
    border-color: var(--primary);
}

/* Empty state */
.fin-empty {
    padding: var(--space-12) var(--space-4);
    text-align: center;
    color: var(--text-muted);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
}

@media (max-width: 768px) {
    .fin-filter,
    .fin-filter-search {
        flex-basis: 100%;
    }

    .fin-filter-actions {
        margin-left: 0;
        width: 100%;
    }
}

/* @toc Financials overview */
/* Financials overview — two tabs. Budget: a clean income / fixed-bills / flexible breakdown list.
 * Calendar: a scroll-back carousel of real month calendars (financials-calendar.blade.php). Theme
 * tokens so it flips light/dark. View: financials-overview.blade.php. */

.fin-overview {
    display: block;
}

.fin-overview .tabs-panel {
    padding-top: var(--space-6);
}

.fin-ov-muted {
    color: var(--text-muted);
    font-weight: 400;
    font-size: var(--text-sm);
}

/* --- BUDGET tab: the breakdown list --- */
.fin-bud2 {
    display: flex;
    flex-direction: column;
    gap: var(--space-7, 32px);
    max-width: 600px;
}

.fin-bud2-h {
    margin: 0 0 6px;
    font-size: var(--text-sm);
    font-weight: 700;
}

.fin-bud2-h span {
    font-weight: 400;
    color: var(--text-muted);
}

.fin-bud2-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px solid var(--line);
}

.fin-bud2-row.is-total {
    border-bottom: none;
    border-top: 1px solid var(--line);
    margin-top: 2px;
}

.fin-bud2-name {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 3px 9px;
    font-weight: 600;
    min-width: 0;
}

.fin-bud2-row.is-total .fin-bud2-name {
    font-weight: 700;
}

.fin-bud2-name small {
    font-weight: 400;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.fin-bud2-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
}

.fin-bud2-dot.is-in {
    background: var(--success);
}

.fin-bud2-dot.is-flex {
    background: var(--warning);
}

.fin-bud2-logo {
    width: 20px;
    height: 20px;
    border-radius: 5px;
    flex: none;
}

.fin-bud2-logo.is-blank {
    display: grid;
    place-items: center;
    font-size: 11px;
    font-weight: 700;
    color: #fff;
    background: var(--neutral-500, #737373);
}

.fin-bud2-amt {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.fin-bud2-row.is-total.is-flex .fin-bud2-name,
.fin-bud2-row.is-total.is-flex .fin-bud2-amt {
    color: var(--warning);
}

/* --- CALENDAR tab: header + book key --- */
.fin-ov-calhead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.fin-ov-calhead .fin-ov-muted {
    margin: 0;
}

.fin-ov-key {
    display: flex;
    gap: 14px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.fin-ov-key span {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.fin-ov-key-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
}

.fin-ov-key-dot.is-house {
    background: var(--neutral-400, #a3a3a3);
}

.fin-ov-key-dot.is-biz {
    background: var(--palette-indigo-500, #6366f1);
}

/* --- carousel: one big month per viewport (two only on very wide screens) --- */
.fin-car-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}

.fin-car-hint {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.fin-car-nav {
    display: flex;
    gap: 6px;
}

.fin-car-btn {
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    font-size: 18px;
    line-height: 1;
    background: var(--surface-raised, var(--neutral-100));
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--text);
    cursor: pointer;
}

.fin-car-btn:hover {
    border-color: var(--primary-500, var(--palette-rose-500));
}

.fin-car-track {
    container-type: inline-size;
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: 10px;
}

.fin-car-item {
    flex: 0 0 100%;
    scroll-snap-align: start;
}

/* 1080px: the carousel TRACK's own inline size (a container query, not the app --bp-* viewport seam, with
   which it coexists) — the month cards go 2-up once the track itself is wide enough, so it responds to its
   slot rather than the window. .fin-car-track sets container-type above. */
@container (min-width: 1080px) {
    .fin-car-item {
        flex: 0 0 calc(50% - 8px);
    }
}

/* --- month calendar grid --- */
.fin-cal2-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 8px;
}

.fin-cal2-label {
    font-size: var(--text-base, 15px);
    font-weight: 700;
}

.fin-cal2-tag {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 2px 7px;
    border-radius: 5px;
}

.fin-cal2-tag.is-actual {
    background: transparent;
    border: 1px solid var(--line);
    color: var(--text-muted);
}

.fin-cal2-tag.is-projected {
    background: transparent;
    border: 1px dashed var(--palette-indigo-500, #6366f1);
    color: var(--palette-indigo-500, #6366f1);
}

.fin-cal2-total {
    margin-left: auto;
    font-size: var(--text-sm);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.fin-cal2-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
}

.fin-cal2-dow {
    text-align: center;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
    padding-bottom: 2px;
}

.fin-cal2-cell {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 80px;
    padding: 4px 5px;
    border: 1px solid var(--line);
    border-radius: 7px;
}

.fin-cal2-cell.is-empty {
    border: none;
    min-height: 0;
}

.fin-cal2-cell.has-bills {
    background: var(--surface-raised, var(--neutral-100));
    cursor: help;
}

.fin-cal2-cell.is-heavy {
    background: color-mix(in srgb, var(--danger) 9%, var(--surface-raised, var(--neutral-100)));
}

.fin-cal2-cell.has-bills:hover,
.fin-cal2-cell.has-bills:focus-visible {
    outline: none;
    border-color: var(--primary-500, var(--palette-rose-500));
    z-index: 5;
}

.fin-cal2-num {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
}

.fin-cal2-cell.has-bills .fin-cal2-num {
    color: var(--text);
}

.fin-cal2-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
    margin-top: 3px;
}

.fin-cal2-chip {
    width: 22px;
    height: 22px;
    border-radius: 6px;
    display: grid;
    place-items: center;
    background: var(--surface, #fff);
    box-shadow: 0 0 0 1.5px var(--cal-ring, var(--line));
}

.fin-cal2-chip.is-house {
    --cal-ring: var(--neutral-400, #a3a3a3);
}

.fin-cal2-chip.is-biz {
    --cal-ring: var(--palette-indigo-500, #6366f1);
}

.fin-cal2-chip img {
    width: 18px;
    height: 18px;
    border-radius: 4px;
}

.fin-cal2-blank {
    width: 18px;
    height: 18px;
    border-radius: 4px;
    display: grid;
    place-items: center;
    font-size: 10px;
    font-weight: 700;
    color: #fff;
    background: var(--neutral-500, #737373);
}

.fin-cal2-more {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    align-self: center;
}

.fin-cal2-daysum {
    margin-top: auto;
    padding-top: 3px;
    text-align: end;
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.fin-cal2-cell.is-heavy .fin-cal2-daysum {
    color: var(--danger);
}

.fin-cal2-tip {
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    min-width: 160px;
    padding: 8px 11px;
    border-radius: 8px;
    background: var(--neutral-900, #18181b);
    color: #fff;
    font-size: var(--text-xs);
    display: flex;
    flex-direction: column;
    gap: 2px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
    z-index: 20;
}

.fin-cal2-tip strong {
    font-weight: 700;
    margin-bottom: 3px;
    white-space: nowrap;
}

.fin-cal2-tip span {
    white-space: nowrap;
    color: rgba(255, 255, 255, 0.85);
}

.fin-cal2-cell.has-bills:hover .fin-cal2-tip,
.fin-cal2-cell.has-bills:focus-visible .fin-cal2-tip {
    opacity: 1;
}

/* @toc Financials pnl */
/* Financials P&L - summary tiles + monthly and by-category tables. Colours from
 * --financials-pnl-* (config); the tables reuse the ledger's .fin-table classes.
 * View: financials-pnl.blade.php. */

.financials-pnl {
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
}

/* Book toggle - a segmented control switching the eightythree (business) and Household views. */
.fin-book-toggle {
    display: inline-flex;
    align-self: flex-start;
    gap: 4px;
    padding: 4px;
    background: var(--surface-sunken, var(--neutral-200));
    border-radius: 10px;
}

.fin-book-toggle a {
    padding: 8px 18px;
    border-radius: 7px;
    font-weight: 700;
    font-size: var(--text-sm);
    text-decoration: none;
    color: var(--text-muted);
}

.fin-book-toggle a:hover {
    color: var(--text);
}

.fin-book-toggle a.is-active {
    background: var(--surface, var(--neutral-50));
    color: var(--text);
}

.fin-pnl-note {
    margin: 10px 2px 0;
    font-size: var(--text-sm);
    color: var(--text-muted);
    line-height: 1.5;
}

/* Summary tiles - total income / expense / net for the whole period. */
.fin-pnl-summary {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
}

.fin-pnl-tile {
    flex: 1 1 160px;
    padding: var(--space-4) var(--space-5);
    background: var(--financials-pnl-tile-bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
}

.fin-pnl-tile-label {
    margin: 0 0 var(--space-2);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: 700;
    color: var(--text-muted);
}

.fin-pnl-tile-value {
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

.fin-pnl-tile-value.is-income {
    color: var(--financials-pnl-income);
}

.fin-pnl-tile-value.is-expense {
    color: var(--financials-pnl-expense);
}

.fin-pnl-section-h {
    margin: 0 0 var(--space-3);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text);
}

/* A type subheading row inside the by-category table (cell-level, since the table
 * primitive renders rows from arrays - every cell in the row carries the class). */
td.fin-pnl-grouprow {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: 700;
    color: var(--text-muted);
    background: var(--surface-raised);
}

.fin-pnl-amount {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.fin-pnl-amount.is-income {
    color: var(--financials-pnl-income);
}

.fin-pnl-amount.is-expense {
    color: var(--financials-pnl-expense);
}

.fin-pnl-cat {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.fin-pnl-dot {
    flex-shrink: 0;
    width: 9px;
    height: 9px;
    border-radius: var(--radius-full);
    background: var(--dot, var(--text-muted));
}

td.fin-pnl-subtotal {
    font-weight: 700;
    color: var(--text);
    border-top: 2px solid var(--line);
}

/* @toc Flow */
/* Flow — server-rendered SVG flow diagram for the docs (our no-library take on
 * Mermaid). A vertical stack of connected nodes; every colour is a --flow-* token
 * from config/components/flow.php, so it themes like any component. No JS.
 * View: /resources/views/components/flow.blade.php. */

.flow {
    margin: 0;
}

.flow-svg {
    display: block;
    max-width: 100%;
    height: auto;
    font-family: var(--font-sans);
}

.flow-box {
    fill: var(--flow-node-bg);
    stroke: var(--flow-node-border);
    stroke-width: 1;
}

.flow-box.is-terminal {
    fill: var(--flow-accent-bg);
    stroke: var(--flow-accent-bg);
}

.flow-label {
    fill: var(--flow-node-text);
    font-size: var(--text-sm);
    font-weight: 500;
}

.flow-label.is-terminal {
    fill: var(--flow-accent-text);
}

.flow-note {
    fill: var(--flow-note-text);
    font-size: var(--text-xs);
}

.flow-edge {
    fill: none;
    stroke: var(--flow-edge);
    stroke-width: 1.5;
}

.flow-arrowhead {
    fill: none;
    stroke: var(--flow-edge);
    stroke-width: 1.5;
}

.flow-caption {
    margin-top: 10px;
    font-size: var(--text-xs);
    color: var(--text-muted);
    text-align: center;
}

/* @toc Flowchart */
/* Flowchart — a no-library, server-laid-out SVG flow diagram. Nodes in top-down
 * layers with shapes (process / start-end pill / decision diamond / cylinder) and
 * curved connectors; every colour is a --flowchart-* token (config). The viewport
 * is a window flowchart.js upgrades to pan/zoom (without JS it just scrolls).
 * View: /resources/views/components/flowchart.blade.php. */

.flowchart {
    margin: 0;
}

.flowchart-viewport {
    position: relative;
    overflow: auto;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--flowchart-bg);
}

.flowchart.is-grab .flowchart-viewport {
    cursor: grab;
    overflow: hidden;
}

.flowchart.is-grabbing .flowchart-viewport {
    cursor: grabbing;
}

.flowchart-canvas {
    transform-origin: 0 0;
}

.flowchart-svg {
    display: block;
    max-width: 100%;
    height: auto;
    font-family: var(--font-sans);
}

.flowchart-node {
    fill: var(--flowchart-node-bg);
    stroke: var(--flowchart-node-border);
    stroke-width: 1.5;
}

.flowchart-node.is-accent {
    fill: var(--flowchart-accent-bg);
    stroke: var(--flowchart-accent-bg);
}

.flowchart-cylinder-top {
    fill: none;
    stroke: var(--flowchart-node-border);
    stroke-width: 1.5;
}

.flowchart-label {
    fill: var(--flowchart-node-text);
    font-size: var(--text-sm);
    font-weight: 500;
}

.flowchart-label.is-accent {
    fill: var(--flowchart-accent-text);
}

.flowchart-note {
    fill: var(--flowchart-note-text);
    font-size: var(--text-xs);
}

.flowchart-note.is-accent {
    fill: var(--flowchart-accent-text);
}

.flowchart-edge {
    fill: none;
    stroke: var(--flowchart-edge);
    stroke-width: 1.5;
}

.flowchart-arrowhead {
    fill: none;
    stroke: var(--flowchart-edge);
    stroke-width: 1.5;
}

.flowchart-edge-label {
    fill: var(--flowchart-edge-label);
    font-size: var(--text-xs);
}

.flowchart-caption {
    margin-top: 10px;
    font-size: var(--text-xs);
    color: var(--text-muted);
    text-align: center;
}

/* @toc Footer */
.footer {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--footer-gap, 1rem);
    padding: var(--footer-padding, 2rem 1rem);
    color: var(--text-muted);
    font-size: 0.85rem;
}

.footer-link {
    color: inherit;
}

/* @toc Input */
/* Input. All --input-* tokens come from config/components/input.php['css']. */

.input {
    margin-bottom: 14px;
}

.input-label {
    display: block;
    margin-bottom: 6px;
    color: var(--input-label-color);
    font-size: var(--input-label-size);
}

.input-field {
    width: 100%;
    padding: var(--input-padding-y) var(--input-padding-x);
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius);
    background: var(--input-bg);
    color: var(--input-fg);
    font-size: var(--input-font-size);
    font-family: var(--font-sans);
}

/* opacity:1 stops the UA re-fading the placeholder below the token's contrast. */
.input-field::placeholder {
    color: var(--text-muted);
    opacity: 1;
}

/* Invalid / hint / required affordances. Error colour is the categorical --danger
 * token (red-700 light / red-400 dark) — self-flipping and menu-hover-certified, so
 * the text clears 4.5:1 and the border 3:1 on every surface without a per-theme rule. */
.input-field[aria-invalid="true"] {
    border-color: var(--danger);
}

.input-error {
    margin-top: 6px;
    font-size: var(--text-xs);
    color: var(--danger);
}

.input-hint {
    margin-top: 6px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.input-required {
    margin-left: 2px;
    color: var(--danger);
}

/* Keyboard focus recolors the border as a secondary cue; the global :focus-visible
 * ring (base.css) supplies the primary indicator. :focus-visible (not :focus) so a
 * mouse click doesn't trigger either. */
.input-field:focus-visible {
    border-color: var(--input-focus-border);
}

/* Disabled — a native attribute (it arrives via the passthrough channel). Dim + not-allowed so the field
 * reads inert against the tokened bg/fg instead of the UA default. No color literals: opacity carries it,
 * and disabled controls are exempt from the contrast minimum (WCAG 1.4.3). */
.input-field:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* @toc Invoice doc */
/* Invoice document — the print-ready 83 Consulting invoice + engineering log, authored as a themed
 * page: real palette vars (var(--neutral-*)/var(--primary-*)) from PaletteService, so it themes per
 * tenant and stays consistent on screen and on paper. Rasterized to a TAGGED PDF by browser/render.js.
 * Neutral ink on white; only the brand mark carries primary. @media print drops the app chrome. */

.invoice-doc {
    max-width: 780px;
    margin: 0 auto;
    padding: 40px 44px;
    background: var(--paper);
    color: var(--neutral-900);
    border: 1px solid var(--line);
    border-radius: 12px;
    font-size: 14px;
    line-height: 1.5;
    font-variant-numeric: tabular-nums;
}

.invoice-doc p {
    margin: 0;
}

/* Page 1 fills the printable area (letter content box = 792pt - 2·54pt margin ≈ 684pt; trimmed a hair
 * to avoid a rounding overflow onto a blank page), so the footer can sit at the foot of the page. */
.inv-page {
    display: flex;
    flex-direction: column;
    min-height: 672pt;
}

/* --- header --- */
.inv-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 24px;
    padding-bottom: 22px;
    border-bottom: 1.5px solid var(--neutral-900);
}

.inv-brand {
    display: flex;
    align-items: center;
    gap: 13px;
}

.inv-brand .brand-logo svg,
.inv-log-brand .brand-logo svg {
    display: block;
    width: 40px;
    height: 40px;
}

.inv-issuer-name {
    font-size: 17px;
    font-weight: 700;
}

.inv-issuer {
    text-align: right;
    color: var(--neutral-500);
    font-size: 12px;
    line-height: 1.5;
}

.inv-title {
    font-size: 26px;
    font-weight: 800;
    color: var(--neutral-900);
    letter-spacing: -0.01em;
    margin-bottom: 6px !important;
}

/* --- parties + meta --- */
.inv-parties {
    display: flex;
    justify-content: space-between;
    gap: 32px;
    margin: 26px 0 30px;
}

.inv-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--neutral-500);
    margin-bottom: 8px !important;
}

.inv-client {
    font-weight: 700;
    font-size: 15px;
}

.inv-billto p {
    color: var(--neutral-700);
}

.inv-meta {
    min-width: 250px;
    margin: 0;
    font-size: 13px;
}

.inv-meta > div {
    display: flex;
    justify-content: space-between;
    gap: 28px;
    padding: 4px 0;
}

.inv-meta dt {
    margin: 0;
    color: var(--neutral-500);
}

.inv-meta dd {
    margin: 0;
    text-align: right;
    color: var(--neutral-700);
    white-space: nowrap;
}

/* --- items: the line-items table is now <x-table>; re-dress its .table markup as the invoice
   document table (invoice-scoped, so the app data-table look never leaks in and vice-versa). The
   print surface forces the light theme, so the neutral rungs read as a dark bar on white paper. --- */
.invoice-doc .table-wrap {
    overflow: visible;
}

.invoice-doc .table {
    width: 100%;
    border-collapse: collapse;
    font-size: inherit;
}

.invoice-doc .table th {
    padding: 8px 14px;
    font-size: 10.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    text-align: left;
    color: var(--paper);
    background: var(--neutral-900);
    border-bottom: none;
    white-space: nowrap;
}

.invoice-doc .table th.table-right {
    text-align: right;
}

.invoice-doc .table td {
    padding: 13px 14px;
    border-bottom: 1px solid var(--neutral-200);
    vertical-align: top;
}

.invoice-doc .table td.table-right {
    text-align: right;
    white-space: nowrap;
}

/* The stacked "Items" cell (Cell::stacked): the description bold over the muted period. */
.invoice-doc .cell-stacked > div:first-child {
    font-weight: 700;
}

.invoice-doc .cell-stacked > div + div {
    margin-top: 2px;
    color: var(--neutral-500);
    font-size: 13px;
}

/* --- totals --- */
.inv-totals-wrap {
    display: flex;
    justify-content: flex-end;
    margin-top: 20px;
}

.inv-totals {
    min-width: 250px;
    margin: 0;
}

.inv-total-row {
    display: flex;
    justify-content: space-between;
    gap: 24px;
    padding: 5px 14px;
}

.inv-total-row dt {
    margin: 0;
    color: var(--neutral-500);
}

.inv-total-row dd {
    margin: 0;
    text-align: right;
    color: var(--neutral-700);
    white-space: nowrap;
}

/* amount due — a highlighted bar (gray fill, no border) spanning the label + figure row */
.inv-totals-due {
    margin-top: 5px;
    padding-top: 10px;
    padding-bottom: 10px;
    background: var(--neutral-100);
}

.inv-totals-due dt {
    color: var(--neutral-900);
    font-weight: 700;
    font-size: 15px;
}

.inv-totals-due dd {
    color: var(--neutral-900);
    font-weight: 800;
    font-size: 15px;
}

/* --- footer (foot of the invoice page) --- */
.inv-foot {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: 24px;
    margin-top: auto;
    padding-top: 14px;
    border-top: 1px solid var(--neutral-200);
    font-size: 11px;
    color: var(--neutral-500);
}

.inv-foot-issuer {
    text-align: right;
    font-weight: 600;
}

/* --- engineering log — its own page --- */
.inv-log {
    break-before: page;
    padding-top: 4px;
}

.inv-log-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 26px;
}

.inv-log-brand {
    display: inline-flex;
    align-items: center;
    gap: 11px;
    font-size: 13px;
    font-weight: 700;
}

.inv-log-brand .brand-logo svg {
    width: 30px;
    height: 30px;
}

.inv-log-num {
    color: var(--neutral-500);
    font-size: 12px;
}

.inv-log-h {
    margin: 0 0 4px;
    font-size: 22px;
    font-weight: 800;
    color: var(--neutral-900);
}

.inv-log-sub {
    margin: 0 0 18px !important;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--neutral-200);
    color: var(--neutral-500);
    font-size: 12px;
}

.inv-log-entry {
    margin-bottom: 16px;
    break-inside: avoid;
}

.inv-log-title {
    font-weight: 700;
    font-size: 13px;
    margin-bottom: 5px !important;
}

.inv-log-bullets {
    margin: 0;
    padding-left: 18px;
    color: var(--neutral-700);
    font-size: 12px;
    line-height: 1.5;
}

.inv-log-bullets li {
    margin-bottom: 3px;
}

/* --- screen chrome: toolbar, list, form (all theme-tokened; hidden or irrelevant on print) --- */
.inv-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: var(--space-5);
}

.inv-tb-back {
    font-size: var(--text-sm);
    color: var(--text-muted);
    text-decoration: none;
}

.inv-tb-back:hover {
    color: var(--text);
}

.inv-tb-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

.inv-tb-actions form {
    margin: 0;
}

.inv-tb-btn {
    padding: 8px 14px;
    font-size: var(--text-sm);
    font-weight: 600;
    background: var(--surface-raised, var(--neutral-100));
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--text);
    text-decoration: none;
    cursor: pointer;
}

.inv-tb-btn:hover {
    border-color: var(--line-strong, var(--neutral-400));
}

.inv-tb-btn.is-primary {
    background: var(--primary-600, var(--palette-rose-600));
    border-color: transparent;
    color: var(--on-primary);
}

/* The undo/revert action (Revert to draft) — lower-emphasis than the solid forward
   buttons: muted text + a dashed border set it apart as a step back, not forward. */
.inv-tb-btn.is-undo {
    color: var(--text-muted);
    border-style: dashed;
}

.inv-tb-btn.is-undo:hover {
    color: var(--text);
}

.inv-status {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 3px 9px;
    border-radius: 20px;
}

.inv-status.is-draft {
    background: var(--neutral-200, #e5e5e5);
    color: var(--text-muted);
}

.inv-status.is-sent {
    background: var(--warning-surface);
    color: var(--warning);
}

.inv-status.is-paid {
    background: var(--success-surface);
    color: var(--success);
}

.inv-list-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: var(--space-5);
}

.inv-list {
    display: flex;
    flex-direction: column;
}

.inv-row {
    display: block;
    padding: 14px 12px;
    border-bottom: 1px solid var(--line);
    border-radius: 8px;
    color: var(--text);
    text-decoration: none;
}

.inv-row:hover,
.inv-row:active {
    background: var(--surface-raised, var(--neutral-100));
}

.inv-row-main {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.inv-row-num {
    font-size: 15px;
    font-weight: 700;
}

.inv-row-amt {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.inv-row-sub {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 4px;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.inv-form {
    max-width: 720px;
}

.inv-form-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-bottom: var(--space-4);
}

.inv-form-num {
    font-size: 20px;
    font-weight: 800;
}

.inv-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    margin-bottom: 16px;
}

.inv-form label {
    display: flex;
    flex-direction: column;
    gap: 5px;
    font-size: var(--text-sm);
    font-weight: 600;
}

.inv-form input,
.inv-form textarea {
    padding: 9px 12px;
    font-size: var(--text-sm);
    font-weight: 400;
    background: var(--surface-raised, var(--neutral-100));
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--text);
    font-family: inherit;
}

.inv-form input:focus,
.inv-form textarea:focus {
    outline: none;
    border-color: var(--primary-500, var(--palette-rose-500));
}

.inv-form-log {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.inv-form-log textarea {
    resize: vertical;
    line-height: 1.5;
}

.inv-form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 18px;
}

/* --- invoice view page: mobile-friendly summary + desktop PDF embed --- */
.inv-view {
    max-width: 720px;
}

.inv-view-card {
    padding: 20px 22px;
    background: var(--surface-raised, var(--neutral-100));
    border: 1px solid var(--line);
    border-radius: 14px;
}

.inv-view-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
}

.inv-view-logo {
    display: inline-flex;
}

.inv-view-logo svg {
    display: block;
    width: 44px;
    height: auto;
    border-radius: 8px;
}

.inv-view-num {
    margin: 0;
    font-size: 22px;
    font-weight: 800;
}

.inv-view-sub {
    margin: 2px 0 20px;
    color: var(--text-muted);
}

.inv-view-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin: 0 0 22px;
}

.inv-view-amtbox {
    grid-column: 1 / -1;
}

.inv-view-grid dt {
    margin-bottom: 4px;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.inv-view-grid dd {
    margin: 0;
    font-weight: 600;
}

.inv-view-amt {
    font-size: 26px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

.inv-view-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.inv-view-actions form {
    margin: 0;
}

.inv-view-back {
    margin: 16px 0 0;
}

.inv-view-back a {
    color: var(--text-muted);
    font-size: var(--text-sm);
    text-decoration: none;
}

.inv-view-embed {
    display: none;
    width: 100%;
    border: 1px solid var(--line);
    border-radius: 12px;
}

/* 900px: invoice document layout breakpoint; document-specific, not the app seam. */
@media (min-width: 900px) {
    .inv-view {
        max-width: 900px;
    }

    .inv-view-embed {
        display: block;
        height: 1000px;
        margin-top: 24px;
    }
}

/* --- print: drop the app chrome, print just the document --- */
@media print {
    .app-shell-bar,
    .sidebar-nav,
    .app-shell-backdrop,
    .app-header-bar,
    .inv-toolbar {
        display: none !important;
    }

    .app-shell-main,
    .app-shell-wrap {
        padding: 0 !important;
        margin: 0 !important;
    }

    .invoice-doc {
        max-width: none;
        border: none;
        border-radius: 0;
        padding: 0;
    }
}

/* @toc Link */
/*
 * Link. A themed native <a>: accent colour, underline-on-hover, a visible
 * :focus-visible ring, and a trailing external-link icon. Every token is a
 * --link-* var (emitted from config/components/link.php['css']) with a global
 * token fallback, so the config is the full theming surface. Dark-first.
 */

.link {
    color: var(--link-color, var(--link));
    text-decoration: none;
    border-radius: var(--link-radius, 2px);
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}

.link:hover {
    text-decoration: underline;
    text-decoration-thickness: var(--link-underline-thickness, 1px);
    text-underline-offset: var(--link-underline-offset, 2px);
}

/* muted — sits quietly inline; reads as text until hovered, then it accents. */
.link-muted {
    color: var(--link-muted-color, var(--text-muted));
}

.link-muted:hover {
    color: var(--link-muted-color-hover, var(--text));
}

/* The visible ring is a box-shadow (hugs the text box). The transparent outline
 * is a forced-colors fallback: box-shadow is dropped in high-contrast mode, so
 * the outline gets promoted to a system colour and focus stays visible. */
.link:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--link-focus-ring, var(--primary));
}

/* External affordance — a trailing icon that inherits the link colour, aligned
 * to the text baseline and never wrapping away from the last word. */
.link-external-icon {
    display: inline-block;
    margin-left: 2px;
    vertical-align: -0.125em;
    white-space: nowrap;
}

.link-external-icon svg {
    display: inline-block;
    width: var(--link-icon-size, 0.9em);
    height: var(--link-icon-size, 0.9em);
}

/* @toc Login */
/* Login component. All --login-* tokens from config/components/login.php['css']. */

.login {
    width: 100%;
    max-width: 360px;
    padding: var(--login-padding);
    background: var(--login-bg);
    color: var(--login-fg);
    border: 1px solid var(--login-border);
    border-radius: var(--login-radius);
}

.login-title {
    margin: 0 0 18px;
    font-size: var(--text-xl);
    font-weight: 500;
}

.login-error {
    margin-bottom: 12px;
    color: var(--login-error-color);
    font-size: var(--text-sm);
}

.login .checkbox {
    margin: 14px 0 4px;
    font-size: var(--text-sm);
}

.login .button {
    width: 100%;
    justify-content: center;
    margin-top: 6px;
}

@keyframes login-shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-6px); }
    75% { transform: translateX(6px); }
}

/* @toc Mps food */
/* Meal Prep Sundays — the Food Library (index + detail). Green/light, mobile-first.
 * Colours from the --palette-* catalog so it themes under the eightythree.com host. */

/* --- index --- */
.mps-foods {
    padding: 40px 24px 72px;
}

.mps-foods-title {
    margin: 0 0 10px;
    font-size: 36px;
    font-weight: 900;
    color: var(--palette-slate-900);
}

.mps-foods-lead {
    margin: 0 0 28px;
    max-width: 60ch;
    font-size: 18px;
    line-height: 1.5;
    color: var(--palette-slate-500);
}

.mps-foods-cat {
    margin: 28px 0 12px;
    font-size: 14px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--palette-green-700);
}

.mps-foods-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px;
}

.mps-food-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px 16px;
    background: #fff;
    border: 1px solid var(--palette-slate-200);
    border-radius: 12px;
    text-decoration: none;
    transition: border-color 0.12s ease, transform 0.12s ease;
}

.mps-food-card:hover {
    border-color: var(--palette-green-400);
    transform: translateY(-2px);
}

.mps-food-card-name {
    font-weight: 800;
    color: var(--palette-slate-900);
}

.mps-food-card-cal {
    font-size: 13px;
    color: var(--palette-slate-500);
}

/* --- detail --- */
.mps-food {
    padding: 28px 24px 72px;
}

.mps-food-back {
    display: inline-block;
    margin-bottom: 16px;
    font-size: 14px;
    font-weight: 700;
    color: var(--palette-green-700);
    text-decoration: none;
}

.mps-food-back:hover {
    text-decoration: underline;
}

.mps-food-cat {
    margin: 0 0 4px;
    font-size: 13px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--palette-green-600);
}

.mps-food-title {
    margin: 0 0 8px;
    font-size: 38px;
    font-weight: 900;
    color: var(--palette-slate-900);
}

.mps-food-aliases {
    margin: 0 0 8px;
    font-size: 15px;
    color: var(--palette-slate-500);
}

.mps-food-cols {
    display: grid;
    grid-template-columns: 1fr;
    gap: 28px;
    margin-top: 24px;
}

/* 880px: MPS content-column breakpoint (intentional, shared across the recipe layout); not the app --bp-* seam. */
@media (min-width: 880px) {
    .mps-food-cols {
        grid-template-columns: 1fr 280px;
    }
}

.mps-food-h2 {
    margin: 28px 0 10px;
    font-size: 20px;
    font-weight: 800;
    color: var(--palette-slate-900);
}

.mps-food-h2 span {
    font-weight: 500;
    font-size: 15px;
    color: var(--palette-slate-400);
}

.mps-food-note {
    margin: 0 0 10px;
    max-width: 62ch;
    font-size: 16px;
    line-height: 1.6;
    color: var(--palette-slate-600);
}

.mps-food-src {
    color: var(--palette-green-700);
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.mps-food-src:hover {
    color: var(--palette-green-800);
}

.mps-food-nut {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    max-width: 480px;
}

.mps-food-nut div {
    display: flex;
    flex-direction: column;
    padding: 14px;
    background: var(--palette-green-50);
    border: 1px solid var(--palette-green-100);
    border-radius: 12px;
}

.mps-food-nut b {
    font-size: 24px;
    font-weight: 900;
    color: var(--palette-green-700);
    font-variant-numeric: tabular-nums;
}

.mps-food-nut span {
    font-size: 12px;
    color: var(--palette-slate-500);
}

.mps-food-units {
    margin-top: 12px;
    min-width: 240px;
    border-collapse: collapse;
}

.mps-food-units td {
    padding: 8px 16px 8px 0;
    border-bottom: 1px solid var(--palette-slate-100);
    color: var(--palette-slate-700);
}

.mps-food-units td:last-child {
    font-weight: 700;
    color: var(--palette-slate-900);
    font-variant-numeric: tabular-nums;
}

/* 880px: MPS content-column breakpoint (intentional, shared across the recipe layout); not the app --bp-* seam. */
@media (min-width: 880px) {
    .mps-food-side {
        position: sticky;
        top: 88px;
        align-self: start;
    }
}

/* Picture slot */
.mps-food-photo {
    aspect-ratio: 16 / 10;
    margin-bottom: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--palette-green-100);
    border-radius: 16px;
    overflow: hidden;
}

.mps-food-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.mps-food-photo-empty {
    font-size: 14px;
    font-weight: 700;
    color: var(--palette-green-700);
    opacity: 0.7;
}

/* Blurb */
.mps-food-blurb {
    margin: 0 0 8px;
    max-width: 64ch;
    font-size: 18px;
    line-height: 1.6;
    color: var(--palette-slate-700);
}

/* Tips rail (pick / store / cook / grow) */
.mps-food-side {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.mps-food-tip {
    padding: 16px;
    background: #fff;
    border: 1px solid var(--palette-slate-200);
    border-radius: 14px;
}

.mps-food-tip-h {
    margin: 0 0 6px;
    font-size: 12px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--palette-green-700);
}

.mps-food-tip-b {
    margin: 0;
    font-size: 14px;
    line-height: 1.55;
    color: var(--palette-slate-600);
}

/* @toc Mps page */
/* Meal Prep Sundays tenant shell — Satoshi font, light/green theme (data-primary="green"
 * + data-neutral="slate"), a sticky header and a 2-tier footer. Scoped to body.mps so it
 * never touches eightythree.com. Brand greens come from the always-on --palette-green-*
 * catalog (the orange accent will use --palette-orange-* / --palette-amber-*). */

@font-face {
    font-family: 'Satoshi';
    src: url('/fonts/satoshi/Satoshi-Variable.woff2') format('woff2'),
         url('/fonts/satoshi/Satoshi-Variable.ttf') format('truetype');
    font-weight: 300 900;
    font-style: normal;
    font-display: swap;
}

body.mps {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    font-family: 'Satoshi', system-ui, -apple-system, sans-serif;
    color: var(--palette-slate-800, #1e293b);
    background: var(--palette-green-50, #f0fdf4);
    -webkit-font-smoothing: antialiased;
}

.mps-shell {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
}

/* Header */
.mps-header {
    position: sticky;
    top: 0;
    z-index: 20;
    background: #fff;
    border-bottom: 2px solid var(--palette-slate-900, #0f172a);
}

.mps-header-inner {
    height: 72px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.mps-logo {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
}

/* The wordmark scales with the viewport, clamped to a sensible min/max. */
.mps-logo img {
    display: block;
    width: clamp(150px, 22vw, 196px);
    height: auto;
}

.mps-nav {
    display: flex;
    gap: 28px;
}

.mps-nav a {
    font-weight: 700;
    font-size: 16px;
    text-decoration: none;
    color: var(--palette-slate-900, #0f172a);
}

.mps-nav a:hover {
    color: var(--palette-green-600, #16a34a);
}

/* Main — grows to fill so the footer sits at the bottom, never a gap below it. */
.mps-main {
    flex: 1 0 auto;
}

/* Footer */
.mps-footer {
    flex-shrink: 0;
}

.mps-footer-links {
    background: var(--palette-green-800, #166534);
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 14px 28px;
    padding: 28px 24px;
}

.mps-footer-links a {
    color: #fff;
    font-size: 14px;
    text-decoration: none;
}

.mps-footer-links a:hover {
    text-decoration: underline;
}

.mps-footer-copy {
    background: var(--palette-green-900, #14532d);
    color: rgba(255, 255, 255, 0.85);
    text-align: center;
    font-size: 12px;
    padding: 14px 24px;
}

@media (max-width: 640px) {
    .mps-header-inner {
        height: 56px;
    }

    .mps-shell {
        padding: 0 16px;
    }
}

/* --- responsive menu: inline nav on desktop, a hamburger drawer on mobile --- */
.mps-burger {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    padding: 11px 9px;
    cursor: pointer;
    background: #fff;
    border: 2px solid var(--palette-slate-900, #0f172a);
    border-radius: 8px;
    box-shadow: 2px 2px 0 var(--palette-slate-900, #0f172a);
}

.mps-burger span {
    display: block;
    height: 2px;
    background: var(--palette-slate-900, #0f172a);
    border-radius: 2px;
    transition: transform 0.18s, opacity 0.18s;
}

.mps-burger[aria-expanded="true"] span:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
}

.mps-burger[aria-expanded="true"] span:nth-child(2) {
    opacity: 0;
}

.mps-burger[aria-expanded="true"] span:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
}

.mps-drawer {
    display: flex;
    flex-direction: column;
    background: #fff;
    border-bottom: 2px solid var(--palette-slate-900, #0f172a);
}

.mps-drawer[hidden] {
    display: none;
}

.mps-drawer a {
    padding: 14px 24px;
    font-weight: 800;
    font-size: 18px;
    color: var(--palette-slate-900, #0f172a);
    text-decoration: none;
    border-top: 1px solid var(--palette-green-100, #dcfce7);
}

.mps-drawer a:hover {
    background: var(--palette-green-50, #f0fdf4);
}

@media (max-width: 768px) {
    .mps-nav {
        display: none;
    }

    .mps-burger {
        display: flex;
    }
}

@media (min-width: 769px) {
    .mps-drawer {
        display: none;
    }
}

/* --- neo-brutalist contrast (MPS only): thick near-black borders + hard offset shadows, no blur,
   so the cards/buttons/table have real punch. Scoped to body.mps; eightythree.com is untouched. --- */
body.mps {
    --mps-ink: var(--palette-slate-900, #0f172a);
    --mps-shadow: 4px 4px 0 var(--palette-slate-900, #0f172a);
    --mps-shadow-sm: 2px 2px 0 var(--palette-slate-900, #0f172a);
}

body.mps .mps-scaler,
body.mps .mps-nutrition,
body.mps .mps-mealkit,
body.mps .mps-scale-anchor,
body.mps .mps-modal,
body.mps .table-wrap {
    border: 2px solid var(--mps-ink);
    border-radius: 10px;
    box-shadow: var(--mps-shadow);
}

body.mps .mps-stepper-btn,
body.mps .mps-quick,
body.mps .mps-mealkit-cta,
body.mps .mps-modal-submit,
body.mps .mps-modal-cancel {
    border: 2px solid var(--mps-ink);
    box-shadow: var(--mps-shadow-sm);
}

body.mps .mps-stepper-btn:active,
body.mps .mps-quick:active,
body.mps .mps-mealkit-cta:active,
body.mps .mps-modal-submit:active,
body.mps .mps-modal-cancel:active {
    box-shadow: none;
    transform: translate(2px, 2px);
}

body.mps .table thead th {
    border-bottom: 2px solid var(--mps-ink);
    color: var(--mps-ink);
}

/* Tabs — a black rule under the bar, a thicker indicator, bolder active tab. */
body.mps .tabs-nav {
    border-bottom: 2px solid var(--mps-ink);
}

body.mps .tabs-tab.is-active {
    color: var(--mps-ink);
}

body.mps .tabs-tab.is-active .tabs-tab-label {
    font-weight: 800;
}

body.mps .tabs-indicator {
    height: 3px;
    background: var(--palette-green-600, #16a34a);
}

/* --- distinctive recipe card (the gallery): bordered, hard-shadow, image-top, highlighter badge --- */
.mps-recipe-card {
    display: block;
    background: #fff;
    border: 2px solid var(--palette-slate-900, #0f172a);
    border-radius: 12px;
    box-shadow: 4px 4px 0 var(--palette-slate-900, #0f172a);
    text-decoration: none;
    overflow: hidden;
    transition: transform 0.12s, box-shadow 0.12s;
}

.mps-recipe-card:hover {
    transform: translate(-2px, -2px);
    box-shadow: 6px 6px 0 var(--palette-slate-900, #0f172a);
}

.mps-recipe-card-img {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    border-bottom: 2px solid var(--palette-slate-900, #0f172a);
    background: var(--palette-green-100, #dcfce7);
}

.mps-recipe-card-body {
    padding: 16px 18px;
}

.mps-recipe-card-title {
    display: block;
    font-weight: 900;
    font-size: 20px;
    line-height: 1.15;
    color: var(--palette-slate-900, #0f172a);
}

/* Green highlighter badge — the signature MPS marker swipe. */
.mps-recipe-card-badge {
    display: inline-block;
    margin-top: 10px;
    font-weight: 800;
    font-size: 13px;
    color: var(--palette-green-900, #14532d);
    background: var(--palette-green-200, #bbf7d0);
    padding: 3px 8px;
}

/* --- "Grow" tab: the recipe's home-growable produce (the vegetable class) --- */
.mps-grow-lead {
    margin: 0 0 12px;
    font-weight: 800;
    color: var(--palette-slate-900, #0f172a);
}

.mps-grow-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.mps-grow-list li {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 16px;
    color: var(--palette-slate-800, #1e293b);
}

.mps-grow-class {
    font-size: 12px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--palette-green-800, #166534);
    background: var(--palette-green-100, #dcfce7);
    padding: 2px 7px;
    border-radius: 999px;
}

/* @toc Mps recipe */
/* Meal Prep Sundays — the recipe (weight-scaler) page. Light/green, mobile-first, two
 * columns on desktop (recipe + a sticky nutrition rail). Colours from the --palette-*
 * catalog so it themes correctly even when served under the eightythree.com host. */

.mps-recipe {
    padding: 28px 24px 72px;
}

.mps-recipe-back {
    display: inline-block;
    margin-bottom: 16px;
    font-size: 14px;
    font-weight: 700;
    color: var(--palette-green-700);
    text-decoration: none;
}

.mps-recipe-back:hover {
    text-decoration: underline;
}

.mps-recipe-title {
    margin: 0 0 10px;
    font-size: 36px;
    font-weight: 900;
    line-height: 1.1;
    color: var(--palette-slate-900);
}

.mps-recipe-desc {
    margin: 0 0 12px;
    max-width: 60ch;
    font-size: 18px;
    line-height: 1.5;
    color: var(--palette-slate-600);
}

.mps-recipe-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.mps-tag {
    font-size: 12px;
    font-weight: 700;
    color: var(--palette-green-700);
    background: var(--palette-green-100);
    padding: 3px 10px;
    border-radius: 999px;
}

.mps-recipe-video {
    aspect-ratio: 16 / 9;
    margin: 20px 0;
    border-radius: 14px;
    overflow: hidden;
}

.mps-recipe-video iframe {
    width: 100%;
    height: 100%;
    border: 0;
}

.mps-recipe-body {
    display: grid;
    grid-template-columns: 1fr;
    gap: 28px;
    margin-top: 24px;
}

/* 880px: MPS content-column breakpoint (intentional, shared across the recipe layout); not the app --bp-* seam. */
@media (min-width: 880px) {
    .mps-recipe-body {
        grid-template-columns: 1fr 300px;
    }
}

/* --- the scaler control --- */
.mps-scaler {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px 16px;
    margin-bottom: 8px;
    padding: 16px;
    background: #fff;
    border: 1px solid var(--palette-slate-200);
    border-radius: 14px;
}

.mps-scaler-label {
    font-weight: 700;
    color: var(--palette-slate-700);
}

.mps-stepper {
    display: flex;
    align-items: center;
    gap: 6px;
}

.mps-stepper-btn {
    width: 40px;
    height: 40px;
    font-size: 22px;
    font-weight: 700;
    color: #fff;
    background: var(--palette-green-600);
    border: 0;
    border-radius: 10px;
    cursor: pointer;
}

.mps-stepper-btn:hover {
    background: var(--palette-green-700);
}

.mps-stepper-val {
    min-width: 120px;
    text-align: center;
    font-weight: 800;
    color: var(--palette-slate-900);
}

.mps-stepper-val output {
    font-size: 22px;
    font-variant-numeric: tabular-nums;
}

.mps-scaler-quick {
    display: flex;
    gap: 6px;
}

.mps-quick {
    width: 38px;
    height: 38px;
    font-weight: 700;
    color: var(--palette-green-700);
    background: var(--palette-green-50);
    border: 1px solid var(--palette-green-200);
    border-radius: 10px;
    cursor: pointer;
}

.mps-quick:hover {
    background: var(--palette-green-100);
}

.mps-recipe-h2 {
    margin: 28px 0 12px;
    font-size: 22px;
    font-weight: 800;
    color: var(--palette-slate-900);
}

/* --- ingredient cells — the table chrome (scroll, header, zebra, borders) comes from the shared
   .table component; here we only style the recipe-specific cell content + the tap-to-scale affordance. */

/* Amount = imperial · metric in one cell, and the tap-to-scale target. */
.mps-ing-measure {
    white-space: nowrap;
    cursor: pointer;
    border-radius: 8px;
    font-weight: 700;
    color: var(--palette-slate-900);
}

.mps-ing-measure.is-volume {
    color: var(--palette-green-700);
}

.mps-ing-measure:hover,
.mps-ing-measure:focus-visible {
    background: var(--palette-green-50);
    outline: none;
}

/* Count = the friendly piece count (green). When a size equivalent exists it's a clickable button. */
.mps-ing-count {
    font-weight: 800;
    color: var(--palette-green-700);
}

.mps-ing-count.is-clickable {
    font: inherit;
    font-weight: 800;
    color: var(--palette-green-700);
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    text-decoration: underline dotted var(--palette-green-400);
    text-underline-offset: 3px;
}

/* The revealed size equivalent ("about a baseball"). */
.mps-ing-size {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--palette-slate-500);
}

/* Placeholder copy for the stubbed tabs (History / Grow / empty Procedure). */
.mps-tab-empty {
    padding: 24px 4px;
    color: var(--palette-slate-500);
    font-size: 14px;
}

/* "Now scaling off X" banner, shown after a scale-by-ingredient. */
.mps-scale-anchor {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 0 16px;
    padding: 10px 14px;
    background: var(--palette-green-100);
    border: 1px solid var(--palette-green-300);
    border-radius: 10px;
    font-weight: 700;
    color: var(--palette-green-800);
}

.mps-scale-anchor[hidden] {
    display: none;
}

.mps-scale-reset {
    margin-left: auto;
    font-size: 13px;
    font-weight: 700;
    color: var(--palette-green-700);
    background: none;
    border: none;
    cursor: pointer;
    text-decoration: underline;
}

/* Scale-by-ingredient modal (native <dialog>). */
.mps-modal {
    border: none;
    border-radius: 16px;
    padding: 0;
    max-width: 380px;
    width: calc(100% - 32px);
    background: #fff;
    box-shadow: 0 20px 60px rgba(15, 23, 42, 0.25);
}

.mps-modal::backdrop {
    background: rgba(15, 23, 42, 0.5);
}

.mps-modal-card {
    padding: 24px;
}

.mps-modal-title {
    margin: 0 0 6px;
    font-size: 20px;
    font-weight: 900;
    color: var(--palette-slate-900);
}

.mps-modal-title span {
    color: var(--palette-green-700);
    text-transform: capitalize;
}

.mps-modal-sub {
    margin: 0 0 14px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--palette-slate-500);
}

.mps-modal-fields {
    display: flex;
    gap: 12px;
}

.mps-modal-field {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.mps-modal-unit {
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--palette-slate-500);
}

.mps-modal-input {
    width: 100%;
    box-sizing: border-box;
    font-size: 18px;
    font-weight: 700;
    padding: 10px 12px;
    border: 1px solid var(--palette-slate-300);
    border-radius: 10px;
}

.mps-modal-input:focus {
    outline: none;
    border-color: var(--palette-green-500);
}

.mps-modal-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    margin-top: 16px;
}

.mps-modal-cancel,
.mps-modal-submit {
    font-weight: 700;
    padding: 9px 16px;
    border-radius: 10px;
    cursor: pointer;
    border: 1px solid transparent;
}

.mps-modal-cancel {
    background: none;
    color: var(--palette-slate-600);
    border-color: var(--palette-slate-300);
}

.mps-modal-submit {
    background: var(--palette-green-600);
    color: #fff;
}

.mps-modal-submit:hover {
    background: var(--palette-green-700);
}

.mps-ing-hint {
    margin: 0 0 14px;
    font-size: 13px;
    color: var(--palette-slate-500);
}

.mps-ing-name {
    display: block;
    color: var(--palette-slate-900);
    text-transform: capitalize;
}

/* Prep ("diced") — a quiet cooking note under the name, separated from the WHAT you shop for. */
.mps-ing-prep {
    display: block;
    margin-top: 1px;
    font-size: 13px;
    color: var(--palette-slate-500);
}

/* Ingredient -> its Food Library page. Subtle (inherits colour) — a green underline signals
   it's clickable without turning the list into a wall of links. */
.mps-ing-link {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: var(--palette-green-300);
    text-underline-offset: 2px;
}

.mps-ing-link:hover {
    text-decoration-color: var(--palette-green-600);
}

.mps-ing-buy {
    display: inline-block;
    margin-top: 6px;
    font-size: 12px;
    font-weight: 700;
    color: var(--palette-green-700);
    border: 1px solid var(--palette-green-300);
    border-radius: 8px;
    padding: 4px 10px;
    text-decoration: none;
}

.mps-ing-buy:hover {
    background: var(--palette-green-50);
}

/* --- instructions --- */
.mps-steps {
    margin: 0;
    padding-left: 22px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.mps-steps li {
    padding-left: 4px;
    color: var(--palette-slate-700);
    line-height: 1.55;
}

/* --- nutrition + meal-kit rail --- */
.mps-recipe-side {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* 880px: MPS content-column breakpoint (intentional, shared across the recipe layout); not the app --bp-* seam. */
@media (min-width: 880px) {
    .mps-recipe-side {
        position: sticky;
        top: 88px;
        align-self: start;
    }
}

.mps-nutrition {
    padding: 18px;
    background: var(--palette-green-700);
    color: #fff;
    border-radius: 14px;
}

.mps-nutrition-h {
    margin: 0 0 12px;
    font-size: 14px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.mps-nutrition-h span {
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    opacity: 0.8;
}

.mps-nutrition-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.mps-nut {
    display: flex;
    flex-direction: column;
}

.mps-nut-v {
    font-size: 26px;
    font-weight: 900;
    font-variant-numeric: tabular-nums;
}

.mps-nut-k {
    font-size: 12px;
    opacity: 0.85;
}

.mps-mealkit {
    padding: 18px;
    background: #fff;
    border: 1px solid var(--palette-slate-200);
    border-radius: 14px;
}

.mps-mealkit-lead {
    margin: 0 0 4px;
    font-weight: 800;
    color: var(--palette-slate-900);
}

.mps-mealkit-body {
    margin: 0 0 12px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--palette-slate-600);
}

.mps-mealkit-cta {
    display: block;
    text-align: center;
    font-weight: 800;
    color: #fff;
    background: var(--palette-amber-500);
    padding: 11px;
    border-radius: 10px;
    text-decoration: none;
}

.mps-mealkit-cta:hover {
    background: var(--palette-amber-600);
}

.mps-mealkit-disc {
    margin: 8px 0 0;
    font-size: 11px;
    text-align: center;
    color: var(--palette-slate-400);
}

/* @toc Pagination */
/* Pagination — count line + prev/page/next, laid out as a row. Reuses .button
 * (button-ghost + is-disabled) for the controls; only layout + muted text here. */

.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-top: 16px;
}

.pagination-info {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.pagination-controls {
    display: flex;
    align-items: center;
    gap: 10px;
}

.pagination-page {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* @toc Planner board */
/* Planner board — a Trello-style kanban. Lists scroll horizontally (scroll-snap one
 * per screen on mobile); cards are dragged with a single unified pointer drag (mouse +
 * touch) that leaves a placeholder gap; the card back is a full detail dialog. Colours
 * ride the semantic tokens; layout knobs come through --planner-board-* with fallbacks.
 * View: planner-board.blade.php · JS: planner-board.js */

.planner-board {
    display: flex;
    gap: var(--planner-board-gap, 14px);
    align-items: flex-start;
    overflow-x: auto;
    padding-bottom: 10px;
    /* The board scrolls horizontally, lists scroll vertically. A drag captures its
     * pointer (setPointerCapture in the JS) the moment it starts, so neither scroller
     * can steal the gesture mid-drag. The scrollbar itself is the global themed one (base.css). */
}

/* While a drag is live, kill text selection + native scroll gestures board-wide (the
 * descendants too, so a touch can't start a scroll mid-drag). */
.planner-board.is-dragging,
.planner-board.is-dragging * {
    touch-action: none;
    user-select: none;
}

.planner-board.is-dragging {
    cursor: grabbing;
    /* Let the edge auto-scroll glide across lists mid-drag — scroll-snap would fight it. */
    scroll-snap-type: none;
}

/* --- a list (lane) — an elevated panel that sits above the page --- */
.planner-list {
    flex: 0 0 var(--planner-board-list-width, 286px);
    display: flex;
    flex-direction: column;
    max-height: calc(100dvh - 184px);
    background: var(--planner-board-list-bg, var(--surface));
    border: 1px solid var(--line);
    border-radius: 14px;
    box-shadow:
        0 1px 3px color-mix(in srgb, #000 26%, transparent),
        0 1px 1px color-mix(in srgb, #000 18%, transparent);
}

.planner-list-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px 9px;
    /* A hairline parts the header from the card well, so the regions read distinctly. */
    border-bottom: 1px solid color-mix(in srgb, var(--line) 65%, transparent);
}

.planner-list-title {
    margin: 0;
    flex: 1;
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--text);
}

.planner-list-count {
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--text-muted);
    background: color-mix(in srgb, var(--text-muted) 16%, transparent);
    border-radius: var(--radius-full, 999px);
    padding: 1px 9px;
    min-width: 22px;
    text-align: center;
}

.planner-list-cards {
    flex: 1 1 auto;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 9px;
    padding: 9px 8px 4px;
    min-height: 12px;
}

/* --- a card — elevated above the list, with a tactile hover lift + press --- */
.planner-card {
    position: relative;
    background: var(--planner-board-card-bg, var(--surface-raised));
    border: 1px solid var(--planner-board-card-border, var(--line));
    border-radius: 10px;
    padding: 10px 12px;
    cursor: grab;
    /* iOS: the long-press that starts a drag must NOT trigger text selection or the
     * copy/define callout magnifier; also kill the grey tap-flash. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
    /* Two stacked shadows (contact + ambient) + a top highlight so it reads as a real
     * raised card, not a flat tile. */
    box-shadow:
        0 1px 2px color-mix(in srgb, #000 30%, transparent),
        0 2px 6px color-mix(in srgb, #000 16%, transparent),
        inset 0 1px 0 color-mix(in srgb, #fff 5%, transparent);
    transition: transform 130ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 130ms ease, border-color 130ms ease;
    will-change: transform;
}

.planner-card:hover {
    border-color: color-mix(in srgb, var(--primary-500) 42%, var(--line));
    transform: translateY(-2px);
    box-shadow:
        0 6px 16px color-mix(in srgb, #000 40%, transparent),
        0 2px 5px color-mix(in srgb, #000 24%, transparent),
        inset 0 1px 0 color-mix(in srgb, #fff 7%, transparent);
}

/* Press it and it pushes in — comes after :hover so the push wins while held. */
.planner-card:active {
    transform: scale(0.985);
    transition-duration: 80ms;
}

.planner-card:focus-visible {
    outline: 2px solid var(--primary-500);
    outline-offset: 2px;
}

/* Settle animation when a card lands from a drop. */
@keyframes planner-card-drop {
    0% { transform: scale(1.05); }
    55% { transform: scale(0.98); }
    100% { transform: scale(1); }
}

.planner-card.just-dropped {
    animation: planner-card-drop 0.24s cubic-bezier(0.2, 0.85, 0.3, 1);
}

/* Live board: a card whose lane just changed (or that just appeared) gets a fading primary ring,
   so a move the poller swapped in is visible. Decoration only (ramp rung + alpha via color-mix) —
   the primary ramp flips per tenant, so this themes for free. */
@keyframes planner-card-live-move {
    0% {
        box-shadow: 0 0 0 2px var(--primary-500), 0 0 14px 2px color-mix(in srgb, var(--primary-500) 45%, transparent);
    }
    100% {
        box-shadow: 0 0 0 2px transparent, 0 0 0 0 transparent;
    }
}

.planner-card.is-live-moved {
    animation: planner-card-live-move 1.6s ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .planner-card.is-live-moved {
        animation: none;
        box-shadow: 0 0 0 2px var(--primary-500);
    }
}

/* (The old Trello colour-bar "label dot" retired with Tag model v2 — the project is now a full chip.) */

.planner-card-title {
    margin: 0;
    font-size: var(--text-sm);
    line-height: 1.35;
    color: var(--text);
    word-break: break-word;
}

/* Card foot — the bottom meta row: checklist progress + the "has a description" glyph. */
.planner-card-foot {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-top: 7px;
}

/* Checklist progress (Card anatomy v2) — a scannable done/total badge on the card face. */
.planner-card-progress {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.planner-card-progress svg {
    width: 13px;
    height: 13px;
}

/* Checklist-as-status at a glance: undone items tint the progress badge --warning (or --danger on a Done
   card that shipped incomplete — a real anomaly); a complete checklist stays calm/muted. Committed status
   tokens (config/theme.php), AA by construction, NO new colours. The done/total number carries the state
   too, so the tint reinforces rather than being the sole signal. */
.planner-card-progress.is-warning {
    color: var(--warning);
    background: var(--warning-surface);
    padding: 0 7px;
    border-radius: var(--radius-full, 999px);
}

.planner-card-progress.is-danger {
    color: var(--danger);
    background: var(--danger-surface);
    padding: 0 7px;
    border-radius: var(--radius-full, 999px);
}

/* Bottom block (Done) — one coherent row: checklist · commit message · commit id. Non-Done cards show the
   lone progress badge; both get a little top breathing room from the chips. */
.planner-card > .planner-card-progress,
.planner-card-done {
    margin-top: 7px;
}

.planner-card-done {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.planner-card-commit-msg {
    flex: 1 1 100%;
    min-width: 0;
    overflow: hidden;
    font-size: 11px;
    line-height: 1.4;
    color: var(--text-muted);
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* "Has a description" hint glyph. */
.planner-card-desc-flag {
    display: inline-flex;
    color: var(--text-muted);
}

.planner-card-desc-flag svg {
    width: 14px;
    height: 14px;
}

/* The card being dragged: hidden in place, a clone (ghost) follows the pointer. */
.planner-card.is-source {
    display: none;
}

/* The gap the ghost will drop into — a recessed slot, not a dashed outline. */
.planner-card-placeholder {
    border-radius: 10px;
    background: color-mix(in srgb, var(--primary-500) 9%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary-500) 28%, transparent);
    flex: 0 0 auto;
}

/* The element that follows the pointer. Its transform (position + velocity tilt + lift
 * scale) is driven per-frame by the JS — don't set transform here. */
.planner-card-ghost {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1000;
    width: var(--ghost-w, 260px);
    margin: 0;
    pointer-events: none;
    border-color: color-mix(in srgb, var(--primary-500) 45%, var(--line));
    box-shadow:
        0 22px 48px color-mix(in srgb, #000 52%, transparent),
        0 8px 16px color-mix(in srgb, #000 36%, transparent);
}

/* --- composer (add a card) --- */
.planner-list-composer {
    padding: 4px 8px 10px;
}

.planner-list-add {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 8px 10px;
    font: inherit;
    font-size: var(--text-sm);
    text-align: left;
    color: var(--text-muted);
    background: transparent;
    border: none;
    border-radius: 8px;
    cursor: pointer;
}

.planner-list-add:hover {
    background: color-mix(in srgb, var(--text) 7%, transparent);
    color: var(--text);
}

.planner-list-add-plus {
    font-size: 18px;
    line-height: 1;
}

.planner-composer {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.planner-composer-input {
    width: 100%;
    resize: none;
    font: inherit;
    font-size: var(--text-sm);
    line-height: 1.35;
    color: var(--text);
    background: var(--surface-raised);
    border: 1px solid var(--primary-500);
    border-radius: 10px;
    padding: 9px 11px;
    box-shadow: 0 1px 2px color-mix(in srgb, #000 20%, transparent);
}

.planner-composer-input::placeholder {
    color: var(--text-muted);
}

.planner-composer-input:focus-visible {
    outline: none;
}

.planner-composer-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

.planner-composer-add {
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    color: #fff;
    /* #fff on primary-500 is 3.76:1 (red tenant); primary-600 clears 4.5 (4.83) and flips per tenant. */
    background: var(--primary-600);
    border: none;
    border-radius: 8px;
    padding: 7px 14px;
    cursor: pointer;
}

.planner-composer-add:hover {
    background: var(--primary-700, color-mix(in srgb, var(--primary-500) 72%, #000));
}

.planner-composer-cancel {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    color: var(--text-muted);
    background: transparent;
    border: none;
    border-radius: 8px;
    cursor: pointer;
}

.planner-composer-cancel:hover {
    color: var(--text);
    background: color-mix(in srgb, var(--text) 8%, transparent);
}

.planner-composer-cancel svg {
    width: 20px;
    height: 20px;
}

/* --- card back (the detail dialog) --- */
/* Card-open v2 — the card back widens on desktop to hold two columns. It's a composed x-dialog, so we
 * set --dialog-width and let `.dialog { width: min(var(--dialog-width), calc(100vw - 32px)) }` read it.
 * NO #id `width:` override here — id specificity would beat the var() and pin the old single-column
 * width (the bug an earlier pass shipped). Mobile full-page sizing is set in the max-width:768px block
 * below. (The card-detail MODAL sizes itself by #planner-card-modal in the modal block near the foot.) */
#planner-card-back {
    --dialog-width: 840px;
}

/* Two views — the content (title + description + checklist) and the structural rail (tier · project ·
   move · actions). On desktop they sit side-by-side; on mobile they are <x-tabs> panels (Details |
   Info & actions), so a tall card back is two short tabs, not one long scroll. Tabs are the default
   (mobile) presentation; the min-width rule below UNWRAPS them into the two-column grid — hides the
   tablist, lays both panels out side-by-side. */
.planner-back {
    min-width: 0;
}

.planner-back-content,
.planner-back-rail {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}

/* Desktop unwrap. The seam is 768/769 — the app's ONE breakpoint (sidebar-nav.js's drawer, the board's
   mobile rules, all use 768). Above it the card back is two columns (tablist hidden, panels gridded
   side-by-side); at/below it the panels are real <x-tabs> (the max-width:768px block below). The same
   769/768 pair governs the card-back editor's full-page takeover. */
@media (min-width: 769px) {
    .planner-back .tabs-nav {
        display: none;
    }

    .planner-back .tabs-body {
        display: grid;
        grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr);
        gap: 22px;
        align-items: start;
        padding-top: 0;
    }

    .planner-back .tabs-panel {
        display: block;
    }

    .planner-back-rail {
        padding-left: 20px;
        border-left: 1px solid var(--line);
    }
}

.planner-back-title {
    width: 100%;
    resize: none;
    overflow: hidden;
    font: inherit;
    font-size: var(--text-lg);
    font-weight: 700;
    line-height: 1.3;
    color: var(--text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    padding: 6px 8px;
    margin: 0;
}

.planner-back-title:hover {
    background: color-mix(in srgb, var(--text) 5%, transparent);
}

.planner-back-title:focus-visible {
    outline: none;
    border-color: var(--primary-500);
    background: var(--surface-raised);
}

.planner-back-section-label {
    margin: 0;
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
}

.planner-back-labels,
.planner-back-tier {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: -6px;
}

/* Card-back picker buttons (Tier + Project) — the PILL pattern: at rest, adaptive category-tinted text on
   a faint category bg (color-mix toward --text, so it self-flips and clears AA in BOTH themes); SELECTED
   (.is-active) fills solid with the family's --planner-active-fill + white text + a white ring. The old
   white-text-at-opacity-0.72 shape failed AA — dimmed white floors at ~2.5:1 over a light surface — so
   adaptive text is the color-law-correct fix, matching .planner-pill (which already passes). */
.planner-back-chip {
    font: inherit;
    font-size: var(--text-xs);
    font-weight: 600;
    color: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 45%, var(--text));
    background: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 12%, var(--surface-raised));
    border: 2px solid color-mix(in srgb, var(--planner-cat, var(--text-muted)) 30%, var(--line));
    border-radius: 8px;
    padding: 7px 14px;
    cursor: pointer;
    transition: color 100ms ease, background 100ms ease, border-color 100ms ease;
}

.planner-back-chip:hover {
    color: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 62%, var(--text));
    border-color: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 52%, var(--line));
}

.planner-back-chip.is-active {
    color: #ffffff;
    background: var(--planner-active-fill, var(--text-muted));
    border-color: color-mix(in srgb, #fff 65%, transparent);
}

.planner-back-chip-none {
    color: var(--text);
    background: color-mix(in srgb, var(--text-muted) 30%, transparent);
}

.planner-back-desc {
    width: 100%;
    resize: vertical;
    min-height: 96px;
    font: inherit;
    font-size: var(--text-sm);
    line-height: 1.45;
    color: var(--text);
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 10px 12px;
    margin-top: -6px;
}

.planner-back-desc:focus-visible {
    outline: none;
    border-color: var(--primary-500);
}

/* Card-back checklist (Card anatomy v2) — clickable rows derived from the `- [ ]` lines above. */
.planner-back-checklist {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-top: -6px;
}

.planner-back-check {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 5px 7px;
    border-radius: 7px;
    font-size: var(--text-sm);
    line-height: 1.4;
    color: var(--text);
    cursor: pointer;
}

.planner-back-check:hover {
    background: color-mix(in srgb, var(--text) 5%, transparent);
}

.planner-back-check input {
    margin: 2px 0 0;
    accent-color: var(--primary-500);
    cursor: pointer;
    flex-shrink: 0;
}

.planner-back-check.is-done span {
    color: var(--text-muted);
    text-decoration: line-through;
}

.planner-back-move {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: -6px;
}

.planner-back-move-btn {
    font: inherit;
    font-size: var(--text-sm);
    color: var(--text);
    background: color-mix(in srgb, var(--text) 7%, transparent);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 6px 14px;
    cursor: pointer;
}

.planner-back-move-btn:hover {
    background: color-mix(in srgb, var(--text) 12%, transparent);
}

/* The "you are here" lane button. Its primary-tinted text must clear 4.5:1 on the 12%-primary wash over
   BOTH surfaces, so the rung owns the light/dark flip (color-law rule 2): primary-700 on the light wash,
   primary-400 on the dark one — raw primary-500 sat at ~3.2 (light) / ~4.2 (dark). */
.planner-back-move-btn.is-current {
    color: var(--primary-700);
    border-color: var(--primary-500);
    background: color-mix(in srgb, var(--primary-500) 12%, transparent);
    cursor: default;
}

:root[data-theme="dark"] .planner-back-move-btn.is-current {
    color: var(--primary-400);
}

.planner-back-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 4px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
}

.planner-back-delete {
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--danger);
    background: transparent;
    border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent);
    border-radius: 8px;
    padding: 7px 14px;
    cursor: pointer;
}

.planner-back-delete:hover {
    background: color-mix(in srgb, var(--danger) 12%, transparent);
}

.planner-back-save {
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    color: #fff;
    /* #fff on primary-500 is only 3.76:1 (red tenant); primary-600 clears 4.5 (4.83) and flips per tenant. */
    background: var(--primary-600);
    border: none;
    border-radius: 8px;
    padding: 8px 20px;
    cursor: pointer;
}

.planner-back-save:hover {
    background: var(--primary-700, color-mix(in srgb, var(--primary-500) 72%, #000));
}

/* --- mobile: one list per screen, swipe between; full-screen card back --- */
@media (max-width: 768px) {
    .planner-board {
        gap: 0;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        margin: 0 -16px;
        padding: 0 0 12px;
    }

    .planner-list {
        flex-basis: 86vw;
        scroll-snap-align: center;
        max-height: calc(100dvh - 168px);
        margin: 0 7px;
    }

    .planner-list:first-child {
        margin-left: 16px;
    }

    .planner-list:last-child {
        margin-right: 16px;
    }

    /* Roomier tap targets on touch. */
    .planner-card {
        padding: 11px 13px;
    }

    .planner-card-title {
        font-size: var(--text-base, 1rem);
    }
    /* The card-back editor's mobile full-page takeover lives in the unified max-width:768px block below
       (#planner-card-back) — one seam, no 621–768 hybrid. */
}

/* --- read-only repo board (the "Eightythree Planner" tab — PLANNER.md via PlanBoard) --- */

/* Board tabs — a quiet segmented control above the board (personal vs the repo board). */
.planner-tabs {
    display: inline-flex;
    gap: 2px;
    margin-bottom: var(--space-4, 16px);
    padding: 3px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-full, 999px);
}

.planner-tabs a {
    padding: 5px 14px;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text-muted);
    text-decoration: none;
    border-radius: var(--radius-full, 999px);
}

.planner-tabs a.is-active {
    background: var(--surface-raised);
    color: var(--text);
    box-shadow: 0 1px 2px color-mix(in srgb, #000 20%, transparent);
}

/* A read-only (source) card: a link that JS upgrades into the five-pane MODAL — so it lays its face out
   as a stack and sheds the raw <a> underline/colour. The base .planner-card gives the surface + hover. */
.planner-card-readonly {
    display: flex;
    flex-direction: column;
    gap: 6px;
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}

/* A parse-fallback card (planner.enabled off) has no five-pane — a static, non-interactive tile: no
   pointer, no hover-lift (the lift would falsely imply it opens something). */
.planner-card-static,
.planner-card-static:hover {
    cursor: default;
    transform: none;
}

.planner-card-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.planner-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.5;
    padding: 0 7px 0 6px;
    border-radius: var(--radius-full, 999px);
    color: var(--text-muted);
    background: color-mix(in srgb, var(--text-muted) 15%, transparent);
}

/* Tag scent icon (Card anatomy v2) — inherits the chip's themed text colour via currentColor. */
.planner-chip svg {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
}

.planner-chip-decision {
    color: var(--primary-600, var(--primary-500));
    background: color-mix(in srgb, var(--primary-500) 16%, transparent);
}

.planner-chip-committed {
    font-family: var(--font-mono, monospace);
    color: var(--success);
    background: var(--success-surface);
}

/* Builder BADGE (Card anatomy v3) — top-left, ABOVE the title: an accent-tinted pill with a Heroicon lock
   (the HOLDER, in-progress/review) or user (BUILT-BY, Done) + the builder's name. A block-level flex on its
   own line (width: fit-content) so it sits above the title; the card-open banner (.cardo-holder) mirrors it. */
.planner-card-badge {
    display: flex;
    align-items: center;
    gap: 4px;
    width: fit-content;
    max-width: 100%;
    margin-bottom: 5px;
    padding: 1px 8px 1px 6px;
    border-radius: var(--radius-full, 999px);
    font-size: 11px;
    font-weight: 600;
    color: color-mix(in srgb, var(--primary-500) 52%, var(--text));
    background: color-mix(in srgb, var(--primary-500) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--primary-500) 28%, transparent);
}

.planner-card-badge svg {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
}

.planner-card-badge > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* CATEGORICAL colours (Tag model v2). Every card's two chips — the master TIER and the PROJECT — plus
   the filter pills colour-code from the --palette-<family>-* catalog, deliberately NOT the tenant ramp:
   these are identities that must stay stable across tenants (the sanctioned catalog use; precedent:
   callout's success/warning). A .planner-fam-<family> class sets --planner-cat; the chip/pill rules
   color-mix it into a tinted background + border with contrast-safe text that flips per theme by
   blending toward --text. No two chips/pills in one render share a family (PlanBoard::familyMap). */
.planner-chip-tier,
.planner-chip-project {
    color: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 46%, var(--text));
    background: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--planner-cat, var(--text-muted)) 28%, transparent);
}

/* Family → catalog stops. Each family sets --planner-cat (the -500 tint base for inactive pills/chips)
   AND --planner-active-fill (the SOLID rung a pill/back-chip fills with when SELECTED — WCAG ≥4.5:1
   with white text, computed per family: 600 for most, 700 for the light families amber / cyan /
   emerald / green / lime / orange / sky / teal / yellow. scratchpad/compute-active-fill.mjs). One rule
   per palette family PaletteService emits, so any family config/planner.php picks always resolves. */
.planner-fam-slate   { --planner-cat: var(--palette-slate-500);   --planner-active-fill: var(--palette-slate-600); }
.planner-fam-gray    { --planner-cat: var(--palette-gray-500);    --planner-active-fill: var(--palette-gray-600); }
.planner-fam-zinc    { --planner-cat: var(--palette-zinc-500);    --planner-active-fill: var(--palette-zinc-600); }
.planner-fam-neutral { --planner-cat: var(--palette-neutral-500); --planner-active-fill: var(--palette-neutral-600); }
.planner-fam-stone   { --planner-cat: var(--palette-stone-500);   --planner-active-fill: var(--palette-stone-600); }
.planner-fam-red     { --planner-cat: var(--palette-red-500);     --planner-active-fill: var(--palette-red-600); }
.planner-fam-orange  { --planner-cat: var(--palette-orange-500);  --planner-active-fill: var(--palette-orange-700); }
.planner-fam-amber   { --planner-cat: var(--palette-amber-500);   --planner-active-fill: var(--palette-amber-700); }
.planner-fam-yellow  { --planner-cat: var(--palette-yellow-500);  --planner-active-fill: var(--palette-yellow-700); }
.planner-fam-lime    { --planner-cat: var(--palette-lime-500);    --planner-active-fill: var(--palette-lime-700); }
.planner-fam-green   { --planner-cat: var(--palette-green-500);   --planner-active-fill: var(--palette-green-700); }
.planner-fam-emerald { --planner-cat: var(--palette-emerald-500); --planner-active-fill: var(--palette-emerald-700); }
.planner-fam-teal    { --planner-cat: var(--palette-teal-500);    --planner-active-fill: var(--palette-teal-700); }
.planner-fam-cyan    { --planner-cat: var(--palette-cyan-500);    --planner-active-fill: var(--palette-cyan-700); }
.planner-fam-sky     { --planner-cat: var(--palette-sky-500);     --planner-active-fill: var(--palette-sky-700); }
.planner-fam-blue    { --planner-cat: var(--palette-blue-500);    --planner-active-fill: var(--palette-blue-600); }
.planner-fam-indigo  { --planner-cat: var(--palette-indigo-500);  --planner-active-fill: var(--palette-indigo-600); }
.planner-fam-violet  { --planner-cat: var(--palette-violet-500);  --planner-active-fill: var(--palette-violet-600); }
.planner-fam-purple  { --planner-cat: var(--palette-purple-500);  --planner-active-fill: var(--palette-purple-600); }
.planner-fam-fuchsia { --planner-cat: var(--palette-fuchsia-500); --planner-active-fill: var(--palette-fuchsia-600); }
.planner-fam-pink    { --planner-cat: var(--palette-pink-500);    --planner-active-fill: var(--palette-pink-600); }
.planner-fam-rose    { --planner-cat: var(--palette-rose-500);    --planner-active-fill: var(--palette-rose-600); }

/* --- filter pills (the unified board's single filter row, replacing the tabs) --- */
.planner-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: var(--space-4, 16px);
}

/* Inactive pills tint by their family (--planner-cat); 'All' has none, so it falls back to a neutral
   --text-muted tint. Text blends toward --text so it stays readable in both themes. */
.planner-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 13px 5px 11px;
    font-size: var(--text-sm);
    font-weight: 500;
    color: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 45%, var(--text));
    background: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 9%, var(--surface-raised));
    border: 1px solid color-mix(in srgb, var(--planner-cat, var(--text-muted)) 30%, var(--line));
    border-radius: var(--radius-full, 999px);
    text-decoration: none;
    cursor: pointer;
}

/* Pill scent icon (Card anatomy v2). The 'All' pill has none. */
.planner-pill svg {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}

.planner-pill:hover {
    color: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 62%, var(--text));
    border-color: color-mix(in srgb, var(--planner-cat, var(--text-muted)) 52%, var(--line));
}

/* Active pill = a SOLID inversion of its OWN family (Tag model v2: red-as-active would falsely read as
   a category in a hue-means-category row). --planner-active-fill is the per-family rung verified ≥4.5:1
   with white text; 'All' has no family, so it falls back to the tenant primary. Distinguishable by more
   than colour — the solid fill + aria-current carry the selected state, never the hue alone (a11y). */
.planner-pill.is-active {
    color: #ffffff;
    background: var(--planner-active-fill, var(--primary-600));
    border-color: transparent;
}

.planner-pill-tag {
    font-size: 12px;
}

/* The live card count trailing each pill's label. Dimmed so the name leads; inverts with the pill when
   active (inherits the white text, sits on a translucent wash of it). */
.planner-pill-count {
    margin-left: 5px;
    font-variant-numeric: tabular-nums;
    opacity: 0.66;
}

.planner-pill.is-active .planner-pill-count {
    opacity: 0.82;
}

.planner-pills-sep {
    width: 1px;
    align-self: stretch;
    min-height: 20px;
    margin: 0 2px;
    background: var(--line);
}

/* ===== Card-detail MODAL — the ONE detail view, opened as a fast overlay over the board. The five-pane
   body is AJAX-injected (planner-board.js) into an x-dialog; wide enough for the desktop five-column
   layout, and on mobile it's the x-tabs one-pane-at-a-time view (the same body the deep-link page uses).
   Sized by ID — x-dialog keeps its literal class="dialog", so a passed class attribute is dropped (the
   other dialogs set their width by #id too). */
#planner-card-modal {
    --dialog-width: 1400px;   /* the .dialog min() caps this to the viewport width */
}

#planner-card-modal .dialog-body {
    display: flex;
    min-height: 0;
    padding-top: 4px;
}

.planner-card-modal-body {
    flex: 1;
    min-width: 0;
    min-height: 45vh;   /* holds the loading state + keeps the modal from collapsing before the fetch lands */
}

/* @toc Progress */
/*
 * Progress. A themed native <progress>. Every token is a --progress-* var from
 * config/components/progress.php['css']; this file only references those plus the
 * structural rules the element needs. Styling <progress> means resetting the UA
 * appearance, then painting the track (-webkit-progress-bar / -moz the host) and
 * the fill (-webkit-progress-value / -moz-progress-bar) separately. An element
 * with no value attribute is indeterminate — the browser animates it for free.
 */

.progress {
    /* Reset the UA chrome so the track/value pseudo-elements paint cleanly. */
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    display: block;
    width: var(--progress-width, 100%);
    height: var(--progress-height, 8px);
    border: none;
    border-radius: var(--progress-radius, var(--radius-full, 9999px));
    /* Firefox paints the track on the host element, not a -bar pseudo. */
    background: var(--progress-track, var(--surface-raised));
    overflow: hidden;
    color: var(--progress-value, var(--primary));
}

/* WebKit / Blink: the track. */
.progress::-webkit-progress-bar {
    background: var(--progress-track, var(--surface-raised));
    border-radius: var(--progress-radius, var(--radius-full, 9999px));
}

/* WebKit / Blink: the filled portion. */
.progress::-webkit-progress-value {
    background: var(--progress-value, var(--primary));
    border-radius: var(--progress-radius, var(--radius-full, 9999px));
    transition: width 0.25s ease;
}

/* Firefox: the filled portion. */
.progress::-moz-progress-bar {
    background: var(--progress-value, var(--primary));
    border-radius: var(--progress-radius, var(--radius-full, 9999px));
}

/* Indeterminate: no value attribute. WebKit ignores ::-webkit-progress-value
 * here, so we drive a looping sweep on the track itself. */
.progress:indeterminate {
    background-image:
        linear-gradient(
            90deg,
            transparent 0%,
            var(--progress-value, var(--primary)) 50%,
            transparent 100%
        );
    background-size: 40% 100%;
    background-repeat: no-repeat;
    background-position: -40% 0;
    animation: progress-indeterminate 1.2s infinite ease-in-out;
}

/* Firefox draws a solid -moz-progress-bar while indeterminate; blank it so only
 * the host's looping sweep shows. */
.progress:indeterminate::-moz-progress-bar {
    background: transparent;
}

@keyframes progress-indeterminate {
    0% {
        background-position: -40% 0;
    }
    100% {
        background-position: 140% 0;
    }
}

/* Respect reduced-motion: hold the sweep mid-track instead of looping. */
@media (prefers-reduced-motion: reduce) {
    .progress:indeterminate {
        animation: none;
        background-position: 50% 0;
    }

    .progress::-webkit-progress-value {
        transition: none;
    }
}

/* Keyboard focus ring, matching the other primitives. The transparent outline is
 * the forced-colors fallback (box-shadow is dropped in high-contrast mode). */
.progress:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--progress-focus-ring, var(--primary));
}

/* @toc Select */
/* Select. All --select-* tokens come from config/components/select.php['css'].
 * We restyle only the box (appearance:none) and overlay a chevron — the native
 * option list, keyboard, and typeahead are left to the platform. */

.select {
    margin-bottom: 14px;
}

.select-label {
    display: block;
    margin-bottom: 6px;
    color: var(--select-label-color);
    font-size: var(--select-label-size);
}

/* The box wraps the field so the chevron can sit over it. The chevron itself is
 * a masked pseudo-element, so its colour is a real token (var) and themes with
 * the page — a background-image data-URI can't read CSS vars. pointer-events are
 * off so clicks fall through to the native control underneath. */
.select-box {
    position: relative;
    display: block;
}

.select-box::after {
    content: "";
    position: absolute;
    top: 50%;
    right: var(--select-padding-x);
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    pointer-events: none;
    background-color: var(--select-chevron);
    -webkit-mask: var(--select-chevron-image) center / contain no-repeat;
    mask: var(--select-chevron-image) center / contain no-repeat;
}

.select-field {
    appearance: none;
    -webkit-appearance: none;
    width: 100%;
    /* Extra right padding clears the chevron. */
    padding: var(--select-padding-y) calc(var(--select-padding-x) * 2 + 16px) var(--select-padding-y) var(--select-padding-x);
    border: 1px solid var(--select-border);
    border-radius: var(--select-radius);
    background: var(--select-bg);
    color: var(--select-fg);
    font-size: var(--select-font-size);
    font-family: var(--font-sans);
    line-height: 1.2;
    cursor: pointer;
}

/* The placeholder (disabled first option) reads as muted while it's the value. */
.select-field:has(option[value=""]:checked) {
    color: var(--text-muted);
}

/* Native option list: paint it from tokens so it isn't bright-white on dark UAs
 * that honour these (most do not, and that's fine — the box is the surface we own). */
.select-field option {
    background: var(--select-bg);
    color: var(--text);
}

/* Invalid affordance — the categorical --danger token (red-700 light / red-400 dark),
 * self-flipping, so the border clears 3:1 on each theme without a per-theme rule. */
.select-field[aria-invalid="true"] {
    border-color: var(--danger);
}

.select-error {
    margin-top: 6px;
    font-size: var(--text-xs);
    color: var(--danger);
}

.select-required {
    margin-left: 2px;
    color: var(--danger);
}

/* Keyboard focus recolors the border as a secondary cue; the global :focus-visible
 * ring (base.css) supplies the primary indicator. :focus-visible so a mouse open
 * doesn't trigger it. */
.select-field:focus-visible {
    border-color: var(--select-focus-border);
}

.select-field:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* @toc Sidebar nav */
/* Sidebar nav — brand (top), nav items (middle, scrolls), footer (bottom: theme + logout). */

.sidebar-nav {
    width: var(--sidebar-nav-width, 240px);
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    background: var(--page-bg);
    border-inline-end: 1px solid var(--line);
}

.sidebar-nav-brand {
    display: block;
    padding: 20px 16px 12px;
    line-height: 0;
    text-decoration: none;
}

.sidebar-nav-brand .brand-logo svg {
    width: 40px;
}

.sidebar-nav-list {
    flex: 1;
    overflow-y: auto;
    margin: 0;
    padding: 4px 10px;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.sidebar-nav-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-radius: 8px;
    color: var(--text-muted);
    font-size: var(--text-sm);
    text-decoration: none;
}

.sidebar-nav-link:hover {
    color: var(--text);
    background: var(--line);
}

.sidebar-nav-link.is-active {
    color: var(--text);
    background: var(--line);
}

.sidebar-nav-icon {
    display: inline-flex;
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}

.sidebar-nav-icon svg {
    display: block;
    width: 100%;
    height: 100%;
}

.sidebar-nav-label {
    flex: 1;
}

/* A section is a toggle button styled like a link. */
.sidebar-nav-section {
    width: 100%;
    appearance: none;
    border: none;
    background: transparent;
    font-family: inherit;
    text-align: start;
    cursor: pointer;
}

.sidebar-nav-chevron {
    display: inline-flex;
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    color: var(--text-muted);
    transition: transform 0.15s ease;
}

.sidebar-nav-chevron svg {
    width: 100%;
    height: 100%;
}

.sidebar-nav-item.is-expanded .sidebar-nav-chevron {
    transform: rotate(90deg);
}

.sidebar-nav-sublist {
    margin: 2px 0 6px;
    padding-inline-start: 30px;
    list-style: none;
    display: none;
    flex-direction: column;
    gap: 1px;
}

.sidebar-nav-item.is-expanded .sidebar-nav-sublist {
    display: flex;
}

.sidebar-nav-sublink {
    display: block;
    padding: 6px 10px;
    border-radius: 6px;
    color: var(--text-muted);
    font-size: var(--text-sm);
    text-decoration: none;
}

.sidebar-nav-sublink:hover {
    color: var(--text);
    background: var(--line);
}

.sidebar-nav-sublink.is-active {
    color: var(--text);
    background: var(--line);
}

/* Section header within the submenu (Foundations / Components / Internals). */
.sidebar-nav-subhead {
    margin: 10px 0 2px;
    padding: 0 10px;
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    opacity: 0.75;
}

.sidebar-nav-subhead:first-child {
    margin-top: 0;
}

/* Footer — theme controls, user, and logout. Each is a uniform [icon] [label]
 * row that matches the nav items above: the same 20px .sidebar-nav-icon, the same
 * text size, the same left alignment, so every icon and label lines up. Theme
 * adds its toggle + dropper on the right; logout is its own clickable row.
 * Stays position: relative so the theme-colors popup anchors to the footer. */
.sidebar-nav-footer {
    position: relative;
    margin-top: auto;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 8px 10px;
    border-top: 1px solid var(--line);
}

.sidebar-nav-footer-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 40px;
    padding: 0 12px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    font-family: inherit;
    font-size: var(--text-sm);
    text-align: start;
    /* The Admin row is an <a> - kill the default underline in every state so it
     * reads like the button rows beside it (no link/visited decoration). */
    text-decoration: none;
}

.sidebar-nav-footer-row .sidebar-nav-label {
    flex: 1;
    min-width: 0;
}

.sidebar-nav-username {
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sidebar-nav-footer-controls {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.sidebar-nav-footer-form {
    margin: 0;
}

.sidebar-nav-footer-action {
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
}

.sidebar-nav-footer-action:hover {
    color: var(--text);
    background: var(--line);
}

/* @toc Slider */
/*
 * Slider — themed native <input type="range">. All --slider-* tokens come from
 * config/components/slider.php['css']. We only paint the track + thumb; the
 * browser keeps the drag, keyboard stepping, and value semantics. Track/thumb
 * pseudo-elements must each get their own rule — a combined -webkit-/-moz-
 * selector list is dropped wholesale if the engine doesn't know one half.
 */

.slider {
    margin-bottom: 14px;
}

.slider-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 8px;
}

.slider-label {
    color: var(--slider-label-color);
    font-size: var(--slider-label-size);
}

.slider-value {
    color: var(--slider-value-color);
    font-size: var(--slider-value-size);
    font-variant-numeric: tabular-nums;
}

/* Reset the UA chrome so the track + thumb rules below are the whole surface. */
.slider-range {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: var(--slider-thumb-size);
    margin: 0;
    background: transparent;
    cursor: pointer;
}

/* Track — WebKit/Blink. */
.slider-range::-webkit-slider-runnable-track {
    height: var(--slider-track-height);
    border-radius: var(--slider-track-radius);
    background: var(--slider-track-bg);
}

/* Track — Firefox. */
.slider-range::-moz-range-track {
    height: var(--slider-track-height);
    border-radius: var(--slider-track-radius);
    background: var(--slider-track-bg);
}

/* Filled portion before the thumb — Firefox only (WebKit has no native fill). */
.slider-range::-moz-range-progress {
    height: var(--slider-track-height);
    border-radius: var(--slider-track-radius);
    background: var(--slider-fill);
}

/* Thumb — WebKit/Blink. margin-top recentres it on the thinner track. */
.slider-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: var(--slider-thumb-size);
    height: var(--slider-thumb-size);
    margin-top: calc((var(--slider-track-height) - var(--slider-thumb-size)) / 2);
    border: var(--slider-thumb-border-width) solid var(--slider-thumb-border);
    border-radius: 50%;
    background: var(--slider-thumb-bg);
    transition: box-shadow 0.15s ease, background 0.15s ease;
}

/* Thumb — Firefox. */
.slider-range::-moz-range-thumb {
    width: var(--slider-thumb-size);
    height: var(--slider-thumb-size);
    border: var(--slider-thumb-border-width) solid var(--slider-thumb-border);
    border-radius: 50%;
    background: var(--slider-thumb-bg);
    transition: box-shadow 0.15s ease, background 0.15s ease;
}

/* Keyboard focus ring on the thumb — mirrors the button :focus-visible ring.
   The transparent outline is the forced-colors fallback (box-shadow is dropped
   in high-contrast mode, so the outline gets promoted to a system colour). */
.slider-range:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
}

.slider-range:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 var(--slider-focus-ring-width) var(--slider-focus-ring);
}

.slider-range:focus-visible::-moz-range-thumb {
    box-shadow: 0 0 0 var(--slider-focus-ring-width) var(--slider-focus-ring);
}

.slider-range:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* @toc Study issue */
/* Study issue — the bilingual checklist card (Darbru answer key). A left-accent by
 * type (must = green, nice = neutral, trap = red), then a two-column EN | RU grid
 * (stacks on mobile). Surface + text are theme tokens; the type/lang accents pull
 * fixed hues from the palette catalog. View: study-issue.blade.php. */

.study-issue {
    container-type: inline-size;
    margin: 9px 0;
    padding: 11px 14px;
    border-inline-start: 4px solid var(--study-issue-accent, var(--palette-emerald-500));
    border-radius: var(--radius);
    background: var(--surface-raised);
}

.study-issue-must { --study-issue-accent: var(--palette-emerald-500); }
.study-issue-nice { --study-issue-accent: var(--text-muted); }
.study-issue-trap { --study-issue-accent: var(--palette-red-500); }

.study-issue-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

.study-issue-mark {
    display: inline-flex;
    align-items: center;
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--study-issue-accent);
}

.study-issue-mark svg {
    width: 100%;
    height: 100%;
}

.study-issue-title {
    font-weight: 700;
    font-size: var(--text-sm);
    color: var(--text);
}

.study-issue-bilingual {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.study-issue-lang {
    background: var(--surface);
    border-inline-start: 3px solid var(--study-lang-accent, var(--line));
    border-radius: var(--radius-sm);
    padding: 9px 11px;
    font-size: var(--text-sm);
}

.study-issue-en { --study-lang-accent: var(--palette-emerald-500); }
.study-issue-ru { --study-lang-accent: var(--palette-amber-500); }

.study-issue-lang p {
    margin: 6px 0;
    line-height: 1.5;
}

.study-issue-lang p:first-of-type { margin-top: 0; }
.study-issue-lang p:last-of-type { margin-bottom: 0; }

.study-issue-lang strong {
    color: var(--text);
}

.study-issue-langlabel {
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 700;
    color: var(--text-muted);
    margin-bottom: 5px;
}

/* 720px: the card's OWN inline size, via a container query — NOT the app --bp-* viewport seam (with which it
   coexists). The EN | RU split collapses to one column whenever the card itself is narrow (e.g. dropped into a
   disclosure or a narrow slot), independent of the viewport width. .study-issue sets container-type above. */
@container (max-width: 720px) {
    .study-issue-bilingual {
        grid-template-columns: 1fr;
    }
}

/* @toc Study sheet */
/* Study sheet — the Darbru bar-essay renderer. Head banner, trigger legend, the
 * fact pattern with colour-coded trigger spans, and the collapsible checklist. The
 * trigger hues (t1-t6) come from the palette catalog; their highlight tint flips per
 * theme via a [data-theme="dark"] override (light tint on light, dark tint on dark).
 * Boxes (source/instructions/call/hook) are x-callout; cards are x-study-issue.
 * View: study-sheet.blade.php. */

.study-sheet {
    max-width: var(--max-w-3xl);
    margin: 0 auto;
}

/* HEAD */
.study-sheet-head {
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    padding: 16px 20px;
    margin-bottom: 16px;
}

.study-sheet-sub {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* LEGEND */
.study-legend {
    background: var(--surface-raised);
    border-radius: var(--radius);
    padding: 12px 16px;
    margin: 14px 0;
}

.study-legend-title {
    margin: 0 0 8px;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--text-muted);
}

.study-legend-list {
    margin: 0;
    padding-left: 18px;
    font-size: var(--text-sm);
}

.study-legend-list li {
    margin-bottom: 5px;
}

/* A solid colour chip (its own classes, not the trigger-* tint, so the theme
 * override doesn't fade it). The hue matches the matching fact-pattern highlight. */
.study-swatch {
    display: inline-block;
    width: 26px;
    height: 12px;
    border-radius: 3px;
    margin-right: 8px;
    vertical-align: middle;
}

.study-swatch-t1 { background: var(--palette-red-500); }
.study-swatch-t2 { background: var(--palette-emerald-600); }
.study-swatch-t3 { background: var(--palette-blue-500); }
.study-swatch-t4 { background: var(--palette-violet-500); }
.study-swatch-t5 { background: var(--palette-amber-500); }
.study-swatch-t6 { background: var(--palette-pink-500); }

/* FACT PATTERN */
.study-fact {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 16px 20px;
    margin: 14px 0;
    font-size: var(--text-base);
    line-height: 1.65;
}

.study-fact-label {
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 700;
    color: var(--text-muted);
    margin-bottom: 6px;
}

.study-fact p {
    margin: 8px 0;
}

/* The inline trigger highlight + the legend swatch share the .trigger-* colours. */
.trigger {
    padding: 1px 4px;
    border-radius: 3px;
    font-weight: 500;
}

.trigger-t1 { background: var(--palette-red-100); border-bottom: 2px solid var(--palette-red-500); }
.trigger-t2 { background: var(--palette-emerald-100); border-bottom: 2px solid var(--palette-emerald-600); }
.trigger-t3 { background: var(--palette-blue-100); border-bottom: 2px solid var(--palette-blue-500); }
.trigger-t4 { background: var(--palette-violet-100); border-bottom: 2px solid var(--palette-violet-500); }
.trigger-t5 { background: var(--palette-amber-100); border-bottom: 2px solid var(--palette-amber-500); }
.trigger-t6 { background: var(--palette-pink-100); border-bottom: 2px solid var(--palette-pink-500); }

:root[data-theme="dark"] .trigger-t1 { background: var(--palette-red-950); }
:root[data-theme="dark"] .trigger-t2 { background: var(--palette-emerald-950); }
:root[data-theme="dark"] .trigger-t3 { background: var(--palette-blue-950); }
:root[data-theme="dark"] .trigger-t4 { background: var(--palette-violet-950); }
:root[data-theme="dark"] .trigger-t5 { background: var(--palette-amber-950); }
:root[data-theme="dark"] .trigger-t6 { background: var(--palette-pink-950); }

/* CALL */
.study-call-list {
    margin: 8px 0 0 20px;
    padding: 0;
}

.study-call-list > li {
    margin-bottom: 6px;
}

.study-call-ru {
    margin-top: 8px;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* CHECKLIST — now the x-details primitive (themed disclosure); no bespoke styles. */

/* @toc Switch */
/*
 * Switch. A native <input type="checkbox" role="switch"> drives a decorative
 * track + knob: the real input is visually hidden (it keeps behaviour, focus and
 * keyboard), and its :checked / :focus-visible state styles the sibling pill. All
 * --switch-* tokens come from config/components/switch.php['css']. Dark-first.
 */

.switch {
    display: inline-flex;
    align-items: center;
    gap: var(--switch-gap, 10px);
    cursor: pointer;
    font-size: var(--switch-label-size, var(--text-sm));
    color: var(--switch-label-color, var(--text));
    line-height: 1.3;
    -webkit-user-select: none;
    user-select: none;
}

/* The real control: present for behaviour + a11y, visually replaced by the pill.
 * Not display:none — that would drop it from the tab order and kill keyboard. */
.switch-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

.switch-track {
    position: relative;
    display: inline-block;
    flex: none;
    width: var(--switch-width, 40px);
    height: var(--switch-height, 22px);
    border: 1px solid var(--switch-track-border, var(--line));
    border-radius: var(--radius-full);
    background: var(--switch-track-bg, var(--surface-raised));
    transition: background 0.18s ease, border-color 0.18s ease;
}

.switch-knob {
    position: absolute;
    top: 50%;
    left: var(--switch-knob-gap, 2px);
    width: var(--switch-knob-size, 16px);
    height: var(--switch-knob-size, 16px);
    border-radius: var(--radius-full);
    background: var(--switch-knob-bg, var(--text-muted));
    transform: translateY(-50%);
    transition: transform 0.18s ease, background 0.18s ease;
}

/* Checked: tint the track and slide the knob to the right edge. The travel is the
 * track width minus the knob and both gaps, so it never clips the rounded end. */
.switch-input:checked + .switch-track {
    background: var(--switch-track-bg-on, var(--primary));
    border-color: var(--switch-track-bg-on, var(--primary));
}

.switch-input:checked + .switch-track .switch-knob {
    background: var(--switch-knob-bg-on, var(--on-primary));
    transform: translateY(-50%) translateX(calc(var(--switch-width, 40px) - var(--switch-knob-size, 16px) - (var(--switch-knob-gap, 2px) * 2) - 2px));
}

/* Keyboard focus ring on the pill, mirroring the other primitives' :focus-visible.
 * The transparent outline is the forced-colors fallback (box-shadow is dropped in
 * high-contrast mode, so the outline gets promoted and focus stays visible). */
.switch-input:focus-visible + .switch-track {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--switch-focus-ring, var(--primary));
}

.switch.is-disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* Submit-on-change fallback button (rendered only with `submit`; switch.js hides it and auto-submits on
 * change). It's a composed <x-button> — all styling is the button primitive's; this only positions it. */
.switch-apply {
    margin-left: 10px;
}

/* @toc Table */
/* Table — generic data table. Themeable via --table-* (config), falling back to
 * the global theme tokens. Signed/numeric cells use .is-positive / .is-negative. */

.table-wrap {
    overflow-x: auto;
}

.table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.table th {
    padding: 10px 12px;
    border-bottom: 1px solid var(--table-border, var(--line));
    color: var(--table-head-text, var(--text-muted));
    font-size: var(--text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    text-align: start;
    white-space: nowrap;
}

.table td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--table-border, var(--line));
    color: var(--text);
}

.table tbody tr:hover {
    background: var(--table-row-hover, var(--surface-raised));
}

.table-right {
    text-align: end;
}

.table-sort {
    color: inherit;
    text-decoration: none;
}

.table-sort:hover {
    color: var(--text);
}

.table th.is-sorted-asc .table-sort::after {
    content: " \2191";
}

.table th.is-sorted-desc .table-sort::after {
    content: " \2193";
}

.table .is-positive {
    color: var(--table-positive, var(--success));
}

.table .is-negative {
    color: var(--table-negative, var(--danger));
}

.table-empty {
    padding: 40px 16px;
    text-align: center;
    color: var(--text-muted);
}

/* --- polish (adapted from leadthink): zebra, scrollbar, and the Cell formatters --- */

/* Horizontal scroll on mobile; the scrollbar itself is the global themed one (base.css). */
.table-wrap {
    -webkit-overflow-scrolling: touch;
}

/* Zebra + hover, theme-agnostic (a tint of the current text colour works on light or dark). The
   hover rule is last so it wins over the stripe. */
.table tbody tr:nth-child(2n) {
    background: color-mix(in srgb, currentColor 3%, transparent);
}

.table tbody tr:hover {
    background: color-mix(in srgb, currentColor 7%, transparent);
}

/* Cell formatters (App\Support\Cell): numeric alignment, negatives, badges, stacked lines. */
.cell-num {
    font-variant-numeric: tabular-nums;
}

.cell-negative {
    color: var(--table-negative, var(--danger));
    font-weight: 600;
}

.cell-stacked {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.cell-stacked > div + div {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: var(--text-xs);
    font-weight: 600;
}

.badge-success {
    background: var(--success-surface);
    color: var(--success);
}

.badge-secondary {
    background: var(--neutral-100);
    color: var(--text-muted);
}

/* @toc Tabs */
/* Tabs — a horizontal tab list with a sliding indicator. tabs.js measures the
 * active trigger and sets the indicator's transform + width; CSS transitions them
 * and styles them per variant (set on the root):
 *   .tabs-underline (default) - a thin bar slides along a bottom rule; triggers
 *                               get a faint rounded hover highlight.
 *   .tabs-pill                - a segmented control; a filled pill slides behind
 *                               the active trigger.
 * Triggers take an optional icon (recolours with the trigger; accent when active).
 * States: active / inactive (hover) / .is-empty (dimmed). Colours: --tabs-*
 * (config). View: tabs.blade.php; switcher + indicator: tabs.js. */

.tabs-nav {
    position: relative;
    display: flex;
    gap: 2px;
    /* Scroll the tab row when it overflows (e.g. 6 facet tabs on mobile) rather
     * than wrapping or squashing; the scrollbar is hidden. */
    overflow-x: auto;
    scrollbar-width: none;
}

.tabs-nav::-webkit-scrollbar {
    display: none;
}

.tabs-tab {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex-shrink: 0;
    appearance: none;
    border: none;
    border-radius: 7px;
    background: transparent;
    color: var(--tabs-tab-text);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    padding: 8px 12px;
    white-space: nowrap;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
}

.tabs-tab:hover:not(.is-active):not(.is-empty) {
    color: var(--tabs-tab-text-hover);
}

.tabs-tab.is-active {
    color: var(--tabs-tab-text-active);
}

.tabs-tab.is-empty {
    opacity: 0.4;
}

.tabs-tab.is-empty.is-active {
    opacity: 1;
}

/* Icon — inherits the trigger colour (currentColor), turns accent when active. */
.tabs-tab-icon {
    display: inline-flex;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    transition: color 0.15s ease;
}

.tabs-tab-icon svg {
    display: block;
    width: 100%;
    height: 100%;
}

.tabs-tab.is-active .tabs-tab-icon {
    color: var(--tabs-accent);
}

/* The sliding indicator (one per nav). tabs.js sets width + translateX; the
 * variant rules below give it its position + look. */
.tabs-indicator {
    position: absolute;
    left: 0;
    width: 0;
    transform: translateX(0);
    transition: transform 0.2s ease, width 0.2s ease;
    pointer-events: none;
}

/* underline variant */
.tabs-underline > .tabs-nav {
    border-bottom: 1px solid var(--tabs-line);
}

.tabs-underline > .tabs-nav > .tabs-tab:hover:not(.is-active):not(.is-empty) {
    background: var(--tabs-tab-hover-bg);
}

.tabs-underline > .tabs-nav > .tabs-indicator {
    /* bottom: 0 (not -1px) so the horizontal-scroll overflow doesn't clip it. */
    bottom: 0;
    height: 2px;
    background: var(--tabs-accent);
    border-radius: 2px;
}

/* pill variant */
.tabs-pill > .tabs-nav {
    gap: 0;
    padding: 4px;
    border-radius: var(--radius-lg);
    background: var(--tabs-pill-track);
}

.tabs-pill > .tabs-nav > .tabs-tab {
    padding: 7px 14px;
}

.tabs-pill > .tabs-nav > .tabs-indicator {
    top: 4px;
    bottom: 4px;
    background: var(--tabs-pill-active);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}

.tabs-body {
    padding-top: 18px;
}

.tabs-panel {
    display: none;
}

.tabs-panel.is-active {
    display: block;
}

/* @toc Textarea */
/* Textarea. All --textarea-* tokens come from config/components/textarea.php['css']. */

.textarea {
    margin-bottom: 14px;
}

.textarea-label {
    display: block;
    margin-bottom: 6px;
    color: var(--textarea-label-color);
    font-size: var(--textarea-label-size);
}

.textarea-field {
    width: 100%;
    padding: var(--textarea-padding-y) var(--textarea-padding-x);
    border: 1px solid var(--textarea-border);
    border-radius: var(--textarea-radius);
    background: var(--textarea-bg);
    color: var(--textarea-fg);
    font-size: var(--textarea-font-size);
    font-family: var(--font-sans);
    line-height: var(--textarea-line-height);
    /* Vertical-only drag handle: width stays on the layout grid, height grows with content. */
    resize: var(--textarea-resize);
    min-height: var(--textarea-min-height);
}

/* opacity:1 stops the UA re-fading the placeholder below the token's contrast. */
.textarea-field::placeholder {
    color: var(--text-muted);
    opacity: 1;
}

/* Code variant — a monospace face + tighter type for pasting JSON, config, or code. */
.textarea-field-code {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
}

/* Invalid / hint / required affordances. Error colour is the categorical --danger
 * token (red-700 light / red-400 dark) — self-flipping and menu-hover-certified, so
 * the text clears 4.5:1 and the border 3:1 on every surface without a per-theme rule. */
.textarea-field[aria-invalid="true"] {
    border-color: var(--danger);
}

.textarea-error {
    margin-top: 6px;
    font-size: var(--text-xs);
    color: var(--danger);
}

.textarea-hint {
    margin-top: 6px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.textarea-required {
    margin-left: 2px;
    color: var(--danger);
}

/* Keyboard focus recolors the border as a secondary cue; the global :focus-visible
 * ring (base.css) supplies the primary indicator. :focus-visible (not :focus) so a
 * mouse click doesn't trigger either. */
.textarea-field:focus-visible {
    border-color: var(--textarea-focus-border);
}

/* @toc Theme colors */
/* Accent/surface colour picker — a dropper button with a popover of swatch
 * grids, sits in the sidebar footer beside the light/dark theme-picker.
 *
 * The swatch fill (background: var(--swatch-color)) is the load-bearing line:
 * theme-colors.js sets --swatch-color to var(--palette-<name>-500) per swatch,
 * pulling from the catalog ramps CssService emits.
 *
 * The popup is absolutely positioned and needs a wider positioned ancestor than
 * the 34px dropper, so it anchors to .sidebar-nav-footer (which is position:
 * relative). Used by /resources/views/components/theme-colors.blade.php.
 */

.theme-colors {
    display: inline-flex;
}

.theme-colors-dropper {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
}

.theme-colors-dropper:hover {
    color: var(--text);
    background: var(--line);
}

.theme-colors-dropper svg {
    width: 17px;
    height: 17px;
}

.theme-colors-popup {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(100% + 8px);
    z-index: 20;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--page-bg);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
    transform: scale(0.96);
    transform-origin: bottom center;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: transform 160ms ease, opacity 160ms ease, visibility 0s linear 160ms;
}

.theme-colors-popup.is-open {
    transform: scale(1);
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: transform 160ms ease, opacity 160ms ease, visibility 0s;
}

.theme-colors-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.theme-colors-label {
    color: var(--text-muted);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.theme-colors-grid {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 6px;
}

.theme-colors-swatch {
    appearance: none;
    width: 100%;
    aspect-ratio: 1 / 1;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 5px;
    background: var(--swatch-color);
    cursor: pointer;
    outline: 2px solid transparent;
    outline-offset: 1px;
    transition: transform 120ms ease, outline-color 120ms ease;
}

.theme-colors-swatch:hover {
    transform: scale(1.08);
}

.theme-colors-swatch.is-active {
    outline-color: var(--text);
}

/* @toc Theme picker */
/* Theme picker — a small segmented control. --theme-picker-* from config. */

.theme-picker {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--line);
    border-radius: var(--theme-picker-radius, 8px);
    background: transparent;
}

.theme-picker-opt {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: var(--text-muted);
    padding: 5px 8px;
    border-radius: 6px;
    cursor: pointer;
}

.theme-picker-opt svg {
    width: 16px;
    height: 16px;
}

.theme-picker-opt:hover:not(.is-active) {
    color: var(--text);
}

.theme-picker-opt.is-active {
    background: var(--surface-raised);
    color: var(--text);
}

/* @toc Theory sheet */
/* Theory study sheet — bilingual rule-sheet (Theory mode) + interactive MBE drill
 * (Drills mode). App design tokens throughout; no hardcoded artifact colours.
 * View: theory-sheet.blade.php; behaviour: theory-sheet.js.
 *
 * Bilingual primitive: .theory-bilingual is EN | RU side-by-side on desktop and
 * stacked (EN over RU, divider between) on mobile. EN is authoritative; RU is the
 * condensed gloss (slightly muted). */

.theory-sheet {
    max-width: var(--max-w-4xl);
}

/* ---- meta head ---- */
.theory-head {
    margin-bottom: 18px;
}

.theory-sub {
    margin: 0;
    color: var(--text);
    font-weight: 600;
}

.theory-sub-ru {
    display: block;
    color: var(--text-muted);
    font-weight: 400;
    font-size: var(--text-sm);
}

.theory-arc {
    margin: 6px 0 0;
    color: var(--text-muted);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.6px;
}

.theory-prov-ru {
    display: block;
    opacity: 0.8;
    font-size: var(--text-xs);
}

.theory-next {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 10px 0 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.theory-next svg {
    width: 15px;
    height: 15px;
}

/* The Theory/Drills mode switch is the shared <x-tabs> (pill variant) — its own
 * component owns the toggle look + behaviour. This sheet only reinstates the
 * mobile-sticky tab bar (see the responsive block). */

/* ---- bilingual primitive ---- */
.theory-bilingual {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.theory-lang {
    padding: 12px 14px;
    border-radius: var(--radius);
    background: var(--surface-raised);
    border-left: 3px solid var(--palette-blue-500, var(--primary));
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--text);
}

.theory-lang-ru {
    border-left-color: var(--palette-emerald-600, var(--primary));
    color: var(--text-muted);
}

.theory-lang-label {
    font-size: var(--text-2xs, 10px);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: var(--text-muted);
    margin-bottom: 5px;
}

.theory-lang strong {
    color: var(--text);
}

.theory-lang em {
    color: var(--primary);
    font-style: italic;
}

/* ---- section heads ---- */
.theory-section-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 26px 0 12px;
    padding-bottom: 8px;
    border-bottom: 2px solid var(--accent, var(--line));
}

.theory-section-icon {
    display: inline-flex;
    width: 20px;
    height: 20px;
    color: var(--accent, var(--primary));
}

.theory-section-icon svg {
    width: 100%;
    height: 100%;
}

.theory-section-title {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: 700;
    color: var(--text);
}

.theory-section-ru {
    display: block;
    font-size: var(--text-xs);
    font-weight: 400;
    color: var(--text-muted);
}

.theory-tier {
    margin-left: auto;
    flex-shrink: 0;
    padding: 3px 10px;
    border-radius: var(--radius-full, 999px);
    font-size: var(--text-2xs, 10px);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
}

.theory-tier-1 { background: color-mix(in srgb, var(--palette-red-500) 16%, var(--surface)); color: var(--palette-red-500); border: 1px solid var(--palette-red-500); }
.theory-tier-2 { background: color-mix(in srgb, var(--palette-amber-500) 16%, var(--surface)); color: var(--palette-amber-500); border: 1px solid var(--palette-amber-500); }
.theory-tier-3 { background: var(--surface-sunken, var(--surface)); color: var(--text-muted); border: 1px solid var(--line); }

/* ---- tier grid (the master comparison) ---- */
.theory-tiergrid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
    margin: 12px 0;
}

.theory-tiercard {
    padding: 14px;
    border-radius: var(--radius-lg);
    border: 2px solid var(--accent, var(--line));
    background: color-mix(in srgb, var(--accent, var(--primary)) 8%, var(--surface));
}

.theory-tiercard-name {
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    text-align: center;
    color: var(--accent, var(--text));
    margin-bottom: 8px;
}

.theory-tiercard-formula {
    font-size: var(--text-xs);
    line-height: 1.5;
    padding: 8px;
    border-radius: var(--radius);
    background: rgba(0, 0, 0, 0.18);
    color: var(--text);
}

.theory-tiercard-formula strong { color: var(--accent, var(--text)); }

.theory-tiercard-burden {
    font-size: var(--text-2xs, 11px);
    font-style: italic;
    color: var(--text-muted);
    margin: 8px 0 6px;
}

.theory-tiercard-triggers {
    list-style: none;
    padding: 0;
    margin: 0;
}

.theory-tiercard-triggers li {
    font-size: var(--text-xs);
    padding: 2px 0;
    color: var(--text);
}

.theory-tiercard-triggers li::before {
    content: "▸ ";
    color: var(--accent, var(--primary));
}

/* ---- checklist ---- */
.theory-checklist {
    margin: 14px 0;
    padding: 16px;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
}

.theory-checklist-title {
    margin: 0 0 12px;
    font-size: var(--text-base);
    font-weight: 700;
    color: var(--text);
}

.theory-checklist-steps {
    margin: 0;
    padding-left: 22px;
}

.theory-checklist-steps li {
    margin-bottom: 10px;
}

.theory-checklist-example {
    margin-top: 12px;
}

/* ---- mnemonic ---- */
.theory-mnemonic {
    margin: 14px 0;
    padding: 14px 16px;
    text-align: center;
    border: 2px solid var(--palette-amber-500);
    border-radius: var(--radius-lg);
    background: color-mix(in srgb, var(--palette-amber-500) 10%, var(--surface));
}

.theory-mnemonic-text {
    font-size: var(--text-lg);
    font-weight: 700;
    letter-spacing: 1px;
    color: var(--palette-amber-400, var(--palette-amber-500));
}

.theory-mnemonic-gloss {
    margin-top: 6px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.theory-trap-tested {
    margin-top: 8px;
    font-size: var(--text-2xs, 11px);
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--text-muted);
}

/* ---- cases ---- */
.theory-case {
    margin: 10px 0;
    padding: 10px 14px;
    border-left: 3px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-raised);
}

.theory-case-key {
    border-left-color: var(--primary);
}

.theory-case-cite {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
    color: var(--text);
}

.theory-case-cite svg { width: 14px; height: 14px; color: var(--primary); }

/* ---- examples ---- */
.theory-examples { margin: 12px 0; }

.theory-example {
    display: flex;
    gap: 8px;
    margin-bottom: 8px;
}

.theory-example-mark { flex-shrink: 0; }
.theory-example-mark svg { width: 18px; height: 18px; }
.theory-example-win .theory-example-mark { color: var(--success); }
.theory-example-fail .theory-example-mark { color: var(--danger); }
.theory-example .theory-bilingual { flex: 1; }

/* ===================== QUIZ ===================== */
.theory-quiz-bar {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
    margin-bottom: 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.theory-quiz-pct {
    font-weight: 800;
    color: var(--primary);
}

.theory-q {
    display: none;
    padding: 20px;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
}

.theory-q.is-active { display: block; }

.theory-q-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.theory-q-topic, .theory-q-source, .theory-q-diff {
    font-size: var(--text-2xs, 10px);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    padding: 3px 9px;
    border-radius: var(--radius-full, 999px);
}

.theory-q-topic { background: color-mix(in srgb, var(--primary) 14%, var(--surface)); color: var(--primary); border: 1px solid var(--primary); }
.theory-q-source { background: var(--surface-sunken, var(--surface)); color: var(--text-muted); border: 1px solid var(--line); }
.theory-q-diff-easy { background: color-mix(in srgb, var(--palette-emerald-500) 14%, var(--surface)); color: var(--palette-emerald-500); }
.theory-q-diff-medium { background: color-mix(in srgb, var(--palette-amber-500) 14%, var(--surface)); color: var(--palette-amber-500); }
.theory-q-diff-hard { background: color-mix(in srgb, var(--palette-red-500) 14%, var(--surface)); color: var(--palette-red-500); }
.theory-q-num { margin-left: auto; font-size: var(--text-xs); color: var(--text-muted); }

.theory-q-question { margin-bottom: 14px; font-size: var(--text-base); }

.theory-q-options {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.theory-q-option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.theory-q-option:hover:not(:disabled) { border-color: var(--primary); }
.theory-q-option:disabled { cursor: default; }

.theory-q-option .theory-bilingual { flex: 1; gap: 8px; }
.theory-q-option .theory-lang { padding: 0; background: none; border: none; }

.theory-q-letter {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    line-height: 22px;
    text-align: center;
    border-radius: 50%;
    background: var(--surface-sunken, var(--line));
    font-size: var(--text-xs);
    font-weight: 700;
}

.theory-q-option.is-correct { border-color: var(--success); background: color-mix(in srgb, var(--success) 12%, var(--surface)); }
/* The letter chips stay on the fixed palette: they are SOLID-fill status (white --on-primary text on a
   filled chip), and the flipping --success/--danger main token is a LIGHT rung in dark theme (green-400),
   so white-on-fill would fail there (~1.7:1). Solid-fill status needs its own vetted on-fill text token —
   out of this migration's two-token scope (the parent status-token ruling flagged the same case). */
.theory-q-option.is-correct .theory-q-letter { background: var(--palette-emerald-600); color: var(--on-primary); }
.theory-q-option.is-incorrect { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, var(--surface)); }
.theory-q-option.is-incorrect .theory-q-letter { background: var(--palette-red-600); color: var(--on-primary); }

.theory-q-feedback { margin-top: 14px; }

.theory-q-verdict { font-weight: 700; margin-bottom: 10px; }
.theory-q-verdict.is-correct { color: var(--success); }
.theory-q-verdict.is-incorrect { color: var(--danger); }

.next-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 12px;
    padding: 9px 18px;
    border: none;
    border-radius: var(--radius);
    background: var(--primary);
    color: var(--on-primary);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

/* ---- score screen ---- */
.theory-quiz-screen {
    text-align: center;
    padding: 32px 20px;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
}

.theory-quiz-final {
    font-size: 56px;
    font-weight: 800;
    color: var(--primary);
    line-height: 1;
}

.theory-quiz-detail { margin-top: 6px; color: var(--text-muted); }
.theory-quiz-msg { margin: 18px auto; max-width: 480px; color: var(--text); line-height: 1.6; }

/* ===================== RESPONSIVE ===================== */
/* 760px: local component breakpoint, intentionally a touch before --bp-md (768); not the app seam. */
@media (max-width: 760px) {
    /* Bilingual stacks: EN over RU with a divider, not side-by-side. */
    .theory-bilingual { grid-template-columns: 1fr; gap: 8px; }
    .theory-lang-ru { border-left: none; border-top: 2px solid var(--palette-emerald-600, var(--line)); border-radius: 0; padding-top: 10px; }
    .theory-q-option .theory-lang-ru { border-top: 1px solid var(--line); padding-top: 6px; }

    /* Tier cards stack instead of cramming 3-up. */
    .theory-tiergrid { grid-template-columns: 1fr; }

    /* Mode switch (the <x-tabs> nav) pins to the top so she can flip without
     * scrolling back — the old .theory-switch sticky behaviour, scoped to the
     * tablist inside this sheet only. */
    .theory-sheet .tabs-nav {
        position: sticky;
        top: 0;
        z-index: 10;
        background: var(--surface);
    }
    .theory-sheet .tabs-pill > .tabs-nav > .tabs-tab { flex: 1; justify-content: center; }
}

/* @toc Tooltip */
/* Tooltip — a single floating label (one host per page) shown on hover of any
 * [data-tooltip] element, positioned by tooltip.js. An inverted chip (dark in
 * light mode, light in dark) so it reads over any surface. Colours: --tooltip-*. */

.tooltip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: var(--z-tooltip);
    padding: 4px 8px;
    border-radius: 6px;
    background: var(--tooltip-bg);
    color: var(--tooltip-text);
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    font-weight: 500;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transform: translateY(3px);
    transition: opacity 110ms ease, transform 110ms ease;
}

.tooltip.is-visible {
    opacity: 1;
    transform: translateY(0);
}

/* @toc Tree */
/* Tree — the APG Tree View primitive. Indentation via --tree-depth (set per node); collapse hides the
   child group (out of the a11y tree); the folder icon swaps closed↔open on aria-expanded and the chevron
   twists; leaf status (new/updated/deleted) reads at a glance via a glyph + row treatment. Ramp-lawful. */
.tree,
.tree-group {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Collapsed folder — its children leave the tree entirely (not just visually). */
.tree-node[aria-expanded="false"] > .tree-group {
    display: none;
}

.tree-row {
    display: flex;
    align-items: center;
    gap: 6px;
    height: var(--tree-row-height);
    padding-inline-end: 8px;
    padding-inline-start: calc(6px + var(--tree-depth, 1) * var(--tree-indent));
    border-radius: var(--tree-radius);
    cursor: pointer;
    color: var(--text);
    user-select: none;
}

.tree-row:hover {
    background: var(--tree-row-hover-bg);
}

/* Roving focus lands on the <li> (role=treeitem); ring the row. */
.tree-node:focus {
    outline: none;
}

.tree-node:focus-visible > .tree-row,
.tree-node:focus > .tree-row {
    outline: 2px solid var(--primary);
    outline-offset: -2px;
}

.tree-node.is-selected > .tree-row {
    background: var(--tree-row-selected-bg);
    font-weight: 600;
}

/* Chevron twist — points right when collapsed, down when expanded. Leaves get an inert spacer so their
   labels align with folder labels. */
.tree-twist {
    display: inline-flex;
    flex: 0 0 auto;
    width: 14px;
    height: 14px;
    color: var(--tree-twist);
    transition: transform 0.12s ease;
}

.tree-twist svg {
    width: 14px;
    height: 14px;
}

.tree-node[aria-expanded="true"] > .tree-row > .tree-twist {
    transform: rotate(90deg);
}

.tree-twist-leaf {
    visibility: hidden;
}

/* Folder icon swaps on state; leaf shows the document glyph. */
.tree-icon {
    display: inline-flex;
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    color: var(--tree-icon);
}

.tree-icon svg {
    width: 16px;
    height: 16px;
}

.tree-node[aria-expanded="true"] > .tree-row > .tree-icon-closed,
.tree-node[aria-expanded="false"] > .tree-row > .tree-icon-open {
    display: none;
}

.tree-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--text-sm);
}

/* Leaf status — the change each file carries, readable at a glance. */
.tree-status {
    flex: 0 0 auto;
    width: 1.1em;
    text-align: center;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.tree-status-new {
    color: var(--tree-status-new);
}

.tree-status-updated {
    color: var(--tree-status-updated);
}

.tree-status-deleted {
    color: var(--tree-status-deleted);
}

/* new = additive accent on the icon; deleted = struck + dimmed name + danger accent. */
.tree-row-new .tree-icon {
    color: var(--tree-status-new);
}

.tree-row-deleted .tree-label {
    text-decoration: line-through;
    color: var(--text-muted);
}

.tree-row-deleted .tree-icon {
    color: var(--tree-status-deleted);
}

/* @toc Uploads gallery */
/* Uploads gallery — the bulk file dropper, the upload grid, and the modal viewer.
 * Self-contained (no cross-component class deps); theme tokens throughout.
 * View: uploads-gallery.blade.php; behaviour: uploads-gallery.js. */

.uploads-gallery {
    max-width: var(--max-w-4xl);
}

.uploads-gallery-upload {
    margin-bottom: 28px;
}

/* The big bulk drop zone — the whole uploader is this one click/drop target. */
.uploads-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 190px;
    padding: 48px 24px;
    text-align: center;
    border: 2px dashed var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    color: var(--text-muted);
    font-size: var(--text-base);
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.uploads-drop:hover,
.uploads-drop.is-over {
    border-color: var(--primary);
    color: var(--text);
}

/* Keyboard focus — the file <input> is .sr-only (focusable but off-screen), so its focus ring must ride
 * the visible drop-zone label (WCAG 2.4.7). :focus-visible = keyboard only; the transparent outline is the
 * forced-colors fallback (box-shadow is dropped in high-contrast, so the outline gets promoted). */
.uploads-drop:has(input:focus-visible) {
    border-color: var(--primary);
    color: var(--text);
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--primary);
}

/* While the batch posts, dim the zone and stop further drops. */
.uploads-drop.is-busy {
    opacity: 0.6;
    pointer-events: none;
}

.uploads-drop-icon {
    display: inline-flex;
    width: 30px;
    height: 30px;
    color: var(--text-muted);
}

.uploads-drop-icon svg {
    width: 100%;
    height: 100%;
}

.uploads-drop-hint {
    font-weight: 600;
    color: var(--text);
}

.uploads-drop-sub {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.uploads-empty {
    margin-top: 8px;
}

/* The grid of uploaded files. */
.uploads-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 14px;
    margin-top: 8px;
}

.uploads-tile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding: 16px;
    text-align: left;
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    color: var(--text);
    cursor: pointer;
    font: inherit;
    transition: border-color 0.15s ease;
}

.uploads-tile:hover,
.uploads-tile:focus-visible {
    border-color: var(--primary);
}

.uploads-tile-icon {
    display: inline-flex;
    width: 22px;
    height: 22px;
    color: var(--text-muted);
}

.uploads-tile-icon svg {
    width: 100%;
    height: 100%;
}

.uploads-tile-title {
    font-weight: 600;
    line-height: 1.3;
    word-break: break-word;
}

.uploads-tile-meta {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--text-muted);
}

/* Modal viewer — a reusable <x-dialog>. The default .dialog is content-width with a
 * padded scrolling body; these id/class-scoped overrides make the viewer large and let
 * the iframe fill it. .uploads-viewer lands on the native <dialog> (class merge). */
.uploads-viewer {
    width: min(1000px, calc(100vw - 32px));
}

.uploads-viewer .dialog-panel {
    height: min(90vh, calc(100vh - 64px));
}

.uploads-viewer .dialog-body {
    padding: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.uploads-viewer-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--line);
}

.uploads-modal-title {
    font-weight: 600;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.uploads-viewer .uploads-modal-frame {
    flex: 1 1 auto;
    width: 100%;
    border: 0;
}

/* @toc Wishlist */
/* Wishlist — a core, standalone intake box: a short compose form plus the author's (or, for
 * admins, everyone's) list of wishes with a New/Doing/Done status. Built on primitives
 * (x-textarea / x-select / x-input / x-button), so this sheet themes only the wishlist's own
 * composition + the status chips. View: wishlist.blade.php. No JS — plain server forms. */

.wishlist {
    max-width: var(--max-w-3xl);
    margin-top: 32px;
}

.wishlist-head {
    margin-bottom: 12px;
}

.wishlist-h {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text);
}

.wishlist-h-ru {
    margin-inline-start: 8px;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text-muted);
}

.wishlist-lead {
    margin: 4px 0 0;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.wishlist-form {
    margin: 14px 0 24px;
}

.wishlist-form-actions {
    margin-top: 10px;
}

/* The list of wishes — newest first, a coloured leading (inline-start) edge per status. */
.wishlist-items {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.wishlist-item {
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-inline-start: 3px solid var(--text-muted);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
}

.wishlist-item-doing {
    border-inline-start-color: var(--warning);
}

.wishlist-item-done {
    border-inline-start-color: var(--success);
}

.wishlist-item-body {
    color: var(--text);
    white-space: pre-wrap;
    word-break: break-word;
}

.wishlist-item-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.wishlist-status {
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: var(--radius-full, 999px);
    color: var(--text-muted);
    background: color-mix(in srgb, var(--text-muted) 16%, transparent);
}

/* Active-status chips ride the vetted self-contrasting token pair (token on its own
 * -surface tint is AA by construction), replacing the ad-hoc palette tints. The New
 * chip above stays a neutral --text-muted wash — it carries no status hue. */
.wishlist-status-doing {
    color: var(--warning);
    background: var(--warning-surface);
}

.wishlist-status-done {
    color: var(--success);
    background: var(--success-surface);
}

/* Admin's reply back to the author. */
.wishlist-item-note {
    display: flex;
    gap: 8px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--line);
    font-size: var(--text-sm);
    color: var(--text);
}

.wishlist-item-note-label {
    flex-shrink: 0;
    font-weight: 600;
    color: var(--text-muted);
}

/* Admin status control — a compact row of primitives (status select + reply + save). The
 * primitives theme themselves; this only lays them out, growing the note input to fill. */
.wishlist-admin {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 8px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--line);
}

.wishlist-admin .input {
    flex: 1;
    min-width: 160px;
}

.wishlist-empty {
    margin-top: 4px;
}

/* @toc Accessibility page */
/*
 * /accessibility page — landing-grade presentation (hero, icon cards, stat band, status card, chip
 * nav). All class-scoped .a11y-*; no JS. Same sections/copy/derivation as the restructure.
 *
 * COLOR LAW (this page's original axe violations were opacity-muted text): NEVER de-emphasize text
 * with opacity on glyphs, and NEVER hand-author a colour. Every colour is a ramp/semantic token
 * (--text, --text-muted, --line, --surface, --surface-raised, --primary-*); for a tint, color-mix on
 * a BACKGROUND only. No hex, no --palette-*. Decorative icons are aria-hidden (MediaService), so their
 * colour is free of contrast rules; real text stays on tokens that hold AA.
 *
 * RHYTHM: a wide container (~1080px) lets the bands (hero, cards, stat band) breathe; prose + the
 * contract ledger stay at a readable measure. De-emphasis is size/weight/spacing, never glyph opacity.
 */

.a11y {
    max-width: 1080px;
    margin-inline: auto;
}

.a11y section {
    margin: 3rem 0;
}

.a11y section > h2 {
    font-size: var(--text-2xl);
    margin: 0 0 1.1rem;
    scroll-margin-top: 5rem;
}

/* Prose + ledger keep a readable measure; the bands above break wider. */
.a11y section > p,
.a11y-list,
.a11y-audit-caveat,
.a11y-rules,
.a11y-fineprint p {
    max-width: 66ch;
}

/* ── 1. Hero band ─────────────────────────────────────────────────────────── */
.a11y-hero {
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: 16px;
    padding: 2.75rem 2.25rem;
    margin-bottom: 2.5rem;
}

.a11y-kicker {
    font-size: var(--text-sm);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    /* --text-muted (not --primary-500, which is only 4.02:1 on the dark surface) so the eyebrow holds
       AA on both tenants; the brand red still carries the card icons, which are decorative. */
    color: var(--text-muted);
    margin: 0 0 0.85rem;
}

.a11y-lead {
    font-size: var(--text-3xl);
    line-height: 1.2;
    font-weight: 650;
    color: var(--text);
    margin: 0 0 1rem;
    max-width: 24ch;
}

.a11y-aim {
    font-size: var(--text-lg);
    color: var(--text-muted);
    margin: 0 0 1.5rem;
    max-width: 56ch;
}

.a11y-cta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.65rem 1rem;
    margin: 0 0 1.5rem;
}

.a11y-cta-email {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

/* The hero shows the address as readable TEXT (the button is the mailto action) — full-ink so it holds
   AA, and it avoids a second brand-red link that wouldn't. */
.a11y-email {
    color: var(--text);
    font-weight: 600;
}

/* ── Chips (status row, audit date, mirror source, group counts) ──────────── */
.a11y-chiprow {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.a11y-chip {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.7rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface);
    font-size: var(--text-sm);
    color: var(--text-muted);
    white-space: nowrap;
}

/* ── 2. On this page — pill-chip nav, CSS-only sticky on desktop ───────────── */
.a11y-toc {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0 0 2.5rem;
}

.a11y-toc a {
    display: inline-flex;
    padding: 0.35rem 0.85rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-raised);
    font-size: var(--text-sm);
    color: var(--text-muted);
    text-decoration: none;
}

.a11y-toc a:hover,
.a11y-toc a:focus-visible {
    color: var(--text);
    border-color: var(--primary-500);
}

@media (min-width: 768px) {
    .a11y-toc {
        position: sticky;
        top: 0;
        z-index: 5;
        margin-inline: -0.75rem;
        padding: 0.75rem;
        background: color-mix(in srgb, var(--surface) 88%, transparent);
        backdrop-filter: blur(6px);
        border-radius: 12px;
    }
}

/* ── 3. What you can expect — icon cards ──────────────────────────────────── */
.a11y-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 1rem;
    margin: 0 0 1.25rem;
}

.a11y-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1.35rem;
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: 12px;
}

.a11y-card-icon {
    display: inline-flex;
    color: var(--primary-500);
}

.a11y-card-icon svg {
    width: 26px;
    height: 26px;
}

.a11y-card p {
    margin: 0;
    color: var(--text);
    line-height: 1.45;
}

.a11y-honest {
    color: var(--text-muted);
    font-style: italic;
    margin: 0;
}

/* ── 4/5. Callouts (report + audit status card) ───────────────────────────── */
.a11y section > .callout {
    max-width: 66ch;
}

.a11y .callout p:first-child {
    margin: 0;
}

.a11y-hint {
    font-size: var(--text-sm);
    color: var(--text-muted);
    margin: 0.5rem 0 0;
}

/* Audit as a status card: big count beside the meta; the callout supplies the variant edge. */
.a11y-audit-card .callout-body {
    display: flex;
    align-items: center;
    gap: 1.15rem;
}

.a11y-audit-num {
    font-size: var(--text-4xl);
    font-weight: 700;
    line-height: 1;
    color: var(--text);
}

.a11y-audit-meta {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.a11y-audit-headline {
    margin: 0;
    font-weight: 600;
    color: var(--text);
}

.a11y-audit-sub {
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.a11y-audit-caveat {
    font-size: var(--text-sm);
    color: var(--text-muted);
    margin: 0.9rem 0 0;
}

.a11y-rules {
    margin: 1rem 0 0;
    padding-left: 1.15rem;
}

.a11y-rules li {
    margin: 0.35rem 0;
    font-size: var(--text-sm);
    color: var(--text);
}

.a11y-audit-details {
    margin-top: 1rem;
    max-width: 66ch;
}

/* ── How-it's-built list ──────────────────────────────────────────────────── */
.a11y-list {
    margin: 0;
    padding-left: 1.2rem;
}

.a11y-list li {
    margin: 0.6rem 0;
    color: var(--text);
    line-height: 1.55;
}

/* ── 8. Stat band (contractStats) ─────────────────────────────────────────── */
.a11y-lead-sub {
    font-size: var(--text-lg);
    color: var(--text-muted);
    margin: 0 0 1.5rem;
    max-width: 56ch;
}

.a11y-statband {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
    margin: 0 0 2.5rem;
}

.a11y-stat-card {
    padding: 1.5rem;
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: 12px;
}

.a11y-stat-num {
    display: block;
    font-size: var(--text-4xl);
    font-weight: 700;
    line-height: 1;
    color: var(--text);
    margin-bottom: 0.4rem;
}

.a11y-stat-label {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

/* ── 5/7. Contract ledger — rows, not cards; group headers with count badges ── */
.a11y-group {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: var(--text-sm);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin: 2rem 0 0.4rem;
}

.a11y-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--text) 8%, transparent);
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0;
    color: var(--text-muted);
}

.a11y-ledger {
    max-width: 72ch;
    border-top: 1px solid var(--line);
}

/* Strip the <x-details> card chrome → ledger rows (1px rule between), with a subtle hover tint. */
.a11y-row.details {
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
    background: transparent;
}

.a11y-row.details:hover {
    background: color-mix(in srgb, var(--text) 4%, transparent);
}

.a11y-kind {
    display: inline-block;
    margin-left: 0.55rem;
    padding: 0.05rem 0.5rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    vertical-align: middle;
}

.a11y-adherence {
    margin: 0 0 0.5rem;
    font-size: 0.92rem;
    color: var(--text);
}

.a11y-desc {
    margin: 0 0 0.5rem;
    font-size: 0.9rem;
    color: var(--text-muted);
}

.a11y-points {
    margin: 0.4rem 0 0;
    padding-left: 1.15rem;
}

.a11y-points li {
    margin: 0.3rem 0;
    font-size: 0.9rem;
    color: var(--text);
}

/* "Keyboard" label above the shared keyboard-table in a contract row. */
.a11y-kbd-label {
    margin: 0.85rem 0 0.35rem;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.a11y-anchor {
    display: inline-block;
    margin-top: 0.65rem;
    font-size: 0.9rem;
}

/* The per-page mirror's intro carries a chip; keep it on a readable measure. */
.a11y-stat {
    color: var(--text-muted);
    margin: 0 0 1rem;
    max-width: 66ch;
}

/* ── 9. Fine print ────────────────────────────────────────────────────────── */
.a11y-fineprint {
    margin-top: 3rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--line);
}

.a11y-fineprint p {
    font-size: var(--text-sm);
    color: var(--text-muted);
    margin: 0 0 0.5rem;
}

/* @toc Admin page */
/* Admin access panel — the user x section grid. Page CSS, bundled by CssService. */

.admin-panel {
    max-width: var(--max-w-2xl);
}

.admin-table {
    width: 100%;
    border-collapse: collapse;
    margin: 14px 0;
}

.admin-table th,
.admin-table td {
    padding: 10px 12px;
    text-align: left;
    border-bottom: 1px solid var(--line);
}

.admin-table th {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: 700;
    color: var(--text-muted);
}

.admin-table td {
    font-size: var(--text-sm);
}

.admin-user-name {
    display: block;
    font-weight: 600;
    color: var(--text);
}

.admin-user-email {
    display: block;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.admin-admin-cell {
    color: var(--text-muted);
    font-style: italic;
}

.admin-table input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--primary);
    cursor: pointer;
}

.admin-actions {
    margin-top: 12px;
}

/* @toc Darbru page */
/* Darbru index — study sheets in two segments (Essays / Theory), each a responsive
 * grid of cards. Page CSS, bundled by CssService. The sheet renderers are the
 * study-sheet (essay) and theory-sheet components. */

.darbru-actions {
    margin-bottom: 8px;
}

.darbru-segment {
    margin-top: 22px;
}

.darbru-segment-title {
    margin: 0 0 12px;
    font-size: var(--text-lg);
    font-weight: 700;
    color: var(--text);
}

.darbru-segment-empty {
    margin: 0;
}

.darbru-index {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 14px;
}

.darbru-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 16px;
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    text-decoration: none;
    transition: border-color 0.15s ease;
}

.darbru-card:hover {
    border-color: var(--primary);
}

.darbru-card-subject {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--text-muted);
}

.darbru-card-title {
    font-weight: 600;
    color: var(--text);
}

/* The create card — dashed to read as an action, not a sheet. */
.darbru-card-new {
    border-style: dashed;
    justify-content: center;
}

.darbru-card-new .darbru-card-title {
    color: var(--text-muted);
}

.darbru-card-new:hover .darbru-card-title {
    color: var(--text);
}

/* @toc Demo page */
/* Demo page — the living style guide layout. */

.demo {
    width: 100%;
    max-width: 760px;
}

.demo-sec {
    padding: 26px 0;
    border-bottom: 0.5px solid var(--line);
}

.demo-sec:last-child {
    border-bottom: none;
}

.demo-h {
    margin: 0 0 16px;
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: 500;
    letter-spacing: 0.04em;
}

.demo-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

.demo-narrow {
    max-width: 360px;
}

.demo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
}

.demo-mono {
    font-family: var(--font-mono);
    color: var(--text-muted);
}

/* @toc Docs page */
/* Documentation pages (/docs/<page>) — Palette ramps, the Typography scale, and
 * the tabbed component docs. Each page's body is resources/views/docs/<key>.blade.php.
 * Code blocks are the x-code-block component (see code-block.css). */

/* Palette showcase — the three reference forms, the live active ramps, and the
 * full catalog as rows of click-to-copy chips. */
.palette-ref {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
    margin: 0 0 32px;
}

.palette-ref-item {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 10px;
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface);
}

.palette-ref-swatch {
    width: 28px;
    height: 28px;
    flex: none;
    border-radius: 7px;
    border: 1px solid var(--line);
}

.palette-ref-item code {
    font-size: var(--text-sm);
}

.palette-ref-note {
    flex-basis: 100%;
    color: var(--text-muted);
    font-size: var(--text-xs);
    line-height: 1.5;
}

.palette-active,
.palette-catalog {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.palette-active {
    margin-bottom: 32px;
}

.palette-active-row {
    display: flex;
    align-items: center;
    gap: 12px;
}

.palette-active-label {
    flex: none;
    width: 84px;
    text-align: right;
    font-size: var(--text-sm);
    color: var(--text-muted);
    text-transform: capitalize;
}

.palette-ramp {
    display: flex;
    flex: 1;
    min-width: 0;
    gap: 4px;
}

.palette-chip {
    position: relative;
    flex: 1;
    min-width: 0;
    height: 44px;
    padding: 0;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: transform 120ms ease, box-shadow 120ms ease;
}

.palette-chip:hover {
    transform: scale(1.12);
    box-shadow: var(--shadow-lg);
    z-index: 2;
}

.palette-chip-step {
    position: absolute;
    inset: auto 0 4px 0;
    text-align: center;
    font-size: 9px;
    color: #fff;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.65);
    opacity: 0;
    pointer-events: none;
    transition: opacity 120ms ease;
}

.palette-chip:hover .palette-chip-step {
    opacity: 1;
}

@media (max-width: 768px) {
    .palette-chip { height: 34px; }
    .palette-active-label { width: 60px; }
}

/* Click-to-copy button on code blocks, and the copy toast (palette chips + code). */
.doc-copy-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    padding: 3px 10px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text-muted);
    font-size: var(--text-xs);
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease, color 120ms ease, border-color 120ms ease;
}

.code-block:hover .doc-copy-btn,
.doc-copy-btn:focus-visible {
    opacity: 1;
}

.doc-copy-btn:hover {
    color: var(--text);
    border-color: var(--text-muted);
}

.doc-copy-toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translate(-50%, 10px);
    padding: 8px 16px;
    border-radius: 999px;
    background: var(--text);
    color: var(--surface);
    font-size: var(--text-sm);
    opacity: 0;
    pointer-events: none;
    transition: opacity 150ms ease, transform 150ms ease;
    z-index: 1000;
}

.doc-copy-toast.is-visible {
    opacity: 1;
    transform: translate(-50%, 0);
}

/* Typography: a sample per scale step with a mono spec line under it. */
/* Typography showcase — family cards over a live type-scale specimen. */
.type-families {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
    margin: 0 0 32px;
}

.type-family {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 16px 18px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface);
}

.type-family-sample {
    flex: none;
    font-size: 40px;
    line-height: 1;
    color: var(--text);
}

.type-family-meta {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.type-family-meta code {
    cursor: pointer;
}

.type-family-meta span {
    font-size: var(--text-xs);
    color: var(--text-muted);
    font-family: var(--font-mono);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.type-scale-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 24px;
    padding: 14px 0;
    border-bottom: 1px solid var(--line);
}

.type-scale-row:last-child {
    border-bottom: none;
}

.type-scale-sample {
    margin: 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.type-scale-meta {
    flex: none;
    font-size: var(--text-xs);
    color: var(--text-muted);
    font-family: var(--font-mono);
    white-space: nowrap;
}

/* Component doc: each component page renders inside an x-tabs (Example / View /
 * CSS / JS); these are the panel-content styles. */
.doc-example {
    padding: 0 0 28px;
}

.doc-example-label {
    margin: 0 0 10px;
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

/* The example "canvas" — a raised, padded surface so a live demo reads as a
 * deliberate preview, not markup dropped on the page. */
.doc-example-demo {
    padding: 28px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface-raised);
}

.doc-subhead {
    margin: 0 0 8px;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.doc-subhead:not(:first-child) {
    margin-top: 20px;
}

.doc-empty {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* Intro paragraph above a component doc's tab strip (meta.description). */
.doc-intro {
    margin: 0 0 20px;
    max-width: 68ch;
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: 1.6;
}

/* A one-line lead-in inside a tab panel (e.g. the HTML tab's "compiles to"). */
.doc-lead {
    margin: 0 0 12px;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* Section accents — one catalog hue per section, used by the home cards, the
 * per-page eyebrow badge, and card hovers. Catalog colours are theme-independent. */
.docs-accent-foundations { --section: var(--palette-emerald-500); }
.docs-accent-primitives  { --section: var(--palette-violet-500); }
.docs-accent-patterns    { --section: var(--palette-amber-500); }
.docs-accent-internals   { --section: var(--palette-sky-500); }

/* /docs home — the section-card front door. */
.docs-home-lead {
    margin: 0 0 24px;
    color: var(--text-muted);
    font-size: var(--text-base);
}

.docs-home-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
}

.docs-home-card {
    display: block;
    padding: 20px;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--surface);
    color: var(--text);
    text-decoration: none;
    transition: border-color 0.15s ease;
}

.docs-home-card:hover {
    border-color: var(--section);
}

.docs-home-card-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
}

.docs-home-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 10px;
    background: var(--surface-raised);
    color: var(--section);
}

.docs-home-icon svg {
    width: 24px;
    height: 24px;
}

.docs-home-card-title {
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text);
}

.docs-home-card-count {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.docs-home-blurb {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: 1.6;
}

/* Per-page section eyebrow badge. */
.doc-eyebrow {
    margin: 0 0 20px;
}

.doc-eyebrow-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 11px 3px 8px;
    border: 1px solid var(--section);
    border-radius: 999px;
    color: var(--section);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.doc-eyebrow-icon {
    display: inline-flex;
}

.doc-eyebrow-icon svg {
    width: 14px;
    height: 14px;
}

/* Provenance — "based on the <button> element", grounding a primitive in MDN. */
.doc-basedon {
    margin: -8px 0 20px;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.doc-basedon code {
    color: var(--text);
}

.doc-basedon-link {
    white-space: nowrap;
}

/* "Composed of" — a Pattern's Primitive ingredients, derived from its Blade source. */
.doc-composed {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 24px;
}

.doc-composed-label {
    font-size: var(--text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.doc-composed-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: var(--text-sm);
    color: var(--text);
    text-decoration: none;
}

.doc-composed-chip:hover {
    border-color: var(--text-muted);
    background: var(--surface-raised);
}

.doc-composed-count {
    color: var(--text-muted);
    font-size: var(--text-xs);
}

/* Props/attributes table at the top of a component doc. */
.doc-props {
    width: 100%;
    margin: 0 0 24px;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.doc-props th {
    padding: 8px 12px;
    border-bottom: 1px solid var(--line);
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    text-align: left;
}

.doc-props td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--line);
    vertical-align: top;
}

.doc-props td:nth-child(4) {
    color: var(--text-muted);
}

.doc-props-req {
    color: var(--primary-500);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Per-component accessibility notes, below the tabs. */
.doc-a11y {
    margin-top: 28px;
}

.doc-a11y-list {
    margin: 0 0 10px;
    padding-left: 20px;
}

.doc-a11y-list li {
    margin-bottom: 6px;
    font-size: var(--text-sm);
    line-height: 1.6;
}

.doc-a11y-link {
    font-size: var(--text-sm);
}

/* Prev/next pager at the foot of every doc page. */
.doc-pager {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    margin-top: 48px;
    padding-top: 20px;
    border-top: 1px solid var(--line);
}

.doc-pager-link {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 16px;
    border: 1px solid var(--line);
    border-radius: 10px;
    text-decoration: none;
    max-width: 48%;
}

.doc-pager-next {
    margin-left: auto;
    text-align: right;
}

.doc-pager-link:hover {
    border-color: var(--text-muted);
    background: var(--surface-raised);
}

.doc-pager-dir {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.doc-pager-label {
    color: var(--text);
    font-weight: 500;
}

/* Internals reference — sectioned services/models/routes map. */
.doc-internals {
    max-width: 80ch;
}

.doc-internals-head {
    margin: 32px 0 4px;
    font-size: var(--text-lg);
}

.doc-internals-head span {
    margin-left: 8px;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 400;
    color: var(--text-muted);
}

.doc-internals-list {
    margin: 0 0 8px;
    padding-left: 20px;
}

.doc-internals-list li {
    margin-bottom: 10px;
    line-height: 1.6;
}

/* ── Markup Element Ledger (the /docs/markup census page) ─────────────────────────────────
   Reusable so a future JS/CSS census page adopts the same shell. Colours from the ramp + semantic
   tokens only; badge/dot tints via color-mix (decoration alpha, sanctioned). */
.ledger-accounting {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 18px;
    margin: 20px 0 8px;
    padding: 14px 18px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface-raised);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.ledger-stat strong {
    color: var(--text);
    font-size: var(--text-base);
}

.ledger-stat-clean {
    color: var(--primary);
    font-weight: 600;
}

.ledger-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 12px 0;
    background: var(--page-bg);
}

.ledger-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.ledger-chip {
    padding: 4px 12px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text-muted);
    font-size: var(--text-sm);
    cursor: pointer;
}

.ledger-chip:hover {
    border-color: var(--line-strong);
    color: var(--text);
}

.ledger-chip.is-active {
    border-color: var(--primary);
    background: color-mix(in oklab, var(--primary) 14%, transparent);
    color: var(--primary);
    font-weight: 600;
}

.ledger-search {
    flex: 1 1 220px;
    max-width: 320px;
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text);
    font-size: var(--text-sm);
}

.ledger-count {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.ledger-cat {
    margin-top: 28px;
}

.ledger-cat-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 10px;
}

.ledger-cat-n {
    font-size: var(--text-sm);
    font-weight: 400;
    color: var(--text-muted);
}

/* Disposition badges — one accent per state, all ramp-lawful. */
.ledger-badge {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    border: 1px solid var(--line);
    font-size: var(--text-xs);
    font-weight: 600;
    white-space: nowrap;
    text-transform: lowercase;
    color: var(--text-muted);
}

.ledger-badge-covered {
    color: var(--primary);
    border-color: color-mix(in oklab, var(--primary) 40%, transparent);
    background: color-mix(in oklab, var(--primary) 12%, transparent);
}

.ledger-badge-deferred {
    color: var(--primary);
    border-color: color-mix(in oklab, var(--primary) 40%, transparent);
}

.ledger-badge-watch {
    color: var(--text-danger);
    border-color: color-mix(in oklab, var(--text-danger) 40%, transparent);
}

.ledger-badge-raw-ok {
    color: var(--text);
    border-color: var(--line-strong);
}

.ledger-badge-not-used {
    color: var(--text-muted);
    border-style: dashed;
}

.ledger-type,
.ledger-guidance {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.ledger-guidance {
    display: block;
    max-width: 46ch;
    line-height: 1.5;
}

.ledger-muted {
    color: var(--text-muted);
}

/* Link-health dot — brand for reachable, danger for broken, muted-hollow for unchecked. */
.ledger-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    vertical-align: middle;
    background: var(--primary);
}

.ledger-dot-bad {
    background: var(--text-danger);
}

.ledger-dot-unknown {
    background: transparent;
    border: 1px solid var(--line-strong);
}

.ledger-health {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 12px;
    margin-top: 28px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.ledger-health-form {
    margin-left: auto;
}

/* @toc Landing page */
/* Landing page — the public eightythree.com home: just the brand mark, centered.
 * Renders inside the public shell (x-app-page: topbar + main).
 * View: /resources/views/pages/landing.blade.php. */

.landing {
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* @toc Planner card page */
/* Card-open (⑨) — the five-pane ticket workspace. Mobile (< 1024px): x-tabs default, one pane at a time,
 * the tablist visible. Desktop (>= 1024px, the x-tabs `unwrap` seam): the tablist is hidden (tabs.js drops
 * its tab semantics to match) and every panel shows as a labelled column, side by side. Tokens only. */

.card-open {
    max-width: 100%;
}

/* The return path to the board (this page is reached from a board card's open affordance). */
.cardo-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-bottom: 14px;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text-muted);
    text-decoration: none;
    transition: color 120ms ease;
}

.cardo-back svg {
    width: 16px;
    height: 16px;
}

.cardo-back:hover {
    color: var(--text);
}

.cardo-back:focus-visible {
    outline: 2px solid var(--primary-500);
    outline-offset: 2px;
    border-radius: 4px;
    color: var(--text);
}

/* Builder attribution — the HOLDER above the card (James's placement ask): an accent-tinted pill with a
   Heroicon lock, shown while the card is actively held (in-progress / review). */
.cardo-holder {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 12px;
    padding: 4px 12px 4px 10px;
    border-radius: var(--radius-full, 999px);
    font-size: var(--text-sm);
    font-weight: 600;
    color: color-mix(in srgb, var(--primary-500) 52%, var(--text));
    background: color-mix(in srgb, var(--primary-500) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--primary-500) 28%, transparent);
}

.cardo-holder svg {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

.card-open .pane {
    padding: 4px 2px 8px;
}

.pane-empty,
.pane-meaning-state {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* Description */
.pane-status {
    font-size: var(--text-sm);
    color: var(--text);
    margin-bottom: 12px;
}

.pane-status-label {
    display: inline-block;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    font-size: var(--text-xs);
    letter-spacing: 0.04em;
    margin-right: 6px;
}

.pane-prose {
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--text);
}

.pane-prose :is(p, ul, ol) {
    margin: 0 0 10px;
}

.pane-subhead {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: var(--text-sm);
    font-weight: 600;
    margin: 16px 0 8px;
}

.pane-progress {
    font-size: var(--text-xs);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.cardo-checklist {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.cardo-check-label {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    font-size: var(--text-sm);
    line-height: 1.4;
    cursor: pointer;
}

.cardo-check-box {
    margin-top: 2px;
    accent-color: var(--primary);
    flex-shrink: 0;
}

/* Files — a flat clickable list (the pre-Tree fallback) */
.cardo-files {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.cardo-file {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    border-radius: var(--radius-md);
    padding: 6px 8px;
    cursor: pointer;
    color: var(--text);
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xs);
}

.cardo-file:hover,
.cardo-file.is-active {
    background: color-mix(in srgb, var(--primary) 12%, transparent);
}

.cardo-file-kind {
    flex-shrink: 0;
    font-family: var(--font-body, sans-serif);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
}

.cardo-file-kind-touched {
    color: var(--primary);
}

.cardo-file-path {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Code — a bare <pre> live window */
.pane-code-ref {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xs);
    color: var(--text-muted);
    margin-bottom: 8px;
}

.cardo-code {
    margin: 0;
    padding: 12px;
    background: var(--neutral-900);
    color: var(--neutral-100);
    border-radius: var(--radius-md);
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xs);
    line-height: 1.5;
    white-space: pre;
    overflow: auto;
    max-height: 60vh;
}

:root[data-theme="light"] .cardo-code {
    background: var(--neutral-100);
    color: var(--neutral-900);
}

/* Explanation */
.pane-meaning-prose {
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--text);
    white-space: pre-wrap;
}

/* Settings rail */
.cardo-rail {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 12px;
    margin: 0;
    font-size: var(--text-sm);
}

.cardo-rail dt {
    color: var(--text-muted);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    align-self: center;
}

.cardo-rail dd {
    margin: 0;
    color: var(--text);
}

.cardo-rail-note {
    margin-top: 16px;
    font-size: var(--text-xs);
    color: var(--text-muted);
    font-style: italic;
}

/* Desktop: five panes side by side (the unwrap seam). */
@media (min-width: 1024px) {
    .card-open .tabs-nav {
        display: none;
    }

    .card-open .tabs-body {
        padding-top: 0;
        display: grid;
        /* Description · Settings · Files(tree) · Code · Explanation — Code widest, Settings narrowest. */
        grid-template-columns: 1.5fr 0.9fr 1.3fr 1.6fr 1.1fr;
        gap: 1px;
        background: var(--line);
        border: 1px solid var(--line);
        border-radius: var(--radius-lg);
        overflow: hidden;
    }

    .card-open .tabs-panel {
        display: block;
        background: var(--surface);
        padding: 16px;
        overflow-y: auto;
        max-height: calc(100vh - 150px);
    }

    /* Visible column label (the hidden tab still supplies the AT name via aria-labelledby). */
    .card-open .tabs-panel::before {
        display: block;
        font-size: var(--text-xs);
        text-transform: uppercase;
        letter-spacing: 0.05em;
        color: var(--text-muted);
        font-weight: 600;
        margin-bottom: 12px;
    }

    .card-open .tabs-panel:nth-of-type(1)::before { content: "Description"; }
    .card-open .tabs-panel:nth-of-type(2)::before { content: "Settings"; }
    .card-open .tabs-panel:nth-of-type(3)::before { content: "Files"; }
    .card-open .tabs-panel:nth-of-type(4)::before { content: "Code"; }
    .card-open .tabs-panel:nth-of-type(5)::before { content: "Explanation"; }
}

/* @toc Uploads page */
/* Uploads page — the single-file viewer (/uploads/<slug>). The gallery itself is the
 * uploads-gallery component; this is just the embedded-file page. Page CSS, bundled by
 * CssService. */

.uploads-view {
    display: flex;
    flex-direction: column;
    gap: 12px;
    height: calc(100vh - 180px);
    min-height: 460px;
}

.uploads-view-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.uploads-view-meta {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.uploads-view-frame {
    flex: 1;
    width: 100%;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
}

