/* ============================================================================
   SIPS component-rule stylesheet (FE-04 / CODE_REVIEW F4)
   Relocated byte-for-byte from base.html's inline <style> blocks, in source
   order, to shrink base.html. Theme TOKENS (:root + html[data-theme] CSS
   variable overrides) stay INLINE in base.html so first paint is themed (D-07,
   no FOUC); this sheet is loaded via a render-blocking {% static %} <link> in
   <head> AFTER the inline token <style>, so the var(--*) references below resolve.
   Do not reorder: same-specificity later rules override earlier ones.
   ============================================================================ */

/* ===== Component rules relocated from the head <style> block ===== */
        /* Helpers texte (compatible light/dark) */
        .text-strong { color: var(--text); }
        /* Tailwind cannot generate variants of custom classes, so the
           hover:text-strong the templates already use must be defined here. */
        .hover\:text-strong:hover { color: var(--text); }
        .text-muted  { color: var(--muted); }

        /* ====== Cards (Stripe-inspired: subtle radius, layered shadows) ====== */
        .glass-card{
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            box-shadow: var(--glass-shadow);
            border-radius: var(--radius-md);
            transition: border-color 0.15s ease, box-shadow 0.15s ease;
        }
        .glass-card:hover{
            border-color: var(--hover-border);
            box-shadow: var(--hover-shadow);
        }
        .glass-soft{
            background: var(--soft-bg);
            border: 1px solid var(--soft-border);
            border-radius: 6px;
        }

        /* ====== Glass-soft input field (folds the repeated input utility cluster) ======
           Computes identically to the Tailwind run
           `w-full px-3 py-2 rounded-lg bg-transparent outline-none focus:ring-2 focus:ring-emerald-400/30`.
           `text-strong` and `glass-soft` stay as SEPARATE classes on each element so the
           load-bearing webkit backdrop-blur prefix inside `.glass-soft` is never moved. */
        .ui-field{
            width: 100%;                 /* w-full */
            padding: 0.5rem 0.75rem;     /* py-2 px-3 */
            border-radius: 0.5rem;       /* rounded-lg */
            background-color: transparent; /* bg-transparent */
            outline: none;               /* outline-none */
        }
        .ui-field:focus{
            /* focus:ring-2 focus:ring-emerald-400/30 -> emerald-400 rgb(52,211,153) @ 0.3, 2px, no offset */
            box-shadow: 0 0 0 2px rgba(52, 211, 153, 0.3);
        }


        /* ====== Sidebar theme-aware (Stripe-clean) ====== */
        .sidebar-shell{
            background: var(--sidebar-bg);
            border-inline-end: 1px solid var(--sidebar-border);
            box-shadow: var(--sidebar-shadow);
        }
        .sidebar-divider{
            border-color: var(--divider) !important;
        }

        /* Nav items: Stripe-style (no border, subtle bg on hover, left accent) */
        .nav-item{
            display:flex;
            align-items:center;
            padding:0.5rem 0.75rem;
            border-radius: 0.5rem;
            background: var(--nav-bg);
            border: 1px solid var(--nav-border);
            border-inline-start: 2px solid transparent;
            transition: all 0.15s ease;
            color: var(--text);
            font-size: 0.875rem;
        }
        .nav-item:hover{
            background: var(--nav-hover-bg);
            border-color: var(--nav-hover-border);
            border-inline-start-color: var(--nav-active-accent);
            box-shadow: none;
        }

        .nav-icon-btn{
            width: 1rem;
            height: 1rem;
            display:flex;
            align-items:center;
            justify-content:center;
            border-radius: 6px;
            /* background: var(--soft-bg); */
            /* border: 1px solid var(--soft-border); */
            transition: all 0.15s ease;
            color: var(--text);
        }
        .nav-icon-btn:hover{
            /* background: var(--nav-hover-bg); */
            /* border-color: var(--nav-hover-border); */
        }

        .sidebar-toggle{
            <!-- background: var(--glass-bg); -->
            <!-- border: 1px solid var(--glass-border); -->
            color: var(--text);
            box-shadow: var(--glass-shadow);
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
        }
        .sidebar-toggle:hover{
            <!-- border-color: var(--hover-border); -->
            box-shadow: var(--hover-shadow);
        }
        .sidebar-toggle:active{ transform: scale(0.96); }

        /* Unified emerald-600 accent in light mode */
        html[data-theme="light"] .text-emerald-200,
        html[data-theme="light"] .text-emerald-300,
        html[data-theme="light"] .text-emerald-400,
        html[data-theme="light"] .text-emerald-500{
            color: #059669 !important;   /* emerald-600 — single accent */
        }

        /* Light theme: normalize Tailwind white text utilities */
        html[data-theme="light"] .text-white\/90{
            color: var(--text) !important;
        }
        html[data-theme="light"] .text-white\/70,
        html[data-theme="light"] .text-white\/60,
        html[data-theme="light"] .text-white\/45,
        html[data-theme="light"] .text-white\/40{
            color: var(--muted) !important;
        }
        html[data-theme="light"] .bg-white\/5{
            background-color: rgba(15,23,42,0.04) !important;
        }
        html[data-theme="light"] .border-white\/10{
            border-color: var(--soft-border) !important;
        }

        /* ====== Stripe-style table rows ====== */
        .stripe-row{
            border-bottom: 1px solid var(--divider);
            transition: background 0.12s ease;
        }
        .stripe-row:hover{
            background: var(--row-hover);
        }
        .stripe-row:last-child{
            border-bottom: none;
        }

        /* ====== Stripe-style stat card ====== */
        .stat-card{
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            border-radius: var(--radius-md);
            padding: 1.25rem 1.5rem;
            box-shadow: var(--glass-shadow);
            transition: border-color 0.15s ease, box-shadow 0.15s ease;
        }
        .stat-card:hover{
            border-color: var(--hover-border);
            box-shadow: var(--hover-shadow);
        }
        .stat-label{
            font-size: 0.8125rem;
            font-weight: 500;
            color: var(--muted);
            text-transform: uppercase;
            letter-spacing: 0.03em;
        }
        .stat-value{
            font-size: 1.75rem;
            font-weight: 600;
            color: var(--text);
            line-height: 1.2;
            margin-top: 0.25rem;
        }

        /* ====== Finance widget (Uiverse original structure) ====== */
        .theme-light{
            --color-dark-subtle: linear-gradient(135deg, color-mix(in oklab, var(--brand-700) 24%, transparent), color-mix(in oklab, var(--bg-1) 94%, transparent));
            --color-dark: var(--surface);
            --color-light: var(--text-main);
            --color-white: #ffffff;
            --color-red: color-mix(in oklab, var(--brand-600) 78%, var(--bg-2));
            --color-red-light: var(--error);
            --color-shadow-dark: color-mix(in oklab, var(--bg-3) 88%, transparent);
            --color-shadow-text: color-mix(in oklab, var(--bg-3) 65%, transparent);
            --color-accent-bg: var(--brand-soft);
            --shadow-light: -5px -5px 9px color-mix(in oklab, var(--bg-2) 65%, transparent);
            --shadow-dark: 5px 5px 9px color-mix(in oklab, var(--bg-3) 75%, transparent);
            --shadow-bar: -5px -5px 9px color-mix(in oklab, var(--bg-2) 70%, transparent), 5px 5px 9px color-mix(in oklab, var(--bg-3) 70%, transparent);
            display: flex;
            justify-content: center;
            width: 100%;
        }
        html[data-theme="light"] .theme-light{
            --color-dark-subtle: linear-gradient(135deg, color-mix(in oklab, var(--brand-700) 16%, transparent), color-mix(in oklab, #ffffff 94%, transparent));
            --color-dark: #ffffff;
            --color-light: var(--text-main);
            --color-shadow-dark: color-mix(in oklab, var(--text-main) 14%, transparent);
            --shadow-light: -5px -5px 9px rgba(255,255,255,0.6);
            --shadow-dark: 5px 5px 9px color-mix(in oklab, var(--text-main) 14%, transparent);
            --shadow-bar: -5px -5px 9px color-mix(in oklab, var(--text-main) 6%, transparent), 5px 5px 9px color-mix(in oklab, var(--text-main) 10%, transparent);
        }
        .theme-light .card{
            border-radius: 16px;
            padding: 10px;
            width: 100%;
            max-width: none;
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            box-shadow: var(--glass-shadow);
            position: relative;
            transition-duration: 0.2s;
            user-select: none;
        }
        .theme-light .card.is-animated{
            animation: financeFadeIn 0.6s ease both;
        }
        .finance-card{
            cursor: pointer;
        }
        .theme-light .flex{
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 18px;
        }
        .theme-light .bar-label{
            text-shadow: 1px 1px 3px var(--color-shadow-text);
            font-size: 12px;
        }
        .theme-light .flex .tag{
            fill: var(--color-white);
            display: flex;
            align-items: center;
            justify-content: center;
            height: 25px;
            width: 25px;
            box-shadow: 4px 4px 3px var(--color-shadow-dark);
        }
        .theme-light .bg-custom{
            background: var(--color-dark-subtle);
            padding: 18px;
            margin-bottom: 10px;
            border-radius: 10px;
            box-shadow: inset var(--shadow-light), inset var(--shadow-dark);
        }
        .theme-light .bg-custom-2{
            font-weight: 600;
        }
        .theme-light .pr-day{
            position: absolute;
            top: -25px;
            font-size: 12px;
            font-weight: 700;
            color: var(--color-white);
        }
        .theme-light .text-red{
            color: var(--color-red-light);
        }
        .theme-light .text-light{
            color: var(--color-light);
        }
        .theme-light .text-green{
            color: var(--color-white);
        }
        .theme-light .card .heading{
            margin: 0;
            font-size: 16px;
            font-weight: 500;
            color: var(--color-light);
            text-shadow: 4px 4px 3px var(--color-shadow-dark);
        }
        .theme-light .amount{
            font-size: 24px;
            font-weight: bold;
            margin: 10px 0;
            line-height: 1.2;
            text-shadow: 4px 4px 3px var(--color-shadow-dark);
            display: inline-flex;
            align-items: center;
        }
        .theme-light .typing{
            overflow: hidden;
            white-space: nowrap;
            border-inline-end: 3px solid var(--color-white);
            display: inline-block;
        }
        .theme-light .card.is-animated .typing,
        .theme-light .card:hover .typing{
            animation: typing 8s steps(20) 1, blink 1.2s step-end 1;
        }
        @keyframes typing{
            0%{ width: 0; }
            40%{ width: 128px; }
            60%{ width: 128px; }
            100%{ width: 0; }
        }
        @keyframes blink{
            50%{ border-color: transparent; }
        }
        .theme-light .amount .main-pr{
            font-size: 14px;
            color: var(--color-white);
            margin-inline-start: 6px;
            font-weight: 500;
            background: var(--brand-soft);
            border-radius: 5px;
            padding: 2px 4px;
        }
        .theme-light .amount .main-pr.negative{
            color: var(--error);
            background: var(--error-soft);
        }
        .theme-light .compare{
            font-size: 12px;
            text-shadow: 4px 4px 3px var(--color-shadow-dark);
            letter-spacing: 1px;
        }
        .theme-light .chart{
            display: flex;
            justify-content: space-between;
            align-items: flex-end;
            width: 300px;
            height: 140px;
            padding: 10px;
            border-radius: 12px;
        }
        .theme-light .finance-split{
            display: flex;
            gap: 12px;
            align-items: stretch;
        }
        .theme-light .finance-left{
            flex: 1;
            min-width: 0;
        }
        .theme-light .finance-split .chart{
            flex: 1;
            width: 100%;
        }
        @media (max-width: 720px){
            .theme-light .finance-split{
                flex-direction: column;
            }
            .theme-light .finance-split .chart{
                width: 300px;
            }
        }
        .theme-light .bar{
            width: 20px;
            font-size: 15px;
            background: linear-gradient(180deg, color-mix(in oklab, var(--brand-600) 92%, transparent), color-mix(in oklab, var(--brand-700) 70%, var(--bg-2)));
            color: var(--color-dark);
            border-radius: 4px 4px 0 0;
            display: flex;
            justify-content: center;
            align-items: flex-end;
            position: relative;
            transform: scaleY(0);
            transform-origin: bottom;
            box-shadow: var(--shadow-bar);
            transition: height 0.6s ease;
        }
        .theme-light .chart.is-animated .bar{
            animation: barIntro 2.5s ease-out both;
        }
        .theme-light .chart.bars-ready .bar{
            transform: scaleY(1);
        }
        .theme-light .chart.is-hovering .bar:nth-child(1){ animation: rippleA 21s ease-in-out infinite 0.05s; }
        .theme-light .chart.is-hovering .bar:nth-child(2){ animation: rippleB 27s ease-in-out infinite 0.15s; }
        .theme-light .chart.is-hovering .bar:nth-child(3){ animation: rippleC 22s ease-in-out infinite 0.25s; }
        .theme-light .chart.is-hovering .bar:nth-child(4){ animation: rippleA 28s ease-in-out infinite 0.1s; }
        .theme-light .chart.is-hovering .bar:nth-child(5){ animation: rippleB 22s ease-in-out infinite 0.3s; }
        .theme-light .chart.is-hovering .bar:nth-child(6){ animation: rippleC 25s ease-in-out infinite 0.2s; }
        .theme-light .chart.is-hovering .bar:nth-child(7){ animation: rippleA 30s ease-in-out infinite 0.08s; }
        .theme-light .bar.positive{
            background: linear-gradient(180deg, color-mix(in oklab, var(--brand-600) 92%, transparent), color-mix(in oklab, var(--brand-700) 75%, var(--bg-2)));
        }
        .theme-light .bar.negative{
            background: var(--color-white);
        }
        @keyframes rippleA{
            0%{ transform: scaleY(0.6); }
            12%{ transform: scaleY(1.1); }
            26%{ transform: scaleY(0.7); }
            40%{ transform: scaleY(1.05); }
            55%{ transform: scaleY(0.65); }
            70%{ transform: scaleY(1.12); }
            85%{ transform: scaleY(0.72); }
            100%{ transform: scaleY(0.6); }
        }
        @keyframes rippleB{
            0%{ transform: scaleY(0.7); }
            10%{ transform: scaleY(1.15); }
            24%{ transform: scaleY(0.6); }
            38%{ transform: scaleY(1.05); }
            52%{ transform: scaleY(0.72); }
            66%{ transform: scaleY(1.1); }
            80%{ transform: scaleY(0.62); }
            92%{ transform: scaleY(1.08); }
            100%{ transform: scaleY(0.7); }
        }
        @keyframes rippleC{
            0%{ transform: scaleY(0.65); }
            15%{ transform: scaleY(1.08); }
            28%{ transform: scaleY(0.6); }
            42%{ transform: scaleY(1.15); }
            58%{ transform: scaleY(0.68); }
            72%{ transform: scaleY(1.05); }
            88%{ transform: scaleY(0.62); }
            100%{ transform: scaleY(0.65); }
        }
        @keyframes barIntro{
            0%{ transform: scaleY(0); }
            60%{ transform: scaleY(1.15); }
            80%{ transform: scaleY(0.95); }
            100%{ transform: scaleY(1); }
        }
        @keyframes financeFadeIn{
            from{ opacity: 0; transform: translateY(8px); }
            to{ opacity: 1; transform: translateY(0); }
        }
        .theme-light .chart.is-animated .bar:nth-child(1){ animation-delay: 0.12s; }
        .theme-light .chart.is-animated .bar:nth-child(2){ animation-delay: 0.22s; }
        .theme-light .chart.is-animated .bar:nth-child(3){ animation-delay: 0.32s; }
        .theme-light .chart.is-animated .bar:nth-child(4){ animation-delay: 0.42s; }
        .theme-light .chart.is-animated .bar:nth-child(5){ animation-delay: 0.52s; }
        .theme-light .chart.is-animated .bar:nth-child(6){ animation-delay: 0.62s; }
        .theme-light .chart.is-animated .bar:nth-child(7){ animation-delay: 0.72s; }
        .theme-light .bar:nth-child(1){ height: 30px; animation-delay: 0.2s; }
        .theme-light .bar:nth-child(2){ height: 80px; animation-delay: 0.4s; }
        .theme-light .bar:nth-child(3){ height: 50px; animation-delay: 0.6s; }
        .theme-light .bar:nth-child(4){ height: 85px; animation-delay: 0.8s; }
        .theme-light .bar:nth-child(5){ height: 70px; animation-delay: 1s; }
        .theme-light .bar:nth-child(6){ height: 80px; animation-delay: 1.2s; }
        .theme-light .bar:nth-child(7){ height: 60px; animation-delay: 1.4s; }
        .theme-light .card .more-button{
            font-size: 16px;
            padding: 12px;
            border-radius: 10px;
            color: var(--color-dark);
            border: 0;
            width: 100%;
            margin-top: 10px;
            outline: none;
            background-color: var(--color-white);
            box-shadow: var(--shadow-bar);
            transition: ease all 0.3s;
            cursor: pointer;
        }
        .theme-light .card .more-button:hover{
            transform: translateY(-3px);
        }
        .theme-light .card .more-button:active{
            transform: scale(0.9);
        }
        .theme-light .tags{
            display: flex;
            margin-bottom: 8px;
            justify-content: end;
            color: var(--color-light);
            font-size: 12px;
            font-weight: 500;
            gap: 4px;
            text-shadow: 2px 3px 3px var(--color-shadow-dark);
        }
        .theme-light .tags span{
            padding: 3px 10px;
            background-color: rgba(15,23,42,0.45);
            border-radius: 3px;
        }

        /* ====== Stripe-style section headers ====== */
        .section-header{
            font-size: 0.72rem;
            font-weight: 600;
            color: var(--muted);
            text-transform: uppercase;
            letter-spacing: 0.08em;
            padding-bottom: 0.65rem;
            border-bottom: 1px solid var(--divider);
            margin-bottom: 0.9rem;
        }

        /* ====== Layout: decale le main quand sidebar ouverte (desktop) ====== */
        @media (min-width: 1024px) {
            body.sidebar-open #app-main {
                padding-inline-start: 20rem;
                transition: padding-inline-start .3s ease;
            }
            /* Compact rail: sidebar collapsed to icons only */
            body.sidebar-open.sidebar-collapsed #app-main {
                padding-inline-start: 7rem;
            }
        }

        .animate-spin-slow { animation: spin 4s linear infinite; }
        @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

        /* ====== Mobile enhancements ====== */
        @media (max-width: 639px) {
            .glass-card { padding: 1rem; }
            .glass-soft { padding: 0.75rem; }
            h1 { font-size: 1.5rem !important; }
            h2 { font-size: 1.25rem !important; }
            table { font-size: 0.8rem; }
            table th, table td { padding: 0.5rem 0.25rem; }
        }

        /* Input and select styling for mobile */
        @media (max-width: 639px) {
            input, select, textarea {
                font-size: 16px !important; /* Prevents zoom on iOS */
            }
        }

        /* Status badges responsive */
        @media (max-width: 639px) {
            .status-badge {
                font-size: 0.65rem;
                padding: 0.25rem 0.5rem;
            }
        }

        /* ====== Light theme: global card overrides ====== */
        html[data-theme="light"] .glass-card {
            background: #ffffff;
            border: 1px solid #cbd8d1;
            box-shadow: 0 1px 3px rgba(15,23,42,0.04),
                        0 4px 24px rgba(15,23,42,0.08);
        }
        html[data-theme="light"] .glass-card:hover {
            border-color: #b8d4c4;
            box-shadow: 0 1px 3px rgba(15,23,42,0.06),
                        0 6px 28px rgba(15,23,42,0.12);
        }
        html[data-theme="light"] .stat-card {
            background: #ffffff;
            border: 1px solid #cbd8d1;
            box-shadow: 0 1px 3px rgba(15,23,42,0.04),
                        0 4px 24px rgba(15,23,42,0.08);
        }
        html[data-theme="light"] .stat-card:hover {
            border-color: #b8d4c4;
            box-shadow: 0 1px 3px rgba(15,23,42,0.06),
                        0 6px 28px rgba(15,23,42,0.12);
        }
        html[data-theme="light"] .stat-label {
            color: #334155;
            font-weight: 600;
        }
        html[data-theme="light"] .stat-value {
            color: #0f172a;
        }
        html[data-theme="light"] .glass-soft {
            background: #e8eeeb;
            border: 1px solid #cbd8d1;
        }

        /* Text hierarchy */
        html[data-theme="light"] .text-strong,
        html[data-theme="light"] [style*="color: var(--text)"] {
            color: #0f172a !important;
        }
        html[data-theme="light"] .text-muted {
            color: #334155 !important;
        }
        html[data-theme="light"] h1,
        html[data-theme="light"] h2,
        html[data-theme="light"] h3 {
            color: #0f172a;
        }

        /* Stripe rows */
        html[data-theme="light"] .stripe-row {
            border-color: #cbd8d1;
        }
        html[data-theme="light"] .stripe-row:hover {
            background: rgba(5,150,105,0.05);
        }

        /* Unread dot */
        html[data-theme="light"] .bg-emerald-400 {
            background-color: #059669 !important;
        }

        /* ====== Light theme: emerald glow background (mirrors dark) ====== */
        html[data-theme="light"] body{
            background: var(--bg-1);
        }
        html[data-theme="light"] .fixed.-z-10{
            background: radial-gradient(circle at 18% 12%, rgba(5,150,105,0.08), transparent 48%),
                        radial-gradient(circle at 84% 78%, rgba(5,150,105,0.05), transparent 56%),
                        linear-gradient(160deg, #f5f8f6, #eef3f0 55%, #e8eeeb 100%) !important;
        }

        /* Better light theme inputs */
        html[data-theme="light"] input,
        html[data-theme="light"] select,
        html[data-theme="light"] textarea{
            background: #ffffff;
            border-color: var(--border);
            color: var(--text-main);
        }
        html[data-theme="light"] input::placeholder,
        html[data-theme="light"] textarea::placeholder{
            color: #64748b;
        }

        /* Light theme table headers */
        html[data-theme="light"] th{
            color: var(--text-muted);
            font-weight: 600;
        }

        /* ====== Light theme: inner blocks (nested) — subtle tint ====== */
        html[data-theme="light"] .glass-card .stat-card {
            background: #f4f7f5;
        }
        html[data-theme="light"] .glass-card .glass-soft {
            background: #f4f7f5;
        }
        html[data-theme="light"] .glass-card .glass-soft:hover {
            background: #eef3f0;
        }

        /* ======================================================================
           LIGHT THEME — Comprehensive Tailwind utility overrides
           All pages inherit these. Converts dark-mode-oriented utility classes
           to readable light-mode equivalents.
           ====================================================================== */

        /* ── Status badge text colors (light-on-dark → dark-on-light) ── */
        html[data-theme="light"] .text-amber-200,
        html[data-theme="light"] .text-amber-300 {
            color: #92400e !important;
        }
        html[data-theme="light"] .text-yellow-200,
        html[data-theme="light"] .text-yellow-300 {
            color: #854d0e !important;
        }
        html[data-theme="light"] .text-yellow-400 {
            color: #a16207 !important;
        }
        html[data-theme="light"] .text-blue-200,
        html[data-theme="light"] .text-blue-300 {
            color: #1e40af !important;
        }
        html[data-theme="light"] .text-purple-200,
        html[data-theme="light"] .text-purple-300 {
            color: #7c3aed !important;
        }
        html[data-theme="light"] .text-red-200,
        html[data-theme="light"] .text-red-300 {
            color: #dc2626 !important;
        }
        html[data-theme="light"] .text-red-400 {
            color: #dc2626 !important;
        }
        html[data-theme="light"] .text-gray-300 {
            color: #334155 !important;
        }
        html[data-theme="light"] .text-sky-400 {
            color: #0284c7 !important;
        }
        html[data-theme="light"] .text-blue-400 {
            color: #0284c7 !important; /* secondary-500 from palette */
        }
        html[data-theme="light"] .text-amber-400 {
            color: #d97706 !important; /* amber-600: readable on light bg */
        }
        html[data-theme="light"] .text-emerald-100 {
            color: #059669 !important;
        }

        /* ── White-overlay → dark-overlay equivalents ── */
        html[data-theme="light"] .bg-white\/10 {
            background-color: rgba(15,23,42,0.07) !important;
        }
        html[data-theme="light"] .border-white\/10 {
            border-color: rgba(15,23,42,0.10) !important;
        }
        html[data-theme="light"] .border-white\/20 {
            border-color: rgba(15,23,42,0.14) !important;
        }
        html[data-theme="light"] .text-white\/60 {
            color: #334155 !important;
        }
        html[data-theme="light"] .hover\:bg-white\/5:hover {
            background-color: rgba(15,23,42,0.04) !important;
        }
        html[data-theme="light"] .hover\:bg-white\/10:hover {
            background-color: rgba(15,23,42,0.06) !important;
        }

        /* ── Amber badge / button backgrounds ── */
        html[data-theme="light"] .bg-amber-500\/15 {
            background-color: rgba(245,158,11,0.10) !important;
        }
        html[data-theme="light"] .border-amber-500\/20 {
            border-color: rgba(245,158,11,0.25) !important;
        }

        /* ── Yellow badge / button backgrounds ── */
        html[data-theme="light"] .bg-yellow-500\/15,
        html[data-theme="light"] .bg-yellow-500\/20 {
            background-color: rgba(234,179,8,0.10) !important;
        }
        html[data-theme="light"] .border-yellow-500\/20 {
            border-color: rgba(234,179,8,0.25) !important;
        }
        html[data-theme="light"] .border-yellow-400\/25 {
            border-color: rgba(234,179,8,0.25) !important;
        }
        html[data-theme="light"] .hover\:bg-yellow-500\/30:hover {
            background-color: rgba(234,179,8,0.16) !important;
        }
        html[data-theme="light"] .bg-yellow-500\/10 {
            background-color: rgba(234,179,8,0.08) !important;
        }

        /* ── Blue badge / button backgrounds ── */
        html[data-theme="light"] .bg-blue-500\/15,
        html[data-theme="light"] .bg-blue-500\/20 {
            background-color: rgba(59,130,246,0.10) !important;
        }
        html[data-theme="light"] .border-blue-500\/20 {
            border-color: rgba(59,130,246,0.25) !important;
        }
        html[data-theme="light"] .border-blue-400\/25 {
            border-color: rgba(59,130,246,0.25) !important;
        }

        /* ── Purple badge / button backgrounds ── */
        html[data-theme="light"] .bg-purple-500\/15,
        html[data-theme="light"] .bg-purple-500\/20 {
            background-color: rgba(168,85,247,0.10) !important;
        }
        html[data-theme="light"] .border-purple-500\/20 {
            border-color: rgba(168,85,247,0.25) !important;
        }
        html[data-theme="light"] .border-purple-400\/25 {
            border-color: rgba(168,85,247,0.25) !important;
        }

        /* ── Red badge / button backgrounds ── */
        html[data-theme="light"] .bg-red-500\/15 {
            background-color: rgba(220,38,38,0.08) !important;
        }
        html[data-theme="light"] .border-red-500\/20 {
            border-color: rgba(220,38,38,0.25) !important;
        }
        html[data-theme="light"] .border-red-400\/25 {
            border-color: rgba(220,38,38,0.25) !important;
        }
        html[data-theme="light"] .hover\:bg-red-500\/10:hover {
            background-color: rgba(220,38,38,0.06) !important;
        }
        html[data-theme="light"] .hover\:text-red-300:hover {
            color: #dc2626 !important;
        }

        /* ── Gray badge backgrounds ── */
        html[data-theme="light"] .bg-gray-500\/20 {
            background-color: rgba(100,116,139,0.10) !important;
        }
        html[data-theme="light"] .border-gray-400\/25 {
            border-color: rgba(100,116,139,0.25) !important;
        }

        /* ── Emerald badge / button backgrounds ── */
        html[data-theme="light"] .bg-emerald-500\/15,
        html[data-theme="light"] .bg-emerald-500\/20 {
            background-color: rgba(5,150,105,0.10) !important;
        }
        html[data-theme="light"] .border-emerald-500\/20 {
            border-color: rgba(5,150,105,0.25) !important;
        }
        html[data-theme="light"] .border-emerald-400\/25 {
            border-color: rgba(5,150,105,0.25) !important;
        }
        html[data-theme="light"] .border-emerald-400\/20 {
            border-color: rgba(5,150,105,0.22) !important;
        }
        html[data-theme="light"] .hover\:bg-emerald-500\/10:hover {
            background-color: rgba(5,150,105,0.08) !important;
        }
        html[data-theme="light"] .hover\:bg-emerald-500\/25:hover {
            background-color: rgba(5,150,105,0.18) !important;
        }
        html[data-theme="light"] .hover\:bg-emerald-500\/30:hover {
            background-color: rgba(5,150,105,0.16) !important;
        }
        html[data-theme="light"] .bg-emerald-500\/20 {
            background-color: rgba(5,150,105,0.10) !important;
        }
        html[data-theme="light"] .bg-emerald-700\/40,
        html[data-theme="light"] .bg-emerald-700\/30,
        html[data-theme="light"] .bg-emerald-700\/20 {
            background-color: rgba(5,150,105,0.12) !important;
        }

        /* ── Select option backgrounds (dark dropdown → white) ── */
        html[data-theme="light"] select option {
            background: #ffffff;
            color: #0f172a;
        }

        /* ── Sidebar notification badges (text-white → dark text) ── */
        html[data-theme="light"] #sidebar .text-white {
            color: #dc2626 !important;
        }

        /* ── Users list: status badge override ── */
        html[data-theme="light"] .role-badge.admin {
            color: #dc2626;
        }
        html[data-theme="light"] .role-badge.pharma {
            color: #059669;
        }
        html[data-theme="light"] .role-badge.supplier {
            color: #0284c7;
        }

        /* ── Chat: message bubbles ── */
        html[data-theme="light"] .bg-white\/5 {
            background-color: rgba(15,23,42,0.04) !important;
        }

        /* ── Black/60 overlay stays fine, but backdrop blur might need adjustment ── */
        html[data-theme="light"] .bg-black\/60 {
            background-color: rgba(15,23,42,0.40) !important;
        }

        /* ── Focus rings ── */
        html[data-theme="light"] .focus\:ring-emerald-400\/30:focus {
            --tw-ring-color: rgba(5,150,105,0.30) !important;
        }

        /* ── Loading spinners ── */
        html[data-theme="light"] .border-emerald-400\/30 {
            border-color: rgba(5,150,105,0.30) !important;
        }
        html[data-theme="light"] .border-t-emerald-400 {
            border-top-color: #059669 !important;
        }

        /* Fond HTML — défini en haut du <head> (anti-flash), voir bootstrap. */

        /* ====== Typography ====== */
        /* Self-hosted OFL Cairo (Arabic subset). Scoped by unicode-range so it is
           consulted ONLY for Arabic codepoints — Latin/English rendering is
           byte-identical and English-only pages never fetch the file. Static .css:
           {% static %} does NOT resolve here, so the src is a relative ../fonts/ url
           (collectstatic preserves the tree under STATIC_ROOT). No dir/RTL swap — Phase 31. */
        @font-face {
            font-family: 'Cairo';
            src: url("../fonts/cairo-arabic.4d90a59cb181.woff2") format('woff2');
            font-weight: 200 1000;
            font-display: swap;
            unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF;
        }
        body {
            font-family: 'DM Sans', 'Cairo', system-ui, -apple-system, sans-serif;
            animation: bodyEnter .45s ease both;
        }
        @keyframes bodyEnter {
            from { opacity: 0; }
            to   { opacity: 1; }
        }
        @media (prefers-reduced-motion: reduce) {
            body, #app-main { animation: none !important; }
        }
        h1, h2, h3, h4, h5, h6 {
            font-family: 'Outfit', 'Cairo', system-ui, sans-serif;
        }

        /* ====== Page entrance animation ====== */
        @keyframes pageIn {
            from { transform: translateY(10px); }
            to   { transform: translateY(0); }
        }
        #app-main {
            /* `backwards` (not `both`): the entrance still plays translateY(10px)→0,
               but the end-state transform is NOT retained. `both`/`forwards` would
               leave a permanent `transform: translateY(0)` on #app-main, which makes
               it the containing block for every `position: fixed` descendant — that
               trapped the included modals (user/company/stock) inside the tall main
               element, so they centered mid-page and fell off-screen on phones
               instead of centering in the viewport. translateY(0) ≡ no offset, so
               dropping the retained transform is visually identical. */
            animation: pageIn 0.4s cubic-bezier(.22,1,.36,1) backwards;
        }

        /* ====== Active sidebar nav item ====== */
        .nav-item.active {
            background: var(--nav-hover-bg);
            border-inline-start-color: var(--nav-active-accent);
        }

        /* ====== Page title h1 accent underline ====== */
        .page-title {
            letter-spacing: -0.02em;
        }
        .page-title::after {
            content: '';
            display: block;
            width: 2rem;
            height: 3px;
            background: var(--brand-700);
            border-radius: 2px;
            margin-top: 0.45rem;
        }
        html[data-theme="light"] .page-title::after {
            background: #059669;
        }

        /* ====== Stat card semantic accent (top border) ====== */
        .stat-card-accent-emerald { border-top: 2px solid var(--brand-700); }
        .stat-card-accent-blue    { border-top: 2px solid #0284c7; }
        .stat-card-accent-amber   { border-top: 2px solid #f59e0b; }
        .stat-card-accent-red     { border-top: 2px solid #ef4444; }
        html[data-theme="light"] .stat-card-accent-emerald { border-top-color: #059669; }
        html[data-theme="light"] .stat-card-accent-amber   { border-top-color: #d97706; }
        html[data-theme="light"] .stat-card-accent-red     { border-top-color: #dc2626; }

        /* ====== Primary button ====== */
        .btn-primary {
            display: inline-flex;
            align-items: center;
            gap: 0.5rem;
            padding: 0.5rem 1rem;
            border-radius: 0.5rem;
            font-size: 0.875rem;
            font-weight: 600;
            font-family: inherit;
            background: var(--brand-700);
            color: #ffffff;
            border: none;
            cursor: pointer;
            text-decoration: none;
            transition: background 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease;
        }
        .btn-primary:hover {
            background: var(--brand-600);
            box-shadow: 0 2px 10px rgba(16,185,129,0.28);
            transform: translateY(-1px);
        }
        .btn-primary:active { transform: translateY(0); }
        html[data-theme="light"] .btn-primary          { background: #059669; color: #ffffff; }
        html[data-theme="light"] .btn-primary:hover    { background: #047857; box-shadow: 0 2px 10px rgba(5,150,105,0.22); }

        /* ====== Empty state ====== */
        .empty-state {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 3rem 1.5rem;
            text-align: center;
        }
        .empty-state-icon {
            font-size: 2.5rem;
            color: var(--brand-500);
            opacity: 0.25;
            margin-bottom: 0.75rem;
        }
        .empty-state-title {
            font-size: 0.9375rem;
            font-weight: 600;
            color: var(--text);
            margin-bottom: 0.25rem;
        }
        .empty-state-desc {
            font-size: 0.8125rem;
            color: var(--muted);
            max-width: 280px;
            line-height: 1.5;
        }

        /* ====== Minimalist panel vocabulary (clean / declutter pass) ======
           Shared building blocks so every page uses ONE calm visual language:
           quiet section headers, hairline-separated rows, flat hover lists,
           and small neutral pills. Color is reserved for state, not decoration. */
        .ui-panel-head {
            display: flex; align-items: center; justify-content: space-between;
            gap: 0.75rem; margin-bottom: 1.1rem;
        }
        .ui-panel-title {
            font-size: 1rem; font-weight: 600; color: var(--text);
            letter-spacing: -0.01em;
        }
        .ui-panel-sub { font-size: 0.75rem; color: var(--muted); }
        .ui-panel-link {
            font-size: 0.75rem; font-weight: 500; color: var(--muted);
            text-decoration: none; transition: color 0.15s ease;
        }
        .ui-panel-link:hover { color: var(--text); }

        /* Static rows separated by a hairline (no nested cards) */
        .ui-row {
            display: flex; align-items: center; justify-content: space-between;
            gap: 0.75rem; padding: 0.625rem 0;
        }
        .ui-row-divide + .ui-row-divide { border-top: 1px solid var(--soft-border); }
        .ui-dot { width: 0.5rem; height: 0.5rem; border-radius: 9999px; flex: none; display: inline-block; }

        /* Interactive list rows: flat, subtle hover */
        .ui-list { display: flex; flex-direction: column; gap: 0.125rem; }
        .ui-list-row {
            display: flex; align-items: center; justify-content: space-between;
            gap: 0.75rem; padding: 0.5rem; margin: 0 -0.5rem;
            border-radius: 0.5rem; text-decoration: none;
            transition: background 0.15s ease;
        }
        .ui-list-row:hover { background: var(--soft-bg); }
        .ui-list-icon { width: 1rem; text-align: center; font-size: 0.75rem; color: var(--muted); flex: none; }
        .ui-rank { width: 1.25rem; flex: none; font-size: 0.75rem; color: var(--muted); font-variant-numeric: tabular-nums; }

        /* Small count pill — neutral by default; semantic variants only when meaningful */
        .ui-badge {
            display: inline-flex; align-items: center;
            font-size: 0.6875rem; font-weight: 600; line-height: 1;
            padding: 0.25rem 0.5rem; border-radius: 9999px;
            background: var(--soft-bg); color: var(--muted);
            /* A pill is one token. Inside .rcard (`overflow-wrap: anywhere`) a
               two-word status broke across two lines and the 9999px radius drew
               a shattered capsule around the halves. */
            white-space: nowrap;
        }
        /* Badge text tokens (CLR-04): each theme supplies a value that passes
           >=4.5:1 against its soft background. Dark uses the bright glass mints
           (they pass on the dark surface); light remaps to darker shades because
           the bright mints fail 4.5:1 on the near-white soft fills. */
        .ui-badge-attn   { background: var(--warning-soft); color: var(--ui-badge-attn-text); }
        .ui-badge-danger { background: var(--error-soft);   color: var(--ui-badge-danger-text); }
        .ui-badge-ok     { background: var(--success-soft); color: var(--ui-badge-ok-text); }

        /* Muted-metadata class (CLR-04) — de-emphasis for secondary metadata
           (timestamps, IDs, counts). Consumed by DTL-05 / DASH-01. Never bold. */
        .ui-meta {
            color: var(--muted);
            font-size: var(--text-meta);
            font-weight: 500;
            line-height: 1.4;
        }

        /* Status component (CLR-03/CLR-04) — ALWAYS pairs color + a text label so
           color is never the only signal. Wrap a .ui-badge / .ui-badge-{token}
           (token = the lifecycle map's `color` field) with the human label text:
             <span class="ui-status">
               <span class="ui-badge ui-badge-ok"></span>
               <span class="ui-status-label">Received</span>
             </span>
           The label is mandatory — the static guard (Plan 04) asserts its presence. */
        .ui-status {
            display: inline-flex; align-items: center; gap: 0.375rem;
        }
        .ui-status-label {
            font-size: var(--text-meta);
            font-weight: 600;
            color: var(--text);
            line-height: 1.4;
        }

        /* ====== Disclosure primitives (CLR-02) — native <details>/<summary> ====== */
        /* Visible summary label; >=44px touch target; theme through glass vars. */
        .ui-accordion-summary,
        .ui-show-more-summary {
            display: flex; align-items: center; gap: 0.5rem;
            min-height: 44px;                 /* mobile touch target (CLAUDE.md) */
            font-size: var(--text-body); font-weight: 600; color: var(--text);
        }
        .ui-accordion-summary::-webkit-details-marker,
        .ui-show-more-summary::-webkit-details-marker { display: none; }
        .ui-accordion-summary::before,
        .ui-show-more-summary::before {
            content: "\25B8";                 /* ▸ disclosure caret */
            color: var(--muted); font-size: 0.75rem;
            transition: transform 0.15s ease; flex: none;
        }
        details[open] > .ui-accordion-summary::before,
        details[open] > .ui-show-more-summary::before { transform: rotate(90deg); }
        .ui-accordion-body { padding-top: 0.625rem; }
        .ui-show-more-summary {
            font-size: var(--text-meta); font-weight: 600; color: var(--muted);
            min-height: 44px;
        }
        .ui-show-more-summary:hover { color: var(--text); }
        .ui-show-more-body { padding-top: 0.5rem; }

        /* ====== Shared right-side drawer (CLR-02) — ONE <dialog id="ui-drawer"> ====== */
        .ui-drawer {
            margin-inline-start: auto;        /* pin to the inline-end (right in LTR) */
            margin-inline-end: 0;
            height: 100dvh; max-height: 100dvh;
            width: min(440px, 100vw);
            max-width: 100vw;
            padding: 1.5rem;                  /* lg spacing — drawer inner padding */
            background: var(--glass-bg);
            color: var(--text);
            border: none;
            border-inline-start: 1px solid var(--glass-border);
            box-shadow: var(--glass-shadow);
            overflow-y: auto;
        }
        .ui-drawer::backdrop {
            background: var(--sidebar-overlay);
            backdrop-filter: blur(2px);
        }
        .ui-drawer[open] { display: flex; flex-direction: column; gap: 1rem; }

        /* Opt-in per openDrawer(): for drawer bodies that hold a real data table
           rather than a single form column. 760px minus the 1.5rem padding on each
           side leaves 712px of inner width, which clears the 640px
           `@container rcard` collapse threshold, so the table renders as a table on
           desktop instead of a 4-column grid at ~98px per column. min() falls back
           to 100vw on a phone, so the card collapse still fires there.

           This has to sit AFTER the base .ui-drawer rule: both are single-class
           selectors and the dialog carries both, so the variant only wins the
           specificity tie on source order.

           There is exactly ONE <dialog id="ui-drawer"> per page, shared by every
           feature, so the class is toggled per-open and cleared on `close`. */
        .ui-drawer--wide { width: min(760px, 100vw); }

        .ui-drawer-title {
            font-size: var(--text-heading); font-weight: 600; color: var(--text);
            line-height: 1.3; margin: 0;
        }
        .ui-drawer-trigger {
            display: inline-flex; align-items: center; gap: 0.375rem;
            min-height: 44px;                 /* touch target */
            font-size: var(--text-body); font-weight: 600; color: var(--brand-700);
            background: none; border: none; cursor: pointer;
        }
        .ui-drawer-trigger:hover { color: var(--text); }

        /* ====== Global iPhone-style notification toasts (J08-TOAST) ======
           iOS-notification anatomy (app-icon tile · title + relative time ·
           message) rendered on the SIPS emerald-glass surface. When several
           arrive together they collapse into a peeking stack that fans open on
           hover / keyboard focus — the real iOS interaction, not a decorative
           layer. All colour via theme tokens, so it reads AA in both themes;
           --dy/--s/--op are driven by the stacking JS in base.html. */
        .sips-toast-container {
            position: fixed;
            top: 1rem;
            inset-inline-end: 1rem;
            z-index: 9999;
            display: flex;
            flex-direction: column;
            gap: 0.625rem;
            width: min(360px, calc(100vw - 2rem));
            pointer-events: none;
        }
        .sips-toast {
            --dy: 0px;
            --s: 1;
            --op: 1;
            position: relative;
            pointer-events: auto;
            display: flex;
            align-items: flex-start;
            gap: 0.75rem;
            padding: 0.8rem 0.85rem;
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            box-shadow: var(--glass-shadow), 0 20px 44px -26px rgba(0, 0, 0, 0.6);
            border-radius: 1.15rem;
            -webkit-backdrop-filter: blur(16px) saturate(1.4);
            backdrop-filter: blur(16px) saturate(1.4);
            transform: translateY(var(--dy)) scale(var(--s));
            transform-origin: top center;
            opacity: var(--op);
            animation: sips-toast-in 0.42s cubic-bezier(0.16, 1, 0.3, 1);
        }
        @media (prefers-reduced-motion: no-preference) {
            .sips-toast {
                transition: transform 0.46s cubic-bezier(0.16, 1, 0.3, 1),
                            opacity 0.32s ease, border-color 0.2s ease;
            }
        }
        .sips-toast.is-clickable { cursor: pointer; }
        .sips-toast.is-clickable:hover { border-color: var(--border-strong); }

        /* App-icon tile — iOS squircle, emerald identity in both themes */
        .sips-toast-icon {
            flex: 0 0 auto;
            width: 2.4rem;
            height: 2.4rem;
            border-radius: 0.72rem;
            display: grid;
            place-items: center;
            background: var(--brand-soft);
            border: 1px solid var(--glass-border);
            color: var(--brand-600);
            font-size: 1.05rem;
        }
        [data-theme="light"] .sips-toast-icon { color: var(--brand-800); }

        .sips-toast-body { min-width: 0; flex: 1 1 auto; }
        .sips-toast-head {
            display: flex;
            align-items: baseline;
            justify-content: space-between;
            gap: 0.5rem;
        }
        .sips-toast-title {
            min-width: 0;
            font-size: 0.86rem;
            font-weight: 600;
            color: var(--text);
            line-height: 1.3;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .sips-toast-time {
            flex: 0 0 auto;
            font-size: 0.72rem;
            color: var(--muted);
            font-variant-numeric: tabular-nums;
        }
        .sips-toast-message {
            margin-top: 0.15rem;
            font-size: 0.8rem;
            color: var(--muted);
            line-height: 1.4;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }
        .sips-toast-close {
            position: absolute;
            top: 0.35rem;
            inset-inline-end: 0.4rem;
            width: 1.25rem;
            height: 1.25rem;
            border-radius: 999px;
            display: grid;
            place-items: center;
            background: none;
            border: none;
            color: var(--muted);
            font-size: 0.95rem;
            line-height: 1;
            cursor: pointer;
            opacity: 0;
            transition: opacity 0.2s ease, background 0.2s ease, color 0.2s ease;
        }
        .sips-toast:hover .sips-toast-close,
        .sips-toast:focus-within .sips-toast-close { opacity: 0.75; }
        .sips-toast-close:hover { opacity: 1; color: var(--text); background: var(--soft-bg); }
        .sips-toast.is-leaving {
            opacity: 0 !important;
            transform: translateY(var(--dy)) scale(0.94) !important;
            transition: opacity 0.28s ease, transform 0.28s ease;
        }
        @keyframes sips-toast-in {
            from { opacity: 0; transform: translateX(110%); }
            to   { opacity: 1; transform: translateX(0); }
        }
        @media (max-width: 640px) {
            .sips-toast-container {
                left: 0.75rem;
                right: 0.75rem;
                width: auto;
            }
        }

        /* ====== Lifecycle stepper (CLR-02) — detail-only; accent = active step ====== */
        .ui-stepper { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem; }
        .ui-step {
            display: inline-flex; align-items: center; gap: 0.375rem;
            min-height: 44px; padding: 0 0.625rem;       /* touch target */
            font-size: var(--text-meta); font-weight: 600; color: var(--muted);
            border-radius: 9999px; background: var(--soft-bg);
            cursor: default;
        }
        .ui-step[aria-current="step"] {
            color: var(--brand-700);                     /* accent ONLY on active */
            background: var(--brand-soft);
        }
        .ui-step-index {
            width: 1.25rem; height: 1.25rem; flex: none;
            display: inline-flex; align-items: center; justify-content: center;
            border-radius: 9999px; font-size: 0.6875rem;
            background: var(--glass-border);
        }
        .ui-step[aria-current="step"] .ui-step-index { background: var(--brand-700); color: #fff; }

        /* ====== Tabs (CLR-02) — role=tablist/tab/tabpanel; accent = active tab ====== */
        .ui-tablist { display: flex; flex-wrap: wrap; gap: 0.25rem; border-bottom: 1px solid var(--soft-border); }
        .ui-tab {
            min-height: 44px; padding: 0 0.875rem;       /* touch target */
            font-size: var(--text-body); font-weight: 600; color: var(--muted);
            background: none; border: none; cursor: pointer;
            border-bottom: 2px solid transparent; margin-bottom: -1px;
        }
        .ui-tab:hover { color: var(--text); }
        .ui-tab[aria-selected="true"] {
            color: var(--brand-700);                     /* accent ONLY on active */
            border-bottom-color: var(--brand-700);
        }
        .ui-tabpanel { padding-top: 1rem; }
        .ui-tabpanel[hidden] { display: none; }

        /* ====== Table sort headers ====== */
        .th-sort {
            cursor: pointer;
            user-select: none;
            white-space: nowrap;
        }
        .th-sort::after {
            content: ' ⇅';
            font-size: 0.65em;
            opacity: 0.35;
            margin-inline-start: 2px;
            font-family: system-ui;
        }
        .th-sort.asc::after  { content: ' ↑'; opacity: 1; color: var(--brand-500); }
        .th-sort.desc::after { content: ' ↓'; opacity: 1; color: var(--brand-500); }

        /* ====== Focus rings (uniform keyboard nav) ====== */
        :focus-visible {
            outline: 2px solid var(--brand-700);
            outline-offset: 2px;
            border-radius: 4px;
        }
        html[data-theme="light"] :focus-visible {
            outline-color: #059669;
        }

        /* ====== Shared bento container-query grid (FND-01) ======
           Generalized verbatim from .dash-root/.dash-grid in admin_dashboard_v2.html
           (lines 163-233). container-name renamed dash→bento so pages nesting a
           .dash-root child don't mis-resolve the @container queries. */
        .bento {
            container-type: inline-size;
            container-name: bento;
            display: grid;
            grid-template-columns: 1fr;
            gap: 1rem;
            align-items: start;
        }
        .bento > * { min-width: 0; }       /* prevents grid overflow blowups */

        /* Tier 1 — 2-column layout */
        @container bento (min-width: 700px) {
            .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
            .bento > * { order: 0; }        /* natural reading order once 2-up */
        }

        /* Tier 2 — 3-column layout */
        @container bento (min-width: 1120px) {
            .bento { grid-template-columns: repeat(3, minmax(0, 1fr)); }
        }

        /* ====== KPI strip (FND-01) ======
           Generalized from .dash-kpis (admin_dashboard_v2 lines 168-227).
           Reuses existing .stat-card/.stat-label/.stat-value — do not reinvent. */
        .kpi-strip {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: 0.625rem;
        }

        /* KPI Tier 1 — 3 columns */
        @container bento (min-width: 560px) {
            .kpi-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; }
        }

        /* KPI Tier 2 — 5-up full strip */
        @container bento (min-width: 1000px) {
            .kpi-strip { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 1rem; }
        }

        /* ====== Detail-page header band (DTL-01) ======
           Shared glass band for every Phase-8 detail page: reference + status pill
           row, lifecycle stepper, owed-action string, KPI band, single primary
           action. Declares its OWN container (container-type: inline-size +
           container-name: detailhdr) — copied verbatim from .bento (above) — so it
           reflows when the sidebar opens/closes, following CONTENT width, NOT the
           viewport. Reuses --radius-md (12px) + the established glass surface vars;
           lg (1.5rem) block padding, xl (2rem) gap to line items. Existing tokens
           (.ui-badge*, .ui-status, .ui-stepper/.ui-step, .ui-meta, .ui-drawer) are
           consumed as-is — never redefined here. */
        .ui-detail-header {
            container-type: inline-size;
            container-name: detailhdr;
            display: flex;
            flex-direction: column;
            gap: 1rem;
            padding: 1.5rem;                          /* lg block padding */
            margin-bottom: 2rem;                      /* xl gap to line items */
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            box-shadow: var(--glass-shadow);
            border-radius: var(--radius-md);
        }
        .ui-detail-header > * { min-width: 0; }       /* prevents reflow overflow */
        /* Reference + status pill row — stacks on narrow, justifies wide. */
        .ui-detail-header-top {
            display: flex; flex-wrap: wrap; align-items: center;
            justify-content: space-between; gap: 0.75rem;
        }
        /* Owed-action string — body weight, muted, NEVER accent (IC-1). */
        .ui-detail-header-owed {
            font-size: var(--text-body); color: var(--muted); line-height: 1.4;
        }
        /* Single primary action — full-width on narrow, intrinsic on wide; the
           >=44px min-height satisfies the mobile touch-target rule (CLAUDE.md). */
        .ui-detail-header-action {
            display: inline-flex; align-items: center; justify-content: center;
            gap: 0.5rem; min-height: 44px; padding: 0 1.25rem;
            border-radius: var(--radius-md); border: none; cursor: pointer;
            font-size: var(--text-body); font-weight: 600;
            background: var(--brand-700); color: #fff;
        }
        .ui-detail-header-action:hover { filter: brightness(1.05); }
        /* Wide container: lay the action beside the owed-action string. */
        @container detailhdr (min-width: 560px) {
            .ui-detail-header-action { align-self: flex-start; }
        }

        /* Condensed-stepper rule (IC-2 / Pitfall 6): below ~560px container width
           collapse the stepper to the active step ± 1 neighbour. The hidden steps
           use display:none ONLY visually — the full <ol> stays in the DOM so the
           keyboard / AT reading order is intact. Callers flag neighbours with
           .ui-step-edge (active ± 1); everything not adjacent and not active hides.
           At >=560px every step shows and wraps (flex-wrap on .ui-stepper), never
           horizontal-scrolls. Scoped under .ui-detail-header so it only affects the
           detail-page stepper, not other .ui-stepper usages. */
        @container detailhdr (max-width: 559.98px) {
            .ui-detail-header .ui-stepper .ui-step {
                display: none;
            }
            .ui-detail-header .ui-stepper .ui-step[aria-current="step"],
            .ui-detail-header .ui-stepper .ui-step.ui-step-edge {
                display: inline-flex;                 /* active ± 1 stay visible */
            }
        }

        /* ====== Detail-page KPI band (DTL-03) ======
           Reuses the .kpi-strip grid + its container-query breakpoints but CAPS at
           4 tiles: 2-up at base, 4-up at wide container width — never the 5th
           column. Lives inside .ui-detail-header so it answers the detailhdr
           container, reflowing on sidebar toggle like the band itself. */
        .ui-kpi-band {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: 0.625rem;
        }
        @container detailhdr (min-width: 560px) {
            .ui-kpi-band { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.75rem; }
        }
        @container detailhdr (min-width: 1000px) {
            .ui-kpi-band { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; }
        }

        /* ====== .rcard table-to-card primitive (FND-02) ======
           Net-new — no in-repo analog. Uses display:flex (NOT block) so table/row/cell
           roles stay in the a11y tree (QUAL-02, adrianroselli.com gotcha).
           data-label is emitted by markup/renderRows JS helper per cell. */
        .rcard {
            width: 100%;
            container-type: inline-size;
            container-name: rcard;
        }
        /* Two rules, both required, both about ONE failure: an 11-column grid at
           ~690px of content width (iPad landscape, sidebar open) rendered ~1000px
           wide and scrolled the DOCUMENT horizontally. container-type:inline-size
           gives layout+inline-size containment, which does NOT clip — the overflow
           paints outside the box and propagates.

           overflow-wrap:anywhere — the ONLY value that also shrinks min-content
           intrinsic size (word-break:break-word does not), so an unbreakable
           token like sarah.aldosari@alnahdapharmacy.com.sa stops dictating the
           table's minimum width.

           table-layout:fixed — belt and braces, because overflow-wrap cannot
           rescue a cell whose inner component sets white-space:nowrap (badges,
           dates, ids). Fixed layout sizes columns from the container instead of
           the content, so the TABLE can never exceed its box regardless.

           overflow-x:auto is deliberately absent: an inner scroller is the
           banned degradation, not the fix.

           .rcard is always the WRAPPER DIV, never the <table> itself: size
           containment does not apply to table boxes, so a <table class="rcard">
           silently never becomes the `rcard` query container and the stack and
           column-shedding rules below can never fire. */
        .rcard table { width: 100%; table-layout: fixed; }
        .rcard th, .rcard td {
            padding: 0.625rem 0.75rem;
            text-align: start;
            border-bottom: 1px solid var(--divider);
            color: var(--text);
            overflow-wrap: anywhere;
        }
        .rcard th {
            font-size: 0.75rem;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.04em;
            color: var(--text-muted);
        }

        /* Collapse threshold: rows → stacked labeled cards */
        @container rcard (max-width: 640px) {
            /* Visually hide thead but keep it in the a11y tree (NOT display:none) */
            .rcard thead {
                position: absolute;
                width: 1px;
                height: 1px;
                overflow: hidden;
                clip: rect(0 0 0 0);
            }
            /* FLEX not block — preserves table/row/cell roles (Chrome 80+) */
            .rcard tr { display: flex; flex-direction: column; border-bottom: 1px solid var(--divider); }
            .rcard tr:last-child { border-bottom: none; }
            .rcard td {
                display: flex;
                justify-content: space-between;
                align-items: center;
                gap: 1rem;
                border-bottom: none;
            }
            /* Label revealed from data-label attribute — color uses theme token (QUAL-01) */
            .rcard td::before {
                content: attr(data-label);
                color: var(--text-muted);
                font-weight: 500;
                font-size: 0.75rem;
                flex-shrink: 0;
            }
            /* A stacked card should not spend a row on a label with nothing after
               it (an empty "Note" on every quote line). ::before is generated
               content, so it does not defeat :empty. Columns keep their
               placeholder in table mode, where a column needs one. */
            .rcard td:empty { display: none; }
        }

        /* ====== Numeric cells: money · quantities · durations ======
           .rcard sets `overflow-wrap: anywhere` so one unbreakable token (an email,
           a long SKU) cannot dictate the table's minimum width. Applied to a NUMBER
           that same rule is destructive: in the 440px quote drawer it broke
           "3000.00" across three lines as "300 / 0.0 / 0" — the decimals rendered
           under the integer part. Numbers opt out of it and opt in to tabular
           figures, so decimal points line up down a column and two totals can be
           compared without reading them digit by digit.

           The alignment rules carry the .rcard prefix on purpose: they have to
           outrank `.rcard th, .rcard td { text-align: start }`, and a bare
           single-class utility loses that specificity tie — which is exactly why
           the `text-end` sitting on the offers table's Actions column never
           actually did anything. */
        .ui-num {
            font-variant-numeric: tabular-nums;
            white-space: nowrap;
            overflow-wrap: normal;
            word-break: normal;
        }
        .rcard th.ui-num, .rcard td.ui-num { overflow-wrap: normal; }
        .rcard th.ui-col-num, .rcard td.ui-col-num,
        .rcard th.ui-col-end, .rcard td.ui-col-end { text-align: end; }

        /* ====== .qlines — priced line-item entry list ======
           Supersedes the 4-column table for the supplier quote drawer.

           A table was the wrong primitive here. Pharmaceutical names run long
           ("10% DEXTROSE AND 0.225% SODIUM CHLORIDE USP") and every row carries
           a 44px input, so `table-layout: fixed` had to reconcile a prose column
           and a control column against one shared width — unwinnable in a
           drawer, and it degraded to `.rcard`'s generic data-label stacking on a
           phone, which repeats a "Unit price" label on every one of 20 rows.

           This is a list whose ROW is the layout unit: the name owns its own
           line and the controls own theirs, so nothing competes for width. It
           reads from 320px up with no collapse mode to fall into and no
           horizontal scroll at any size.

           Named container, not a viewport query: the drawer is 440/760px inside
           a much wider window, so only the content width is meaningful. */
        .qlines { container-type: inline-size; container-name: qlines; }

        .qline {
            display: grid;
            grid-template-columns: minmax(0, 1fr);
            gap: 0.5rem;
            padding: 0.75rem;
            border-block-end: 1px solid var(--divider);
        }
        .qline:last-child { border-block-end: none; }

        /* break-word, NOT `anywhere` (which .rcard sets): `anywhere` is what
           split headings into "LIN / E TO / TA L". Whole words wrap, and only a
           token longer than the line ever breaks mid-character. */
        .qline-name {
            font-size: var(--text-body);
            font-weight: 600;
            line-height: 1.35;
            color: var(--text);
            overflow-wrap: break-word;
        }
        .qline-meta {
            margin-block-start: 0.125rem;
            font-size: 0.75rem;
            color: var(--muted);
            font-variant-numeric: tabular-nums;
        }

        .qline-controls {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 0.75rem;
        }

        /* The currency rides inside the field instead of in a column header, so
           the control is self-describing wherever the row wraps to. */
        .qline-price { position: relative; flex: 0 1 10rem; }
        .qline-price input {
            width: 100%;
            /* Load-bearing, not inherited-by-luck: the "SAR" affix is positioned
               against the WRAPPER, so a content-box input would grow past it by
               its own padding and print the value straight through the affix.
               Tailwind preflight already sets this globally; pinned here so the
               rule survives independently of it. */
            box-sizing: border-box;
            min-height: 44px;                 /* touch target */
            /* LOGICAL, not the physical shorthand: the affix is pinned to
               inset-inline-end, so in RTL a physical padding-right would leave
               the reserved gap on the wrong side and print the value straight
               through "SAR". */
            padding-block: 0.5rem;
            padding-inline: 0.625rem 2.75rem;
            border-radius: 0.5rem;
            text-align: end;
            font-variant-numeric: tabular-nums;
            transition: border-color 160ms ease-out, box-shadow 160ms ease-out;
        }
        .qline-price input::-webkit-outer-spin-button,
        .qline-price input::-webkit-inner-spin-button {
            -webkit-appearance: none; margin: 0;   /* spinners cost width and misfire on scroll */
        }
        .qline-price input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
        .qline-price input:focus-visible {
            outline: none;
            border-color: var(--brand-600);
            box-shadow: 0 0 0 3px color-mix(in oklab, var(--brand-600) 22%, transparent);
        }
        .qline-cur {
            position: absolute;
            inset-inline-end: 0.625rem;
            inset-block-start: 50%;
            transform: translateY(-50%);
            font-size: 0.75rem;
            color: var(--muted);
            pointer-events: none;
        }

        /* Unpriced is the resting state, so the amount stays quiet until it
           means something. Filling the row is the only thing that promotes it —
           that IS the progress signal, no badge required. */
        .qline-total {
            flex: 0 0 auto;
            min-width: 5.5rem;
            text-align: end;
            font-size: var(--text-body);
            color: var(--muted);
            font-variant-numeric: tabular-nums;
            transition: color 160ms ease-out;
        }
        .qline.is-priced .qline-total { color: var(--text); font-weight: 600; }

        /* Past ~30rem the name and the controls stop needing separate lines.
           Both control widths are FIXED so the inputs and the amounts line up
           into real columns down the list — the one thing the table did well. */
        @container qlines (min-width: 30rem) {
            .qline {
                grid-template-columns: minmax(0, 1fr) auto;
                align-items: center;
                column-gap: 1rem;
            }
            .qline-controls { justify-content: flex-end; }
            .qline-price { flex: 0 0 9.5rem; }
            .qline-total { min-width: 7rem; }
        }

        /* Total + submit pinned to the drawer's scrollport: a 40-line quote
           otherwise buries the button and the running total under a scroll. */
        .qfoot {
            position: sticky;
            inset-block-end: 0;
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            justify-content: space-between;
            gap: 0.75rem;
            padding: 0.75rem;
            background: var(--glass-bg);        /* opaque in both themes — rows scroll UNDER it */
            border-block-start: 1px solid var(--glass-border);
            /* The card cannot use overflow-hidden (it would kill the sticky), so
               the footer rounds its own bottom corners to match .rounded-lg. */
            border-end-start-radius: 0.5rem;
            border-end-end-radius: 0.5rem;
        }

        /* Unit stated once in the column header rather than on every row: eight
           repetitions of "SAR" down one column is noise, and that repetition is
           what pushed the amount onto a second line to begin with. */
        .ui-th-unit {
            font-weight: 500;
            text-transform: none;
            letter-spacing: 0;
            opacity: 0.6;
        }

        /* A primary figure with a secondary one beneath it (shipping under the
           total). Column flex, so `flex-end` follows the writing direction and the
           stack stays inline-end aligned in RTL too. */
        .ui-num-stack {
            display: inline-flex;
            flex-direction: column;
            align-items: flex-end;
            gap: 0.1rem;
            min-width: 0;
        }
        .ui-num-strong {
            font-weight: 600;
            font-variant-numeric: tabular-nums;
            white-space: nowrap;
        }
        .ui-num-sub {
            font-size: 0.6875rem;
            color: var(--muted);
            font-variant-numeric: tabular-nums;
            white-space: nowrap;
        }

        /* ====== Content-width grids for drawer bodies ======
           Both replace `grid-cols-2 md:grid-cols-4` inside drawers. `md:` is a
           VIEWPORT query, so a 440px dialog on a 1920px desktop was laying four
           columns into 392px — labels wrapping, amounts splitting off their
           currency. auto-fit + minmax follows the real width of the box, which is
           the same rule the rest of the app follows (PRODUCT.md §3).
           .ui-metric-grid: read-only label/value pairs.
           .ui-form-grid:   labelled inputs; .ui-form-grid-wide spans the row. */
        .ui-metric-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
            gap: 0.75rem 1rem;
        }
        .ui-form-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 0.75rem;
        }
        .ui-form-grid-wide { grid-column: 1 / -1; }

        /* ====== Secondary row action ======
           A quiet sibling for a solid primary inside a table row. Two chromed
           buttons of equal weight in one cell read as a collision and, in a
           fixed-width column, literally became one: they no longer fit side by
           side and wrapped onto each other. This carries no chrome until hover,
           so it costs about half the width and states the hierarchy — the
           decision is Choose; Details is a detour. Full 44px touch target. */
        .ui-action-link {
            display: inline-flex; align-items: center; justify-content: center;
            min-height: 44px; padding: 0 0.5rem;
            font-size: 0.75rem; font-weight: 600;
            color: var(--muted); background: none; border: none;
            border-radius: 0.5rem; cursor: pointer; white-space: nowrap;
            transition: color 0.15s ease, background 0.15s ease;
        }
        .ui-action-link:hover { color: var(--text); background: var(--row-hover); }
        .ui-action-link:focus-visible {
            outline: 2px solid var(--brand-700);
            outline-offset: 2px;
        }

        /* ====== .rec-grid record-card grid (FND-06) ======
           Companion to .rcard for pages that LIST records. Where .rcard keeps
           one column of rows (wastes wide screens, blurs on phones), .rec-grid
           flows records into 1→4 columns driven by CONTENT width (container
           query) — fills big screens, degrades to framed cards on phones.
           Wrapper owns the container; the grid is a descendant (same shape as
           the proven .dash-root/.dash-grid on the admin dashboard). */
        .rec-scope { container-type: inline-size; container-name: recgrid; }
        .rec-grid {
            display: grid;
            grid-template-columns: 1fr;
            gap: 0.875rem;
            align-items: start;
        }
        .rec-grid > * { min-width: 0; }
        @container recgrid (min-width: 540px)  { .rec-grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1rem; } }
        @container recgrid (min-width: 880px)  { .rec-grid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
        @container recgrid (min-width: 1220px) { .rec-grid { grid-template-columns: repeat(4, minmax(0,1fr)); } }
        /* Responsive column priority (LIST-04): at the narrowest container width the
           least-essential Layer-2 muted meta drops before Layer-1 fields. Container
           query (not viewport media) so it follows content width like the grid above;
           cards already stack to 1-col, so this only hides the data-priority="low"
           meta cells — #id, status pill, owed-action, total, primary name are untouched. */
        @container recgrid (max-width: 360px) { .rec-grid .rec-meta-item[data-priority="low"] { display: none; } }

        .rec-card {
            position: relative; overflow: hidden;
            display: flex; flex-direction: column;
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            box-shadow: var(--glass-shadow);
            border-radius: var(--radius-md);
            padding: 0.9rem 1rem 0.8rem 1.15rem;
            text-decoration: none; color: inherit;
            transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
        }
        .rec-card:hover { border-color: var(--hover-border); box-shadow: var(--hover-shadow); transform: translateY(-2px); }
        /* Status spine — color set per-card via inline --rec-accent */
        .rec-card::before {
            content: ""; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 3px;
            background: var(--rec-accent, var(--brand-600));
        }
        .rec-head { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; }
        .rec-id {
            font-family: 'Outfit', system-ui, sans-serif; font-weight: 700; font-size: 1.02rem;
            letter-spacing: -0.01em; color: var(--text);
            display: inline-flex; align-items: baseline; gap: 0.35rem;
        }
        .rec-id .hash { color: var(--muted); font-weight: 500; font-size: 0.8em; }
        /* Optional card title (schema.title): a prominent name with the id demoted
           to a small muted subtitle. Absent → original id-as-headline behavior. */
        .rec-headings { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
        .rec-title {
            font-family: 'Outfit', system-ui, sans-serif; font-weight: 700; font-size: 1.02rem;
            letter-spacing: -0.01em; color: var(--text);
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
        }
        .rec-id.rec-id-sub { font-size: 0.75rem; font-weight: 500; }
        .rec-prio { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.6875rem; font-weight: 600; color: var(--muted); white-space: nowrap; }
        .rec-prio .dot { width: 0.5rem; height: 0.5rem; border-radius: 999px; background: currentColor; }
        .rec-prio.urgent { color: var(--error); }
        .rec-prio.high   { color: var(--warning); }
        .rec-prio.normal { color: var(--muted); }
        .rec-meta { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 0.6rem 0.9rem; margin-top: 0.85rem; }
        .rec-meta-item { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
        .rec-meta-label { font-size: 0.625rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
        .rec-meta-val { font-size: 0.8125rem; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .rec-meta-val.tabular { font-variant-numeric: tabular-nums; }
        .rec-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-top: 0.9rem; padding-top: 0.7rem; border-top: 1px solid var(--soft-border); }
        .rec-link { font-size: 0.8125rem; font-weight: 600; color: var(--brand-600); display: inline-flex; align-items: center; gap: 0.35rem; }
        .rec-card:hover .rec-link { gap: 0.5rem; }
        .rec-link i { transition: transform 0.15s ease; font-size: 0.7rem; }
        .rec-card:hover .rec-link i { transform: translateX(2px); }
        .rec-when { font-size: 0.6875rem; color: var(--muted); white-space: nowrap; }
        @keyframes recIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
        .rec-card { animation: recIn 0.42s cubic-bezier(0.22,0.61,0.36,1) both; }
        @media (prefers-reduced-motion: reduce) { .rec-card { animation: none; } .rec-card:hover { transform: none; } }

        /* ====== Secondary-screen container-query reflow (CQ-01) ======
           Additive polish for 8 secondary screens: makes their existing Tailwind
           grids answer CONTENT width (sidebar open / closed) instead of the
           viewport, mirroring the proven .bento / .rec-grid pattern above.
           The .cq-scope wrapper owns the query context (container-type: inline-size
           + container-name: cq — copied verbatim from .rec-scope). Each column rule
           is scoped UNDER .cq-scope so its specificity (0,2,0) deterministically
           beats the co-existing Tailwind grid-cols-* / col-span-* utilities (0,1,0)
           that stay in the DOM untouched (existing markup + guard-test contract).
           Only grid-template-columns / grid-column are set here — display:grid and
           every gap-* keep coming from the Tailwind classes, so spacing is unchanged
           and only the reflow follows content width. Base rules restate each grid's
           current mobile column count so nothing shifts on phones. */
        .cq-scope { container-type: inline-size; container-name: cq; }
        .cq-scope > * { min-width: 0; }

        /* Base (narrow container) — mirrors each grid's existing mobile columns */
        .cq-scope .cq-2-4        { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        .cq-scope .cq-1-2        { grid-template-columns: 1fr; }
        .cq-scope .cq-1-3        { grid-template-columns: 1fr; }
        .cq-scope .cq-1-2-3      { grid-template-columns: 1fr; }
        .cq-scope .cq-1-2-4      { grid-template-columns: 1fr; }
        .cq-scope .cq-ann-compose{ grid-template-columns: 1fr; }

        @container cq (min-width: 560px) {
            .cq-scope .cq-1-2-3  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            .cq-scope .cq-1-2-4  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        }
        @container cq (min-width: 640px) {
            .cq-scope .cq-1-2    { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            .cq-scope .cq-1-3    { grid-template-columns: repeat(3, minmax(0, 1fr)); }
            /* Compose row (announcements): restore the 3-1-1 span rhythm by CONTENT
               width. nth-child (0,2,1) beats the Tailwind lg:col-span-* on the kids. */
            .cq-scope .cq-ann-compose { grid-template-columns: repeat(5, minmax(0, 1fr)); }
            .cq-scope .cq-ann-compose > :nth-child(1) { grid-column: span 3; }
            .cq-scope .cq-ann-compose > :nth-child(2) { grid-column: span 1; }
            .cq-scope .cq-ann-compose > :nth-child(3) { grid-column: span 1; }
        }
        @container cq (min-width: 900px) {
            .cq-scope .cq-2-4    { grid-template-columns: repeat(4, minmax(0, 1fr)); }
        }
        @container cq (min-width: 960px) {
            .cq-scope .cq-1-2-3  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
        }
        @container cq (min-width: 1000px) {
            .cq-scope .cq-1-2-4  { grid-template-columns: repeat(4, minmax(0, 1fr)); }
        }

/* ===== Help FAB (HELP-01) — relocated from the body-level <style> block ===== */
      /* ── Help FAB (HELP-01) — pure-CSS popover, glass DNA, bottom-right, safe-area aware ── */
      .help-fab-wrap { /* container only; children are position:fixed */ }
      /* Hidden checkbox drives the reveal; visually hidden but still keyboard-focusable */
      .help-fab-input { position: fixed; bottom: 0; inset-inline-end: 0; opacity: 0; pointer-events: none; }

      /* The floating round action button — owns the otherwise-empty bottom-right corner */
      .help-fab {
        position: fixed;
        inset-inline-end: 1.25rem;
        bottom: max(1.25rem, env(safe-area-inset-bottom));
        z-index: 60;                 /* above z-50 cart toast, below the z-[99999] toast stacks (D-08) */
        display: inline-flex; align-items: center; justify-content: center;
        width: 3.25rem; height: 3.25rem; border-radius: 9999px;
        cursor: pointer; user-select: none;
        background: var(--glass-bg);
        border: 1px solid var(--glass-border);
        box-shadow: var(--glass-shadow);
        backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
        transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
      }
      .help-fab:hover { transform: translateY(-1px); border-color: var(--hover-border); box-shadow: var(--hover-shadow); }
      .help-fab-icon { color: var(--brand-600); font-size: 1.25rem; }
      /* Keyboard focus visibility on the label (the checkbox itself is offscreen) */
      .help-fab-input:focus-visible ~ .help-fab { outline: 2px solid var(--brand-600); outline-offset: 2px; }

      /* Popover — hidden by default, revealed by the checked checkbox (pure-CSS, D-02) */
      .help-popover {
        display: none;
        position: fixed;
        inset-inline-end: 1.25rem;
        bottom: calc(max(1.25rem, env(safe-area-inset-bottom)) + 4rem);
        z-index: 60;
        min-width: 14rem;
        padding: 0.375rem;
        background: var(--glass-bg);
        border: 1px solid var(--glass-border);
        box-shadow: var(--glass-shadow);
        backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
        border-radius: 0.875rem;
      }
      .help-fab-input:checked ~ .help-popover { display: block; }

      .help-popover-link {
        display: flex; align-items: center; gap: 0.625rem;
        padding: 0.625rem 0.75rem; min-height: 2.75rem;
        font-size: 0.875rem; font-weight: 500; color: var(--text-main);
        border-radius: 0.625rem; text-decoration: none;
        transition: background 0.15s ease;
      }
      .help-popover-link:hover { background: var(--soft-bg); }
      .help-popover-link + .help-popover-link { margin-top: 0.125rem; }
      .help-popover-icon { color: var(--brand-600); width: 1.125rem; text-align: center; flex: none; }

      /* Mobile (D-09): smaller diameter, stays bottom-right + safe-area aware */
      @media (max-width: 640px) {
        .help-fab { width: 2.75rem; height: 2.75rem; inset-inline-end: 1rem; }
        .help-fab-icon { font-size: 1.125rem; }
        .help-popover {
          inset-inline-end: 1rem;
          bottom: calc(max(1.25rem, env(safe-area-inset-bottom)) + 3.5rem);
          min-width: 12rem;
        }
      }

      /* Respect reduced-motion (mirror faq.html L388/L400) */
      @media (prefers-reduced-motion: reduce) {
        .help-fab { transition: none; }
        .help-popover-link { transition: none; }
      }

/* ===== Worklist + next-step vocabulary (SPEC §3.10/§3.11) =====
   Tokens only — every color/size below resolves through the base.html token
   sheet in BOTH themes (no literal colors except the established light-theme
   emerald pair used by .btn-primary). Container queries (not viewport) follow
   CONTENT width per the admin-dashboard pattern. */

/* ── .wl-head — worklist section header (renderWorklist) ──
   Uppercase .ui-meta voice + a count-badge slot (.ui-badge / .ui-badge-attn). */
.wl-head {
    display: flex; align-items: center; gap: 0.5rem;
    font-size: var(--text-meta); font-weight: 600; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.06em;
    margin: 1.5rem 0 0.75rem;
}
.wl-head:first-child { margin-top: 0; }
/* One-line .ui-meta companions emitted by renderWorklist */
.wl-empty, .wl-sub { margin: 0 0 0.75rem; }
/* "Open archive →" link under the finished bucket — quiet, 44px target */
.wl-archive-link {
    display: inline-flex; align-items: center;
    min-height: 44px;                            /* touch target */
    font-size: var(--text-meta); font-weight: 600; color: var(--muted);
    text-decoration: none; transition: color 0.15s ease;
}
.wl-archive-link:hover { color: var(--text); }

/* ── .ui-next-card — client-hydrated "what happens next" card (nextStepCard) ──
   Glass-soft base + a 3px left tone spine. Tone modifiers set --next-tone:
     .act  var(--warning)        — the viewer holds the ball
     .wait var(--border-strong)  — someone else holds it
     .done var(--success)        — terminal, nothing owed
   Declares its OWN container (like .ui-detail-header) so the action slot
   answers the CARD's width, not the viewport. */
.ui-next-card {
    container-type: inline-size;
    container-name: nextcard;
    position: relative; overflow: hidden;
    display: flex; flex-direction: column; gap: 0.5rem;
    padding: 1rem 1.15rem;
    background: var(--soft-bg);
    border: 1px solid var(--soft-border);
    border-radius: var(--radius-md);
}
.ui-next-card::before {
    content: ""; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 3px;
    background: var(--next-tone, var(--border-strong));
}
.ui-next-card.act  { --next-tone: var(--warning); }
.ui-next-card.wait { --next-tone: var(--border-strong); }
.ui-next-card.done { --next-tone: var(--success); }
/* Kicker row — stage label in the uppercase .ui-meta voice + tone dot */
.ui-next-kicker {
    display: flex; align-items: center; gap: 0.4rem;
    font-size: var(--text-meta); font-weight: 600; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.06em;
}
.ui-next-dot {
    width: 0.5rem; height: 0.5rem; border-radius: 9999px; flex: none;
    background: var(--next-tone, var(--border-strong));
}
.ui-next-headline {
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 1rem; font-weight: 600; color: var(--text); line-height: 1.35;
}
.ui-next-body { margin: 0; }
/* Action slot — intrinsic beside content on wide cards; ≥44px target */
.ui-next-action {
    display: inline-flex; align-items: center; justify-content: center;
    gap: 0.5rem; min-height: 44px; padding: 0 1.25rem;
    margin-top: 0.25rem; align-self: flex-start;
    border-radius: 0.5rem; border: none; cursor: pointer;
    font-size: var(--text-body); font-weight: 600; font-family: inherit;
    background: var(--brand-700); color: #ffffff; text-decoration: none;
    transition: background 0.15s ease;
}
.ui-next-action:hover { background: var(--brand-600); }
html[data-theme="light"] .ui-next-action        { background: #059669; }  /* matches .btn-primary */
html[data-theme="light"] .ui-next-action:hover  { background: #047857; }
/* Narrow card: the action goes full-width (CONTAINER query, not viewport) */
@container nextcard (max-width: 479.98px) {
    .ui-next-action { align-self: stretch; width: 100%; }
}
/* Optional thin progress bar — 1.5px track, tone-colored fill */
.ui-next-progress {
    height: 1.5px; border-radius: 9999px; overflow: hidden;
    background: var(--soft-border); margin-top: 0.5rem;
}
.ui-next-progress-bar {
    display: block; height: 100%;
    background: var(--next-tone, var(--brand-600));
    border-radius: inherit;
    transition: width 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
    .ui-next-action, .ui-next-progress-bar, .wl-archive-link { transition: none; }
}

/* ── .ui-sticky-actions — form action bar (SPEC §3.11) ──
   Static normal flow on wide containers; under 640px CONTAINER width it
   becomes a sticky glass-soft bottom bar with safe-area padding. The unnamed
   @container resolves against the NEAREST ancestor container — wrap forms in
   .ui-form-scope (below) when no .bento/.rec-scope/.ui-detail-header ancestor
   already provides one (no ancestor container ⇒ the rule never engages and
   the bar simply stays static). */
.ui-form-scope { container-type: inline-size; container-name: formscope; }
.ui-sticky-actions {
    display: flex; flex-wrap: wrap; align-items: center;
    justify-content: flex-end; gap: 0.625rem;
    padding: 0.75rem 0;
}
@container (max-width: 639.98px) {
    .ui-sticky-actions {
        position: sticky; bottom: 0; z-index: 30;
        padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom));
        background: var(--soft-bg);
        border-top: 1px solid var(--soft-border);
        border-radius: 0.75rem 0.75rem 0 0;
        backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
    }
    /* Buttons stretch to comfortable full-width touch targets when stuck */
    .ui-sticky-actions > a,
    .ui-sticky-actions > button {
        flex: 1 1 auto; min-height: 44px;
        display: inline-flex; align-items: center; justify-content: center;
    }
}

/* ── .ui-stepper-vertical — labelled vertical stepper variant (SPEC §3.11) ──
   For narrow containers where the horizontal .ui-stepper condenses: a column
   of full-width pills with labels ALWAYS visible. Reuses .ui-step tokens. */
.ui-stepper.ui-stepper-vertical {
    flex-direction: column; align-items: stretch; gap: 0.125rem;
}
.ui-stepper.ui-stepper-vertical .ui-step {
    display: flex; justify-content: flex-start;
    border-radius: 0.625rem;
}
/* Labels always visible: out-specificity the detailhdr condensed rule
   (which display:none's non-adjacent steps under 560px) — 4 classes beat
   its 3 regardless of @container order. */
.ui-detail-header .ui-stepper.ui-stepper-vertical .ui-step { display: flex; }

/* ── .seg-tabs / .seg-tab base (renderFilterPills) ──
   Byte-mirror of partials/restock_tabs.html so pill markup emitted by
   renderFilterPills() is styled on pages that DON'T include that partial.
   Where both load, the partial's body-level <style> comes later in document
   order and wins every same-specificity tie with identical declarations —
   zero visual drift. */
.seg-tabs { display: inline-flex; gap: .25rem; padding: .25rem; border-radius: .75rem; }
.seg-tab {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .5rem .9rem; border-radius: .55rem;
    font-size: .875rem; font-weight: 500; color: var(--muted);
    white-space: nowrap; transition: background .2s ease, color .2s ease;
}
.seg-tab:hover { color: var(--text); }
.seg-tab i { font-size: .8rem; }
.seg-tab.is-active { background: var(--brand-600); color: #fff; font-weight: 600; }
.seg-tab.is-active i { color: #fff; }
@media (max-width: 480px) {
    .seg-tabs { display: flex; width: 100%; }
    .seg-tab { flex: 1; justify-content: center; }
}
/* Filter-pill buttons only (.ui-filter-pills wrapper from renderFilterPills):
   reset UA <button> chrome + enforce the 44px touch target. Scoped so the
   partial's <a>-based tabs keep their exact current look. */
.ui-filter-pills .seg-tab {
    background: none; border: none; cursor: pointer; font-family: inherit;
    min-height: 44px;                            /* touch target */
}
.ui-filter-pills .seg-tab.is-active { background: var(--brand-600); color: #fff; font-weight: 600; }

/* ===== Scan (sips-scan) =====
   Camera scan sheet rendered by static/js/sips-scan.js into a native
   top-layer <dialog id="sips-scan-modal"> (immune to #app-main transforms
   and stacks above an open #ui-drawer). Tokens only: both themes for free. */
.sips-scan-modal {
    margin: auto;
    width: min(560px, calc(100vw - 2rem));
    max-width: 100vw;
    padding: 1rem;
    background: var(--glass-bg);
    color: var(--text);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-md);
    box-shadow: var(--glass-shadow);
}
.sips-scan-modal::backdrop {
    background: var(--sidebar-overlay);
    backdrop-filter: blur(2px);
}
.sips-scan-modal[open] { display: flex; flex-direction: column; gap: 0.75rem; }
@media (max-width: 640px) {
    /* bottom-sheet feel on phones */
    .sips-scan-modal {
        margin: auto 0 0 0;
        width: 100vw;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
        border-left: none; border-right: none; border-bottom: none;
    }
}
.sips-scan-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.sips-scan-title { font-size: var(--text-heading); font-weight: 600; color: var(--text); margin: 0; line-height: 1.3; }
.sips-scan-stage {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-md);
    background: #000;                       /* live video letterbox, theme-invariant */
}
.sips-scan-video { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.sips-scan-reticle { position: absolute; inset: 12%; pointer-events: none; }
.sips-scan-reticle::before {
    content: ""; position: absolute; inset: 0;
    border: 2px solid var(--brand-600);
    border-radius: var(--radius-md);
    opacity: 0.85;
    /* corner marks: mask away the edge midsections */
    -webkit-mask: linear-gradient(#000 0 0) top left / 28px 28px no-repeat,
                  linear-gradient(#000 0 0) top right / 28px 28px no-repeat,
                  linear-gradient(#000 0 0) bottom left / 28px 28px no-repeat,
                  linear-gradient(#000 0 0) bottom right / 28px 28px no-repeat;
    mask: linear-gradient(#000 0 0) top left / 28px 28px no-repeat,
          linear-gradient(#000 0 0) top right / 28px 28px no-repeat,
          linear-gradient(#000 0 0) bottom left / 28px 28px no-repeat,
          linear-gradient(#000 0 0) bottom right / 28px 28px no-repeat;
}
.sips-scan-reticle::after {
    content: ""; position: absolute; left: 6%; right: 6%; top: 50%; height: 2px;
    background: var(--brand-600);
    opacity: 0.6;
    animation: sipsScanline 2.2s ease-in-out infinite;
}
@keyframes sipsScanline {
    0%, 100% { transform: translateY(-2.6rem); }
    50%      { transform: translateY(2.6rem); }
}
.sips-scan-reticle.sips-scan-flash::before {
    border-color: var(--success, var(--brand-600));
    opacity: 1;
    transition: opacity 120ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
    .sips-scan-reticle::after { animation: none; display: none; }
}
.sips-scan-hint { color: var(--muted); font-size: 0.8125rem; margin: 0; }
.sips-scan-status {
    color: var(--muted); font-size: 0.8125rem; margin: 0;
    min-height: 1.25rem;                    /* keep layout stable across updates */
}
.sips-scan-actions { display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem; }
.sips-scan-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 0.375rem;
    min-height: 44px; min-width: 44px;      /* touch target */
    padding: 0.5rem 0.875rem;
    background: var(--glass-bg);
    color: var(--text);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-md);
    font-size: 0.8125rem; font-weight: 500; font-family: inherit;
    cursor: pointer;
    transition: border-color 150ms ease-out, background 150ms ease-out;
}
.sips-scan-btn:hover { border-color: var(--brand-600); }
.sips-scan-btn:focus-visible { outline: 2px solid var(--brand-600); outline-offset: 2px; }
.sips-scan-btn[aria-pressed="true"] { background: var(--brand-600); color: #fff; }
@media (prefers-reduced-motion: reduce) {
    .sips-scan-btn { transition: none; }
}

/* ==========================================================================
   RTL affordances (Phase 31) — additive, inert in LTR.
   Every rule below is scoped to [dir="rtl"] so the English (LTR) computed
   style is byte-equivalent; the logical-property migration above mirrors the
   layout for free, and these blocks handle the three things logical props
   can't: glyph flipping, JS-set numeric isolation, and transform sign-flips.
   ========================================================================== */

/* RTL-03 — directional FontAwesome glyphs mirror horizontally.
   CRITICAL (MEMORY app-main-traps-fixed-modals): scaleX(-1) is scoped to the
   .fa-* glyph classes ONLY — never a content wrapper. A retained transform on
   #app-main becomes the containing block for every position:fixed modal/drawer
   and traps them. */
[dir="rtl"] .fa-arrow-left,   [dir="rtl"] .fa-arrow-right,
[dir="rtl"] .fa-chevron-left, [dir="rtl"] .fa-chevron-right,
[dir="rtl"] .fa-angle-left,   [dir="rtl"] .fa-angle-right,
[dir="rtl"] .fa-angles-left,  [dir="rtl"] .fa-angles-right,
[dir="rtl"] .fa-caret-left,   [dir="rtl"] .fa-caret-right {
    transform: scaleX(-1);
}

/* The rec-link hover nudge points along the reading direction — flip its sign
   (do NOT logical-swap a transform value). */
[dir="rtl"] .rec-card:hover .rec-link i { transform: translateX(-2px); }

/* RTL-04 — numeric isolation net. JS sets prices/qty/ids via textContent
   (T-31-01: the XSS posture stays textContent/escapeHtml — no <bdi> injected,
   no innerHTML). unicode-bidi does the isolation purely in CSS so Western-digit
   numerics stay LTR and un-reordered inside RTL flow. */
[dir="rtl"] .rcard td,
[dir="rtl"] .amount,
[dir="rtl"] .ui-meta,
[dir="rtl"] .stat-value,
[dir="rtl"] .ui-num,
[dir="rtl"] .ui-num-strong,
[dir="rtl"] .ui-num-sub,
[dir="rtl"] .rec-id { unicode-bidi: plaintext; }

/* RTL-05 — toast slides in from the inline-end under RTL. Sign-flip the
   transform (mirror of sips-toast-in), never a logical swap. (The exit is now a
   direction-neutral fade + shrink shared by both directions.) */
@keyframes sips-toast-in-rtl {
    from { opacity: 0; transform: translateX(-110%); }
    to   { opacity: 1; transform: translateX(0); }
}
[dir="rtl"] .sips-toast { animation-name: sips-toast-in-rtl; }

/* ==========================================================================
   SSR DataGrid (DG-01) — the shared list-page chrome rendered by
   templates/ui/_datagrid.html. Every rule below is container-query driven or
   uses logical properties only; there is not a single viewport media query and
   not a single left/right, so the grid reflows on sidebar open/close and
   mirrors correctly under [dir="rtl"] for free.

   CONTAINER-NAME NESTING HAZARD (same one documented for .ui-form-scope above):
   .ui-grid never reuses an existing container name. The toolbar owns `cq` via
   the existing .cq-scope wrapper, the table wrapper owns `rcard`, the card grid
   owns `recgrid` via .rec-scope; .ui-grid itself declares only the distinct
   `uigrid` name (needed because the pager is a sibling of those containers).
   Nesting .ui-grid inside another element that shares one of those names would
   silently re-point the @container queries to the wrong box.

   No new design tokens and no new Tailwind utilities are introduced — every
   colour resolves from an existing custom property.
   ========================================================================== */

/* `uigrid` is a DISTINCT container name (the hazard above is about re-pointing
   cq/rcard/recgrid, not about naming per se): the pager is a SIBLING of the
   .rcard / .rec-scope containers, so only an ancestor container can drive its
   phone degradation. */
.ui-grid {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    container-type: inline-size;
    container-name: uigrid;
}

/* ---- Toolbar ------------------------------------------------------------ */
/* display:grid + gap live here so the markup needs only the existing
   .cq-scope / .cq-1-2-4 recipe (1 col -> 2 at 560px -> 4 at 1000px of CONTENT
   width) and no new utility classes. */
.ui-grid-toolbar {
    display: grid;
    gap: 0.75rem;
    align-items: end;
}
.ui-field-wrap {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.375rem 0.5rem;
    border-radius: 0.5rem;
    min-width: 0;
}
/* Every interactive target clears the codebase-wide 44px minimum. Inputs also
   inherit the existing mobile `font-size:16px !important` rule, so focusing the
   search box never triggers the iOS zoom. */
.ui-field-wrap .ui-field { min-height: 44px; }
.ui-grid-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.625rem;
    align-self: end;
}
.ui-grid-pills { flex-wrap: wrap; }

/* ---- Window banner ------------------------------------------------------ */
.ui-grid-window {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    background: var(--soft-bg);
    border: 1px solid var(--soft-border);
}

/* ---- Results ------------------------------------------------------------ */
.ui-grid-results { min-width: 0; }
.ui-grid-rowlink { color: var(--brand-600); font-weight: 600; text-decoration: none; }
.ui-grid-rowlink:hover { text-decoration: underline; }

/* Column shedding (GridColumn.priority="low" -> .dg-lo).

   The threshold was 420px, which fires only INSIDE the already-stacked card
   band (the .rcard stack threshold is 640px) — so shedding could never relieve
   TABLE mode. Between 640px and ~900px of CONTENT width an 11-column grid
   (ADMIN_USERS) rendered every column; its min-content width far exceeded the
   box and, since nothing clips, the overflow propagated and scrolled the whole
   document. That band is not exotic: iPad landscape with the sidebar open is
   ~690px, and a phone in landscape with the sidebar collapsed is ~780px.

   Raised to 900px so low-priority columns are shed while still in table mode,
   BEFORE the layout can break — and it still covers the card band below 640px,
   where each column is a stacked line.

   Shed columns are presentation-only: they still appear in the CSV export. */
@container rcard (max-width: 900px) {
    .rcard th.dg-lo, .rcard td.dg-lo { display: none; }
}

/* Custom-range date inputs (ui/_datagrid.html). Inline display:none/grid is set
   server-side and toggled by sips-base.js; this only lays the pair out. */
.ui-grid-custom {
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 0.625rem;
    min-width: 0;
}

/* ---- Pager -------------------------------------------------------------- */
.ui-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--divider);
}
.ui-pager-count { font-variant-numeric: tabular-nums; }
.ui-pager-nav, .ui-pager-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
}
.ui-pager-tools { gap: 0.875rem; }
.ui-pager-btn, .ui-pager-num {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 0.75rem;
    border-radius: 0.5rem;
    font-size: var(--text-meta);
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
    background: var(--soft-bg);
    border: 1px solid var(--soft-border);
}
.ui-pager-num { min-width: 44px; padding: 0 0.5rem; font-variant-numeric: tabular-nums; }
.ui-pager-btn:hover, .ui-pager-num:hover { color: var(--text); border-color: var(--hover-border); }
.ui-pager-num.is-active {
    background: var(--brand-600);
    border-color: var(--brand-600);
    color: #fff;
}
.ui-pager-btn[aria-disabled="true"] {
    opacity: 0.4;
    pointer-events: none;
}
.ui-pager-gap { color: var(--muted); padding: 0 0.25rem; }
.ui-pager-size { padding-block: 0.125rem; }

/* Numbered page links are noise on a phone — Previous / "3 of 12" / Next is
   enough. Queried against the ANCESTOR .ui-grid container (`uigrid`): the pager
   is a sibling of .rec-scope/.rcard, so their containers can never reach it —
   and in table view no `recgrid` container even exists. */
@container uigrid (max-width: 480px) {
    .ui-pager-num, .ui-pager-gap { display: none; }
}

/* ---- KPI tile + delta (rendered by ui/_kpi_tile.html) ------------------- */
.ui-kpi-tile {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    padding: 0.875rem 1rem;
    border-radius: var(--radius-md);
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    text-decoration: none;
    color: inherit;
    min-width: 0;
}
a.ui-kpi-tile:hover { border-color: var(--hover-border); box-shadow: var(--hover-shadow); }
.ui-kpi-head { display: flex; align-items: center; gap: 0.4rem; min-width: 0; }
.ui-kpi-icon { font-size: 0.75rem; color: var(--muted); flex: none; }
.ui-kpi-label { text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.625rem; }
.ui-kpi-value { font-size: 1.375rem; font-weight: 700; color: var(--text); line-height: 1.15; }
.ui-kpi-hint { margin: 0; }

/* Colour is NEVER the only signal — the arrow glyph and the numeric sign carry
   the direction too (same rule as .ui-status / .ui-status-label). Polarity, not
   direction, picks the colour, so a rising cancel-rate reads red. */
.ui-delta {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.3rem;
    font-size: var(--text-meta);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}
.ui-delta i { font-size: 0.625rem; }
.ui-delta-good { color: var(--success); }
.ui-delta-bad { color: var(--error); }
.ui-delta-neutral { color: var(--muted); }
.ui-delta-note { font-weight: 500; }

/* RTL-04 numeric isolation net — the new digit-bearing classes join the
   existing unicode-bidi:plaintext selector list (accounts/tests_rtl.py asserts
   on that block). */
[dir="rtl"] .ui-delta,
[dir="rtl"] .ui-pager-num,
[dir="rtl"] .ui-pager-count,
[dir="rtl"] .ui-kpi-value { unicode-bidi: plaintext; }
