/* ==========================================================
   EKATRA OS — Global Design System
   Premium dark-mode, glassmorphism, smooth animations
   ========================================================== */

@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&family=Lora:ital,wght@0,400;0,500;0,600;1,400;1,600&family=Plus+Jakarta+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Outfit:wght@300;400;500;600;700;800&display=swap');

/* ── CSS Custom Properties — Prototype Design System ── */
:root {
    /* Brand Accent Tokens — Warm Bronze & Earth Palette */
    --color-accent: #b68235;
    --color-accent-hover: #9b7232;
    --color-accent-700: #7d5411;
    --color-accent-100: #fff3e4;
    --color-divider: rgba(32, 31, 29, 0.16);

    --primary: #b68235;
    --primary-hover: #9b7232;
    --primary-glow: rgba(182, 130, 53, 0.22);
    --primary-subtle: #fff3e4;

    --secondary: #10b981;
    --secondary-subtle: rgba(16,185,129,0.08);

    --danger: #ef4444;
    --warning: #f59e0b;

    /* Base Dark Surfaces (Default - Pitch Black Theme) */
    --bg:       #000000;
    --surface:  #111111;
    --surface2: #1c1c1c;
    --border:   #262626;
    --border2:  rgba(255, 255, 255, 0.1);

    /* Text */
    --text-primary:   #f8fafc;
    --text-secondary: #d4d4d8;
    --text-muted:     #a1a1aa;

    /* Sidebar Width */
    --sidebar-w: 240px;

    /*
       The three measurements that decide whether the sidebar lines up.

       Every row in the sidebar — a module or one of its sub-screens —
       uses all three identically, so every icon sits on one vertical
       line and every label on another. Nothing indents and nothing
       dims: a sub-screen is marked as one by the hairline down the
       left of its group, never by moving or fading its icon, because
       the icons are the column the eye actually scans and a faded one
       already means "unavailable" everywhere else in the product.

       Anything that wants to mark a row must therefore paint rather
       than occupy — see the inset shadow on .nav-link.active, which
       was a 3px left border until it was noticed that it pushed the
       active row 2px right of every other one.
    */
    --nav-pad-x:    0.75rem;
    --nav-icon-slot: 1.375rem;
    --nav-gap:      0.625rem;

    /* Prototype Typography Tokens */
    --font-heading: 'Cormorant Garamond', Georgia, serif;
    --font-heading-weight: 600;
    --font-body: 'Lora', 'Plus Jakarta Sans', serif;
    --font-sans: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
    --font-display: 'Cormorant Garamond', serif;

    /* Spacing & Radii */
    --radius-sm: 4px;
    --radius-md: 6px;
    --radius-lg: 12px;
    --radius-xl: 20px;
    --radius-full: 9999px;

    /* Transitions */
    --t-fast:   150ms cubic-bezier(0.4,0,0.2,1);
    --t-smooth: 250ms cubic-bezier(0.4,0,0.2,1);
    --t-spring: 350ms cubic-bezier(0.34,1.56,0.64,1);

    /* Shadows */
    --shadow-sm:  0 1px 2px rgba(45,43,43,0.12);
    --shadow-md:  0 3px 10px rgba(45,43,43,0.16);
    --shadow-lg:  0 12px 32px rgba(45,43,43,0.22);
    --shadow-glow: 0 0 20px var(--primary-glow);
}

/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
    font-family: var(--font-sans);
    background: var(--bg);
    color: var(--text-primary);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    min-height: 100vh;
}
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font-family: inherit; }
img { max-width: 100%; display: block; }
ul { list-style: none; }

/* ── App Shell ── */
.app-shell {
    display: flex;
    height: 100vh;
    overflow: hidden;
}

/* ── Sidebar ── */
.sidebar {
    width: var(--sidebar-w);
    background: var(--surface);
    border-right: 1px solid var(--border2);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    overflow-y: auto;
}

.sidebar-brand {
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid var(--border2);
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.sidebar-brand-icon {
    width: 32px;
    height: 32px;
    background: var(--primary);
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-family: var(--font-display);
    font-size: 1rem;
    color: #fff;
    box-shadow: 0 4px 12px var(--primary-glow);
    flex-shrink: 0;
}

.sidebar-brand-text h1 {
    font-size: 0.9375rem;
    font-weight: 700;
    font-family: var(--font-display);
    color: var(--text-primary);
    letter-spacing: -0.02em;
    line-height: 1.2;
}

.sidebar-brand-text span {
    font-size: 0.6875rem;
    color: var(--primary);
    font-weight: 600;
}

/* Org Context */
.sidebar-org {
    padding: 0.875rem 1.5rem;
    border-bottom: 1px solid var(--border2);
    background: rgba(0,0,0,0.2);
}

.sidebar-org-label {
    font-size: 0.625rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    font-weight: 700;
    margin-bottom: 0.3rem;
}

.sidebar-org-name {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary);
}

.sidebar-org-loc {
    font-size: 0.6875rem;
    color: var(--text-secondary);
    margin-top: 0.125rem;
    display: flex;
    align-items: center;
    gap: 0.3rem;
}

/* Nav */
.sidebar-nav {
    padding: 0.75rem 0.75rem;
    flex: 1;
}

.nav-section-label {
    font-size: 0.625rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    font-weight: 700;
    padding: 0.875rem 0.75rem 0.375rem;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: var(--nav-gap);
    padding: 0.5625rem var(--nav-pad-x);
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    font-size: 0.8125rem;
    font-weight: 500;
    transition: var(--t-fast);
    cursor: pointer;
    border: 1px solid transparent;
    white-space: nowrap;
    overflow: hidden;
}

/*
   Every nav icon gets an identical slot, so the labels start on
   one vertical line and the glyphs sit on another.

   The slot used to be 16px, which is narrower than the icons
   put in it. Font Awesome draws its widest solid glyphs on a
   640-unit grid against a 512-unit em — fa-truck-fast, fa-gears
   and fa-box-open are all 1.25em, or 17.5px at this font size.
   A 1.25em glyph in a 16px box overflows, and because the box
   centres its contents it overflows BOTH sides, so a wide icon
   sat visibly left of a narrow one directly above it.

   1.375rem clears the widest glyph with a little room, and is a
   rem rather than an em so that a row using a smaller icon still
   lines its slot up with every other row.
*/
.nav-link i {
    font-size: 0.875rem;
    width: var(--nav-icon-slot);
    min-width: var(--nav-icon-slot);
    text-align: center;
    flex-shrink: 0;
}

.nav-link:hover {
    color: var(--text-primary);
    background: var(--border2);
}

.nav-link.active {
    color: #fff;
    background: var(--primary);
    box-shadow: var(--shadow-glow);
}

.nav-link.disabled {
    opacity: 0.35;
    cursor: not-allowed;
    pointer-events: none;
}

/* Sidebar footer */
.sidebar-footer {
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--border2);
    display: flex;
    align-items: center;
    gap: 0.625rem;
    background: rgba(0,0,0,0.2);
}

.user-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--surface2);
    border: 1px solid var(--border2);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.75rem;
    color: var(--text-primary);
    flex-shrink: 0;
}

.user-info { flex: 1; min-width: 0; }
.user-name { font-size: 0.75rem; font-weight: 600; color: var(--text-primary); truncate: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-email { font-size: 0.625rem; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.btn-logout {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    padding: 0.375rem;
    border-radius: var(--radius-sm);
    transition: var(--t-fast);
    flex-shrink: 0;
}
.btn-logout:hover { color: var(--danger); background: rgba(239,68,68,0.1); }

/* ── Main Area ── */
.main-area {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/*
 * Topbar — transparent at rest, glass once the page moves.
 *
 * At the top of a page the bar has nothing to separate itself from, so it
 * draws nothing: no fill, no rule, and the breadcrumb sits on the page's own
 * ground. Once .page-content scrolls past 24px the layout adds .is-scrolled
 * and the bar fades in a blurred surface, a hairline and a soft shadow — the
 * cue that content is passing underneath. Built on this file's tokens, so it
 * follows the user's accent and light/dark theme.
 *
 * The plain `background: var(--surface)` before each color-mix() is the
 * fallback for older Android WebViews, which drop the whole declaration they
 * cannot parse (charter #4: low-end phones are a primary target).
 */
.topbar {
    height: 56px;
    padding: 0 1.75rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-shrink: 0;
    position: relative;
    z-index: 20;
    background: transparent;
    border-bottom: 1px solid transparent;
    transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
.topbar.is-scrolled {
    background: var(--surface);
    background: color-mix(in srgb, var(--surface) 82%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom-color: var(--border2);
    box-shadow: 0 10px 24px -18px rgba(0, 0, 0, 0.55);
}

.topbar-breadcrumb {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
}

/*
 * Hidden on desktop: most screens set no $pageTitle, so it read
 * "Ekatra OS > Dashboard" everywhere, and the sidebar already says where you
 * are. Kept below 768px, where the pill's back arrow and title are the only
 * navigation left once the sidebar is gone.
 */
@media (min-width: 769px) {
    .topbar-breadcrumb { display: none; }
    .topbar-actions { margin-left: auto; }
}

.topbar-breadcrumb .crumb { color: var(--text-muted); }
.topbar-breadcrumb .sep { color: var(--text-muted); font-size: 0.625rem; }
.topbar-breadcrumb .current { color: var(--text-primary); font-weight: 600; }

/*
 * The back arrow, first thing in the topbar so it sits in the page's top-left
 * corner on every screen. Sized to the 44px touch target a low-end Android
 * phone needs (charter #4) while reading as a quiet control rather than a
 * button competing with the page's own actions.
 */
.topbar-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    margin-right: 0.125rem;
    flex: 0 0 auto;
    border: 1px solid var(--color-divider, rgba(148,163,184,0.18));
    border-radius: 0.625rem;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.8125rem;
    line-height: 1;
    transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}

.topbar-back:hover {
    color: var(--text-primary);
    background: var(--surface2, rgba(148,163,184,0.10));
    border-color: var(--text-muted);
}

.topbar-back:focus-visible {
    outline: 2px solid var(--color-accent, #38bdf8);
    outline-offset: 2px;
}

.topbar-back:active { transform: translateY(1px); }

@media (prefers-reduced-motion: reduce) {
    .topbar-back { transition: none; }
    .topbar-back:active { transform: none; }
}

@media (max-width: 640px) {
    /* On a phone the trail is what gets dropped, never the way back. */
    .topbar-breadcrumb .crumb,
    .topbar-breadcrumb .sep { display: none; }
    .topbar-breadcrumb .current { display: inline; }
}

body.theme-light .topbar-back { color: #64748b; border-color: rgba(15,23,42,0.12); }
body.theme-light .topbar-back:hover { color: #0f172a; background: rgba(15,23,42,0.05); }

.topbar-actions { display: flex; align-items: center; gap: 0.5rem; }

/* Notification Bell */
.notif-btn {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-secondary);
    padding: 0.375rem 0.5rem;
    border-radius: var(--radius-md);
    transition: var(--t-fast);
    position: relative;
    font-size: 1rem;
}
.notif-btn:hover { color: var(--text-primary); background: var(--border2); }
.notif-dot {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 7px;
    height: 7px;
    background: var(--primary);
    border-radius: 50%;
    border: 2px solid var(--surface);
}

/* Page content scroll */
.page-content {
    flex: 1;
    overflow-y: auto;
    padding: 1.25rem 1.5rem;
}

/* ── Components ── */

/* ── Buttons — Sleek, Elegant & Soft-Pill Aesthetic ── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.45rem 1.15rem;
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.015em;
    border-radius: var(--radius-full);
    border: 1px solid transparent;
    cursor: pointer;
    transition: var(--t-fast);
    white-space: nowrap;
    line-height: 1.3;
    text-decoration: none;
}
.btn:active { transform: scale(0.98); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }

/* Primary Button: Dynamic Accent Color Pill */
.btn-primary {
    background: var(--primary);
    color: #ffffff;
    border: 1px solid rgba(255, 255, 255, 0.15);
    box-shadow: 0 2px 8px var(--primary-glow);
}
.btn-primary:hover {
    background: var(--primary-hover);
    color: #ffffff;
    box-shadow: 0 4px 14px var(--primary-glow);
}

/* Secondary Button: Soft Glass Outline Pill */
.btn-secondary {
    background: rgba(255, 255, 255, 0.04);
    color: var(--text-primary);
    border: 1px solid var(--border);
    backdrop-filter: blur(8px);
}
.btn-secondary:hover {
    color: #ffffff;
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.2);
}

/* Ghost Button: Minimal Subdued Link */
.btn-ghost {
    background: transparent;
    color: var(--text-secondary);
    border-color: transparent;
}
.btn-ghost:hover {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-primary);
}

/* Danger & Success Buttons */
.btn-danger {
    background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
    color: #ffffff;
    border: 1px solid rgba(255, 255, 255, 0.15);
}
.btn-danger:hover { background: #dc2626; }

.btn-success {
    background: linear-gradient(135deg, #10b981 0%, #059669 100%);
    color: #ffffff;
    border: 1px solid rgba(255, 255, 255, 0.15);
}
.btn-success:hover { background: #059669; }

.btn-sm { padding: 0.32rem 0.85rem; font-size: 0.75rem; border-radius: var(--radius-full); }
.btn-lg { padding: 0.65rem 1.6rem; font-size: 0.875rem; border-radius: var(--radius-full); }
.btn-full { width: 100%; }

/* Cards */
.card {
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: var(--radius-lg);
    transition: var(--t-smooth);
}
.card-hover:hover {
    border-color: rgba(182, 130, 53, 0.3);
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}
.card-body { padding: 1.25rem 1.5rem; }

/* Form Elements */
.form-field { margin-bottom: 1.125rem; }
.form-label {
    display: block;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-secondary);
    margin-bottom: 0.4rem;
}

.form-input, .form-select, .form-textarea {
    width: 100%;
    padding: 0.5625rem 0.875rem;
    background: rgba(0,0,0,0.3);
    border: 1px solid var(--border2);
    border-radius: var(--radius-md);
    color: var(--text-primary);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    transition: var(--t-fast);
}
.form-input::placeholder, .form-textarea::placeholder { color: var(--text-muted); }
.form-input:focus, .form-select:focus, .form-textarea:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-glow);
    background: rgba(0,0,0,0.4);
}
.form-textarea { resize: vertical; min-height: 80px; }
.form-select { cursor: pointer; }
.form-select option { background: var(--surface2); }

.form-color {
    width: 40px;
    height: 40px;
    border: 2px solid var(--border2);
    border-radius: var(--radius-md);
    padding: 2px;
    background: transparent;
    cursor: pointer;
}

/* ── Badges & Tags — Lightweight Pill Aesthetic ── */
.badge, .tag {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.75rem;
    border-radius: var(--radius-full);
    font-family: var(--font-sans);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    border: 1px solid transparent;
    backdrop-filter: blur(4px);
}

.badge-primary, .tag-accent {
    background: rgba(182, 130, 53, 0.14);
    color: #e1ad66;
    border-color: rgba(182, 130, 53, 0.25);
}
.badge-success {
    background: rgba(16, 185, 129, 0.12);
    color: #34d399;
    border-color: rgba(16, 185, 129, 0.25);
}
.badge-default, .tag-neutral {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-secondary);
    border-color: rgba(255, 255, 255, 0.1);
}
.badge-warning {
    background: rgba(245, 158, 11, 0.12);
    color: #fbbf24;
    border-color: rgba(245, 158, 11, 0.25);
}
.badge-error, .badge-danger {
    background: rgba(239, 68, 68, 0.12);
    color: #fca5a5;
    border-color: rgba(239, 68, 68, 0.25);
}

/* Glassmorphism */
.glass {
    background: rgba(17, 17, 17, 0.7);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 1px solid var(--border2);
}

/* Stat Cards */
.stat-card {
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: var(--radius-lg);
    padding: 1rem 1.25rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    transition: var(--t-smooth);
}
.stat-card:hover { border-color: rgba(99,102,241,0.2); }
.stat-label { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: 0.375rem; }
.stat-value { font-size: 1.5rem; font-weight: 800; font-family: var(--font-display); color: var(--text-primary); }
.stat-icon {
    width: 44px;
    height: 44px;
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.125rem;
    flex-shrink: 0;
}
.stat-icon-primary { background: var(--primary-subtle); color: #818cf8; }
.stat-icon-success { background: var(--secondary-subtle); color: #34d399; }
.stat-icon-warning { background: rgba(245,158,11,0.1); color: #fbbf24; }

/* Grid Layouts */
.grid-4 { display: grid; grid-template-columns: repeat(4,1fr); gap: 1.25rem; }
.grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: 1.25rem; }
.grid-2 { display: grid; grid-template-columns: repeat(2,1fr); gap: 1.25rem; }
.grid-12 { display: grid; grid-template-columns: repeat(12,1fr); gap: 1.5rem; }
.col-8 { grid-column: span 8; }
.col-4 { grid-column: span 4; }

/* Divider */
.divider { height: 1px; background: var(--border2); margin: 1.25rem 0; }

/* Empty State */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 4rem 2rem;
    color: var(--text-secondary);
    text-align: center;
    gap: 1rem;
}
.empty-state i { font-size: 3rem; color: var(--text-muted); opacity: 0.6; }
.empty-state h3 { font-size: 1rem; font-weight: 600; color: var(--text-secondary); }
.empty-state p { font-size: 0.875rem; color: var(--text-muted); max-width: 300px; line-height: 1.5; }

/* Alerts */
.alert {
    padding: 0.75rem 1rem;
    border-radius: var(--radius-md);
    font-size: 0.8125rem;
    display: flex;
    align-items: center;
    gap: 0.625rem;
    margin-bottom: 1rem;
}
.alert-error { background: rgba(239,68,68,0.1); border: 1px solid rgba(239,68,68,0.25); color: #fca5a5; }

/* Modal */
.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(8px);
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    animation: fadeIn 0.2s ease-out;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal-backdrop.hidden { display: none; }
.modal {
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: var(--radius-xl);
    padding: 1.75rem;
    width: 100%;
    max-width: 480px;
    box-shadow: var(--shadow-lg);
    animation: modalIn 0.2s var(--t-spring);
}
.modal-title { font-size: 1.0625rem; font-weight: 700; font-family: var(--font-display); margin-bottom: 1.25rem; }
.modal-footer { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border2); }

@keyframes modalIn {
    from { opacity: 0; transform: scale(0.95) translateY(8px); }
    to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* Fade in animation */
@keyframes fadeUp {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
}
.animate-fade-up { animation: fadeUp 0.35s cubic-bezier(0.16,1,0.3,1) forwards; }

/* Page header */
.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.75rem;
    flex-wrap: wrap;
}
.page-header h1 {
    font-size: 1.375rem;
    font-weight: 800;
    font-family: var(--font-display);
    letter-spacing: -0.02em;
    color: var(--text-primary);
    line-height: 1.2;
}
.page-header p { font-size: 0.875rem; color: var(--text-secondary); margin-top: 0.25rem; }

/* Audit log */
.log-item { display: flex; gap: 0.75rem; align-items: flex-start; padding: 0.75rem 0; border-bottom: 1px solid var(--border2); }
.log-item:last-child { border-bottom: none; }
.log-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--primary); margin-top: 0.35rem; flex-shrink: 0; }
.log-action { font-size: 0.8125rem; font-weight: 600; color: var(--text-primary); }
.log-meta { font-size: 0.6875rem; color: var(--text-muted); margin-top: 0.125rem; }

/* Responsive */
@media (max-width: 1024px) {
    .grid-4 { grid-template-columns: repeat(2,1fr); }
    .grid-3 { grid-template-columns: repeat(2,1fr); }
}

@media (max-width: 768px) {
    .sidebar { display: none; }
    .grid-4, .grid-3, .grid-2 { grid-template-columns: 1fr; }
    .page-content { padding: 1rem; }
}

/* ==========================================================
   EKATRA OS — Custom Themes & Accent Colors
   ========================================================== */

/* Color themes overrides */
body.color-indigo {
    --primary: #6366f1;
    --primary-hover: #4f46e5;
    --primary-glow: rgba(99,102,241,0.25);
    --primary-subtle: rgba(99,102,241,0.08);
}
body.color-sunset {
    --primary: #f97316;
    --primary-hover: #ea580c;
    --primary-glow: rgba(249,115,22,0.25);
    --primary-subtle: rgba(249,115,22,0.08);
}
body.color-emerald {
    --primary: #10b981;
    --primary-hover: #059669;
    --primary-glow: rgba(16,185,129,0.25);
    --primary-subtle: rgba(16,185,129,0.08);
}
body.color-amber {
    --primary: #f59e0b;
    --primary-hover: #d97706;
    --primary-glow: rgba(245,158,11,0.25);
    --primary-subtle: rgba(245,158,11,0.08);
}
body.color-rose {
    --primary: #f43f5e;
    --primary-hover: #e11d48;
    --primary-glow: rgba(244,63,94,0.25);
    --primary-subtle: rgba(244,63,94,0.08);
}

/* Typography Overrides */
h1, h2, h3, h4, h5, h6, .page-header h1, .modal-title, .card-title, .sidebar-brand-text h1 {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    letter-spacing: -0.015em;
}

.kicker, .card-kicker, .ek-kicker {
    font-size: 0.6875rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 0.25rem;
}

/*
   The active row is marked with an INSET SHADOW, not a left border.

   .nav-link carries `border: 1px solid transparent`, so a 3px
   border-left here made the active row 2px wider on its left than
   every other row — and since padding is measured from inside the
   border, its icon and label sat 2px right of the whole rest of the
   sidebar. Whichever module you were in was the one that did not
   line up with the others.

   An inset shadow paints in the same place and costs no layout.
*/
.nav-link.active {
    color: var(--color-accent-700);
    background: var(--color-accent-100);
    box-shadow: inset 3px 0 0 var(--color-accent);
    font-style: italic;
    font-weight: 600;
}

/* View themes overrides (Light Mode - Organic Warm Theme) */
body.theme-light {
    --bg: #f3f2f2;
    --surface: #ffffff;
    --surface2: #eae7e7;
    --border: #d7d3d3;
    --border2: rgba(32, 31, 29, 0.12);
    --text-primary: #201f1d;
    --text-secondary: #605d5d;
    --text-muted: #7d7979;
}
/* Ensure elements look correct in light mode */
body.theme-light .card {
    background: #ffffff;
    border-color: #d7d3d3;
    box-shadow: 0 1px 3px rgba(45,43,43,0.06);
}
body.theme-light .sidebar {
    background: #f8f4f4;
    border-right-color: #d7d3d3;
}
/* Light mode only tints the scrolled state — at rest the bar stays clear. */
body.theme-light .topbar.is-scrolled {
    border-bottom-color: #d7d3d3;
    box-shadow: 0 10px 24px -18px rgba(45, 43, 43, 0.35);
}
body.theme-light .topbar-breadcrumb .crumb {
    color: #605d5d;
}
body.theme-light .topbar-breadcrumb .current {
    color: #201f1d;
}
body.theme-light .sidebar-org {
    background: #eae7e7;
}
body.theme-light .nav-link:hover {
    background: #fff3e4;
    color: #7d5411;
}
body.theme-light .nav-link.active {
    background: #fff3e4;
    color: #7d5411;
    box-shadow: inset 3px 0 0 #b68235;
}
body.theme-light .nav-link.disabled {
    color: #bab6b6;
}
body.theme-light .sidebar-footer {
    background: #eae7e7;
    border-top-color: #d7d3d3;
}
body.theme-light .user-info .user-name {
    color: #201f1d;
}
body.theme-light .user-info .user-email {
    color: #605d5d;
}
body.theme-light .divider {
    background: #d7d3d3;
}
body.theme-light .empty-state h3 {
    color: #201f1d;
}
body.theme-light .empty-state p {
    color: #605d5d;
}
body.theme-light .form-input, 
body.theme-light .form-select, 
body.theme-light .form-textarea {
    background: #ffffff;
    border-color: #d7d3d3;
    color: #201f1d;
}
body.theme-light .form-input:focus, 
body.theme-light .form-select:focus, 
body.theme-light .form-textarea:focus {
    border-color: #b68235;
    box-shadow: 0 0 0 3px rgba(182, 130, 53, 0.2);
}
body.theme-light .badge-default {
    background: #eae7e7;
    color: #605d5d;
}
body.theme-light .table th {
    background: #eae7e7;
    color: #605d5d;
    border-bottom-color: #d7d3d3;
}
body.theme-light .table td {
    border-bottom-color: #d7d3d3;
}
body.theme-light .btn-secondary {
    background: #eae7e7;
    color: #444141;
    border-color: #d7d3d3;
}
body.theme-light .btn-secondary:hover {
    background: #d7d3d3;
}
body.theme-light .modal {
    background: #ffffff;
    border-color: #d7d3d3;
}


/* ==========================================================
   EKATRA OS — Shared Component Layer
   ----------------------------------------------------------
   Every component below was, before this section existed,
   redefined inside individual views: `.btn-outline` had ELEVEN
   definitions across Commerce, Inventory and the eight ERP
   screens, `.tab-btn` had two, and `.table` was used by the
   Core dashboard while having no rule at all outside light
   mode — so those tables rendered unstyled in the default
   dark theme.

   The rule from here on is: a component that more than one
   screen uses lives here, in app tokens, once. A view's own
   <style> block is for what is genuinely unique to that
   screen. Anything a view redefines here is a divergence
   waiting to happen, which is how ERP drifted into looking
   like a different product.
   ========================================================== */

/* ── Buttons: the outline variant ─────────────────────────
   Distinct from .btn-ghost (which has no border). Views were
   reaching for this name long before it existed globally. */
.btn-outline {
    background: transparent;
    border: 1px solid var(--border2);
    color: var(--text-secondary);
}
.btn-outline:hover {
    border-color: var(--primary);
    color: var(--text-primary);
    background: var(--primary-subtle);
}

/* ── Tables ───────────────────────────────────────────────
   Data tables are the single most common thing an ERP or
   bookkeeping screen renders, so they get real styling rather
   than being hand-rolled with inline border-collapse each
   time. Wrap a wide table in .table-wrap so it scrolls inside
   its own box instead of pushing the page sideways. */
.table-wrap {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8125rem;
    font-family: var(--font-sans);
}
.table th {
    text-align: left;
    padding: 0.625rem 0.875rem;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border2);
    white-space: nowrap;
}
.table td {
    padding: 0.6875rem 0.875rem;
    border-bottom: 1px solid var(--border2);
    color: var(--text-secondary);
    vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr { transition: var(--t-fast); }
.table tbody tr:hover { background: rgba(255,255,255,0.025); }
/* Money and quantities read wrong when they are not aligned on
   the decimal point, so numeric columns are right-aligned and
   given tabular figures rather than left-aligned like text. */
.table .num, .table td.num, .table th.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
}

/* ── Tabs ─────────────────────────────────────────────────
   The pill form used by Inventory. Bookkeeping uses an
   underline form built inline in _tabs.php; both are kept
   because they mark different things — pills switch a panel
   in place, the underline navigates between screens. */
.tabs-nav {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.tab-btn {
    background: transparent;
    border: none;
    padding: 0.625rem 1.25rem;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: 0.875rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    transition: var(--t-fast);
}
.tab-btn:hover { background: var(--surface2); color: var(--text-primary); }
.tab-btn.active { background: var(--primary); color: #fff; }

/* ── Filter bar ───────────────────────────────────────────
   The row of search boxes and selects that sits above almost
   every list in the product. */
.filter-bar {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}
.filter-bar .form-input,
.filter-bar .form-select,
.filter-bar .input,
.filter-bar .select { width: auto; min-width: 200px; }
.filter-bar .grow { flex: 1; min-width: 220px; }

/* ── Form layout helpers ──────────────────────────────────
   .form-field already styles a labelled control; these are
   the layout around it. `.form-group` is the alias the ERP
   screens use for the same thing. */
.form-group { margin-bottom: 1.125rem; }
.form-group > label,
.form-field > label {
    display: block;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-secondary);
    margin-bottom: 0.4rem;
}
.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 1rem;
}
/* Bare `.input` — the short name several screens use for the
   same control that `.form-input` styles. */
.input, .select, .textarea {
    width: 100%;
    padding: 0.5625rem 0.875rem;
    background: rgba(0,0,0,0.3);
    border: 1px solid var(--border2);
    border-radius: var(--radius-md);
    color: var(--text-primary);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    transition: var(--t-fast);
}
.input::placeholder, .textarea::placeholder { color: var(--text-muted); }
.input:focus, .select:focus, .textarea:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-glow);
    background: rgba(0,0,0,0.4);
}
.select { cursor: pointer; }
.select option { background: var(--surface2); }
.textarea { resize: vertical; min-height: 80px; }

/* ── Small text ───────────────────────────────────────────
   `.hint` is the explanatory line under a control; `.empty`
   is the short "nothing here yet" line inside a card, as
   distinct from the full `.empty-state` block. */
.hint { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.35rem; line-height: 1.5; }
.empty { padding: 2rem 1rem; text-align: center; color: var(--text-muted); font-size: 0.875rem; }

/* ── Status badges ────────────────────────────────────────
   Named for the state rather than the colour, so a screen
   says what a row IS instead of picking a colour itself. */
.badge-active   { background: rgba(16,185,129,0.12);  color: #34d399; border-color: rgba(16,185,129,0.25); }
.badge-inactive { background: rgba(255,255,255,0.06); color: var(--text-muted); border-color: rgba(255,255,255,0.1); }
.badge-info     { background: rgba(59,130,246,0.12);  color: #93c5fd; border-color: rgba(59,130,246,0.25); }

/* ── Modal aliases ────────────────────────────────────────
   `.modal-backdrop` and `.modal-footer` are the canonical
   names; these are the synonyms the ERP screens grew, kept so
   their markup does not all have to be rewritten at once. */
/* Unlike .modal-backdrop, which is shown by default and hidden with
   .hidden, .modal-overlay is hidden by default and shown with .active.
   Both idioms are in the codebase and their JS differs, so the two
   names keep their own open/shut convention rather than one silently
   inverting the other. */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(8px);
    animation: fadeIn 0.2s ease-out;
    z-index: 100;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
.modal-overlay.active { display: flex; }
.modal-overlay.hidden { display: none; }
.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border2);
}
/* A modal is as wide as its content needs; the size is named in the
   markup rather than each screen redefining .modal itself. */
.modal-sm { max-width: 420px; }
.modal-md { max-width: 600px; }
/* ── The framed modal ─────────────────────────────────────
   .modal is a single padded box. .modal-container is the
   framed form: a titled header with a close button, a body
   that scrolls, and a footer that stays put. User Manager
   grew it and it was the best of the three modal systems in
   the codebase, so it moved here instead of being a third
   private one. Both sit inside .modal-overlay. */
.modal-container {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    width: 100%;
    max-width: 560px;
    box-shadow: var(--shadow-lg);
    animation: slideUp 0.25s ease;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
}
.modal-header {
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.modal-header h3 {
    margin: 0;
    font-size: 1.125rem;
    font-weight: 700;
    font-family: var(--font-display);
    color: var(--text-primary);
}
.modal-close {
    background: none;
    border: none;
    font-size: 1.5rem;
    color: var(--text-secondary);
    cursor: pointer;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    transition: var(--t-fast);
}
.modal-close:hover { background: var(--surface2); color: var(--text-primary); }
.modal-body { overflow-y: auto; flex: 1; }
@keyframes slideUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
.modal-lg { max-width: 760px; }
.modal-xl { max-width: 1040px; }
/* Tall modals scroll inside themselves rather than off the viewport. */
.modal { max-height: 88vh; overflow-y: auto; }

/* ── Light theme counterparts ─────────────────────────────
   Anything above that assumes a dark surface has to be
   answered here, or light mode gets black on black. */
body.theme-light .btn-outline { border-color: var(--border); color: var(--text-secondary); }
body.theme-light .table tbody tr:hover { background: rgba(0,0,0,0.03); }
body.theme-light .table th,
body.theme-light .table td { border-bottom-color: var(--border); }
body.theme-light .tab-btn:hover { background: var(--surface2); }
body.theme-light .input,
body.theme-light .select,
body.theme-light .textarea {
    background: #ffffff;
    border-color: var(--border);
    color: var(--text-primary);
}
body.theme-light .input:focus,
body.theme-light .select:focus,
body.theme-light .textarea:focus { background: #ffffff; }
body.theme-light .badge-inactive { background: rgba(0,0,0,0.05); border-color: var(--border); }
body.theme-light .modal-overlay { background: rgba(40,38,36,0.45); }
body.theme-light .modal-actions { border-top-color: var(--border); }

/* ==========================================================
   EKATRA OS — Collapsible Sidebar
   ----------------------------------------------------------
   The sidebar collapses to an icon rail rather than vanishing.
   That distinction matters: a hidden sidebar means the user
   has to remember what is in it and click twice to get back,
   whereas a rail keeps every destination one click away and
   just stops spending 240px on the words.

   The collapsed state lives on .app-shell (not .sidebar), so
   .main-area and anything else can respond to it too, and is
   persisted in localStorage by layout_default.php — it is a
   viewport preference, not an account setting, because the
   same person wants it open on a desktop and shut on a
   laptop.
   ========================================================== */

:root { --sidebar-w-rail: 68px; }

.sidebar { transition: width var(--t-smooth); }

/* ── Sub-screen rows ──────────────────────────────────────
   A module's children (Manufacturing's BOMs, Work Orders and
   the rest) are indented and set a step smaller than their
   parent, so the list reads as a hierarchy rather than as
   thirteen equal destinations. This was an inline style on the
   anchor until the rail needed to override it. */
.nav-link.nav-child {
    /*
       A child row is padded exactly like its parent, so EVERY icon in
       the sidebar — Manufacturing and each of its screens alike —
       sits on one vertical line.

       The indent that used to be here put the child icons a slot and
       a gap to the right of their parent's. That is the textbook
       tree indent and it is wrong for this sidebar: the icons are
       what the eye scans down, so indenting them leaves a ragged
       column and the section stops reading as one list.

       What marks a row as a sub-screen is the hairline down the left
       of the group — and only that. Muting was tried and removed: it
       made these read as unavailable rather than as subordinate.
    */
    padding-left: var(--nav-pad-x);
    font-size: 0.8125rem;
    /* Recolours the 1px transparent border .nav-link already reserves,
       so the group gets a continuous hairline down its left edge and
       nothing moves by even a pixel. */
    border-left-color: var(--border2);
}
/*
   A child icon is drawn exactly like its parent's: same size, same
   slot, same colour. It carried opacity: 0.8 for a while, on a row
   that was itself at 0.82 — the two multiplied to 0.656 and the
   sub-screens came out visibly darker than the modules above them,
   which reads as disabled rather than as subordinate.

   Nothing about a nav icon says "child" any more. The hairline down
   the left of the group does that, and it costs no contrast.
*/
/* The fallback for a registry child with no icon of its own. */
.nav-child-dot { font-size: 0.3125rem !important; opacity: 0.5 !important; }
/* ── Collapsed ──────────────────────────────────────────── */
.app-shell.rail .sidebar { width: var(--sidebar-w-rail); }

/* Everything that is words rather than an icon goes away. The
   labels are hidden with display:none rather than opacity so
   they cannot be tabbed to or read aloud while invisible. */
.app-shell.rail .sidebar-brand-title,
.app-shell.rail .sidebar-brand-sub,
.app-shell.rail .sidebar-org,
.app-shell.rail .nav-link span,
.app-shell.rail .user-info,
.app-shell.rail .nav-link .soon-tag { display: none; }

/* A section heading with no room for its text becomes a rule,
   so the grouping survives even when the words do not. */
.app-shell.rail .nav-section-label {
    font-size: 0;
    padding: 0.5rem 0.75rem 0.25rem;
    border-top: 1px solid var(--border2);
    margin-top: 0.5rem;
}

.app-shell.rail .sidebar-brand {
    justify-content: center;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}
.app-shell.rail .nav-link {
    justify-content: center;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}
/* Keep a fixed slot on the rail as well: with width:auto a wide
   glyph is wider than a narrow one, and centring a variable box
   puts each icon on a slightly different vertical line. */
.app-shell.rail .nav-link i {
    width: 1.5rem;
    min-width: 1.5rem;
    font-size: 1rem;
    text-align: center;
}

/* Sub-screens were hidden on the rail while they had no icon —
   an iconless row there is a blank strip you can still click.
   They carry one now, so they stay, indented back to centre and
   drawn a shade smaller than their parent so the rail still
   shows which rows are children. */
/* Children are padded and coloured like every other row, so the rail
   has nothing left to correct. Dimming them here would be worse than
   in the open sidebar: with no labels, a faded icon is the only cue
   the product has for "unavailable", and these are available. */

.app-shell.rail .sidebar-footer {
    justify-content: center;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}
.app-shell.rail .btn-logout { display: none; }

/* A 3px stripe down the left of a 68px rail reads as off-centre,
   so the rail keeps the active background and drops the stripe. */
.app-shell.rail .nav-link.active {
    box-shadow: none;
    font-style: normal;
}

/* ── Rail tooltips ──────────────────────────────────────────
   With the labels gone the icons need names. Built from the
   data-label attribute rather than the browser's own `title`
   delay, which is too slow to scan a menu with. */
.app-shell.rail .nav-link[data-label] { position: relative; }
.app-shell.rail .nav-link[data-label]::after {
    content: attr(data-label);
    position: absolute;
    left: calc(100% + 10px);
    top: 50%;
    transform: translateY(-50%);
    background: var(--surface2);
    color: var(--text-primary);
    border: 1px solid var(--border2);
    border-radius: var(--radius-md);
    padding: 0.3rem 0.6rem;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    box-shadow: var(--shadow-md);
    transition: opacity var(--t-fast);
    z-index: 60;
}
.app-shell.rail .nav-link[data-label]:hover::after { opacity: 1; }

/* ── Hover to reveal ──────────────────────────────────────
   The rail is the resting state: 68px of icons. Hovering it
   floats the full sidebar out over the page, and taking the
   cursor away folds it back — so the navigation costs no
   permanent width and no click.

   It FLOATS: the sidebar is taken out of the flow and
   .main-area keeps a 68px gutter, because widening a flex item
   on hover would reflow every screen under the cursor — tables
   reparagraphing as you brush past the edge is worse than the
   240px it was trying to save.

   The open state is still a click away (the toggle pins it,
   Ctrl+B), which is also the way in on a touch device: a tablet
   has no hover, and hover-only navigation there would be a rail
   that never opens.
   ========================================================== */
@media (min-width: 769px) {
    .app-shell { position: relative; }

    .app-shell.rail .sidebar {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 0;
        z-index: 70;
        width: var(--sidebar-w-rail);
        transition: width 0.18s ease, box-shadow 0.18s ease;
    }
    .app-shell.rail .main-area { margin-left: var(--sidebar-w-rail); }

    /*
     * Text clips while the sidebar is growing, it does not re-wrap.
     *
     * The labels come back the instant the pointer arrives, but the width
     * takes 180ms to follow — so for those 180ms the full "Ekatra OS —
     * Merchant & Logistics" and "Active Organization" were laying themselves
     * out inside 68px, wrapping to four lines each and then snapping back.
     * Nowrap plus the sidebar's own overflow turns that into what it should
     * be: the words sliding into view from behind the edge.
     */
    .app-shell.rail .sidebar { overflow-x: hidden; }
    .app-shell.rail .sidebar .sidebar-brand-title,
    .app-shell.rail .sidebar .sidebar-brand-sub,
    .app-shell.rail .sidebar .sidebar-org-label,
    .app-shell.rail .sidebar .sidebar-org-name,
    .app-shell.rail .sidebar .sidebar-org-loc,
    .app-shell.rail .sidebar .nav-section-label,
    .app-shell.rail .sidebar .user-info { white-space: nowrap; }
}

@media (min-width: 769px) and (hover: hover) and (pointer: fine) {
    /* Keyboard focus opens it too, so tabbing through the links works.
       NOT :focus-within: a mouse click focuses the link it lands on, and
       the sidebar then stayed open with the pointer gone until the next
       page painted - seconds on a slow server, and indefinitely when the
       back button restored the page from bfcache with the link focused.
       :focus-visible matches keyboard focus only. */
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) {
        width: var(--sidebar-w);
        box-shadow: var(--shadow-lg);
        transition-delay: 0.12s;   /* brushing past the edge should not open it */
    }

    /* Everything the rail hid comes back while it is open. */
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .sidebar-brand-title,
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .sidebar-brand-sub,
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .sidebar-org,
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .user-info { display: block; }
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .nav-link span,
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .nav-link .soon-tag { display: inline; }
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .btn-logout { display: inline-flex; }

    /* Rows, icons and headings return to their open geometry. */
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .nav-link {
        justify-content: flex-start;
        padding-left: var(--nav-pad-x);
        padding-right: var(--nav-pad-x);
    }
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .nav-link i {
        width: var(--nav-icon-slot);
        min-width: var(--nav-icon-slot);
        font-size: 0.875rem;
    }
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .nav-section-label {
        font-size: 0.625rem;
        border-top: none;
        margin-top: 0;
        padding: 0.875rem 0.75rem 0.375rem;
    }
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .sidebar-footer {
        justify-content: flex-start;
        padding-left: 1rem;
        padding-right: 1rem;
    }
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .nav-link.active {
        box-shadow: inset 3px 0 0 var(--color-accent);
    }

    /* The rail's tooltips exist because the labels are gone; with the
       sidebar open they would repeat each label an inch to its right. */
    .app-shell.rail .sidebar:is(:hover, :has(:focus-visible)) .nav-link[data-label]::after { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .app-shell.rail .sidebar { transition: none; }
}

/* Breadcrumb links in the topbar are navigation now, not decoration. */
.topbar-breadcrumb a.crumb { transition: var(--t-fast); }
.topbar-breadcrumb a.crumb:hover { color: var(--text-primary); }

/* ==========================================================
   EKATRA OS — Scrollbars
   ----------------------------------------------------------
   There, but not painted. Windows draws a wide light-grey bar
   that is the brightest thing on a dark screen, and it landed
   down the middle of the page the moment the sidebar floated
   open over it.

   The track keeps its width rather than being removed, so
   nothing on the page shifts sideways when a scrollbar appears
   or a thumb fades in. The thumb is transparent until the
   pointer is inside the thing that scrolls, and then it is only
   a hairline in the border colour — enough to say how far down
   the page you are.

   The sidebar and the phone menu show none at all: both are
   short lists that the pointer is always inside, so a bar there
   is permanent decoration.
   ========================================================== */
* { scrollbar-width: thin; scrollbar-color: transparent transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-corner { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 999px;
    /* Inset by a transparent border so the thumb reads as a hairline
       rather than filling the full ten pixels. */
    border: 3px solid transparent;
    background-clip: padding-box;
    transition: background-color var(--t-smooth);
}

.page-content:hover,
.table-wrap:hover,
.modal:hover,
.cmdk-results:hover,
.modal-backdrop:hover { scrollbar-color: var(--border) transparent; }

.page-content:hover::-webkit-scrollbar-thumb,
.table-wrap:hover::-webkit-scrollbar-thumb,
.modal:hover::-webkit-scrollbar-thumb,
.cmdk-results:hover::-webkit-scrollbar-thumb,
.modal-backdrop:hover::-webkit-scrollbar-thumb {
    background: var(--border);
    background-clip: padding-box;
}
.page-content::-webkit-scrollbar-thumb:hover,
.table-wrap::-webkit-scrollbar-thumb:hover,
.cmdk-results::-webkit-scrollbar-thumb:hover { background: var(--text-muted); background-clip: padding-box; }

.sidebar,
.mnav { scrollbar-width: none; }
.sidebar::-webkit-scrollbar,
.mnav::-webkit-scrollbar { width: 0; height: 0; }

/* ==========================================================
   EKATRA OS — Topbar controls, mobile menu, command palette
   ----------------------------------------------------------
   Motion here follows three rules: things arrive by fading and
   rising a few pixels (never sliding across the screen), leave
   faster than they arrive, and do nothing at all under
   prefers-reduced-motion. Open/closed is a class, and the
   closed state is visibility:hidden rather than display:none,
   so a panel can animate out as well as in while still leaving
   the tab order and the accessibility tree when shut.
   ========================================================== */

/* The page title eases in on arrival instead of snapping. */
.topbar-breadcrumb .current { animation: fadeIn 0.35s ease-out; }

/* ── Search trigger ─────────────────────────────────────── */
.topbar-search {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    height: 2.125rem;
    min-width: 15rem;
    padding: 0 0.4375rem 0 0.875rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--border2);
    background: var(--surface2);
    background: color-mix(in srgb, var(--surface2) 55%, transparent);
    color: var(--text-muted);
    font-size: 0.8125rem;
    cursor: pointer;
    transition: border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}
.topbar-search i { color: var(--primary); font-size: 0.8125rem; }
.topbar-search-label { flex: 1; text-align: left; white-space: nowrap; }
.topbar-search:hover { color: var(--text-primary); border-color: var(--text-muted); }
.topbar-search:focus-visible {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-glow);
}

.topbar-kbd {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    padding: 0.0625rem 0.375rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border2);
    background: var(--surface);
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.625rem;
    line-height: 1.5;
}

/* ── Menu button — phones only ──────────────────────────── */
.topbar-menu-btn {
    display: none;
    align-items: center;
    gap: 0.375rem;
    height: 2rem;
    padding: 0 0.875rem;
    border: none;
    border-radius: var(--radius-full);
    background: var(--primary);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
    cursor: pointer;
    flex-shrink: 0;
    transition: opacity 0.2s ease, transform 0.15s ease;
}
.topbar-menu-btn:hover { opacity: 0.9; }
.topbar-menu-btn:active { transform: scale(0.97); }
.topbar-menu-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ── Page-load bar ──────────────────────────────────────── */
.topbar-progress {
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    overflow: hidden;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
}
.topbar-progress::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 35%;
    background: linear-gradient(90deg, transparent, var(--primary), transparent);
    animation: topbarProgress 1.1s cubic-bezier(0.4, 0, 0.2, 1) infinite;
    animation-play-state: paused;
}
.topbar.is-loading .topbar-progress { opacity: 1; }
.topbar.is-loading .topbar-progress::before { animation-play-state: running; }
@keyframes topbarProgress {
    from { transform: translateX(-100%); }
    to   { transform: translateX(300%); }
}

/* ── Page arrival ───────────────────────────────────────────
   No fill-mode on purpose. A transform left in place after the
   animation — even translateY(0) — makes .page-content the
   containing block for every position:fixed modal inside it,
   and they would open pinned to the content area instead of
   the viewport. Without fill-mode the transform is gone the
   moment the 0.45s is up. */
.page-content { animation: pageIn 0.45s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes pageIn {
    from { opacity: 0; transform: translateY(8px); }
}

/* ── Mobile menu (≤768px; see the media block below) ────── */
.mnav,
.mnav-backdrop { display: none; }

/* ── Command palette ────────────────────────────────────── */
.cmdk {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 12vh 1rem 1rem;
    visibility: hidden;
    pointer-events: none;
    transition: visibility 0s linear 0.2s;
}
.cmdk.is-open {
    visibility: visible;
    pointer-events: auto;
    transition-delay: 0s;
}
.cmdk-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 0;
    transition: opacity 0.2s ease;
}
.cmdk.is-open .cmdk-backdrop { opacity: 1; }
.cmdk-panel {
    position: relative;
    width: 100%;
    max-width: 36rem;
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: 0.875rem;
    box-shadow: 0 24px 64px -16px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.03);
    overflow: hidden;
    opacity: 0;
    transform: translateY(-6px) scale(0.96);
    transition: opacity 0.15s ease, transform 0.15s ease;
}
.cmdk.is-open .cmdk-panel {
    opacity: 1;
    transform: none;
    transition: opacity 0.2s ease, transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}
.cmdk-head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--border2);
}
.cmdk-head > i { color: var(--primary); font-size: 0.9375rem; }
.cmdk-head input {
    flex: 1;
    min-width: 0;
    background: transparent;
    border: none;
    outline: none;
    color: var(--text-primary);
    font-size: 0.9375rem;
    font-weight: 500;
}
.cmdk-head input::placeholder { color: var(--text-muted); }
.cmdk-esc { cursor: pointer; }
.cmdk-results {
    max-height: min(22rem, 55vh);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 0.375rem;
}
.cmdk-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.625rem;
    border-radius: 0.625rem;
    color: var(--text-secondary);
    transition: background-color 0.12s ease, color 0.12s ease;
}
.cmdk-item.is-active { background: var(--border2); color: var(--text-primary); }
.cmdk-icon {
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: var(--primary-subtle);
    color: var(--primary);
    font-size: 0.75rem;
    transition: transform 0.15s ease;
}
.cmdk-item.is-active .cmdk-icon { transform: scale(1.05); }
.cmdk-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.cmdk-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.cmdk-cat { font-size: 0.6875rem; color: var(--text-muted); }
.cmdk-go {
    font-size: 0.6875rem;
    color: var(--text-muted);
    opacity: 0;
    transform: translateX(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease;
}
.cmdk-item.is-active .cmdk-go { opacity: 1; transform: none; }
.cmdk-empty { padding: 1.75rem 1rem; text-align: center; font-size: 0.8125rem; color: var(--text-muted); }
.cmdk-foot {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.5rem 1rem;
    border-top: 1px solid var(--border2);
    font-size: 0.6875rem;
    color: var(--text-muted);
}
.cmdk-foot span { display: inline-flex; align-items: center; gap: 0.25rem; }

body.theme-light .cmdk-backdrop { background: rgba(40, 38, 36, 0.35); }
body.theme-light .cmdk-panel { box-shadow: 0 24px 64px -16px rgba(45, 43, 43, 0.35); }

/* ── Phones: the floating pill and its menu ─────────────────
   Below 768px the sidebar is gone (see the Responsive block),
   so the topbar becomes the navigation: a pill floating over
   the top of the page, holding the back arrow, the page title,
   search and a Menu button that opens the full navigation. The
   pill sits above the menu's backdrop so its Close button stays
   reachable while the menu is open. */
@media (max-width: 768px) {
    .topbar,
    .topbar.is-scrolled {
        position: fixed;
        top: calc(0.75rem + var(--admin-bar-h, 0px));
        left: 50%;
        transform: translateX(-50%);
        width: min(94vw, 30rem);
        height: auto;
        padding: 0.375rem 0.375rem 0.375rem 0.5rem;
        gap: 0.5rem;
        border-radius: var(--radius-full);
        border: 1px solid var(--border2);
        background: var(--surface);
        background: color-mix(in srgb, var(--surface) 88%, transparent);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        box-shadow: 0 12px 32px -14px rgba(0, 0, 0, 0.55);
        z-index: 60;
    }
    .topbar-breadcrumb { flex: 1; min-width: 0; }
    .topbar-breadcrumb .current {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .topbar-back { border-radius: var(--radius-full); }
    .topbar-actions { gap: 0.25rem; }
    .topbar-search {
        min-width: 0;
        width: 2rem;
        height: 2rem;
        padding: 0;
        justify-content: center;
    }
    .topbar-search-label,
    .topbar-search .topbar-kbd { display: none; }
    /* The bell has no panel behind it yet; on a pill every slot counts.
       Buttons a view injects here (Inventory's barcode tools) carry an id
       and stay. */
    .topbar-actions > .notif-btn:not([id]) { display: none; }
    .topbar-menu-btn { display: inline-flex; }
    .topbar-progress { left: 1.25rem; right: 1.25rem; bottom: 0; }

    .page-content { padding-top: calc(4.75rem + var(--admin-bar-h, 0px)); }
    body.mnav-open .page-content { overflow: hidden; }

    .mnav-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 55;
        background: rgba(0, 0, 0, 0.6);
        backdrop-filter: blur(4px);
        -webkit-backdrop-filter: blur(4px);
        opacity: 0;
        visibility: hidden;
        transition: opacity 0.2s ease, visibility 0s linear 0.2s;
    }
    .mnav-backdrop.is-open {
        opacity: 1;
        visibility: visible;
        transition: opacity 0.25s ease, visibility 0s;
    }

    .mnav {
        display: flex;
        flex-direction: column;
        position: fixed;
        z-index: 58;
        top: calc(4.25rem + var(--admin-bar-h, 0px));
        left: 50%;
        width: min(94vw, 30rem);
        max-height: calc(100dvh - 5.25rem - var(--admin-bar-h, 0px));
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: 0.5rem;
        border-radius: 1.5rem;
        background: var(--surface);
        border: 1px solid var(--border2);
        box-shadow: 0 24px 48px -16px rgba(0, 0, 0, 0.6);
        opacity: 0;
        visibility: hidden;
        transform: translate(-50%, -8px) scale(0.98);
        transform-origin: top center;
        transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0.15s;
    }
    .mnav.is-open {
        opacity: 1;
        visibility: visible;
        transform: translate(-50%, 0) scale(1);
        transition: opacity 0.22s ease, transform 0.28s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
    }
    .mnav .sidebar-nav { padding: 0.25rem 0; }
    .mnav .nav-section-label { padding: 0.75rem 0.75rem 0.25rem; }
    /* 44px rows: a thumb on a low-end phone, not a mouse (charter #4). */
    .mnav .nav-link { min-height: 44px; }
    .mnav-org {
        padding: 0.75rem;
        margin-bottom: 0.25rem;
        border-radius: 1rem;
        background: var(--surface2);
    }
    .mnav-foot {
        display: flex;
        align-items: center;
        gap: 0.625rem;
        margin-top: 0.25rem;
        padding: 0.75rem 0.5rem 0.25rem;
        border-top: 1px solid var(--border2);
    }

    .cmdk { padding-top: calc(4.75rem + var(--admin-bar-h, 0px)); }
    .cmdk-foot { display: none; }
}

body.theme-light .mnav-backdrop { background: rgba(40, 38, 36, 0.35); }
body.theme-light .mnav { box-shadow: 0 24px 48px -16px rgba(45, 43, 43, 0.3); }

@media (prefers-reduced-motion: reduce) {
    .topbar,
    .topbar-search,
    .topbar-menu-btn,
    .mnav,
    .mnav-backdrop,
    .cmdk,
    .cmdk-backdrop,
    .cmdk-panel,
    .cmdk-item,
    .cmdk-icon,
    .cmdk-go { transition: none !important; }
    .page-content,
    .topbar-breadcrumb .current { animation: none; }
    /* Still say "loading" — just without the sweep. */
    .topbar-progress::before { animation: none; width: 100%; opacity: 0.6; }
}

/* Page-width wrapper used by the wider screens. The page-content area
   already supplies the padding, so this only caps the line length. */
.container { max-width: 1400px; margin: 0 auto; }

/* ── Bare form controls inside a page ─────────────────────
   Several screens (the ERP ones especially) write plain
   <select> and <input> without a class and relied on their own
   `select, input { … }` rule to make them look like part of the
   product. Those rules were global inside a standalone document
   but would reach the app shell here, so the styling moves to
   one place, scoped to the page body.

   Checkboxes, radios, ranges, files and colour swatches are
   excluded — they are native controls whose appearance is not a
   text box, and padding them wrecks them. A control that wants
   the plain browser look opts out with .native. */
.page-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="submit"]):not(.native),
.page-content select:not(.native),
.page-content textarea:not(.native) {
    padding: 0.45rem 0.75rem;
    background: rgba(0,0,0,0.3);
    border: 1px solid var(--border2);
    border-radius: var(--radius-md);
    color: var(--text-primary);
    font-family: var(--font-sans);
    font-size: 0.85rem;
    transition: var(--t-fast);
}
.page-content input:not(.native):focus,
.page-content select:not(.native):focus,
.page-content textarea:not(.native):focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-glow);
}
.page-content input::placeholder,
.page-content textarea::placeholder { color: var(--text-muted); }
.page-content select:not(.native) option { background: var(--surface2); }
.page-content input[type="checkbox"],
.page-content input[type="radio"] { accent-color: var(--primary); }

body.theme-light .page-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="submit"]):not(.native),
body.theme-light .page-content select:not(.native),
body.theme-light .page-content textarea:not(.native) {
    background: #ffffff;
    border-color: var(--border);
}

/* ── Card padding ─────────────────────────────────────────
   .card carried no padding of its own, so every screen that
   used one added `style="padding: 1.25rem"` by hand — Inventory
   does it four times, and the ERP screens each restyled .card
   outright to get it. The padding belongs on the component.

   A card that contains a .card-body keeps padding zero, because
   .card-body supplies its own and the two would stack. */
.card { padding: 1.25rem; }
.card:has(> .card-body) { padding: 0; }
.card > h2, .card > h3 {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: 0.875rem;
}

/* The amber "needs attention but is not an error" button. Named
   for the same states as --warning and .badge-warning. */
.btn-warning {
    background: var(--warning);
    color: #1f1300;
    border: 1px solid rgba(255,255,255,0.15);
}
.btn-warning:hover { background: #d97706; color: #fff; }

/* ==========================================================
   EKATRA OS — .form-control
   ----------------------------------------------------------
   `.form-control` is the class the login screen and the whole
   platform console write on every input, select and textarea.
   It had NO rule anywhere in this file. Those controls were
   therefore rendering as the browser's own widgets — a white
   box with a grey bevel — sitting inside a dark glass card,
   which is exactly why the console read as a different product
   from the rest of the OS.

   Defined here rather than aliased onto `.input` because this
   is the shape the whole product is moving to: transparent
   rather than a black well, a hairline border, and a 10px
   radius. The older `.input` / `.form-input` rules stay as they
   are — the screens using them are not being restyled in this
   pass, and changing them under those screens would be a
   redesign nobody asked for.

   Everything is expressed in tokens, so the accent the user
   picked in Settings drives the focus ring and both themes get
   the same control with a different ground. Nothing here names
   a colour that is not a variable, except the select chevron —
   see below.
   ========================================================== */

.form-control {
    width: 100%;
    padding: 0.625rem 0.875rem;
    background: transparent;
    border: 1px solid var(--border2);
    border-radius: 10px;
    color: var(--text-primary);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    line-height: 1.45;
    transition: border-color var(--t-fast), box-shadow var(--t-fast), background var(--t-fast);
}

.form-control::placeholder { color: var(--text-muted); opacity: 0.75; }

.form-control:hover:not(:focus):not(:disabled) { border-color: var(--border); }

.form-control:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-glow);
    background: var(--surface2);
}

.form-control:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* A select keeps its own chevron unless the appearance is reset,
   and the native one is drawn in the OS accent on a white plate
   — the single most out-of-place thing on the console. Replaced
   with a chevron drawn in a mid grey that reads on both grounds,
   because a data: URI cannot reference currentColor. */
select.form-control {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 2.25rem;
    cursor: pointer;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238a8a8a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 15px 15px;
}
/* The list itself is drawn by the OS, so it needs a real
   background — a transparent one renders white-on-white. */
select.form-control option { background: var(--surface2); color: var(--text-primary); }

textarea.form-control { resize: vertical; min-height: 84px; }

/* A file input is two controls in one: the button and the
   filename beside it. Left with the default padding the button
   sits hard against the border, so the shell loses its padding
   and the button carries its own. */
input[type="file"].form-control {
    padding: 0.375rem 0.5rem;
    cursor: pointer;
    font-size: 0.8125rem;
    color: var(--text-secondary);
}
input[type="file"].form-control::file-selector-button {
    font-family: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    margin-right: 0.75rem;
    padding: 0.4rem 0.85rem;
    border: 1px solid var(--border2);
    border-radius: 7px;
    background: var(--surface2);
    color: var(--text-primary);
    cursor: pointer;
    transition: var(--t-fast);
}
input[type="file"].form-control::file-selector-button:hover {
    border-color: var(--primary);
    color: var(--primary);
}

input[type="checkbox"].form-control,
input[type="radio"].form-control { width: auto; accent-color: var(--primary); }

/* Light theme. Transparent over a white card is already white,
   so only the border and the focus ground need answering — the
   dark theme's --border2 is a white wash that vanishes here. */
body.theme-light .form-control { border-color: var(--border); }
body.theme-light .form-control:hover:not(:focus):not(:disabled) { border-color: #b9b3b3; }
body.theme-light .form-control:focus { background: #ffffff; border-color: var(--primary); }
body.theme-light input[type="file"].form-control::file-selector-button { border-color: var(--border); }

/* ── Centred single-column pages ──────────────────────────
   layout_public.php carried `min-h-screen flex flex-col
   justify-center items-center` on its <body> — Tailwind class
   names, in a project that has never had Tailwind. Not one of
   them matched a rule, so the sign-in card was not centred at
   all: it sat top-left, at whatever width its own max-width
   gave it, which is the off-centre login the design brief is
   about. This is the rule those class names were promising. */
body.page-centered {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
}
