/* ═══ Self-hosted fonts ═══
   The work order forbids a runtime Google Fonts call, so both faces are served
   from /fonts. Inter only — one variable file covers every weight.
   font-display:swap keeps text readable while it loads. */
/* Inter, self-hosted. Manrope was tried per the work order's typography spec
   and rejected on sight — Inter is what the portal used before and what reads
   right here. Still no CDN call: this is the same variable file Google Fonts
   would have served, served from /fonts instead. */
@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-var.woff2') format('woff2-variations'),
         url('/fonts/inter-var.woff2') format('woff2');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

/* ACSPL Operations Platform — World-Class SaaS Theme */

/* Design tokens now live in /css/tokens.css — loaded before this file. */

* { margin: 0; padding: 0; box-sizing: border-box; }
body {
    font-family: var(--font);
    background: var(--bg);
    color: var(--text);
    font-size: 14px;
    line-height: 1.5;
    /* Restored to the original rendering at Sanjay's request. Removing this
       does make macOS render Inter heavier (it only affects WebKit; Windows
       ignores it), but the original look is what he wants back. */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
a { text-decoration: none; color: inherit; }

/* ═══ MICRO-ANIMATIONS ═══ */
@keyframes slideInUp {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}
@keyframes scaleIn {
    from { opacity: 0; transform: scale(0.95); }
    to { opacity: 1; transform: scale(1); }
}
@keyframes shimmer {
    0% { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes spin {
    to { transform: rotate(360deg); }
}
@keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-4px); }
    75% { transform: translateX(4px); }
}

/* ═══ LAYOUT ═══ */
.app { display: flex; min-height: 100vh; }

/* ── Sidebar ── */
.side {
    width: var(--sidebar-collapsed);
    background: linear-gradient(180deg, #223066 0%, #141C40 100%);   /* brand navy, was the old purple */
    /* The rail has no edge of its own. On a white page the navy fill is edge
       enough (11.75:1 against the page), but on a dark page the rail and the
       page are both dark navy — 1.56:1 — and the app loses its structure. The
       fill is deliberately NOT inverted (see the dark-mode note below); it is
       given a lit edge instead, which is what separates two dark surfaces. */
    border-right: 1px solid transparent;
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    z-index: 100;
    transition: width 260ms cubic-bezier(.16,1,.3,1);
    overflow: hidden;
}

/* Sidebar: collapsed by default, expanded on hover or pinned */
.side.expanded, .side.pinned { width: var(--sidebar-w); }

/* Hide text when collapsed — only icons visible */
.side-text { white-space: nowrap; overflow: hidden; transition: opacity 0.2s; }

.side:not(.expanded):not(.pinned) .side-text,
.side:not(.expanded):not(.pinned) .side-brand-info,
.side:not(.expanded):not(.pinned) .side-label,
.side:not(.expanded):not(.pinned) .side-uname,
.side:not(.expanded):not(.pinned) .side-urole,
.side:not(.expanded):not(.pinned) .side-btn .badge,
.side:not(.expanded):not(.pinned) .side-logout,
.side:not(.expanded):not(.pinned) .side-cpwd,
.side:not(.expanded):not(.pinned) .side-user-actions { display: none; }

/* The in-rail toggle is gone entirely — markup and styles. The topbar
   hamburger opens AND closes the rail, so a second control inside it was
   redundant in both states. toggleCollapse() survives; the hamburger calls it. */

.side:not(.expanded):not(.pinned) .side-btn { justify-content: center; padding: 10px 0; gap: 0; }

/* ── Rail collapse / expand ──────────────────────────────────────────────
   At the foot of the nav, above the user block. Deliberately NOT in the brand
   row: that is where it crowded the logo and got removed once already. */
.side-rail-toggle {
    display: flex; align-items: center; gap: 10px;
    width: calc(100% - 16px); margin: 0 8px 6px;
    padding: 8px 12px; border: none; border-radius: 8px;
    background: rgba(255,255,255,.05); color: rgba(255,255,255,.55);
    font-family: var(--font); font-size: 12.5px; font-weight: 500;
    cursor: pointer; white-space: nowrap; overflow: hidden;
    transition: background var(--dur-fast) var(--ease-soft), color var(--dur-fast) var(--ease-soft);
}
.side-rail-toggle:hover { background: rgba(255,255,255,.10); color: #fff; }
.side-rail-toggle svg { flex-shrink: 0; transition: transform var(--dur) var(--ease-soft); }
/* Collapsed: icon only, centred, and the chevron points RIGHT because from
   that state the button expands rather than collapses. */
.side:not(.expanded):not(.pinned) .side-rail-toggle { justify-content: center; padding: 8px 0; gap: 0; }
.side:not(.expanded):not(.pinned) .side-rail-toggle svg { transform: rotate(180deg); }
:root[data-theme="dark"] .side-rail-toggle { background: rgba(16,22,47,.06); color: #626A79; }
:root[data-theme="dark"] .side-rail-toggle:hover { background: rgba(16,22,47,.12); color: #10162F; }

/* Collapsed rail: the icon is centred and the count rides ON it.
   The count is set with an inline display:flex from JS, which beats the CSS
   rule that hides badges when collapsed — so it stayed in the flow, pushed to
   the right edge by margin-left:auto, and was clipped by the 72px rail. Taking
   it out of the flow and pinning it to the icon means a count can never be cut
   off, whatever the rail width. */
.side:not(.expanded):not(.pinned) .side-btn { position: relative; }
.side:not(.expanded):not(.pinned) .side-btn .badge {
    position: absolute;
    top: 3px;
    left: 50%;
    margin-left: 6px;          /* just off the icon's right shoulder */
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    display: flex !important;   /* beats the inline display JS sets */
    align-items: center;
    justify-content: center;
    font-size: 9px;
    line-height: 1;
    border-radius: 99px;
    pointer-events: none;
}
/* …but a badge JS has hidden must stay hidden. */
.side:not(.expanded):not(.pinned) .side-btn .badge[style*="display: none"],
.side:not(.expanded):not(.pinned) .side-btn .badge[style*="display:none"] {
    display: none !important;
}
.side:not(.expanded):not(.pinned) .side-btn svg { margin: 0; flex-shrink: 0; }

/* ── Collapsed rail: everything on the centre line ──────────────────────────
   Three things pulled the glyphs off centre:
   1. .side-btn is width:100% + margin:1px 8px. With border-box that box is 16px
      wider than the rail, overflowing to the right — so content centred inside
      it sat ~8px right of the rail's true centre.
   2. .side-btn carries a 3px transparent border-left, which shifts the content
      box another 1.5px right.
   3. .side-user holds an inline flex:1 wrapper for the name/role. The names are
      hidden when collapsed but the wrapper is not, so it ate the free space and
      pinned the avatar to the left edge.
   Fixed here rather than at the source rules so the expanded rail is untouched. */
.side:not(.expanded):not(.pinned) .side-btn {
    width: calc(100% - 16px);
    border-left-width: 0;
}
/* The active marker rides the rail's own left edge instead of the button's,
   so it can't crowd the icon it is marking. */
.side:not(.expanded):not(.pinned) .side-btn.active::before { left: -12px; }
.side:not(.expanded):not(.pinned) .side-user > div:not(.side-avatar) { display: none; }
.side:not(.expanded):not(.pinned) .side-brand { justify-content: center; gap: 0; padding: 12px 0; }
.side:not(.expanded):not(.pinned) .side-user { justify-content: center; padding: 10px 0; }
.side:not(.expanded):not(.pinned) .side-nav { padding: 6px 4px; }

/* Toggle arrow rotates when pinned */

.side-brand {
    padding: 14px 16px;
    display: flex;
    align-items: center;
    gap: 12px;
    border-bottom: 1px solid rgba(255,255,255,0.06);
    min-height: 56px;
}
.side-brand img {
    width: 34px;
    height: 34px;
    border-radius: 10px;
    flex-shrink: 0;
}
.side-brand-info {
    overflow: hidden;
    white-space: nowrap;
    opacity: 1;
    transition: opacity 0.2s var(--ease);
}
.side-brand-text {
    font-weight: 700;
    font-size: 15px;
    color: #FFFFFF;
    white-space: nowrap;
    overflow: hidden;
}
.side-brand-sub {
    font-size: 10px;
    color: rgba(255,255,255,0.35);
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
}

/* Sidebar toggle */

.side-nav {
    flex: 1;
    padding: 8px 8px;
    overflow-y: auto;
    overflow-x: hidden;
}
.side-nav::-webkit-scrollbar { width: 3px; }
.side-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 4px; }

.side-label {
    font-size: 10px;
    font-weight: 700;
    color: rgba(255,255,255,0.15);
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: 18px 12px 6px;
    white-space: nowrap;
    overflow: hidden;
    opacity: 1;
    transition: opacity 0.2s var(--ease);
}

.side-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 12px;
    border: none;
    background: none;
    border-radius: 8px;
    font-family: var(--font);
    font-size: 14px;
    font-weight: 500;
    color: rgba(255,255,255,0.5);
    cursor: pointer;
    transition: all 0.2s var(--ease);
    text-align: left;
    position: relative;
    white-space: nowrap;
    overflow: hidden;
    border-left: 3px solid transparent;
}
.side-btn svg {
    flex-shrink: 0;
    opacity: 0.45;
    transition: all 0.2s var(--ease);
    width: 18px;
    height: 18px;
}
.side-btn:hover {
    background: rgba(255,255,255,0.04);
    color: rgba(255,255,255,0.85);
}
.side-btn:hover svg { opacity: 0.75; }

.side-btn.active {
    background: linear-gradient(90deg, rgba(91,95,230,0.15), transparent);
    color: #FFFFFF;
    font-weight: 600;
    border-left-color: #5B5FE6;
}
.side-btn.active svg {
    opacity: 1;
    color: #5B5FE6;
}

.side-btn .badge {
    margin-left: auto;
    background: var(--red);
    color: white;
    font-size: 9px;
    font-weight: 700;
    padding: 2px 7px;
    border-radius: 8px;
    flex-shrink: 0;
    opacity: 1;
    transition: opacity 0.2s var(--ease);
}

/* ── Sidebar Group (collapsible) ── */
.side-group { position: relative; }
.side-group-toggle .side-chevron {
    transition: transform 0.2s var(--ease);
}
.side-group.open .side-group-toggle .side-chevron {
    transform: rotate(180deg);
}
.side-group-items {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.25s var(--ease);
    margin-left: 16px;
    border-left: 1px solid rgba(255,255,255,0.08);
    padding-left: 8px;
}
.side-group.open .side-group-items {
    max-height: 200px;
}
.side-btn.side-sub {
    padding: 7px 10px;
    font-size: 13px;
    border-left: none;
    gap: 8px;
}
.side-btn.side-sub svg {
    width: 16px;
    height: 16px;
}

/* Hide group items when sidebar collapsed */
.side:not(.expanded):not(.pinned) .side-group-items { display: none; }
.side:not(.expanded):not(.pinned) .side-chevron { display: none; }

.side-user {
    padding: 14px 16px;
    border-top: 1px solid rgba(255,255,255,0.06);
    display: flex;
    align-items: center;
    gap: 10px;
}
.side-avatar {
    width: 32px;
    height: 32px;
    border-radius: 10px;
    background: linear-gradient(135deg, #5B5FE6, #8B6FE8);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 700;
    flex-shrink: 0;
}
.side-uname {
    font-size: 12px;
    font-weight: 600;
    color: rgba(255,255,255,0.85);
    white-space: nowrap;
    overflow: hidden;
    opacity: 1;
    transition: opacity 0.2s var(--ease);
}
.side-urole {
    font-size: 10px;
    color: rgba(255,255,255,0.35);
    white-space: nowrap;
    overflow: hidden;
    opacity: 1;
    transition: opacity 0.2s var(--ease);
}
.side-cpwd {
    padding: 6px;
    border-radius: 8px;
    border: none;
    background: none;
    color: rgba(255,255,255,0.3);
    cursor: pointer;
    display: flex;
    align-items: center;
    transition: all 0.2s var(--ease);
    flex-shrink: 0;
    margin-left: auto;
}
.side-cpwd:hover {
    background: rgba(255,255,255,0.08);
    color: rgba(255,255,255,0.7);
}
.side-logout {
    padding: 6px;
    border-radius: 8px;
    border: none;
    background: none;
    color: rgba(255,255,255,0.3);
    cursor: pointer;
    display: flex;
    align-items: center;
    transition: all 0.2s var(--ease);
    flex-shrink: 0;
    opacity: 1;
}
.side-logout:hover {
    background: rgba(241,65,108,0.15);
    color: #FCA5A5;
}


/* ── Main ── */
.main {
    flex: 1;
    margin-left: var(--sidebar-collapsed);
    min-height: 100vh;
    transition: margin-left 0.25s cubic-bezier(0.4,0,0.2,1);
}
.side.pinned ~ .main,
.side.pinned ~ * .main { margin-left: var(--sidebar-w); }

.topbar {
    height: var(--topbar-h);
    background: var(--surface);
    box-shadow: 0 1px 0 0 #E8ECF1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 24px;
    position: sticky;
    top: 0;
    z-index: 50;
}
.topbar h1 {
    font-size: 16px;
    font-weight: 700;
    color: var(--text);
}
.topbar-sub {
    font-size: 11px;
    color: var(--text3);
    margin-top: 1px;
}
.topbar-right {
    display: flex;
    align-items: center;
    gap: 14px;
    font-size: 13px;
    color: var(--text2);
}

.page { padding: 20px 24px; }

/* Panel transitions */
.pnl {
    display: none;
}
.pnl.active {
    display: block;
    animation: slideInUp 0.3s var(--ease);
}

/* ═══ COMPONENTS ═══ */

/* Cards */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 24px;
    margin-bottom: 16px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.02);
    transition: box-shadow 0.2s var(--ease);
}
.card:hover {
    box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.card-title {
    font-size: 15px;
    font-weight: 700;
    color: var(--text);
    margin-bottom: 4px;
}
.card-sub { font-size: 12px; color: var(--text3); }
.card-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

/* Stats */
.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 14px;
    margin-bottom: 20px;
}
.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 20px;
    transition: box-shadow 0.2s var(--ease);
    box-shadow: 0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.02);
}
.stat:hover {
    box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.stat-val {
    font-size: 32px;
    font-weight: 800;
    color: var(--accent);
    letter-spacing: -0.03em;
    line-height: 1.1;
}
.stat-label {
    font-size: 12px;
    color: var(--text3);
    font-weight: 600;
    margin-top: 6px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.stat-icon {
    width: 44px;
    height: 44px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    margin-bottom: 12px;
}

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 10px 20px;
    border: none;
    border-radius: 8px;
    background: var(--tint);
    color: var(--text);
    font-family: var(--font);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s var(--ease);
}
.btn:hover {
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.btn:active {
    transform: scale(0.98);
    transition: transform 0.1s var(--ease);
}
.btn-primary {
    background: linear-gradient(135deg, #5B5FE6, #7B6FE8);
    color: white;
}
.btn-primary:hover {
    background: linear-gradient(135deg, #4A4ED4, #6A5ED6);
    box-shadow: 0 4px 12px rgba(91,95,230,0.3);
}
.btn-success { background: var(--green-solid); color: white; }
/* Hover was #09A374 — a teal that does not belong to the palette and drops
   white text to about 2.8:1. This is the same green a step darker, so it stays
   above 4.5:1 while still reading as a hover. */
.btn-success:hover { background: #126B33; box-shadow: 0 4px 12px rgba(21,128,61,0.30); }
.btn-danger { background: var(--red); color: white; }
.btn-danger:hover { background: #D93B5C; box-shadow: 0 4px 12px rgba(241,65,108,0.3); }
.btn-outline {
    background: var(--surface);
    color: var(--text);
    border: 1.5px solid var(--border);
}
.btn-outline:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--accent-bg);
    box-shadow: none;
}
.btn-sm { padding: 6px 12px; font-size: 12px; }
.btn-lg { padding: 12px 24px; font-size: 14px; }
.btn-block { width: 100%; }
.btn-ghost {
    background: none;
    color: var(--text2);
    border: none;
    box-shadow: none;
}
.btn-ghost:hover {
    color: var(--accent);
    background: var(--accent-bg);
    border-radius: 8px;
    box-shadow: none;
}

/* Forms */
.field { margin-bottom: 16px; }
.field label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--text2);
    margin-bottom: 5px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.field label .req { color: var(--red-ink); }
.input, .select {
    width: 100%;
    padding: 10px 14px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    font-family: var(--font);
    font-size: 13px;
    color: var(--text);
    background: var(--surface-2);
    transition: all 0.2s var(--ease);
}
.input:focus, .select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
    background: var(--surface);
}
.input-error, .input-error:focus {
    border-color: var(--red) !important;
    box-shadow: 0 0 0 3px rgba(241,65,108,0.12) !important;
    animation: shake 0.3s var(--ease);
}
.error-msg {
    font-size: 11px;
    color: var(--red-ink);
    font-weight: 600;
    margin-top: 4px;
}
.input::placeholder { color: var(--text3); }
textarea.input { min-height: 80px; resize: vertical; }
.input-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.input-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
.input-row-4 { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 14px; }

/* Quotation form sections */
.q-section {
    padding: 16px 20px;
    margin-bottom: 14px;
    border-radius: 10px;
    border-left: 3px solid transparent;
}
.q-section-blue { background: rgba(0,158,247,0.03); border-left-color: var(--blue); }
.q-section-green { background: rgba(11,183,131,0.03); border-left-color: var(--green); }
.q-section-accent { background: rgba(91,95,230,0.03); border-left-color: var(--accent); }
.q-section-orange { background: rgba(255,199,0,0.03); border-left-color: var(--orange); }
.q-section h4 {
    font-size: 12px;
    font-weight: 700;
    margin-bottom: 10px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.q-section-blue h4 { color: var(--blue); }
.q-section-green h4 { color: var(--green-ink); }
.q-section-accent h4 { color: var(--accent); }
.q-section-orange h4 { color: var(--orange); }

/* Tabs */
.tabs {
    display: flex;
    gap: 4px;
    background: var(--tint);
    padding: 4px;
    border-radius: 12px;
    margin-bottom: 16px;
}
.tab {
    flex: 1;
    padding: 8px 14px;
    text-align: center;
    font-size: 13px;
    font-weight: 600;
    border: none;
    background: none;
    border-radius: 8px;
    cursor: pointer;
    color: var(--text2);
    font-family: var(--font);
    transition: all 0.2s var(--ease);
}
.tab:hover { color: var(--text); background: rgba(255,255,255,0.5); }
.tab.active {
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
}

/* Table */
.tbl-wrap {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: 10px;
}
table { width: 100%; border-collapse: collapse; }
table th {
    padding: 12px 16px;
    text-align: left;
    font-size: 11px;
    font-weight: 600;
    color: var(--text3);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    border-bottom: 1px solid var(--border);
    background: var(--surface-2);
}
table td {
    padding: 12px 16px;
    font-size: 13px;
    border-bottom: 1px solid var(--border-light);
}
table tr:last-child td { border-bottom: none; }
table tr:hover { background: var(--surface-2); }
table td.mono {
    font-family: 'SF Mono', 'Fira Code', monospace;
    font-size: 12px;
}

/* Badge */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: 6px;
    font-size: 11px;
    font-weight: 700;
}
.badge-purple { background: var(--accent-bg); color: var(--accent); }
.badge-green { background: var(--green-bg); color: var(--green-ink); }
.badge-red { background: var(--red-bg); color: var(--red-ink); }
.badge-orange { background: var(--orange-bg); color: var(--orange); }
.badge-blue { background: var(--blue-bg); color: var(--blue); }
.badge-gray { background: var(--tint); color: var(--text3); }

/* Quick Actions */
.quick {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px;
}
.quick-btn {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    padding: 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid transparent;
    border-radius: 12px;
    cursor: pointer;
    transition: all 0.2s var(--ease);
    font-family: var(--font);
    text-align: left;
}
.quick-btn:hover {
    border-left-color: var(--accent);
    box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.quick-btn .qi {
    width: 48px;
    height: 48px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
}
.quick-btn .qt {
    font-size: 13px;
    font-weight: 700;
    color: var(--text);
}
.quick-btn .qd {
    font-size: 11px;
    color: var(--text3);
    line-height: 1.4;
}

/* Steps */
.steps { display: flex; gap: 0; margin-bottom: 20px; }
.step {
    flex: 1;
    text-align: center;
    padding: 10px 8px;
    font-size: 11px;
    font-weight: 700;
    color: var(--text3);
    position: relative;
    border-bottom: 2px solid var(--border-light);
    transition: all 0.2s var(--ease);
}
.step.active { color: var(--accent); border-bottom-color: var(--accent); }
.step.done { color: var(--green-ink); border-bottom-color: var(--green); }

/* Search */
.search-bar { display: flex; gap: 8px; }
.search-bar .input { flex: 1; }

/* Alert cards */
.alert-item {
    padding: 12px 16px;
    border-radius: 10px;
    margin-bottom: 6px;
    border-left: 3px solid;
    background: var(--surface);
    border-color: var(--border);
    cursor: pointer;
    transition: all 0.2s var(--ease);
}
.alert-item:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.06); }
.alert-item.t-urgent { border-left-color: var(--red); }
.alert-item.t-warning { border-left-color: var(--orange); }
.alert-item.t-update { border-left-color: var(--blue); }
.alert-item.t-info { border-left-color: var(--text3); }
.alert-item.unread { background: var(--accent-bg); }
.alert-title { font-size: 13px; font-weight: 700; color: var(--text); }
.alert-body {
    font-size: 12px;
    color: var(--text2);
    margin-top: 3px;
    max-height: 40px;
    overflow: hidden;
    line-height: 1.5;
}
.alert-body.open { max-height: 2000px; }
.alert-meta {
    display: flex;
    gap: 10px;
    margin-top: 4px;
    font-size: 10px;
    color: var(--text3);
}

/* Quotation Preview */
.q-preview {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 28px;
    max-width: 900px;
}
.q-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 20px;
    padding-bottom: 14px;
    border-bottom: 2px solid var(--accent);
}
.q-company { font-size: 16px; font-weight: 800; color: var(--accent); }
.q-tag { font-size: 11px; color: var(--text3); }
.q-label { font-size: 22px; font-weight: 800; color: var(--text3); letter-spacing: 0.04em; }
.q-info {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    margin-bottom: 18px;
}
.q-info-item { font-size: 13px; }
.q-info-item .ql { color: var(--text3); font-weight: 600; }
.q-info-item .qv { color: var(--text); font-weight: 600; }
.q-subject {
    background: var(--accent-bg);
    padding: 12px 16px;
    border-radius: 10px;
    font-size: 13px;
    font-weight: 700;
    color: var(--accent);
    margin-bottom: 18px;
}
.q-subject[contenteditable="true"] {
    border: 1px dashed transparent;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.q-subject[contenteditable="true"]:hover {
    border-color: var(--accent);
}
.q-subject[contenteditable="true"]:focus {
    border-color: var(--accent);
    background: var(--surface);
    box-shadow: 0 0 0 3px rgba(99,102,241,0.12);
}

/* Modal */
.modal-bg {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(30,30,45,0.5);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    z-index: 1000;
    justify-content: center;
    align-items: center;
}
.modal-bg.open { display: flex; }
.modal {
    background: var(--surface);
    border-radius: 16px;
    padding: 24px;
    width: 100%;
    max-width: 540px;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: 0 20px 60px rgba(0,0,0,0.15);
    animation: modalIn 0.2s var(--bounce);
}
@keyframes modalIn {
    from { opacity: 0; transform: translateY(8px) scale(0.95); }
    to { opacity: 1; transform: translateY(0) scale(1); }
}
.modal h3 {
    font-size: 17px;
    font-weight: 700;
    color: var(--text);
    margin-bottom: 18px;
}
.modal-foot {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    margin-top: 18px;
}
.modal-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 18px;
}
.modal-title { font-size: 17px; font-weight: 700; color: var(--text); }
.modal-x {
    background: none;
    border: none;
    font-size: 22px;
    cursor: pointer;
    color: var(--text3);
    padding: 4px;
    border-radius: 8px;
    transition: all 0.2s var(--ease);
}
.modal-x:hover { background: var(--tint); color: var(--text); }

/* Empty */
.empty { text-align: center; padding: 48px 24px; }
.empty-icon { font-size: 40px; margin-bottom: 10px; }
.empty h4 { font-size: 15px; font-weight: 700; color: var(--text); }
.empty p { font-size: 12px; color: var(--text3); margin-top: 6px; }

/* Spinner */
.spin {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-light);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}

/* Hamburger / mobile */
/* Always visible, not mobile-only. With hover-expand gone the rail needs a
   control that sits where the eye already is — next to the page title — rather
   than only inside the rail you are trying to open. */
.hamburger {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    background: none;
    border: 1px solid transparent;
    border-radius: 9px;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    color: var(--text2);
    flex: none;
    transition: background var(--dur-fast) var(--ease-soft),
                color var(--dur-fast) var(--ease-soft),
                border-color var(--dur-fast) var(--ease-soft);
}
.hamburger:hover { background: var(--tint); color: var(--text); border-color: var(--border); }
.side-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(30,30,45,0.4);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    z-index: 99;
}

/* ═══ RESPONSIVE ═══ */
@media (max-width: 1024px) {
    .side:not(.pinned) { width: var(--sidebar-collapsed) !important; }
    .side:not(.pinned).expanded { width: var(--sidebar-w) !important; }
    .main { margin-left: var(--sidebar-collapsed) !important; }
}

@media (max-width: 768px) {
    .side { transform: translateX(-100%); width: var(--sidebar-w) !important; }
    .side.open { transform: translateX(0); }
    .side-overlay.open { display: block; }
    .main { margin-left: 0 !important; }
    .hamburger { display: grid; }
    .page { padding: 14px; }
    .input-row, .input-row-3, .input-row-4 { grid-template-columns: 1fr; }
    .stats { grid-template-columns: 1fr 1fr; }
    .quick { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 480px) {
    .stats { grid-template-columns: 1fr; }
    .quick { grid-template-columns: 1fr; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   MOTION & INTERACTION LAYER (Item 1)

   Restraint is deliberate. The work order rules out animated backgrounds,
   decorative gradients across content, and heavy shadows — this is a tool
   operations staff use all day, so motion is here to explain what changed,
   not to decorate. Everything is short (120–320ms), physical, and interruptible.

   Every effect below is disabled under prefers-reduced-motion at the bottom.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Motion tokens moved to /css/tokens.css */

/* ─── Page transitions ─────────────────────────────────────────────────────
   Panels rise and fade rather than snapping, so switching section reads as
   movement between places instead of a flicker. */
.pnl.active {
    animation: pnlIn var(--dur-slow) var(--ease-out);
}
@keyframes pnlIn {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* ─── Dashboard cards: staggered entrance ──────────────────────────────────
   A 40ms cascade reads as the page assembling itself. Capped at 6 so a long
   list never crawls in. */
.stats .stat { animation: cardIn 420ms var(--ease-out) both; }
.stats .stat:nth-child(1){animation-delay:0ms}
.stats .stat:nth-child(2){animation-delay:40ms}
.stats .stat:nth-child(3){animation-delay:80ms}
.stats .stat:nth-child(4){animation-delay:120ms}
.stats .stat:nth-child(5){animation-delay:160ms}
.stats .stat:nth-child(n+6){animation-delay:200ms}
@keyframes cardIn {
    from { opacity: 0; transform: translateY(10px) scale(.985); }
    to   { opacity: 1; transform: none; }
}

/* Lift on hover — 1px, not a drop shadow the size of the card. */
.stat {
    transition: box-shadow var(--dur) var(--ease-soft),
                transform var(--dur) var(--ease-soft),
                border-color var(--dur) var(--ease-soft);
    will-change: transform;
}
.stat:hover {
    transform: translateY(-2px);
    border-color: var(--border-strong);
    box-shadow: 0 6px 18px rgba(16,22,47,.07);
}
.stat-val { font-variant-numeric: tabular-nums; }

/* ─── Buttons: press feedback ──────────────────────────────────────────────
   A button that visibly depresses feels connected to the click. */
.btn { transition: background var(--dur-fast) var(--ease-soft),
                   border-color var(--dur-fast) var(--ease-soft),
                   color var(--dur-fast) var(--ease-soft),
                   transform 80ms var(--ease-soft),
                   box-shadow var(--dur-fast) var(--ease-soft); }
.btn:active { transform: translateY(1px) scale(.99); }

/* ─── Focus rings ──────────────────────────────────────────────────────────
   Keyboard users get a clear ring; mouse users never see it. Required by the
   work order's accessibility section. */
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 6px;
}

/* ─── Tables ───────────────────────────────────────────────────────────────
   NO sticky header here. It was tried and reverted: these tables don't all sit
   in a bounded scroll container, so `position:sticky; top:0` resolved against
   the page and the header floated up over the tracker toolbar, covering the
   client filter and the Add Shipment button. Sticky headers need to be scoped
   to a wrapper that actually scrolls, with the toolbar height as the offset —
   worth doing, but as a deliberate change to the tracker's layout, not as a
   blanket rule across every table in the app.
   Row hover stays: it's what lets the eye track a row across 30+ columns. */
.tbl tbody tr, table.ship-table tbody tr {
    transition: background var(--dur-fast) linear;
}
.tbl tbody tr:hover, table.ship-table tbody tr:hover { background: var(--tint); }

/* Cell edit: a brief navy wash confirms the save landed, then fades out. */
@keyframes cellSaved {
    0%   { background: rgba(34,197,94,.20); }
    100% { background: transparent; }
}
.ship-cell.saved { animation: cellSaved 900ms var(--ease-soft); }

/* ─── Skeleton loading ─────────────────────────────────────────────────────
   A shimmering placeholder of the right shape beats a spinner: the layout
   doesn't jump when the data lands. */
.skel {
    background: linear-gradient(90deg, #EEF1F6 25%, var(--surface-2) 37%, #EEF1F6 63%);
    background-size: 400% 100%;
    animation: skelShimmer 1.4s ease infinite;
    border-radius: 6px;
    color: transparent !important;
}
@keyframes skelShimmer {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}
.skel-row { height: 14px; margin: 9px 0; }
.skel-row.w40 { width: 40%; } .skel-row.w60 { width: 60%; } .skel-row.w80 { width: 80%; }

/* ─── Modals & drawers ─────────────────────────────────────────────────────
   Scale-in from slightly under full size reads as the panel coming toward you. */
.modal-bg.open > .modal, .modal-bg.open > div {
    animation: modalIn var(--dur) var(--ease-out);
}
@keyframes modalIn {
    from { opacity: 0; transform: translateY(12px) scale(.98); }
    to   { opacity: 1; transform: none; }
}

/* ─── Status pills ─────────────────────────────────────────────────────────
   Colour changes ease rather than jump, so a status edit is visible. */
.badge, .ship-status-dd { transition: background var(--dur) var(--ease-soft),
                                      color var(--dur) var(--ease-soft); }

/* ─── Sidebar ──────────────────────────────────────────────────────────────
   The active item's marker slides; hover reveals a subtle inset. */
.side-btn { transition: background var(--dur-fast) var(--ease-soft),
                        color var(--dur-fast) var(--ease-soft); }
/* No padding shift on hover — it made every item twitch under the cursor.
   The active item gets a left marker instead, which doesn't move anything. */
.side-btn { position: relative; }
.side-btn.active::before {
    content: ''; position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px;
    border-radius: 0 3px 3px 0; background: var(--brand-red);
}

/* ─── Respect the user's motion setting ────────────────────────────────────
   Vestibular disorders are real and this is a workplace tool: if the OS says
   reduce motion, everything above collapses to an instant state change. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    .stat:hover { transform: none; }
    .side-btn:hover { padding-left: inherit; }
}


/* ─── Tracker legibility ───────────────────────────────────────────────────
   NOTHING is overridden here, deliberately. An earlier attempt set the grid to
   11.5px in #10162F and forced the header colour — both were wrong:

     • cells were ALREADY 12px / #0F172A in views/app.html, so the "fix" made
       the text SMALLER than it was;
     • the header is white-on-navy (#1E3A5F) by design, and forcing a dark
       colour onto it rendered dark-on-dark and unreadable.

   The genuine cause of the washed-out look was -webkit-font-smoothing:
   antialiased on the body, which thins text on macOS only — removed above.
   Any future change to grid type belongs in that one block in app.html, not
   as a competing override here. */

/* ─── Inputs, selects and request boxes ────────────────────────────────────
   One consistent focus treatment everywhere: navy ring, no colour shift, no
   size change. Older Windows browsers get a visible border rather than relying
   on a shadow alone. */
.input, .select, input[type=text], input[type=email], input[type=number],
input[type=password], input[type=date], select, textarea {
    transition: border-color 120ms var(--ease-soft), box-shadow 120ms var(--ease-soft);
}
.input:focus, .select:focus, input:focus, select:focus, textarea:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(30,42,94,.12);
    outline: none;
}
.input:hover:not(:focus), .select:hover:not(:focus), textarea:hover:not(:focus) {
    border-color: #C3CBDC;
}

/* ─── Modals / request boxes ───────────────────────────────────────────────
   Backdrop fades in rather than appearing, so a dialog doesn't feel like a
   page swap. */
.modal-bg { transition: opacity 160ms var(--ease-soft); }
.modal-bg:not(.open) { opacity: 0; pointer-events: none; }
.modal-bg.open { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
    .ship-grid td.ship-cell { transition: none; }
}

/* Dashboard tiles are interactive now — they need to look it. */
.stat[role="button"] { cursor: pointer; }
.stat[role="button"]:active { transform: translateY(0) scale(.995); }

/* ═══ THE CORRIDOR ═══════════════════════════════════════════════════════════
   Status as a position on a route. Filled = done, pulsing red = now, grey =
   ahead. Purely a lens over the stored status — see renderCorridor() in app.js.
   Sized to fit the tracker's Status column above the dropdown. */
.corr { display:flex; flex-direction:column; gap:3px; margin-bottom:4px; }
.corr-rail {
    position:relative; height:3px; border-radius:2px; background:#E3E7EF; overflow:hidden;
}
.corr-fill {
    position:absolute; inset:0 auto 0 0; background:var(--accent); border-radius:2px;
    /* No sweep in the grid: it replayed for every row on every re-render,
       including after each inline edit. The drawer keeps the sweep. */
}
@keyframes corrSweep { from { width:0 !important; } }
.corr-dots { display:flex; justify-content:space-between; align-items:center; }
.corr-dot { width:5px; height:5px; border-radius:50%; background:#CBD3E1; flex:0 0 auto; }
.corr-dot.done { background:var(--accent); }
/* Static in the grid — NO animation. This dot appears once per row, so with
   ~100 rows the previous infinite pulse meant ~100 elements repainting
   continuously, and it animated box-shadow, which repaints rather than
   composites. It was the single biggest cause of the tracker feeling slow.
   The colour alone marks "now"; the drawer still pulses, where there is
   exactly one of them. */
.corr-dot.now  { background:var(--brand-red); box-shadow:0 0 0 2px rgba(215,53,44,.18); }
@keyframes corrPulse {
    /* transform/opacity only — both composited, no repaint */
    0%,100% { transform:scale(1);   opacity:1; }
    50%     { transform:scale(1.22); opacity:.65; }
}
/* A status that isn't part of the sequence (cancelled / on hold / query) shows
   as a badge above the rail — never as a fake station. */
.corr-off { margin-bottom:4px; }
.corr-offbadge {
    display:inline-block; font-size:9px; font-weight:800; letter-spacing:.3px;
    text-transform:uppercase; padding:1px 6px; border-radius:999px;
    background:var(--tint); color:var(--text2); border:1px solid var(--border);
}
@media (prefers-reduced-motion: reduce) {
    .corr-fill { animation:none; }
    .corr-dot.now { animation:none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   SPEC STEPS 2 & 3 — shell and components restyled to the prototype
   (~/Downloads/acspl-portal-ui.html). Adapted, not copied: our own class
   names and markup are kept, only the styling is ported. Presentation only —
   no markup structure, no behaviour, no data touched.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Shell: sidebar ── */
.side {
    background: var(--navy-900) !important;   /* flat ink, not the old gradient */
    color: #C7CEE4;
}
.side-btn {
    border-radius: 10px;
    margin: 1px 8px;
    color: #AFB8D2;
    font-weight: 600;
    font-size: 13.5px;
}
.side-btn:hover { background: rgba(255,255,255,.06); color: #fff; }
.side-btn.active { background: rgba(255,255,255,.10); color: #fff; }
.side-brand { border-bottom: 1px solid rgba(255,255,255,.07); }

/* ── Shell: topbar and page ── */
.topbar {
    /* Solid, NOT translucent. A backdrop-filter here re-blurs the region behind
       the bar on every scroll frame, on every page in the portal — it looked
       nice and cost more than it was worth on the machines this runs on. */
    background: var(--surface);
    box-shadow: 0 1px 0 0 var(--border);
}
/* NO width cap. The prototype centres content at 1420px, but this portal's
   main screens are wide data tables — the tracker carries 30+ columns and the
   quotation list a dozen. Capping the page left dead space on the right while
   the tables scrolled internally, which is the opposite of useful. Full width,
   so the data gets the room. */

/* ── Cards ── */
.card {
    border-radius: var(--r-18, 18px);
    border: 1px solid var(--border);
    box-shadow: var(--e1);
    transition: box-shadow var(--mid) var(--ease-proto), border-color var(--mid);
}
.card:hover { border-color: var(--border-strong); }
.card-head, .card-h { border-bottom: 1px solid var(--border); }

/* ── Buttons ── */
.btn {
    height: 38px; padding: 0 15px;
    border-radius: 11px;
    font-weight: 700; font-size: 13px;
    border: 1px solid var(--border);
    transition: background var(--fast) var(--ease-proto),
                border-color var(--fast) var(--ease-proto),
                transform var(--fast) var(--ease-proto),
                box-shadow var(--fast) var(--ease-proto);
}
.btn:hover { border-color: var(--border-strong); transform: translateY(-1px); box-shadow: var(--e1); }
.btn:active { transform: translateY(0); }
.btn-primary { background: var(--navy); border-color: var(--navy); color: #fff; }
.btn-primary:hover { background: var(--navy-700); border-color: var(--navy-700);
                     box-shadow: 0 8px 20px -8px rgba(30,42,94,.7); }
.btn-sm { height: 31px; padding: 0 11px; font-size: 12px; border-radius: 9px; }
.btn-lg { height: 44px; padding: 0 20px; font-size: 14px; border-radius: 12px; }

/* ── Inputs ── */
.input, .select, textarea.input {
    height: 40px; padding: 0 13px;
    border-radius: 11px;
    border: 1px solid var(--border);
    font-size: 13.5px;
}
textarea.input { height: auto; padding: 10px 13px; }
.input:focus, .select:focus, textarea:focus {
    border-color: var(--navy);
    box-shadow: 0 0 0 3px var(--navy-100);
}

/* ── Pills / badges ── */
.badge {
    display: inline-flex; align-items: center; gap: 6px;
    height: 23px; padding: 0 9px;
    border-radius: 999px;
    font-size: 11px; font-weight: 800; letter-spacing: .03em;
    white-space: nowrap;
}

/* ── Tables (list views — NOT the tracker grid, which owns its own styling) ── */
.tbl-wrap { border-radius: var(--r-18, 18px); border: 1px solid var(--border); overflow: auto; }
.tbl thead th {
    background: var(--surface-2);
    font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
    /* NOT --ink-3 (#8A93A8): at 2.97:1 on the tinted header it misses the 3:1
       floor the work order sets. #6B7488 is the same hue, 4.53:1. */
    color: var(--text2);
    padding: 11px 14px;
}
.tbl tbody td { padding: 12px 14px; border-bottom: 1px solid var(--border); }
.tbl tbody tr:last-child td { border-bottom: none; }

/* Small uppercase label, used above section titles */
.eyebrow {
    font-size: 10.5px; font-weight: 700; letter-spacing: .16em;
    text-transform: uppercase; color: var(--ink-3);
}

/* ── Dashboard tiles, prototype styling ──────────────────────────────────────
   A coloured bar down the left edge instead of a filled icon chip, an
   uppercase label, the figure, and a detail line underneath. */
.stat {
    position: relative; overflow: hidden;
    border-radius: var(--r-18, 18px);
    box-shadow: var(--e1);
}
.stat .stat-bar {
    position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
}
.stat-label {
    font-size: 10.5px; font-weight: 800; letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-3);
}
.stat-detail { font-size: 11.5px; color: var(--ink-3); margin-top: 4px; }


/* ═══ RECORD DRAWER ═════════════════════════════════════════════════════════
   Slides over from the right so the list keeps its place, scroll and filters —
   the spec's replacement for a full page load to view one record. */
.drawer-scrim {
    position: fixed; inset: 0; background: rgba(14,20,48,.34);
    opacity: 0; pointer-events: none; z-index: 9996;
    transition: opacity 260ms var(--ease-proto);
    backdrop-filter: blur(1.5px);
}
.drawer-scrim.open { opacity: 1; pointer-events: auto; }
.drawer {
    position: fixed; top: 0; right: 0; bottom: 0;
    width: min(560px, 96vw); z-index: 9997;
    background: var(--surface); border-left: 1px solid var(--border);
    box-shadow: var(--e3);
    transform: translateX(102%);
    transition: transform 420ms var(--ease-out);
    display: flex; flex-direction: column;
}
.drawer.open { transform: none; }
.drawer-head {
    display: flex; align-items: center; gap: 12px;
    padding: 16px 18px; border-bottom: 1px solid var(--border);
}
.drawer-body { padding: 18px; overflow: auto; flex: 1; }

/* Full corridor — the prototype's numbered stations with captions and dates.
   The tracker cell keeps the compact rail; there is room for the real thing
   here. */
.corridor-full { position: relative; margin: 6px 0 22px; }
.corridor-full .cf-track {
    position: absolute; left: 22px; right: 22px; top: 15px; height: 2px;
    background: var(--border); border-radius: 2px; overflow: hidden;
}
.corridor-full .cf-fill {
    display: block; height: 100%; background: var(--accent); border-radius: 2px;
    animation: cfSweep 620ms var(--ease-out);
}
@keyframes cfSweep { from { width: 0 !important; } }
.corridor-full .cf-stations { display: flex; position: relative; }
.cf-stn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 7px; text-align: center; }
.cf-node {
    width: 30px; height: 30px; border-radius: 999px; display: grid; place-items: center;
    background: var(--surface); border: 2px solid var(--border); color: var(--ink-3);
    font-size: 11px; font-weight: 800; transition: all var(--mid) var(--ease-proto);
}
.cf-stn.done .cf-node { background: var(--accent); border-color: var(--accent); color: #fff; }
.cf-stn.now  .cf-node { border-color: var(--brand-red); color: var(--brand-red);
                        box-shadow: 0 0 0 4px rgba(215,53,44,.14); animation: corrPulse 1.8s ease-in-out infinite; }
.cf-cap  { font-size: 10.5px; font-weight: 700; color: var(--ink-2); line-height: 1.25; max-width: 74px; }
.cf-stn.ahead .cf-cap { color: var(--ink-3); font-weight: 600; }
.cf-when { font-size: 9.5px; font-weight: 700; letter-spacing: .06em; color: var(--ink-3); text-transform: uppercase; }

/* Key/value grid inside the drawer */
.dw-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; margin-bottom: 18px; }
.dw-k { font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); }
.dw-v { font-size: 13px; color: var(--ink); font-weight: 600; margin-top: 1px; word-break: break-word; }
.dw-sec { font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
          color: var(--ink-3); margin: 4px 0 10px; }

@media (prefers-reduced-motion: reduce) {
    .drawer { transition: none; }
    .corridor-full .cf-fill { animation: none; }
    .cf-stn.now .cf-node { animation: none; }
}

/* ⌘K palette — the same search, given focus and prominence. */
.gsearch-wrap.palette input {
    background: var(--surface); border-color: var(--navy);
    box-shadow: 0 0 0 3px var(--navy-100), var(--e2);
}
.gsearch-wrap.palette .gsearch-results { box-shadow: var(--e3); }

/* ═══ FULL-WIDTH DATA SCREENS ═══════════════════════════════════════════════
   The tracker and the list views are the working screens; every pixel of
   horizontal room is a column the user doesn't have to scroll to. Page and
   card padding is trimmed right back on those panels so the tables run to the
   edge, rather than sitting in a narrow well with their own scrollbar while
   the window has space to spare.
   Reading-width screens (quotation preview, forms, settings) keep their
   comfortable padding — those are not tables and should not be edge-to-edge. */
#p-shipments .page,
#p-shipments { padding-left: 0; padding-right: 0; }
#p-shipments .card,
#p-quotes .card,
#p-clients .card,
#p-enquiries .card {
    padding: 0;
    border-radius: 12px;
    overflow: hidden;
}
/* The card still needs breathing room around its heading and toolbar — only
   the table itself goes edge to edge. */
#p-quotes .card > *:not(.tbl-wrap):not(#enqTableWrap),
#p-clients .card > *:not(.tbl-wrap),
#p-enquiries .card > *:not(.tbl-wrap):not(#enqTableWrap) {
    padding-left: 18px; padding-right: 18px;
}
#p-quotes .card > .card-head,
#p-clients .card > .card-head,
#p-enquiries .card > .card-head { padding-top: 18px; }
#p-quotes .tbl-wrap, #p-clients .tbl-wrap, #p-enquiries .tbl-wrap,
#enqTableWrap { border-left: none; border-right: none; border-radius: 0; }

/* Tracker sheet: minimal gutter so the grid gets the width. */
#shipSheetBody { padding: 14px 10px 22px !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   DARK THEME ADJUSTMENTS
   The dark palette lives in tokens.css. This block holds only the handful of
   rules whose FOREGROUND has to change because a token swapped lightness —
   not a second copy of the stylesheet.
   ═══════════════════════════════════════════════════════════════════════════ */

/* --accent lifts to a light indigo in dark mode so it stays visible on a
   near-black page. White text on it would be ~2.5:1, so the corridor's
   completed node flips to dark ink instead. */
:root[data-theme="dark"] .cf-stn.done .cf-node { color: var(--bg); }

/* Inputs sit on --surface in both themes; in dark the browser's default
   autofill and date-picker chrome is still light, so normalise it. */
:root[data-theme="dark"] input,
:root[data-theme="dark"] select,
:root[data-theme="dark"] textarea { color-scheme: dark; }
/* …and on the root as well, so the canvas and every native control Safari
   paints itself inherit the dark scheme rather than only the three tags above. */
:root[data-theme="dark"] { color-scheme: dark; }

/* On macOS every browser — Safari, Chrome and Firefox alike — hands <select>
   to the platform to paint as a native popup button, and the platform IGNORES
   the author background-color. The computed style is correct (#131A3A; headless
   Chrome, which uses its own control theme rather than the platform's, renders
   it that way), but on a real Mac the control keeps its light native fill while
   `color: var(--text)` sets the label to #EEF1F9 — white text on a white
   control, at 1.05:1.
   The only reliable fix is to stop the platform painting it: reset appearance
   and draw the control ourselves, chevron included. Applied to every select in
   dark mode, not just the tracker filters, because the cause is platform-wide
   and every dropdown in the portal has it.
   Scoped to dark mode on purpose: light mode keeps the native control exactly
   as it is today, so nothing regresses on the screens that already look right. */
:root[data-theme="dark"] select {
    -webkit-appearance: none;
    appearance: none;
    background-color: var(--surface-2);
    color: var(--text);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7' fill='none'%3E%3Cpath d='M1 1l4.5 4.5L10 1' stroke='%23A8B0C7' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    /* !important, reluctantly: the tracker's filter selects carry an inline
       `padding` shorthand, and an inline declaration beats this rule. Without
       it a long client name runs under the chevron. It only ever adds room. */
    padding-right: 27px !important;
}
/* The native popup list is a separate surface and needs its own colours, or it
   opens as light-on-light even once the closed control is fixed. */
:root[data-theme="dark"] select option {
    background-color: var(--surface-2);
    color: var(--text);
}
/* The grid's status dropdown is deliberately fill-less — it sits in the cell and
   takes a per-status background inline from JS. It still needs the appearance
   reset above (same platform bug), but not the surface fill, and its chevron
   has to tuck into the 22px of right padding the cell already reserves. */
:root[data-theme="dark"] .ship-status-dd {
    background-color: transparent;
    background-position: right 6px center;
    padding-right: 22px !important;
}

/* Table zebra/hover derive from a translucent white in light mode, which
   disappears on a dark page — invert to a translucent light. */
:root[data-theme="dark"] .tab:hover { background: rgba(255,255,255,.05); }

/* Skeleton shimmer is built for a light track. */
:root[data-theme="dark"] .skel,
:root[data-theme="dark"] .skel-row {
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--border) 37%, var(--surface-2) 63%);
    background-size: 400% 100%;
}

/* ═══════════════════════════════════════════════════════════════════════════
   PROTOTYPE PRIMITIVES  (acspl-portal-ui.html)
   Every selector below is a NEW name. Nothing that already existed is renamed
   or repointed, so no current screen changes until markup opts in.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---------- Theme toggle ---------- */
.theme-btn {
    width: 34px; height: 34px; border-radius: 10px; flex: none;
    display: grid; place-items: center; cursor: pointer;
    background: none; border: 1px solid transparent; color: var(--text2);
    transition: background var(--dur-fast) var(--ease-soft),
                color var(--dur-fast) var(--ease-soft),
                border-color var(--dur-fast) var(--ease-soft);
}
.theme-btn:hover { background: var(--tint); color: var(--text); border-color: var(--border); }
.theme-btn svg { grid-area: 1 / 1; transition: opacity var(--dur) var(--ease-soft), transform var(--dur) var(--ease-soft); }
/* Light theme shows the moon (the action: "go dark"), dark shows the sun. */
.theme-btn .ic-sun  { opacity: 0; transform: rotate(-90deg) scale(.6); }
.theme-btn .ic-moon { opacity: 1; transform: none; }
:root[data-theme="dark"] .theme-btn .ic-sun  { opacity: 1; transform: none; }
:root[data-theme="dark"] .theme-btn .ic-moon { opacity: 0; transform: rotate(90deg) scale(.6); }

/* ---------- Pills ---------- */
.pill { display:inline-flex; align-items:center; gap:6px; height:23px; padding:0 9px;
        border-radius:99px; font-size:11px; font-weight:800; letter-spacing:.03em; white-space:nowrap; }
.pill.navy  { background: var(--accent-bg);  color: var(--accent); }
.pill.red   { background: var(--red-bg);     color: var(--red-ink); }
.pill.green { background: var(--green-bg);   color: var(--green-ink); }
.pill.amber { background: var(--orange-bg);  color: #B45309; }
.pill.sky   { background: var(--blue-bg);    color: #0369A1; }
.pill.grey  { background: var(--tint); color: var(--text2); border:1px solid var(--border); }
:root[data-theme="dark"] .pill.amber { color: #FBBF24; }
:root[data-theme="dark"] .pill.sky   { color: #7DD3FC; }
:root[data-theme="dark"] .pill.red   { color: #FCA5A5; }

/* ---------- Live dot ---------- */
.livedot { width:7px; height:7px; border-radius:99px; background: var(--green); position:relative; flex:none; }
.livedot::after { content:''; position:absolute; inset:-4px; border-radius:99px;
    background: var(--green); opacity:.25; animation: halo 2s ease-out infinite; }
@keyframes halo { 0% { transform: scale(.6); opacity:.4 } 100% { transform: scale(1.5); opacity:0 } }

/* ---------- Stat tiles ---------- */
.tiles { display:grid; grid-template-columns:repeat(auto-fit,minmax(215px,1fr)); gap:14px; }
.tile {
    position:relative; overflow:hidden; padding:17px 18px; border-radius: var(--r-18);
    background: var(--surface); border:1px solid var(--border); box-shadow: var(--e1);
    transition: transform var(--dur) var(--ease-soft), box-shadow var(--dur) var(--ease-soft),
                border-color var(--dur) var(--ease-soft);
    cursor:pointer; text-align:left; width:100%; font:inherit; color:inherit;
}
.tile:hover { transform: translateY(-3px); box-shadow: var(--e2); border-color: var(--border-strong); }
.tile .bar { position:absolute; left:0; top:0; bottom:0; width:3px; border-radius:0 3px 3px 0; }
.tile .k { display:flex; align-items:center; gap:7px; font-size:11px; font-weight:700;
           letter-spacing:.09em; text-transform:uppercase; color: var(--text3); }
/* Big numeral. The prototype uses Abril Fatface; we self-host Inter only and
   adding a Google font would cost a blocking request on every load, so this is
   Inter at display weight with tight tracking and tabular figures. */
.tile .v { font-size:38px; line-height:1.05; margin:9px 0 2px; font-weight:800;
           letter-spacing:-.03em; font-variant-numeric: tabular-nums; }
.tile .d { font-size:12px; color: var(--text2); display:flex; align-items:center; gap:5px; }

/* ---------- Agent turnaround bars ---------- */
.perf { display:flex; align-items:center; gap:11px; padding:11px 0; border-bottom:1px solid var(--border); }
.perf:last-child { border-bottom:none; }
.perf .nm  { width:150px; font-weight:700; font-size:13px; flex:none; }
.perf .bar { flex:1; height:9px; border-radius:99px; background: var(--tint); overflow:hidden; }
.perf .bar i { display:block; height:100%; border-radius:99px;
    background: linear-gradient(90deg, var(--navy), var(--blue));
    animation: perfGrow 1s var(--ease-out) both; }
@keyframes perfGrow { from { width:0 !important } }
.perf .vv { width:88px; text-align:right; font-size:12.5px; font-weight:700; flex:none;
            font-variant-numeric: tabular-nums; }
:root[data-theme="dark"] .perf .bar i { background: linear-gradient(90deg, var(--accent), var(--blue)); }

/* ---------- Segmented control ---------- */
.seg { display:inline-flex; padding:3px; border-radius:11px; background: var(--tint);
       border:1px solid var(--border); gap:2px; }
/* This stylesheet has no global <button> reset — every button here carries its
   own class — so these must clear the browser's default button face themselves,
   or they render as light grey chrome (and stay light in dark mode). */
.seg button { padding:6px 13px; border-radius:8px; font-size:12.5px; font-weight:700;
              background: transparent; border: none; cursor: pointer; font-family: inherit;
              color: var(--text2); transition: all var(--dur-fast) var(--ease-soft); }
.seg button.on { background: var(--surface); color: var(--text); box-shadow: var(--e1); }

/* ---------- Toasts ---------- */
.toasts { position:fixed; right:20px; bottom:20px; z-index:95; display:flex; flex-direction:column; gap:9px; }
.toast {
    display:flex; align-items:center; gap:11px; padding:13px 16px; min-width:290px;
    background: var(--navy-900); color:#fff; border-radius:13px; box-shadow: var(--e3);
    font-size:13px; font-weight:600; animation: toastIn 380ms var(--ease-out) both;
}
@keyframes toastIn { from { opacity:0; transform: translateX(40px) scale(.96) } to { opacity:1; transform:none } }
.toast.out { animation: toastOut 280ms var(--ease-soft) forwards; }
@keyframes toastOut { to { opacity:0; transform: translateX(40px) } }
.toast .ico { width:20px; height:20px; border-radius:99px; display:grid; place-items:center; flex:none; background: var(--green); }
.toast .ico svg { width:12px; height:12px; stroke:#062; stroke-width:3.4; }
.toast.err .ico { background: var(--red); }
.toast.err .ico svg { stroke:#fff; }
:root[data-theme="dark"] .toast { background: var(--surface-2); border:1px solid var(--border-strong); }

/* ---------- Entrance motion ----------
   The prototype's "premium" feel is mostly this: content rises into place
   instead of appearing. Kept short and applied once per view switch, never
   per row, so long tables do not pay for it. */
.view-in > * { animation: riseIn 480ms var(--ease-out) both; }
.view-in > *:nth-child(1) { animation-delay: .02s }
.view-in > *:nth-child(2) { animation-delay: .07s }
.view-in > *:nth-child(3) { animation-delay: .12s }
.view-in > *:nth-child(4) { animation-delay: .16s }
.view-in > *:nth-child(n+5) { animation-delay: .2s }
@keyframes riseIn { from { opacity:0; transform: translateY(14px) } to { opacity:1; transform:none } }

@media (prefers-reduced-motion: reduce) {
    .view-in > *, .perf .bar i, .toast, .livedot::after { animation: none !important; }
}

/* ---------- Page head (prototype hero) ---------- */
.page-head { display:flex; align-items:flex-end; gap:18px; flex-wrap:wrap; margin-bottom:22px; }
.page-head h1 { margin:0; font-weight:800; line-height:1.15; }
.page-head-sub { color: var(--text2); font-size:13.5px; margin-top:5px; }
.page-head-acts { margin-left:auto; display:flex; gap:9px; flex-wrap:wrap; }
@media (max-width: 720px) {
    .page-head { align-items:flex-start; }
    .page-head-acts { margin-left:0; width:100%; }
    .page-head h1 { font-size:24px !important; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   SIDEBAR IN DARK MODE — INVERTED
   Sanjay's call: in dark mode the rail flips to light while the content goes
   dark. Every ink inside it has to flip with the fill — the rail's text, icons,
   dividers and hover states are all rgba(255,255,255,x) for a navy background,
   and left alone on a light fill they would be invisible. So this block is long
   on purpose: a half-inverted sidebar is worse than none.
   ═══════════════════════════════════════════════════════════════════════════ */
:root[data-theme="dark"] .side {
    /* !important is required, not preferred: the restyle block above sets
       `background: var(--navy-900) !important` on .side, which beats any
       specificity this selector could reach. The base `color` there is a light
       ink for a navy rail and has to flip too, or every element that inherits
       it renders light-on-light. */
    background: linear-gradient(180deg, #FFFFFF 0%, #EEF1F8 100%) !important;
    color: #475067;
    /* A light rail separates from a dark page by itself; the edge added for the
       dark-on-dark case would only muddy it. */
    border-right: 1px solid #D7DCE8;
    box-shadow: none;
}

/* Brand */
:root[data-theme="dark"] .side-brand      { border-bottom-color: rgba(16,22,47,.10); }
:root[data-theme="dark"] .side-brand-text { color: #10162F; }
:root[data-theme="dark"] .side-brand-sub  { color: #626A79; }

/* Collapse toggle */

/* Scrollbar + section labels. The label was rgba(255,255,255,.15) — a whisper
   that works as a hairline on navy but simply vanishes on white, so it is
   given a real weight rather than a mechanical inversion. */
:root[data-theme="dark"] .side-nav::-webkit-scrollbar-thumb { background: rgba(16,22,47,.14); }
:root[data-theme="dark"] .side-label { color: #626A79; }   /* #8A93A6 measured 2.88:1 here — unreadable */

/* Nav items */
:root[data-theme="dark"] .side-btn            { color: #475067; }
:root[data-theme="dark"] .side-btn:hover      { background: rgba(16,22,47,.05); color: #10162F; }
:root[data-theme="dark"] .side-btn.active {
    background: linear-gradient(90deg, rgba(30,42,94,.12), transparent);
    color: #1E2A5E;
    border-left-color: #1E2A5E;
}
:root[data-theme="dark"] .side-btn.active svg { color: #1E2A5E; }
:root[data-theme="dark"] .side-group-items    { border-left-color: rgba(16,22,47,.10); }

/* Footer: user block and its actions */
:root[data-theme="dark"] .side-user       { border-top-color: rgba(16,22,47,.10); }
:root[data-theme="dark"] .side-uname      { color: #10162F; }
:root[data-theme="dark"] .side-urole      { color: #626A79; }
:root[data-theme="dark"] .side-cpwd,
:root[data-theme="dark"] .side-logout     { color: #626A79; }
:root[data-theme="dark"] .side-cpwd:hover,
:root[data-theme="dark"] .side-logout:hover { background: rgba(16,22,47,.07); color: #10162F; }

/* The red count badge and the avatar keep their fills — both already carry
   white text on a saturated colour and read correctly either way. */
