/*
 * static/css/style.css
 * The ONE stylesheet of the project, served at /static/css/style.css.
 *
 * Every page loads this file and no other. If a screen needs something
 * special, add a class here rather than a second stylesheet - one file is
 * what keeps the pages looking like one product.
 *
 * Order: tokens, base, layout, sidebar, then components. Components are
 * generic on purpose (.table, .btn, .field) so a new screen is markup only.
 *
 * Theming: every colour is a token on :root, and [data-theme="dark"] simply
 * restates the same tokens. Nothing below the token block hard-codes a
 * colour, which is what makes the dark switch a five-line change.
 *
 * Anything public goes in `static/` (css, js, images).
 * Anything a user uploads goes in `uploads/` - never mix the two.
 */

/* ---------------------------------------------------------------- tokens */
:root {
    --sidebar-w: 258px;
    --sidebar-w-collapsed: 0px;
    --topbar-h: 64px;

    /* Neutral greys, R = G = B, so the purple accent is the only hue on the
       screen. Anything tinted here reads as a colour cast beside it. */
    --bg: #ffffff;
    --surface: #ffffff;
    --surface-2: #fafafa;
    --sidebar-bg: #fbfbfb;
    --border: #ededed;
    --border-strong: #dcdcdc;

    --text: #171717;
    --text-2: #5c5c5c;
    --muted: #8f8f8f;

    --accent: #7c3aed;
    --accent-hover: #6d28d9;
    --accent-soft: #f3eeff;
    --accent-text: #ffffff;

    --green: #15803d;
    --green-soft: #eaf7ef;
    /* Second series colour, for the outlet-type donut. */
    --blue: #2563eb;
    --amber: #b45309;
    --amber-soft: #fdf4e7;
    --red: #dc2626;
    --red-soft: #fdeeee;

    --radius: 14px;
    --radius-sm: 9px;
    --gap: 26px;

    --shadow-sm: 0 1px 2px rgba(20, 20, 30, .05);
    --shadow: 0 6px 22px rgba(20, 20, 30, .10);
    --shadow-lg: 0 22px 54px rgba(20, 20, 30, .20);

    --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* Neutral greys, R = G = B. An earlier version leaned blue (#131318 has more
   blue than red or green) which read as a colour cast next to the purple
   accent; true neutrals let the accent be the only hue on the screen. */
[data-theme="dark"] {
    --bg: #0d0d0d;
    --surface: #171717;
    --surface-2: #1f1f1f;
    --sidebar-bg: #141414;
    --border: #272727;
    --border-strong: #383838;

    --text: #f5f5f5;
    --text-2: #a8a8a8;
    --muted: #757575;

    --accent: #8b5cf6;
    --accent-hover: #7c3aed;
    --accent-soft: #241a3d;

    --green: #4ade80;
    --green-soft: #14301d;
    --blue: #60a5fa;
    --amber: #fbbf24;
    --amber-soft: #302510;
    --red: #f87171;
    --red-soft: #331a1a;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
    --shadow: 0 6px 22px rgba(0, 0, 0, .6);
    --shadow-lg: 0 22px 54px rgba(0, 0, 0, .7);

    color-scheme: dark;   /* native scrollbars, form controls and caret follow */
}

/* Light theme, the working area. Page, cards and table headers were all
   white or as near as makes no difference (#fafafa on #fff is 1.04:1), so a
   screen read as one flat sheet ruled with hairlines. Dark mode never had
   that problem — its cards sit a step above the page. This gives light mode
   the same step: a grey working area with the white cards on it.

   On `.content`, not `:root`, so the sidebar and top bar keep their greys
   and nothing outside the working area moves (toasts, the sign-in page).
   Dark mode is untouched. Neutral greys, R = G = B, as above. */
:root:not([data-theme="dark"]) .content {
    background: #f4f4f4;
    --surface-2: #f6f6f6;
    --border: #e6e6e6;
    --border-strong: #d4d4d4;
    /* #8f8f8f was 3.2:1 on white, short of the 4.5:1 small text needs — and
       the qty lines, sub-lines and card labels are all small. */
    --muted: #737373;
    --shadow-sm: 0 1px 2px rgba(20, 20, 30, .05), 0 1px 3px rgba(20, 20, 30, .05);
}

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

body {
    margin: 0;
    font-family: var(--font);
    font-size: 14px;
    line-height: 1.55;
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: none; }

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.01em; }

code {
    font-family: var(--mono);
    font-size: .88em;
    background: var(--accent-soft);
    color: var(--accent);
    padding: 2px 7px;
    border-radius: 5px;
}

/* Inline icons: stroked, never filled, so one SVG works on both themes. */
.ico {
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.9;
    stroke-linecap: round;
    stroke-linejoin: round;
    flex: none;
}

.ico-sm { width: 15px; height: 15px; }

/* --------------------------------------------------------------- sidebar */
.sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--sidebar-w);
    display: flex;
    flex-direction: column;
    background: var(--sidebar-bg);
    border-right: 1px solid var(--border);
    z-index: 30;
    transition: transform .2s ease;
}

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 11px;
    height: var(--topbar-h);
    padding: 0 20px;
    flex: none;
    border-bottom: 1px solid var(--border);
}

.brand-mark {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: 11px;
    background: var(--accent);
    color: var(--accent-text);
    flex: none;
}

.brand-mark .ico { width: 20px; height: 20px; }

.brand-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }

.brand-name {
    font-weight: 700;
    font-size: 16px;
    letter-spacing: -.02em;
    white-space: nowrap;
}

.brand-tag { color: var(--muted); font-size: 11.5px; white-space: nowrap; }

.sidebar-nav {
    display: flex;
    flex-direction: column;
    padding: 18px 14px;
    gap: 2px;
    overflow-y: auto;
    flex: 1;
}

.menu-group + .menu-group { margin-top: 10px; }

.menu-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 9px 10px;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text);
    font: inherit;
    font-size: 14.5px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
}

.menu-head:hover { background: var(--surface-2); }

/* The icon sits in a bordered square, as on the reference: it gives every
   menu the same left edge, so the names line up whatever their length. */
.menu-head-icon {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    flex: none;
    margin-right: 10px;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    color: var(--text-2);
}

.menu-head-icon .ico { width: 16px; height: 16px; }
.menu-head-name { flex: 1; min-width: 0; text-align: left; }

.menu-head:hover .menu-head-icon { border-color: var(--accent); color: var(--accent); }

/* The open menu, and the menu holding the current screen, carry the accent —
   otherwise nine identical rows give no clue which one you are inside. */
.menu-group.is-open .menu-head-icon,
.menu-group:has(.menu-item.is-active) .menu-head-icon {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
}

.menu-chevron {
    width: 16px;
    height: 16px;
    opacity: .5;
    transition: transform .18s ease;
    transform: rotate(-90deg);            /* closed -> pointing right */
}

.menu-group.is-open .menu-chevron { transform: rotate(0deg); }

.menu-items {
    display: none;
    flex-direction: column;
    gap: 1px;
    padding: 4px 0 2px;
}

.menu-group.is-open .menu-items { display: flex; }

.menu-item {
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    color: var(--text-2);
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.menu-item:hover { background: var(--surface-2); color: var(--text); }

/* The screen you are on. This used to be `--surface-2` in light mode — #fafafa
   on a #fbfbfb sidebar, a one-value difference that was effectively invisible;
   only dark mode, which used the accent tint, actually showed it. Now both
   themes use the accent: a tinted fill, accent text, and a bar down the left
   edge that reads even at a glance. */
.menu-item.is-active {
    position: relative;
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

.menu-item.is-active::before {
    content: "";
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--accent);
}

.menu-item.is-active:hover { background: var(--accent-soft); color: var(--accent); }

.sidebar-foot {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 18px;
    border-top: 1px solid var(--border);
    flex: none;
}

.sidebar-foot .user-meta { flex: 1; min-width: 0; }

/* ----------------------------------------------------------------- shell */
.shell {
    margin-left: var(--sidebar-w);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    transition: margin-left .2s ease;
}

/* Collapsed: the menu slides away entirely and the content takes the width. */
body.nav-collapsed .sidebar { transform: translateX(-100%); }
body.nav-collapsed .shell { margin-left: 0; }

.topbar {
    position: sticky;
    top: 0;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 0 22px;
    height: var(--topbar-h);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    z-index: 20;
}

.topbar-divider {
    width: 1px;
    height: 22px;
    background: var(--border);
}

/* The topbar freshness line. Green and blinking when the six imports ran as
   one sweep; amber and still when they did not, because a pulsing dot beside
   stale data reads as reassurance it has not earned. */
.sync-dot {
    width: 8px; height: 8px; border-radius: 50%; flex: none;
    background: var(--green);
    /* box-shadow needs a colour it can fade to transparent, and the token has
       no alpha channel — hence the literal. */
    box-shadow: 0 0 0 0 rgba(63, 174, 122, .55);
    animation: sync-pulse 1.8s infinite;
}
.sync-dot.is-stale { background: var(--amber); animation: none; box-shadow: none; }
.sync-label {
    color: var(--green); font-weight: 700; letter-spacing: .05em; font-size: 11.5px;
}
.sync-label.is-stale { color: var(--amber); }
.sync-when { color: var(--muted); }
@keyframes sync-pulse {
    0%   { box-shadow: 0 0 0 0   rgba(63, 174, 122, .55); }
    70%  { box-shadow: 0 0 0 7px rgba(63, 174, 122, 0); }
    100% { box-shadow: 0 0 0 0   rgba(63, 174, 122, 0); }
}
@media (prefers-reduced-motion: reduce) { .sync-dot { animation: none; } }

/* Page status in the topbar — last synced, row counts, LIVE. Right-aligned
   against the theme toggle, and allowed to shrink before the title does. */
.topbar-status {
    display: flex; align-items: center; gap: 7px;
    margin-left: auto; padding-left: 12px;
    font-size: 12.5px; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    max-width: 46%;
}
.topbar-status:empty { display: none; }
.topbar-status .ico { width: 14px; height: 14px; flex: none; }
/* Narrow screens: no room beside the title, so the status drops to a thin line
   of its own under it. It used to be `display: none` here, which meant no phone
   ever showed the LIVE line — the one screen size where "on every screen" was
   quietly not true. `min-height` keeps the bar its usual height on a page with
   no status to show. */
@media (max-width: 760px) {
    .topbar {
        height: auto; min-height: var(--topbar-h);
        flex-wrap: wrap; row-gap: 2px;
        padding-top: 10px; padding-bottom: 8px;
    }
    .topbar-status {
        order: 99; flex: 1 1 100%;
        max-width: none; margin-left: 0; padding-left: 0;
        font-size: 12px;
    }
    .topbar-title { font-size: 16px; }
}

.topbar-title {
    /* The only title on the screen now that .page-head is gone, so it carries
       the weight the in-page heading used to. */
    font-size: 18px;
    font-weight: 700;
    letter-spacing: -.01em;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.icon-btn {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-2);
    cursor: pointer;
    flex: none;
    transition: background .12s ease, color .12s ease;
}

.icon-btn:hover { background: var(--surface-2); color: var(--text); }

.topbar-user { display: flex; align-items: center; gap: 10px; flex: none; }

.user-meta { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }

.user-name {
    font-weight: 600;
    font-size: 13.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-role { color: var(--muted); font-size: 11.5px; }

.avatar {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--accent-text);
    font-weight: 700;
    font-size: 14px;
    flex: none;
}

.content { flex: 1; padding: var(--gap); }

/* ----------------------------------------------------------- page header */
/* Controls that live in a card heading — a search box, a filter, an Add
   button. `.card-head` is already `space-between`, so the title stays left and
   these sit right, which is where they were when the page had its own heading.

   They must NOT stretch: `.input`/`.select` are `width: 100%`, and in a flex
   row with nothing to balance them one control would take the whole width and
   push the rest onto a second line. Their own min-width still applies. */
.card-head .page-actions > .input,
.card-head .page-actions > .select { width: auto; }

.page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    margin-bottom: 22px;
    flex-wrap: wrap;
}

/* The title block absorbs the spare width; the actions keep their natural
   size. Without this the text block sized to its content, squeezed .page-actions
   and the search box wrapped onto its own line above the button. */
.page-head > :first-child {
    flex: 1 1 300px;
    min-width: 0;
}

.page-eyebrow {
    display: none;
    color: var(--accent);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    margin-bottom: 3px;
}

/* Headings read as they are written — no capitals forced on them. The words
   are the markup's own; `text-transform` used to rewrite them. */
.page-title {
    font-size: 25px;
    font-weight: 700;
    letter-spacing: -.01em;
}

.page-sub {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 6px 0 0;
    color: var(--text-2);
    font-size: 13.5px;
}

.page-sub .ico { width: 16px; height: 16px; color: var(--muted); }

/* nowrap: the search field and the primary action stay on one line. They only
   drop below the title at the narrow breakpoint, where wrapping is intended. */
.page-actions {
    display: flex;
    gap: 10px;
    align-items: center;
    flex: 0 0 auto;
    flex-wrap: nowrap;
}

/* ----------------------------------------------------------------- cards */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    margin-bottom: var(--gap);
    overflow: hidden;
}

.card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 16px 22px;
    flex-wrap: wrap;
}

/* The small dot, then the title as written: "Outlet Ranking", not OUTLET
   RANKING. Large and bold, the way management's reference (Microsoft's own
   site) sets its headings; it used to be 12px tracked capitals. */
.card-title {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--text);
}

.card-title::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    flex: none;
}

.card-body { padding: 0 22px 22px; }

.card-text { margin: 0 0 4px; color: var(--text-2); }

.muted { color: var(--muted); font-size: 12.5px; margin: 0; }

/* ----------------------------------------------------------------- stats */
.stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px;
    margin-bottom: var(--gap);
}

.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 17px 19px;
    box-shadow: var(--shadow-sm);
}

/* As written, like the card titles — it was tracked capitals. */
.stat-label {
    color: var(--text-2);
    font-size: 12.5px;
    font-weight: 600;
}

.stat-value {
    font-size: 27px;
    font-weight: 700;
    letter-spacing: -.02em;
    margin-top: 3px;
}

/* --------------------------------------------------------------- buttons */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 9px 17px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background .12s ease, border-color .12s ease;
}

.btn:hover:not(:disabled) { background: var(--surface-2); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-text);
    box-shadow: 0 1px 2px rgba(124, 58, 237, .3);
}

.btn-primary:hover:not(:disabled) {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
}

.btn-danger { background: var(--red); border-color: var(--red); color: #fff; }
.btn-danger:hover:not(:disabled) { filter: brightness(.93); background: var(--red); }

.btn-sm { padding: 6px 11px; font-size: 12.5px; }

/* Square icon action, used in table rows. */
.btn-icon {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-2);
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}

.btn-icon:hover { background: var(--surface-2); color: var(--text); }

.btn-icon.is-danger:hover {
    background: var(--red-soft);
    border-color: var(--red-soft);
    color: var(--red);
}

/* ---------------------------------------------------------------- tables */
.table-wrap { overflow-x: auto; }

.table { width: 100%; border-collapse: collapse; font-size: 14px; }

.table th {
    text-align: left;
    padding: 12px 22px;
    background: var(--surface-2);
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    color: var(--text-2);
    font-weight: 600;
    font-size: 13px;
    white-space: nowrap;
}

.table td {
    padding: 15px 22px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
    color: var(--text-2);
}

.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover td { background: var(--surface-2); }

.table .num { text-align: right; font-variant-numeric: tabular-nums; }

.table .actions { text-align: right; white-space: nowrap; }

.actions-cell { display: inline-flex; gap: 7px; }

.row-title { font-weight: 600; color: var(--text); }
.row-sub { color: var(--muted); font-size: 12.5px; }

/* ------------------------------------------------------------ pagination */
.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 15px 22px;
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
}

.pager-info { color: var(--text-2); font-size: 13.5px; }

.pager-controls { display: flex; align-items: center; gap: 9px; }

.pager-page { color: var(--text-2); font-size: 13.5px; }

/* --------------------------------------------------------------- badges */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 3px 11px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    background: var(--surface-2);
    color: var(--text-2);
    white-space: nowrap;
}

.badge + .badge { margin-left: 5px; }

.badge-green { background: var(--green-soft); color: var(--green); }
.badge-amber { background: var(--amber-soft); color: var(--amber); }
.badge-red   { background: var(--red-soft);   color: var(--red); }
.badge-blue  { background: var(--accent-soft); color: var(--accent); }

/* ---------------------------------------------------------------- forms */
.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 16px;
}

.field { display: flex; flex-direction: column; gap: 6px; }
.field-wide { grid-column: 1 / -1; }

.label { font-size: 13px; font-weight: 600; color: var(--text); }

.input,
.select {
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 14px;
}

.input:focus,
.select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

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

.hint { color: var(--muted); font-size: 12px; }

.check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.check input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }

.search { width: 250px; min-width: 170px; }

.toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* ----------------------------------------------------------------- modal */
.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(20, 20, 30, .5);
    display: none;
    align-items: flex-start;
    justify-content: center;
    padding: 44px 16px;
    overflow-y: auto;
    z-index: 60;
}

.modal-backdrop.is-open { display: flex; }

.modal {
    width: 100%;
    max-width: 620px;
    background: var(--surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.modal-wide { max-width: 1000px; }

.modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 22px;
    border-bottom: 1px solid var(--border);
}

.modal-title { font-size: 16px; font-weight: 700; }

.modal-close {
    border: 0;
    background: none;
    font-size: 23px;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    padding: 0 4px;
}

.modal-close:hover { color: var(--text); }

.modal-body { padding: 22px; max-height: 64vh; overflow-y: auto; }

/* .card-foot is the same bar outside a modal — a card whose actions sit at the
   bottom rather than in the header. */
.modal-foot,
.card-foot {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 16px 22px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
}

/* Textareas share .input, but need a sane starting height and no sideways
   drag (which would break the card's layout). */
textarea.input {
    min-height: 96px;
    resize: vertical;
    line-height: 1.5;
}

/* --- msel: a multi-select dropdown (app.msel) ------------------------
   Closed it is a single line the same size as a .select; open, its panel is
   overlaid rather than inserted, so opening a filter never moves the buttons
   underneath it.                                                           */
.msel {
    position: relative;
    min-height: 38px;
}

.msel-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}

.msel-toggle:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* An untouched filter and a set one must not look alike. */
.msel-toggle.is-set { border-color: var(--accent); color: var(--accent); font-weight: 600; }

.msel-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.msel-caret { flex: none; color: var(--muted); }
.msel.is-open .msel-caret { transform: rotate(180deg); }

.msel-panel {
    display: none;
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 40;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
    /* The panel follows the field's width, but a 150px filter column is
       narrower than its own footer -- "Select shown / Clear / Done" spilled
       out of the right-hand border. The floor is what those three need on one
       line, so the panel can be wider than the field it hangs from but never
       narrower. */
    min-width: max(100%, 252px);
}

.msel.is-open .msel-panel { display: flex; }

.msel-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px;
    border-bottom: 1px solid var(--border);
    flex: 0 0 auto;
}

.msel-head .msel-search { flex: 1 1 auto; min-width: 0; }

.msel-list {
    flex: 1 1 auto;
    max-height: 220px;
    overflow-y: auto;
    padding: 4px 0;
}

.msel-row {
    display: flex;
    width: 100%;
    padding: 5px 10px;
    font-size: 13px;
    gap: 8px;
}

.msel-row:hover { background: var(--surface-2); }
.msel-name { overflow: hidden; text-overflow: ellipsis; }
.msel-sub {
    color: var(--muted);
    font-size: 12px;
    margin-left: auto;
    padding-left: 8px;
    white-space: nowrap;
}
.msel-none { padding: 14px 10px; text-align: center; }

/* The two segments inside an open panel: what is already ticked, then the
   rest. The heading sticks while its own segment scrolls, so you can always
   see which half you are looking at. */
.msel-group {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 6px 10px 5px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    color: var(--text-2);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}

/* Capped and scrollable on its own: a long selection must not push the
   unticked options out of reach. */
.msel-picked {
    max-height: 132px;
    overflow-y: auto;
    border-bottom: 1px solid var(--border-strong);
}

.msel-picked .msel-name { color: var(--text); font-weight: 600; }

.msel-actions {
    display: flex;
    flex-wrap: wrap;   /* a narrow viewport stacks them rather than spilling */
    gap: 8px;
    padding: 8px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
    flex: 0 0 auto;
}

.btn-sm { padding: 4px 10px; font-size: 12px; }

/* A card holding dropdowns must not clip them. .card hides overflow so its
   rounded corners crop the table inside; a card of msels has no table and
   does need its panels to escape. */
.card-dropdowns { overflow: visible; }

/* The filter row: four msels side by side, wrapping to two and then one as
   the window narrows. Its own grid rather than .form-grid, whose 230px
   minimum would let five squeeze on to a very wide screen. */
.msel-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 16px;
}

@media (max-width: 1200px) {
    .msel-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
    .msel-grid { grid-template-columns: minmax(0, 1fr); }
}

/* A denser page header, for screens whose point is the table underneath it.
   The title, the eyebrow and the filter bar are all navigation — on a report
   they should cost as little of the first screen as possible, so the data
   starts near the top rather than a third of the way down. Opted into with
   `{% block content_class %}is-tight{% endblock %}`. */
.content.is-tight { padding: 14px 22px 22px; }
.content.is-tight .page-head { margin-bottom: 12px; }
.content.is-tight .page-title { font-size: 21px; }
.content.is-tight .page-eyebrow { font-size: 10.5px; margin-bottom: 1px; }
.content.is-tight .page-sub { margin-top: 2px; font-size: 12.5px; }
.content.is-tight .page-sub .ico { width: 14px; height: 14px; }

/* The filter card is a control strip, not a panel: no title bar, and only
   enough padding to keep the inputs off the edges. The labels above each
   control already say what it is, so a "Filters" heading is a whole row of
   the fold spent saying nothing.

   (Not `:first-of-type` — that counts tags, not classes, and the first div
   child here is the page header, so it matched nothing at all.) */
.content.is-tight .card-dropdowns { margin-bottom: 14px; }
.content.is-tight .card-dropdowns > .card-head { display: none; }
.content.is-tight .card-dropdowns > .card-body { padding: 14px 16px; }

/* One size down again, on top of `is-tight`. Product Analysis carries six
   filters, six KPI cards, a forty-column matrix and six panels; at the
   standard scale the chrome — page header, filter card, card heads — used the
   whole first screen and the matrix, the reason the page exists, opened below
   the fold. Everything here is padding and type size; no layout changes, so a
   screen can opt in with `is-tight is-dense` and nothing else. */
.content.is-dense { padding: 10px 18px 18px; }
.content.is-dense .page-head { margin-bottom: 9px; }
.content.is-dense .page-title { font-size: 18px; }
.content.is-dense .page-sub { font-size: 11.5px; }
.content.is-dense .card { margin-bottom: 12px; }
.content.is-dense .card-dropdowns { margin-bottom: 11px; }
.content.is-dense .card-dropdowns > .card-body { padding: 10px 12px; }
.content.is-dense .card-head { padding: 10px 14px; gap: 10px; }
/* As written here too, a step under the 17px standard so the dense heads stay
   short. (11px was right for capitals; normal case at 11px reads as a note.) */
.content.is-dense .card-title { font-size: 14px; }
.content.is-dense .card-body { padding: 0 14px 14px; }

.content.is-dense .filter-bar { gap: 10px; padding-top: 8px; }
.content.is-dense .field { gap: 4px; }
.content.is-dense .label { font-size: 11.5px; }
.content.is-dense .input,
.content.is-dense .msel-toggle { padding: 6px 9px; font-size: 12.5px; }
.content.is-dense .msel { min-height: 32px; }

.content.is-dense .kpi-row {
    grid-template-columns: repeat(auto-fit, minmax(146px, 1fr));
    gap: 10px;
    margin-bottom: 12px;
}
.content.is-dense .kpi { padding: 9px 11px; }
.content.is-dense .kpi-label { font-size: 11px; margin-bottom: 3px; }
.content.is-dense .kpi .delta { padding: 0 6px; }
.content.is-dense .kpi-value { font-size: 17px; }
.content.is-dense .kpi-foot { margin-top: 3px; gap: 5px; }
.content.is-dense .kpi-sub,
.content.is-dense .delta { font-size: 10.5px; }

/* Plain tables too, not only the ones inside a `dash-split`. The base cell is
   15px/22px, which is right for a six-row master list and far too airy for a
   report: it put half a screen of white space between eight rows of data.
   The `dash-split` and `cat-table` rules below are tighter still and, being
   later and no less specific, keep their own values. */
.content.is-dense .table th { padding: 8px 14px; font-size: 11.5px; }
.content.is-dense .table td { padding: 7px 14px; }
.content.is-dense .table { font-size: 13px; }
.content.is-dense .row-sub { line-height: 1.35; }

.content.is-dense .dash-split { margin-bottom: 12px; gap: 12px; }
.content.is-dense .dash-split .card-head { padding: 9px 12px; }
.content.is-dense .dash-split .table { font-size: 12px; }
.content.is-dense .dash-split .table th { padding: 6px 11px; font-size: 10.5px; }
.content.is-dense .dash-split .table td { padding: 5px 11px; }
.content.is-dense .dash-split .table-wrap { max-height: 340px; }
.content.is-dense .row-sub { font-size: 10px; }
.content.is-dense .dash-foot { margin: 0 2px 16px; font-size: 11px; }

/* =========================================================== dashboards ==
   The Sales Performance screen. Layout only — every colour comes from the
   same tokens the rest of the app uses, so both themes follow automatically.
   ======================================================================== */
.filter-bar {
    display: grid;
    grid-template-columns: 210px repeat(3, minmax(0, 1fr));
    gap: 16px;
    align-items: end;
    padding-top: 18px;
}

@media (max-width: 1000px) {
    .filter-bar { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 620px) {
    .filter-bar { grid-template-columns: minmax(0, 1fr); }
}

/* ---- The Coimbatore Bakery switch ----
   Shared by every screen that has one, so the control reads the same
   wherever it appears. Overall Performance and the two penetration screens
   keep their own earlier copies (.op-switch, .snp-switch); this is the one
   new screens use. Sized like the inputs beside it. */
.bak-switch {
    display: inline-flex; align-items: center; gap: 8px; height: 38px;
    padding: 0 12px 0 9px; border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); background: var(--surface);
    color: var(--text-2); font: inherit; font-size: 13.5px; font-weight: 600;
    white-space: nowrap; cursor: pointer;
}
.bak-switch:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }
.bak-switch-track { position: relative; flex: none; width: 30px; height: 18px;
    border-radius: 9px; background: var(--border-strong); transition: background .15s; }
.bak-switch-knob { position: absolute; top: 2px; left: 2px; width: 14px; height: 14px;
    border-radius: 50%; background: var(--surface); box-shadow: 0 1px 2px rgba(0,0,0,.25);
    transition: transform .15s; }
.bak-switch.is-on { border-color: var(--accent); color: var(--accent); }
.bak-switch.is-on .bak-switch-track { background: var(--accent); }
.bak-switch.is-on .bak-switch-knob { transform: translateX(12px); }
/* Said on the page as well as on the switch, so a screenshot of these figures
   cannot be mistaken for the usual Bakery-free ones. */
.bak-note { color: var(--accent); font-weight: 600; }

/* ---- KPI cards ---- */
/* This page carries far more numbers than any other screen, so it runs a size
   down throughout — cards, rows and gaps. At the app's default scale six KPI
   cards and four tables filled two and a half screens. */
.dash-gap { --gap: 16px; }

.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
    gap: 14px;
    margin-bottom: 16px;
}

.kpi {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
    padding: 12px 14px;
}

/* As written — "Sep 2026 MTD Sales (₹)" — and a size up from the 10px
   capitals it was: it is the line that says what the figure is. */
.kpi-label {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-2);
    margin-bottom: 5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The figure a size up from 21px: it is what the card is for. */
.kpi-value { font-size: 24px; font-weight: 700; line-height: 1.15; }
.kpi-value.is-up { color: var(--green); }
.kpi-value.is-down { color: var(--red); }

.kpi-foot {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 5px;
    flex-wrap: wrap;
}

.kpi-sub { font-size: 11.5px; color: var(--muted); }

/* A growth figure: up is green, down is red, and "no basis to compare" is a
   grey dash rather than a green zero. */
.delta { font-size: 12px; font-weight: 600; white-space: nowrap; }
.delta.is-up { color: var(--green); }
.delta.is-down { color: var(--red); }
.delta.is-flat { color: var(--muted); }

/* In a KPI card the growth figure is a pill — coloured text alone was a thin
   line of red and green the eye skipped. Cards only: a pill in every table
   row would be noise. */
.kpi .delta { padding: 1px 7px; border-radius: 999px; }
.kpi .delta.is-up   { background: var(--green-soft); }
.kpi .delta.is-down { background: var(--red-soft); }

/* ---- main grid: table beside the side panels ---- */
/* The main table and the side panels.

   Every panel here scrolls inside a bounded height rather than growing the
   page. Five regions and 322 outlets are the same component, and without a
   cap the Locations tab ran several screens long while the Regions tab left
   a hole beside the side column. A fixed frame fixes both at once, and the
   header stays put while the rows move under it. */
.dash-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 340px;
    gap: 16px;
    align-items: start;
    margin-bottom: 16px;
}

.dash-split { margin-bottom: 16px; }

/* Sized so the default view fits without scrolling — five regions here, six
   rows in the compact cards — and anything longer (13 cities, 322 outlets)
   scrolls inside the same frame instead of stretching the page. */
.dash-grid > .card > .table-wrap { max-height: 490px; overflow-y: auto; }
/* The side column is sized to land near the main table's height, so the two
   end together instead of leaving a slab of empty page beside the shorter
   one. Both scroll inside, which is why they can be capped at all. */
.dash-side .table-wrap { max-height: 245px; overflow-y: auto; }
.dash-split .table-wrap { max-height: 460px; overflow-y: auto; }
.dash-side #insights { max-height: 175px; overflow-y: auto; }

/* Sticky headers, so a scrolled table still says which column is which. */
.dash-grid .table th,
.dash-side .table th,
.dash-split .table th {
    position: sticky;
    top: 0;
    z-index: 1;
}

/* The TOTAL line stays visible at the foot of a scrolling table — it is the
   number most people came for. */
.dash-grid .table .row-total td,
.dash-split .table .row-total td {
    position: sticky;
    bottom: 0;
    z-index: 1;
    border-top: 1px solid var(--border-strong);
}

.dash-side { display: flex; flex-direction: column; gap: 16px; }

/* Trend beside the donut: the chart needs the room, the donut does not. */
.dash-split {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: 16px;
    align-items: start;
}

/* Two tables of equal weight — five money columns each, so an uneven split
   clips the last one. */
.dash-split.is-even { grid-template-columns: repeat(2, minmax(0, 1fr)); }

@media (max-width: 1150px) {
    .dash-grid,
    .dash-split { grid-template-columns: minmax(0, 1fr); }
}

.dash-foot { margin: 4px 2px 24px; font-size: 12px; }

.dash-grid .table,
.dash-side .table,
.dash-split .table { font-size: 13px; }

.dash-grid .table th,
.dash-split .table th { padding: 9px 14px; font-size: 11.5px; }

.dash-grid .table td,
.dash-split .table td { padding: 8px 14px; }

.dash-side .table th { padding: 8px 10px; font-size: 11px; }
.dash-side .table td { padding: 8px 10px; }

/* Headers and the first column carry the labels — region, city, outlet,
   product. Bold makes them the frame the numbers hang off, rather than
   competing with them for attention. */
.dash-grid .table th,
.dash-split .table th,
.dash-side .table th { font-weight: 700; color: var(--text); }

.dash-grid .table td:first-child,
.dash-split .table td:first-child,
.dash-side .table td:first-child,
.dash-side .table .row-title { font-weight: 700; color: var(--text); }

.dash-grid .card-head,
.dash-split .card-head,
.dash-side .card-head { padding: 12px 16px; }

.dash-split .card-body,
.dash-side .card-body { padding: 0 16px 16px; }

.filter-bar { padding: 14px 16px 16px; gap: 14px; }

/* ---- Category Sales ----
   This screen is one dense grid, so it runs tighter than the rest: the filter
   bar is a single row and the table's own padding is cut back, because a
   month block of four categories should be readable without scrolling. */
.cat-filters {
    display: grid;
    /* From, To, Region, Category, Type, Bakery, Non-Core, and the two buttons
       in one cell. The months are fixed rather than shares: a month face reads
       "Sep 2026" and never needs more, so stretching them only pushed the two
       pickers — which carry real lists — into the space the months were
       wasting. Eighth track added 2026-09-29 with the Non-Core switch. */
    grid-template-columns: 170px 170px minmax(0, 1fr) minmax(0, 1fr)
                           auto auto auto auto;
    gap: 12px;
    align-items: end;
    padding: 0;
}

@media (max-width: 1250px) {
    .cat-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 620px) {
    .cat-filters { grid-template-columns: minmax(0, 1fr); }
}

.cat-filters .card-title,
.cat-filters .label { margin-bottom: 2px; }

.month-pick { display: flex; align-items: center; gap: 5px; position: relative; }
.month-pick .input { flex: 1 1 auto; min-width: 0; padding: 7px 9px; }
.month-pick .icon-btn { flex: none; }
.month-pick .icon-btn:disabled { opacity: .4; cursor: default; background: none; }

/* The face and the real input.

   The hidden `<input type="month">` only holds the value; the button in front
   of it carries the short "Aug 2026" label and opens app.monthPicker. The
   browser's own picker is not used: Safari on the Mac and Firefox have none
   for a month input, so clicking opened nothing there. */
.month-face {
    text-align: center;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
}

.month-face:hover { border-color: var(--accent); color: var(--accent); }

.month-native {
    position: absolute;
    left: 50%;
    bottom: 0;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    opacity: 0;
    pointer-events: none;
}

/* app.monthPicker's panel: a year with arrows either side, then the twelve
   months. Fixed and hung off <body>, so it sits above modals (60) but under
   toasts and the tour. */
.mpick-panel {
    position: fixed;
    z-index: 70;
    width: 252px;
    padding: 8px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
}

.mpick-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}

.mpick-step { width: 30px; height: 30px; }
.mpick-step:disabled { opacity: .35; cursor: default; background: none; }
.mpick-year { font-weight: 700; font-size: 14px; color: var(--text); }

.mpick-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 4px;
}

.mpick-month {
    padding: 8px 0;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.mpick-month:hover:not(:disabled) { background: var(--surface-2); }
.mpick-month:focus-visible { outline: none; border-color: var(--accent); }
.mpick-month.is-on { background: var(--accent); color: var(--accent-text); font-weight: 600; }
.mpick-month:disabled { color: var(--muted); opacity: .45; cursor: default; }
.mpick-clear { width: 100%; margin-top: 8px; }

/* The graph/table switch beside the page title. Bigger than a row action —
   it is the way into the charts, not a tidy-up beside a table row — and its
   icon is drawn in the series colours rather than one flat stroke. */
.view-toggle {
    width: 42px;
    height: 42px;
    border-radius: var(--radius-sm);
}

.view-icon { width: 24px; height: 24px; display: block; }

/* Tinted, not filled: a solid accent background would swallow the coloured
   bars the icon exists for. */
.view-toggle.is-on {
    background: var(--accent-soft);
    border-color: var(--accent);
}

.view-toggle:hover { border-color: var(--accent); }

.vi-axis {
    fill: none;
    stroke: var(--text-2);
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Set outright, not from the theme tokens: those are tuned for text and large
   areas, and at 24px the darker greens and ambers read as grey. An icon needs
   its colour to survive being small. */
.vi-bar-1 { fill: #7c3aed; }
.vi-bar-2 { fill: #10b981; }
.vi-bar-3 { fill: #f59e0b; }
[data-theme="dark"] .vi-bar-1 { fill: #a78bfa; }
[data-theme="dark"] .vi-bar-2 { fill: #34d399; }
[data-theme="dark"] .vi-bar-3 { fill: #fbbf24; }
.radio-row { display: flex; gap: 14px; align-items: center; min-height: 34px; }
.radio-row input { accent-color: var(--accent); }
/* The two buttons share one grid cell and sit on the same baseline as the
   inputs, instead of each claiming a row of its own. */
.cat-export {
    justify-self: end;
    flex-direction: row;
    align-items: end;
    gap: 8px;
}

/* The table is wide (a column per region) and long (a block per month), so it
   scrolls in both directions with the header and the Date column pinned. */
.cat-wrap { max-height: 620px; overflow: auto; }

/* Every cell is ruled. A grid this wide is read by tracking across a row and
   down a column, and hairlines between only some of them made that guesswork. */
.cat-table th,
.cat-table td {
    border: 1px solid var(--border-strong);
    padding: 7px 12px;
    font-size: 13px;
}

.cat-table th {
    position: sticky;
    top: 0;
    z-index: 2;
    font-weight: 700;
    color: var(--text);
    padding: 9px 12px;
}

.cat-table td.cat-date,
.cat-table th.cat-date {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--surface);
    vertical-align: middle;
    text-align: center;
    font-weight: 700;
    color: var(--text);
    white-space: nowrap;
}
.cat-table th.cat-date { z-index: 3; background: var(--surface-2); }
.cat-total-col { background: var(--surface-2); font-weight: 700; color: var(--text); }

/* Total rows carry a tint, not just bold text.

   They used to sit on `--surface-2` — #fafafa against a white table, a
   one-value difference that vanished. A month's total is the line most people
   scan for, so it gets a real background, and the grand total a stronger one
   so the two are told apart at a glance rather than by reading them. */
.cat-table .row-subtotal td,
.cat-table .row-total td {
    font-weight: 700;
    color: var(--text);
    border-top: 2px solid var(--border-strong);
}

/* One month's total. `--accent-soft` (#f3eeff) is too close to white to
   register as a fill in light mode, though it reads fine in dark mode, so
   light mode gets its own stronger tint rather than darkening the shared
   token — `--accent-soft` is used elsewhere as a pale hover/badge fill that
   must stay pale there. */
.cat-table .row-subtotal td { background: #e4d9fb; }
[data-theme="dark"] .cat-table .row-subtotal td { background: var(--accent-soft); }

/* Everything in view. Deeper, and ruled off above so it reads as the end of
   the table rather than another month. */
.cat-table .row-total td {
    background: var(--accent);
    color: var(--accent-text);
    border-top-width: 3px;
}

/* The Total column sets its own `--surface-2` background so it reads as a
   summary column; on a total row that would punch a plain hole through the
   coloured band, so the row wins there. (Neither total row carries a
   `.cat-date` cell, so the sticky Date column needs no such override.) */
.cat-table .row-subtotal td.cat-total-col { background: #e4d9fb; }
[data-theme="dark"] .cat-table .row-subtotal td.cat-total-col { background: var(--accent-soft); }

.cat-table .row-total td.cat-total-col {
    background: var(--accent);
    color: var(--accent-text);
}

/* Hover must not repaint a total row: it is a summary, not a row you act on. */
.cat-table tbody tr.row-subtotal:hover td { background: #e4d9fb; }
[data-theme="dark"] .cat-table tbody tr.row-subtotal:hover td { background: var(--accent-soft); }
.cat-table tbody tr.row-total:hover td { background: var(--accent); }

/* A chip per category, coloured from a fixed palette by position, so the same
   category is the same colour in every month block. */
.cat-chip {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 700;
    white-space: nowrap;
}

.chip-0 { background: var(--accent-soft); color: var(--accent); }
.chip-1 { background: var(--green-soft);  color: var(--green); }
.chip-2 { background: var(--amber-soft);  color: var(--amber); }
.chip-3 { background: var(--red-soft);    color: var(--red); }
.chip-4 { background: var(--accent-soft); color: var(--blue); }
.chip-5 { background: var(--green-soft);  color: var(--text-2); }
.chip-6 { background: var(--amber-soft);  color: var(--text-2); }
.chip-7 { background: var(--red-soft);    color: var(--text-2); }

/* ======================================================== skeleton loading ==
   A grey stand-in the shape of the thing that is coming, rather than a
   spinner. The page keeps its layout while it loads, so nothing jumps when
   the data lands — and the wait reads as "this is filling in" rather than
   "this is stuck".

   One shimmer, on a token colour, so both themes follow automatically.
   ======================================================================== */
.skel {
    position: relative;
    overflow: hidden;
    background: var(--surface-2);
    border-radius: var(--radius-sm);
}

.skel::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg,
                transparent, rgba(127, 127, 127, .13), transparent);
    animation: skel-sweep 1.25s ease-in-out infinite;
}

@keyframes skel-sweep {
    100% { transform: translateX(100%); }
}

/* Respect a reader who has asked the OS for less motion: the block still
   shows, it just stops sweeping. */
@media (prefers-reduced-motion: reduce) {
    .skel::after { animation: none; }
}

.skel-line { height: 11px; border-radius: 5px; }
.skel-line.is-sm { height: 9px; }
.skel-line.is-lg { height: 17px; }

/* A KPI card's worth: label, value, footnote. */
.skel-kpi { display: flex; flex-direction: column; gap: 8px; }
.skel-kpi .skel-line:nth-child(1) { width: 55%; }
.skel-kpi .skel-line:nth-child(2) { width: 78%; }
.skel-kpi .skel-line:nth-child(3) { width: 40%; }

/* A panel body — a chart, a donut, a stock summary. */
.skel-block { width: 100%; height: 190px; }

/* Table rows. The first cell is a label and the rest are numbers, so the
   first stand-in is wide and left-aligned and the others are short and
   right-aligned — the shape of the row that replaces it. */
.skel-cell .skel-line { width: 62%; }
.skel-cell.num { display: flex; justify-content: flex-end; }
.skel-cell.num .skel-line { width: 48px; }

/* ---- Promoter / visit reports ---- */
/* Seven controls on one row: the date needs its arrows, the rest share what
   is left, and the button gets a fixed slot so it never wraps alone. */
.pv-filters { grid-template-columns: 190px repeat(5, minmax(0, 1fr)) auto; }

@media (max-width: 1350px) {
    .pv-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .pv-actions { grid-column: 1 / -1; justify-content: flex-start; }
}

@media (max-width: 700px) {
    .pv-filters { grid-template-columns: minmax(0, 1fr); }
}

/* The actions slot in a filter bar. `gap` because it now holds two buttons on
   most screens — Clear plus that page's export — where it used to hold one. */
.pv-actions { flex-direction: row; align-items: end; justify-content: flex-end; gap: 10px; }
.pv-actions .btn { white-space: nowrap; }
/* Sales Performance uses the shared .filter-bar, whose four tracks were full
   before the buttons moved in. SEVEN tracks now — the Non-Core switch joined
   the Bakery one (2026-09-29) and a seventh item on six tracks dropped the
   buttons onto a line of their own. The date holds a fixed slot, the three
   pickers share what is left, and each switch and the buttons take their own
   width, so the whole bar stays on one row. */
.card-dropdowns .filter-bar:not([class*="-filters"]) {
    grid-template-columns: 150px repeat(3, minmax(0, 1fr)) auto auto auto;
}
/* Below this the three pickers drop under ~180px with the switch and both
   buttons still holding their own width, so the row breaks up instead. */
@media (max-width: 1200px) {
    .card-dropdowns .filter-bar:not([class*="-filters"]) {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
    .card-dropdowns .filter-bar:not([class*="-filters"]) > .pv-actions {
        grid-column: 1 / -1;
        justify-content: flex-start;
    }
}
/* ...and that rule outranks .filter-bar's own breakpoints, so without these it
   kept five tracks on a phone: the three pickers were crushed to 23px with
   their labels printed over each other. */
@media (max-width: 1000px) {
    .card-dropdowns .filter-bar:not([class*="-filters"]) {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    .card-dropdowns .filter-bar:not([class*="-filters"]) > .pv-actions { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
    .card-dropdowns .filter-bar:not([class*="-filters"]) { grid-template-columns: minmax(0, 1fr); }
}

/* A sortable header says so, and shows which way it is sorted. */
.pv-table th.is-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.pv-table th.is-sortable::after { content: " ⇅"; color: var(--muted); font-size: 10px; }
.pv-table th.is-asc::after { content: " ↑"; color: var(--accent); }
.pv-table th.is-desc::after { content: " ↓"; color: var(--accent); }

/* ---- the week calendar ---- */
/* A dropdown can only offer consecutive weeks. Real rounds skip weeks, so the
   choice is a calendar and the weeks it holds need not touch each other. */
.wk-field { position: relative; }

.wk-open { text-align: left; cursor: pointer; }
.wk-open:hover { border-color: var(--accent); }

.wk-cal {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 40;
    width: 268px;
    padding: 10px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
}

.wk-cal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}

.wk-cal-month { font-size: 13px; font-weight: 700; }

.wk-dow,
.wk-week {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.wk-dow {
    margin-bottom: 4px;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--muted);
    text-align: center;
}

.wk-weeks { display: flex; flex-direction: column; gap: 3px; }

/* The whole week is the target, not the day: a schedule is weekly, so a day
   is not a thing you can pick here. */
.wk-week {
    width: 100%;
    padding: 4px 2px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    font: inherit;
    cursor: pointer;
}

.wk-week:hover { background: var(--surface-2); border-color: var(--border-strong); }

/* A finished week is shown but cannot be picked — hiding it would leave a
   gap in the month and make the calendar harder to read, not easier. */
.wk-week.is-past { opacity: .38; cursor: not-allowed; }
.wk-week.is-past:hover { background: none; border-color: transparent; }
.wk-week.is-chosen { background: var(--accent-soft); border-color: var(--accent); }

/* The week on screen is always saved, so it reads as chosen and refuses to
   be toggled off — a dashed edge says "fixed", not "available". */
.wk-week.is-open {
    background: var(--accent-soft);
    border-style: dashed;
    border-color: var(--accent);
    cursor: default;
}

.wk-day { font-size: 11.5px; text-align: center; color: var(--text); line-height: 1.6; }
.wk-day.is-outside { color: var(--muted); opacity: .55; }
.wk-day.is-today { font-weight: 700; color: var(--accent); }

.wk-cal-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
}

/* The chosen weeks, spelled out under the selection so the choice survives
   closing the calendar. */
.wk-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }

.wk-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 6px 3px 10px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
}

/* The week on screen: quiet, and it keeps its place in the row rather than
   being pulled to the front. */
.wk-chip.is-open { padding-right: 10px; background: var(--surface-2); color: var(--text-2); }
.wk-chip-here { font-weight: 600; }

/* No separate "edited" mark. Every week picked here is unsaved until Save, so
   the mark fired on all of them and said nothing; the badge already reports
   how many weeks are pending. Selected is the only state worth showing. */

/* The label is the way into that week, so it looks like a link and not like
   part of the chip's padding. */
.wk-chip-open {
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    font-weight: 600;
    padding: 0;
    cursor: pointer;
    text-decoration: underline;
    text-decoration-color: transparent;
    text-underline-offset: 3px;
}

.wk-chip-open:hover { text-decoration-color: currentColor; }

.wk-chip-x {
    border: 0;
    background: none;
    color: inherit;
    font-size: 14px;
    line-height: 1;
    padding: 0 2px;
    cursor: pointer;
}

.wk-chip-x:hover { color: var(--red); }

/* ---- "Same outlets every day" ---- */
/* Sits between the card title and the visit count, so it reads as an option
   on the board rather than another filter up in the selection panel. */
.mirror-check {
    margin-right: auto;
    padding-left: 14px;
    font-size: 12.5px;
    color: var(--text-2);
    cursor: pointer;
    white-space: nowrap;
}

.mirror-check input { accent-color: var(--accent); }

/* A followed day is shown, not hidden: you can see what it will be, but its
   controls are gone because Monday owns them. */
.day-col.is-locked { opacity: .82; }
.day-col.is-locked .day-head { color: var(--muted); }

.day-locked {
    display: block;
    text-align: center;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--accent);
    padding: 7px 0;
}

/* A day the user is on leave. A full day is closed for planning, so it is
   drawn as unavailable rather than merely annotated; a half day carries the
   tag alone and keeps every control. */
.day-col.is-leave { border-color: var(--amber); background: var(--amber-soft); }
.day-col.is-leave .day-head { background: transparent; }

.day-leave-tag {
    display: inline-block;
    margin-top: 5px;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--amber);
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .03em;
}
.day-leave-tag.is-half { background: var(--amber-soft); color: var(--amber); }

.day-leave-body {
    margin: auto;
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.day-leave-title { color: var(--amber); font-weight: 700; font-size: 13px; }
.day-leave-note { color: var(--muted); font-size: 11.5px; }

/* A promoter's day is full at one outlet. Grey, not accent: this is the
   normal finished state of the day, not something needing attention. */
.day-full {
    display: block;
    text-align: center;
    font-size: 11.5px;
    color: var(--muted);
    padding: 7px 0;
}

/* ---- CheckSum / Item Sync ---- */
/* POS | From | To | Region | City | Location | actions, on one line.
   POS and the two dates are fixed: a date input has an intrinsic width, and
   stretching it only pads the space around the calendar icon. The three
   pickers take equal shares of whatever is left, so the row stays balanced
   however many regions or outlets happen to be in the list. */
.cs-filters {
    grid-template-columns:
        168px 150px 150px minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) auto;
}
/* SSS Item Sync has one control more: its "Show all outlets" switch sits
   between the pickers and the buttons, at the height of everything else. */
.cs-filters.has-switch {
    grid-template-columns:
        150px 150px 150px minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) auto auto;
}
.cs-filters .bak-switch { height: 38px; }

/* One row means one height. A native <select>, a date <input> and the msel
   button all take the same padding but measure themselves slightly
   differently, and under `align-items: end` that difference reads as labels
   sitting at three heights. Pinning the control height settles it. */
.cs-filters .select,
.cs-filters .input,
.cs-filters .msel-toggle { height: 38px; }

/* Seven controls stop being legible before they stop fitting, so the pickers
   take a row of their own rather than each shrinking to a truncated label.
   The buttons stay up on the first row, where the eye already is. */
@media (max-width: 1350px) {
    .cs-filters { grid-template-columns: 168px 150px 150px minmax(0, 1fr); }
    .cs-filters .pv-actions { grid-column: 4; grid-row: 1; justify-self: end; }
    /* With the switch: the three pickers and the switch share the second row. */
    .cs-filters.has-switch {
        grid-template-columns: 168px 150px minmax(150px, 1fr) minmax(0, 1fr);
    }
}

@media (max-width: 1000px) {
    .cs-filters, .cs-filters.has-switch { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cs-filters .pv-actions { grid-column: 1 / -1; grid-row: auto;
                              justify-self: start; justify-content: flex-start; }
}

@media (max-width: 620px) {
    .cs-filters, .cs-filters.has-switch { grid-template-columns: minmax(0, 1fr); }
}

.cs-wrap { max-height: 620px; overflow: auto; }
.cs-wrap .pv-table th { position: sticky; top: 0; z-index: 2; }
/* SSS Item Sync: one line under the heading saying what Local and Web are. */
.cs-note { padding: 8px 16px; border-bottom: 1px solid var(--border); }
/* The outlet's name is the way into its products for that day. */
.cs-outlet-link {
    border: 0; padding: 0; background: none; font: inherit; font-weight: 600;
    color: var(--accent); cursor: pointer; text-align: left;
}
.cs-outlet-link:hover, .cs-outlet-link:focus-visible { text-decoration: underline; outline: none; }
.cs-when { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* Which of the two it reached then: the POS, or our own database. */
.cs-when-tag {
    display: inline-block; min-width: 30px; margin-right: 6px;
    font-size: 10px; font-weight: 700; letter-spacing: .04em; color: var(--muted);
}
/* A Web or SSS figure that differs from the outlet's own. The second
   selector is for the cell itself: `.table td` sets a colour of its own. */
.cs-off, .table td.cs-off { color: var(--red); font-weight: 700; }
/* A row can have one issue for the web and one for SSS. */
.cs-issues { display: flex; flex-wrap: wrap; gap: 4px; }
.cs-modal-body { padding: 14px 18px; }
.cs-modal-wrap { max-height: 52vh; overflow: auto; margin: 10px 0; }
.cs-modal-wrap .pv-table th { position: sticky; top: 0; z-index: 2; }
.cs-modal-wrap tfoot td { position: sticky; bottom: 0; background: var(--surface-2);
                          font-weight: 700; }

/* The sign is the diagnosis: positive means the outlet counted rows the web
   never received, which is the direction that loses sales. */
.cs-diff { font-weight: 700; font-variant-numeric: tabular-nums; }
.cs-diff.is-up { color: var(--red); }
.cs-diff.is-down { color: var(--amber); }

.cs-kind {
    display: inline-block;
    padding: 1px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    white-space: nowrap;
}

.cs-kind.is-mismatch { background: var(--amber-soft); color: var(--amber); }
.cs-kind.is-missing-web { background: var(--red-soft); color: var(--red); }
.cs-kind.is-missing-local { background: var(--accent-soft); color: var(--accent); }
/* Nothing wrong — an outlet-day listed only because "Show all outlets" is on. */
.cs-kind.is-match { background: var(--green-soft); color: var(--green); }

/* Item vs Sales: Date | Outlet | Credit switch | actions. The date keeps its
   intrinsic width, the picker takes what is left, the switch and the buttons
   are as wide as they are. */
.ivs-filters { grid-template-columns: 170px minmax(220px, 420px) auto minmax(0, 1fr); }
.ivs-filters .input,
.ivs-filters .msel-toggle { height: 38px; }
.ivs-filters .pv-actions { justify-self: end; }

@media (max-width: 1000px) {
    .ivs-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .ivs-filters .pv-actions { grid-column: 1 / -1; justify-self: start;
                               justify-content: flex-start; }
}

@media (max-width: 620px) {
    .ivs-filters { grid-template-columns: minmax(0, 1fr); }
}

/* A download in progress: the button carries a small spinner and cannot be
   pressed twice. White on the accent, so it reads on a primary button. */
.btn.is-loading { cursor: progress; opacity: .9; }
/* Two classes, so it outranks the plain `.spinner` rule further down the file. */
.spinner.btn-spinner { width: 12px; height: 12px; margin-right: 7px; vertical-align: -1px;
                       border-color: rgba(255, 255, 255, .4); border-top-color: #fff; }

/* ---- Attendance ---- */
.at-filters { grid-template-columns: 210px repeat(5, minmax(0, 1fr)) auto; }

@media (max-width: 1400px) {
    .at-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
    .at-filters { grid-template-columns: minmax(0, 1fr); }
}

/* The week is shown, not typed — it always spans Monday to Sunday, so a free
   text field would only invite entering something that is not a week. */
.at-week {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    font-weight: 600;
    cursor: default;
}

.at-chip {
    display: inline-block;
    padding: 1px 9px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 700;
    white-space: nowrap;
}

.at-chip.type-5 { background: var(--accent-soft); color: var(--accent); }
.at-chip.type-4 { background: var(--green-soft);  color: var(--green); }

/* Still checked in. Amber, not red: it is a gap in the record, not a failure. */
.at-open { color: var(--amber); font-weight: 600; }

/* A merchandiser's day: the name and the date span their visits, and a rule
   across the top of each block keeps one person's round visually together. */
.pv-table td.pv-span {
    vertical-align: middle;
    background: var(--surface-2);
    border-right: 1px solid var(--border-strong);
}

.pv-table tr.pv-group-start > td { border-top: 1px solid var(--border-strong); }
.pv-table tbody tr:first-child > td { border-top: 0; }

/* The row hover must not repaint a cell that belongs to several rows —
   otherwise hovering one visit lights up the whole group's name cell. */
.pv-table tbody tr:hover td.pv-span { background: var(--surface-2); }

.pv-table tfoot .row-total td {
    font-weight: 700;
    background: var(--surface-2);
    border-top: 1px solid var(--border-strong);
}

/* The "20 items" badge. A button, not a chip: it opens the breakdown, and
   anything clickable should look like it can be clicked. */
.pv-badge {
    border: 0;
    border-radius: 999px;
    padding: 3px 11px;
    background: var(--accent-soft);
    color: var(--accent);
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
}

.pv-badge:hover { background: var(--accent); color: var(--accent-text); }

/* Sized to the text beside it, so the button does not set the row height. */
.pv-photo-btn {
    display: inline-grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    background: var(--accent-soft);
    color: var(--accent);
    cursor: pointer;
}

.pv-photo-btn:hover { background: var(--accent); color: var(--accent-text); }

.pv-foot-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.pv-pager { display: flex; align-items: center; gap: 8px; }
.pv-pager .icon-btn:disabled { opacity: .4; cursor: default; }

/* The product breakdown scrolls inside the modal, so a visit with sixty
   products does not push the total off the bottom of the screen. */
.pv-modal-body { max-height: 60vh; overflow-y: auto; }
.pv-modal-body thead th { position: sticky; top: 0; z-index: 1; background: var(--surface); }
.pv-modal-body tfoot .row-total td {
    position: sticky;
    bottom: 0;
    font-weight: 700;
    background: var(--surface-2);
    border-top: 1px solid var(--border-strong);
}

/* Photos: one large, the rest as thumbnails under it. `contain`, not `cover`
   — these are evidence shots, so the whole frame matters more than filling
   a neat rectangle. */
.pv-photo-main {
    display: grid;
    place-items: center;
    height: 340px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    overflow: hidden;
}

.pv-photo-main img { max-width: 100%; max-height: 100%; object-fit: contain; }

.pv-photo-fail {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    color: var(--muted);
    font-size: 12.5px;
}

.pv-thumbs { display: flex; gap: 9px; margin-top: 10px; flex-wrap: wrap; }

.pv-thumb {
    width: 58px;
    height: 58px;
    padding: 0;
    border: 2px solid transparent;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    overflow: hidden;
    cursor: pointer;
}

.pv-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pv-thumb.is-active { border-color: var(--accent); }

/* ---- Cluster Matrix ---- */
/* Date, the four pickers, the Bakery switch and Clear, in one row. */
/* Eight items on one row — the Non-Core switch joined the Bakery one
   (2026-09-29) and the eighth was dropping Clear All onto its own line. */
.cm-filters { grid-template-columns: 150px repeat(4, minmax(0, 1fr)) auto auto auto; }

/* A step between one row and two columns: eight items over two columns is
   five rows, which is a lot of bar to scroll past on a laptop. */
@media (max-width: 1250px) {
    .cm-filters { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
    .cm-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 620px) {
    .cm-filters { grid-template-columns: minmax(0, 1fr); }
}

/* One colour per tier, used by the card, the badge and the bar alike, so the
   tier is the same green everywhere it appears. */
.tier-HTP  { --tier: var(--green);  --tier-soft: var(--green-soft); }
.tier-MTP  { --tier: var(--blue);   --tier-soft: var(--accent-soft); }
.tier-LTP  { --tier: var(--amber);  --tier-soft: var(--amber-soft); }
.tier-ULTP { --tier: var(--red);    --tier-soft: var(--red-soft); }

.cm-tier { background: var(--tier-soft); border-color: var(--tier); }
.cm-tier-head { display: flex; align-items: center; gap: 7px; }
.cm-tier-code { font-size: 14px; font-weight: 700; color: var(--text); }
.cm-tier-slab { font-size: 17px; font-weight: 700; color: var(--tier); margin: 2px 0; }
.cm-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--tier); flex: none; }

.cm-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--tier-soft);
    color: var(--tier);
    font-size: 11.5px;
    font-weight: 700;
}

.cm-wrap { max-height: 620px; overflow: auto; }
.cm-table th { position: sticky; top: 0; z-index: 2; }
.cm-row { cursor: pointer; }
.cm-row.is-open > td { background: var(--surface-2); }
.cm-caret { width: 34px; color: var(--muted); }
.cm-products { color: var(--text-2); font-size: 12px; }

/* The expanded outlet list sits in a full-width cell, so its own columns are
   free to differ from the cluster row's above it. */
.cm-detail-cell { padding: 0 !important; background: var(--surface-2); }
.cm-detail { padding: 10px 14px 14px; }
.cm-detail-head { font-size: 11.5px; color: var(--muted); margin-bottom: 8px; }
.cm-inner-wrap { max-height: 330px; overflow-y: auto; background: var(--surface);
                 border: 1px solid var(--border); border-radius: var(--radius-sm); }
.cm-inner th { position: sticky; top: 0; z-index: 1; background: var(--surface-2); }
.cm-inner td, .cm-inner th { padding: 5px 12px; font-size: 12px; }

.cm-bar { height: 8px; border-radius: 999px; background: var(--border); overflow: hidden;
          min-width: 160px; }
.cm-bar > span { display: block; height: 100%; background: var(--tier); }

/* ---- Sales Report ---- */
/* Seven fields, seven columns, one row: From and To need their own width (a
   date input does not shrink), the four pickers share what is left, and the
   buttons get a fixed slot rather than wrapping onto their own line when the
   row runs out of room. */
.sr-filters { grid-template-columns: 150px 150px repeat(4, minmax(0, 1fr)) 152px; }

/* A date input draws 2px taller than a picker toggle, which lifts the From
   and To labels off the line the others sit on. */
.sr-filters .input { height: 34px; }

/* Two rows of three (dates and Region; City, Location and Product), then the
   buttons on a line of their own. */
@media (max-width: 1350px) {
    .sr-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .sr-actions { grid-column: 1 / -1; justify-content: flex-start; }
}

@media (max-width: 1000px) {
    .sr-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
    .sr-filters { grid-template-columns: minmax(0, 1fr); }
}

.sr-actions { flex-direction: row; align-items: end; gap: 8px; justify-content: flex-end; }
.radio-row { flex-wrap: nowrap; white-space: nowrap; }

/* A Qty/Amount pair per product, so the frame scrolls in both directions
   with the header and the S.No column pinned — the same pattern as Category
   Sales. Two header rows: the product name runs across its pair and wraps
   there (a pair is wide enough to read a name across, where a single column
   was not, which is why names used to run sideways), and Qty/Amount sit under
   it, pinned just below the names. */
.sr-wrap { max-height: 640px; }
.sr-table th.sr-lead { text-align: center; vertical-align: middle; white-space: nowrap; }
/* The pinned S.No header leaves a sliver at its left edge (its collapsed
   border is half outside it), and scrolled product names showed through as
   coloured specks. A background a few pixels wider closes it. */
.sr-table th.cat-date::before {
    content: "";
    position: absolute;
    inset: -2px -1px -2px -4px;
    background: var(--surface-2);
    z-index: -1;
}
.sr-table th.sr-prod {
    text-align: center;
    vertical-align: middle;
    white-space: normal;
    font-size: 11px;
    line-height: 1.3;
    padding: 6px 8px;
}
/* `--sr-head-1` is the first header row's height, measured after each render. */
.sr-table thead tr + tr th { top: var(--sr-head-1, 0px); }
.sr-table th.sr-sub { font-size: 10.5px; font-weight: 600; padding: 5px 10px; }
.sr-table .sr-q { min-width: 56px; border-left: 2px solid var(--border-strong); }
.sr-table .sr-a { min-width: 96px; }
.sr-table td.num { white-space: nowrap; }
.sr-table td.row-title { white-space: nowrap; }

/* The totals stay put. The row lives in the table's foot, pinned to the
   bottom of the same frame the header is pinned to the top of, so the column
   sums are readable beside whichever outlet you have scrolled to instead of
   sitting hundreds of rows below. Its rule rides on an inset shadow rather
   than the border-top it inherits: a sticky cell leaves its collapsed borders
   behind when it detaches, so that line would vanish the moment you scrolled.
   The TOTAL label freezes left as well, in the corner above the pinned S.No
   column, so the row stays labelled across a wide grid of products. */
.sr-table tfoot .row-total td {
    position: sticky;
    bottom: 0;
    z-index: 2;
    box-shadow: inset 0 3px 0 var(--border-strong);
}
.sr-table tfoot .row-total td.sr-foot-label { left: 0; z-index: 3; }

/* ---- SKU Wise Sales ---- */
/* From and To get fixed widths (a date input does not shrink), the three
   pickers share the rest and the buttons keep a slot. */
.sku-filters { grid-template-columns: 150px 150px repeat(3, minmax(0, 1fr)) auto 152px; }
/* The Bakery switch, drawn to the height of the pickers beside it. */
.sku-filters .bak-switch { height: 34px; }
/* A date input draws 2px taller than a picker toggle, which lifts its label
   off the line the others sit on. */
.sku-filters .input { height: 34px; }
.sku-actions { flex-direction: row; align-items: end; gap: 8px; justify-content: flex-end; }

@media (max-width: 1000px) {
    .sku-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sku-actions { grid-column: 1 / -1; justify-content: flex-start; }
}

@media (max-width: 560px) {
    .sku-filters { grid-template-columns: minmax(0, 1fr); }
}

/* The Outlet Ranking's compact table: headers frozen at the top of the frame,
   the TOTAL row frozen at its foot, rules drawn as inset shadows because a
   sticky cell leaves its real borders behind once it detaches. */
.sku-wrap { max-height: 690px; overflow: auto; }
.sku-table th { padding: 7px 10px; font-size: 11.5px; font-weight: 700; color: var(--text); }
.sku-table td { padding: 5px 10px; font-size: 13.5px; }
.sku-table .row-sub { font-size: 11.5px; line-height: 1.2; }
.sku-table .delta { font-size: 12.5px; }
.sku-table td.sku-name .row-title { white-space: nowrap; }
.sku-table .sku-sno { width: 1%; text-align: center; white-space: nowrap; }

.sku-wrap thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--surface-2);
    box-shadow: inset 0 -1px 0 var(--border), inset 0 1px 0 var(--border);
}
.sku-wrap tfoot td {
    position: sticky; bottom: 0; z-index: 2;
    background: var(--surface-2); font-weight: 700; color: var(--text);
    box-shadow: inset 0 1px 0 var(--border-strong);
    white-space: nowrap;
}

/* Sortable headers, as on Overall Performance: the neutral glyph is dimmed so
   the sort in force stands out. Inline-flex keeps `.num` right alignment. */
.sku-table th.sku-sort { cursor: pointer; user-select: none; }
.sku-table th.sku-sort:hover { color: var(--accent); }
.sku-table th.sku-sort:hover .sku-sort-ico { opacity: 1; color: var(--accent); }
.sku-sort-label { display: inline-flex; align-items: center; gap: 6px; }
.sku-sort-ico { font-size: 13px; line-height: 1; font-weight: 700; color: var(--text-2); opacity: .75; }
.sku-sort-ico.is-active { color: var(--accent); opacity: 1; }
.sku-th-sub {
    display: block;
    margin-top: 1px;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--muted);
}

/* ---- Leave Entry ---- */
/* A date input draws ~3px taller than a select; one height keeps the filter
   row's labels and controls on the same line. */
.leave-filters .input,
.leave-filters .select,
.leave-filters .btn { height: 41px; }
/* Short fixed-width columns hug their content; User and Remarks take the rest. */
.leave-table th,
.leave-table td { white-space: nowrap; }
.leave-table .le-sno { width: 1%; text-align: center; }
.leave-table .le-date { width: 1%; }
.leave-table .le-days { width: 1%; text-align: center; }
.leave-table .le-remarks { width: 40%; white-space: normal; }
.leave-table .actions { width: 1%; }

/* ---- Business Intel ----
   Eight items on one row: the month is a face reading "Sep 2026", the four
   pickers share what is left, and the two switches and Clear All take their
   own width. Without this the bar fell back to .filter-bar's four tracks and
   ran to three lines. */
.bi-filters { grid-template-columns: 122px repeat(4, minmax(0, 1fr)) auto auto auto; }

@media (max-width: 1350px) {
    .bi-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .bi-filters > .bi-filter-actions { grid-column: 1 / -1; justify-content: flex-start; }
}

@media (max-width: 700px) {
    .bi-filters { grid-template-columns: minmax(0, 1fr); }
}

/* ---- Outlet & Product Performance ----
   Only four items, so the shared four tracks stretched the two switches to a
   quarter of the bar each. The month and the switches take their own width and
   the buttons are pushed to the far end. */
.opx-filters { grid-template-columns: 122px auto auto minmax(0, 1fr); }
.opx-filters > .opx-actions { justify-self: end; }

@media (max-width: 700px) {
    .opx-filters { grid-template-columns: minmax(0, 1fr); }
    .opx-filters > .opx-actions { justify-self: stretch; }
}

/* A two-state switch — Sales or Qty. Not two buttons: only one can be true. */
.seg {
    display: inline-flex;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.seg-btn {
    border: 0;
    background: var(--surface);
    color: var(--text-2);
    font: inherit;
    font-size: 12px;
    font-weight: 600;
    padding: 5px 14px;
    cursor: pointer;
}

.seg-btn + .seg-btn { border-left: 1px solid var(--border-strong); }
.seg-btn:hover { background: var(--surface-2); }
.seg-btn.is-active { background: var(--accent); color: var(--accent-text); }

.cell-sub { font-size: 11px; color: var(--muted); }
.row-total td { font-weight: 700; background: var(--surface-2); }

/* The side panel is narrow, so give the product name the room and keep the
   three number columns to what their digits actually need — otherwise every
   name wraps to four lines and the panel runs twice the height of the table
   it sits beside. */
#low-rows td:first-child,
#low-rows + thead th:first-child { min-width: 150px; }
.dash-side .table th,
.dash-side .table td { padding-left: 10px; padding-right: 10px; }
.dash-side .table th.num,
.dash-side .table td.num { white-space: nowrap; width: 1%; }

/* ---- insights ---- */
.insight {
    display: flex;
    gap: 9px;
    align-items: flex-start;
    padding: 8px 0;
    font-size: 12.5px;
    line-height: 1.4;
    border-bottom: 1px solid var(--border);
}

.insight:last-child { border-bottom: 0; }
.insight-ico { color: var(--accent); flex: none; margin-top: 2px; }

/* ---- charts (inline SVG; no chart library, the CSP forbids one) ---- */
.chart { width: 100%; height: 210px; display: block; }
.chart-grid { stroke: var(--border); stroke-width: 1; }
.chart-axis { fill: var(--muted); font-size: 10px; }

/* ---- Category Sales, graph view ---- */
/* Taller than the app's other charts: nine categories over a year of months
   is a lot of line to separate, and a short chart turns it into a smudge. */
.cg-chart { width: 100%; height: auto; display: block; }
.cg-wrap { padding-top: 4px; }

.cg-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.cg-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin-top: 12px;
    font-size: 12px;
    color: var(--text-2);
}

.cg-key { display: inline-flex; align-items: center; gap: 6px; }
.cg-dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }

.cg-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.cg-mark { stroke: var(--surface); stroke-width: 1.2; }
.cg-bar { stroke: none; }

/* Small multiples: one chart per category, months across, a bar per region.
   A grid rather than one big chart, because nine categories on one axis is a
   tangle and the comparison being asked for lives INSIDE a category. */
.cg-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
    gap: 12px;
}

.cg-cell {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    padding: 10px 12px 4px;
}

.cg-cell-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 4px;
}

.cg-cell-name {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text);
}

.cg-cell-total { font-size: 11.5px; font-weight: 700; color: var(--accent); }
.cg-small { width: 100%; height: auto; display: block; }

/* The all-in-one chart carries 20 stacked bars and two rows of labels, so its
   viewBox is taller; the width comes from the container either way. */
.cg-tall { width: 100%; height: auto; display: block; }

/* A faint band per alternate month: five bars read as one month without the
   eye having to count them. */
.cg-band { fill: var(--border); fill-opacity: .30; }
[data-theme="dark"] .cg-band { fill: var(--surface-2); fill-opacity: .55; }

.cg-cap { font-size: 9px; fill: var(--text-2); }

/* A small multiple is a thumbnail; clicking it opens the real thing. Saying
   so with the cursor and a lift on hover, because an element that does
   something has to look like it does something. */
.cg-cell.is-clickable { cursor: zoom-in; transition: border-color .12s ease, transform .12s ease; }
.cg-cell.is-clickable:hover { border-color: var(--accent); transform: translateY(-1px); }

.cg-zoom {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: grid;
    place-items: center;
    padding: 24px;
    background: rgba(10, 10, 12, .62);
    backdrop-filter: blur(2px);
}

.cg-zoom-panel {
    width: min(1240px, 100%);
    max-height: 92vh;
    overflow: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 16px 20px 18px;
}

.cg-zoom-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 10px;
}

.cg-zoom-name {
    font-size: 15px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text);
}

.cg-zoom-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.cg-zoom-right { display: flex; align-items: center; gap: 12px; }
.cg-zoom-total { font-size: 17px; font-weight: 700; color: var(--accent); }
.cg-zoom-body { background: var(--surface-2); border-radius: var(--radius-sm); padding: 10px; }
.cg-zoom-chart { width: 100%; height: 58vh; min-height: 340px; display: block; }
.cg-zoom-panel .cg-bar { stroke: var(--surface-2); }
.cg-region-tick { font-size: 9px; }
.cg-month-tick { font-size: 11.5px; font-weight: 700; fill: var(--text); }

/* Eight series colours, reused by the dot, the line and the bar so one series
   is one colour wherever it appears. Deliberately not the chip palette: chips
   tint a background, these have to stand alone as a 2px line.

   Light mode gets the deeper end of each hue. The first pass used the same
   values in both themes, and colours picked to glow on near-black came out
   washed and candy-ish on white — a chart has to look deliberate on paper
   too. */
.series-0 { --series: #6d28d9; }
.series-1 { --series: #0369a1; }
.series-2 { --series: #15803d; }
.series-3 { --series: #b45309; }
.series-4 { --series: #b91c1c; }
.series-5 { --series: #be185d; }
.series-6 { --series: #0f766e; }
.series-7 { --series: #4338ca; }

[data-theme="dark"] .series-0 { --series: #a78bfa; }
[data-theme="dark"] .series-1 { --series: #38bdf8; }
[data-theme="dark"] .series-2 { --series: #4ade80; }
[data-theme="dark"] .series-3 { --series: #fbbf24; }
[data-theme="dark"] .series-4 { --series: #f87171; }
[data-theme="dark"] .series-5 { --series: #f472b6; }
[data-theme="dark"] .series-6 { --series: #2dd4bf; }
[data-theme="dark"] .series-7 { --series: #c4b5fd; }

.cg-line { stroke: var(--series); }
.cg-mark { fill: var(--series); }
.cg-bar { fill: var(--series); }
.cg-dot { background: var(--series); }

/* Light mode: the chart sits on its own tinted panel rather than bare white.
   On a white page the gridlines either vanish or turn into hard rules, and
   the plot area had no edge at all — the numbers floated. A faint ground
   gives the axes something to sit on and the bars something to sit against. */
.cg-wrap {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 14px 14px;
}

[data-theme="dark"] .cg-wrap { background: transparent; border-color: transparent; padding: 4px 0 0; }

.cg-wrap .cg-cell { box-shadow: var(--shadow-sm); }
[data-theme="dark"] .cg-wrap .cg-cell { box-shadow: none; background: var(--surface-2); }

/* Gridlines want to be visible but never louder than the data. */
.cg-chart .chart-grid,
.cg-small .chart-grid { stroke: var(--border-strong); stroke-opacity: .55; }
[data-theme="dark"] .cg-chart .chart-grid,
[data-theme="dark"] .cg-small .chart-grid { stroke: var(--border); stroke-opacity: 1; }

.cg-chart .chart-axis,
.cg-small .chart-axis { fill: var(--text-2); }

/* A hairline of the page colour between stacked segments and grouped bars, so
   two similar hues never read as one block. */
.cg-bar { stroke: var(--surface); stroke-width: .75; }
[data-theme="dark"] .cg-bar { stroke: var(--surface-2); }
.cg-wrap .cg-bar { stroke: var(--surface-2); }
[data-theme="dark"] .cg-wrap .cg-bar { stroke: var(--surface); }
.chart-bar { fill: var(--accent); opacity: .85; }
.chart-bar:hover { opacity: 1; }
.chart-line { fill: none; stroke: var(--amber); stroke-width: 2; }

.chart-legend {
    display: flex;
    align-items: center;
    gap: 8px;
    justify-content: center;
    margin-top: 10px;
    font-size: 12px;
    color: var(--muted);
}

.legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
    background: var(--accent);
}

.legend-dot.is-bar { background: var(--accent); }
.legend-dot.is-line { background: var(--amber); }
.legend-dot.slice-0 { background: var(--accent); }
.legend-dot.slice-1 { background: var(--blue); }
.legend-dot.slice-2 { background: var(--amber); }
.legend-dot.slice-3 { background: var(--green); }

.donut { width: 160px; height: 160px; display: block; margin: 0 auto; }
.donut-track { fill: none; stroke: var(--surface-2); stroke-width: 26; }
.donut-arc { fill: none; stroke-width: 26; }
.donut-arc.slice-0 { stroke: var(--accent); }
.donut-arc.slice-1 { stroke: var(--blue); }
.donut-arc.slice-2 { stroke: var(--amber); }
.donut-arc.slice-3 { stroke: var(--green); }

.donut-legend { margin-top: 12px; display: flex; flex-direction: column; gap: 7px; }
.donut-row { display: flex; align-items: center; gap: 9px; font-size: 13px; }
.donut-name { flex: 1 1 auto; }
.donut-pct { font-weight: 700; }

/* ---------------------------------------------------------------- toasts */
.toasts {
    position: fixed;
    right: 20px;
    bottom: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 80;
}

.toast {
    min-width: 250px;
    max-width: 390px;
    padding: 12px 16px;
    border-radius: var(--radius-sm);
    background: var(--text);
    color: var(--bg);
    font-size: 13.5px;
    font-weight: 500;
    box-shadow: var(--shadow);
    animation: toast-in .18s ease;
}

.toast.is-ok { background: var(--green); color: #fff; }
.toast.is-error { background: var(--red); color: #fff; }
/* Done, but not entirely as asked — something was skipped and the message
   says which. Amber, because it needs reading, not alarming. */
.toast.is-warn { background: var(--amber); color: #fff; }

@keyframes toast-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------ empty/load */
.empty { padding: 48px 20px; text-align: center; color: var(--muted); }
.empty-title { color: var(--text); font-weight: 600; margin-bottom: 4px; }

.spinner {
    display: inline-block;
    width: 18px;
    height: 18px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin .7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------- permission matrix */
.matrix th.perm,
.matrix td.perm { text-align: center; width: 84px; }

.matrix tbody tr.group-row td {
    background: var(--surface-2);
    font-weight: 700;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--text-2);
    padding-top: 10px;
    padding-bottom: 10px;
}

.matrix tbody tr.group-row:hover td { background: var(--surface-2); }

/* -------------------------------------------------- schedule assignment */
/* The filter row above the week. Fields size themselves; the week stepper
   sits at the end and never wraps mid-control. */
.selection-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: flex-end;
}

.selection-grid .field { min-width: 170px; flex: 0 1 auto; }

.week-nav {
    display: flex;
    align-items: center;
    gap: 8px;
}

.week-label {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    font-size: 13.5px;
    font-weight: 600;
    white-space: nowrap;
}

.week-label .ico { color: var(--muted); }

/* Seven equal columns that collapse to a horizontal scroll rather than
   squeezing a day into unreadability. */
.week-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(150px, 1fr));
    gap: 14px;
}

.day-col {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    overflow: hidden;
}

.day-col.is-today { border-color: var(--accent); }

.day-head {
    padding: 11px 13px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}

.day-name {
    display: block;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
}

.day-date {
    display: block;
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
}

.day-body {
    flex: 1;
    min-height: 120px;
    padding: 11px;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.day-empty {
    margin: auto;
    color: var(--muted);
    font-size: 12.5px;
    font-style: italic;
}

/* One assigned outlet inside a day. */
.visit {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 9px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
}

.visit-order {
    display: grid;
    place-items: center;
    width: 19px;
    height: 19px;
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 10.5px;
    font-weight: 700;
    flex: none;
}

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

.visit-name {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.visit-city {
    display: block;
    color: var(--muted);
    font-size: 11px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.visit-remove {
    border: 0;
    background: none;
    color: var(--muted);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    padding: 0 2px;
    flex: none;
}

.visit-remove:hover { color: var(--red); }

.day-foot { padding: 0 11px 11px; }

.day-add {
    width: 100%;
    justify-content: center;
    border-style: dashed;
}

/* The outlet picker, anchored under the day it was opened from. */
.picker {
    position: absolute;
    z-index: 50;
    width: 300px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.picker-head { padding: 11px; border-bottom: 1px solid var(--border); }

.picker-list { max-height: 260px; overflow-y: auto; padding: 6px; }

.picker-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 9px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 13.5px;
}

.picker-item:hover { background: var(--surface-2); }

.picker-item input { width: 16px; height: 16px; accent-color: var(--accent); flex: none; }

.picker-item-text { min-width: 0; }

.picker-item-name {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.picker-item-city { display: block; color: var(--muted); font-size: 11.5px; }

.picker-foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 9px;
    padding: 10px 11px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
}

/* --------------------------------------------------- weekly schedule view */
/* A day cell holds either one outlet, several, or nothing. "Several" shows
   the first plus a count, and expands in place rather than in a popover —
   the table is already wide, and a floating panel would cover the next row. */
.no-visit {
    color: var(--muted);
    font-style: italic;
    font-size: 13px;
}

/* The filter row: week stepper, five dropdowns and Clear, on ONE line.
   The dropdowns give up width instead of wrapping — a filter that has dropped
   to its own row reads as a different kind of control than the six beside it.
   Below ~1150px they wrap in the ordinary way, and the phone rules further
   down still give each field its own row. */
/* The basis, not the minimum, decides where the line breaks: flex wraps on the
   items' basis and only then shares out the slack. 110px keeps all seven on one
   line at 1280 and lets them grow to fill a wider screen. */
.wv-filters .field { min-width: 104px; flex: 1 1 110px; }
.wv-filters .field:first-child { flex: 0 0 auto; }   /* the week stepper is fixed */
.wv-filters .wv-clear { flex: 0 0 auto; min-width: 0; }
.wv-filters .wv-clear .btn { white-space: nowrap; }

/* "On leave" answers the same question "No Visit" does, so it sits in the
   same place — but it is a fact about the person, not an empty cell. */
.leave-chip {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--amber);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
}
.leave-chip.is-half { background: var(--amber-soft); color: var(--amber); }

.cell-more {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 9px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
    max-width: 100%;
}

.cell-more:hover { background: var(--surface-2); }

.cell-more-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cell-more-count { color: var(--accent); font-weight: 600; flex: none; }

.cell-more .ico {
    width: 14px;
    height: 14px;
    opacity: .6;
    flex: none;
    transition: transform .15s ease;
}

.cell-more.is-open .ico { transform: rotate(180deg); }

.cell-list {
    display: none;
    flex-direction: column;
    gap: 3px;
    margin-top: 6px;
}

.cell-list.is-open { display: flex; }

.cell-list-item {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 12.5px;
    color: var(--text-2);
}

.cell-list-item .visit-order { width: 17px; height: 17px; font-size: 10px; }

/* Sortable column headings. */
.th-sort {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    font-weight: 600;
    font-size: 13px;
    color: var(--text-2);
    cursor: pointer;
}

.th-sort:hover { color: var(--text); }

.th-sort .ico { width: 13px; height: 13px; opacity: .45; }
.th-sort.is-sorted { color: var(--accent); }
.th-sort.is-sorted .ico { opacity: 1; }

/* ------------------------------------------------------------ sign in */
/* frontend/login.html only. It does not extend base.html, so it brings its
   own centred layout rather than reusing .shell. */
.auth-body {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 24px;
    background:
        radial-gradient(1100px 460px at 50% -12%, var(--accent-soft) 0%, transparent 62%),
        var(--bg);
}

.auth-panel { width: 100%; max-width: 400px; }

.auth-brand { text-align: center; margin-bottom: 24px; }

.auth-mark {
    display: grid;
    place-items: center;
    width: 52px;
    height: 52px;
    border-radius: 15px;
    background: var(--accent);
    color: var(--accent-text);
    margin: 0 auto 15px;
}

.auth-mark .ico { width: 27px; height: 27px; }

.auth-name { font-size: 22px; font-weight: 700; letter-spacing: -.02em; }
.auth-tag { margin: 5px 0 0; color: var(--text-2); }

.auth-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 28px;
}

.auth-error {
    margin-bottom: 17px;
    padding: 11px 14px;
    border-radius: var(--radius-sm);
    background: var(--red-soft);
    color: var(--red);
    font-size: 13px;
    font-weight: 500;
}

.auth-remember { margin-top: 16px; font-size: 13.5px; color: var(--text); }
.auth-remember-hint { color: var(--muted); }

.auth-submit { width: 100%; margin-top: 18px; padding: 11px; font-size: 14.5px; }

.auth-foot { margin: 20px 0 0; text-align: center; color: var(--muted); font-size: 12px; }

.auth-powered {
    margin: 6px 0 0;
    text-align: center;
    color: var(--muted);
    font-size: 12px;
}

.auth-powered strong { color: var(--text-2); font-weight: 700; }

/* The eye sits inside the field, so the input keeps its full width and the
   button never reflows the form when its icon swaps. */
.pw-wrap { position: relative; }
.pw-wrap .input { padding-right: 42px; width: 100%; }

.pw-eye {
    position: absolute;
    top: 50%;
    right: 6px;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--muted);
    cursor: pointer;
}

.pw-eye:hover { color: var(--text); background: var(--surface-2); }
.pw-eye .ico { width: 17px; height: 17px; }

/* ------------------------------------------------------------ responsive */
@media (max-width: 900px) {
    /* The menu is off-canvas by default here; the same button reveals it. */
    .sidebar { transform: translateX(-100%); box-shadow: var(--shadow-lg); }
    .shell { margin-left: 0; }

    body.nav-open .sidebar { transform: translateX(0); }
    body.nav-collapsed .sidebar { transform: translateX(-100%); }

    .topbar-user .user-meta { display: none; }
    .content { padding: 18px; }
    .page-title { font-size: 21px; }

    /* Now the actions may wrap - there is genuinely no room beside the title. */
    .page-head { align-items: flex-start; }
    .page-actions { flex-wrap: wrap; }
    .search { width: 100%; }

    /* Wrapping alone did nothing in a card heading: the block is `flex: 0 0
       auto`, so it stayed as wide as all its controls in one line and ran off
       the card (the Products and Outlets search boxes were cut in half). Let
       it shrink to the card, and its controls then wrap inside it. */
    .card-head .page-actions { flex: 0 1 auto; min-width: 0; max-width: 100%; justify-content: flex-end; }

    /* A table squeezed into a phone wrapped every cell a word per line — a
       product name five lines tall, a date in three. The wrap already scrolls
       sideways, so cells keep their text on one line and the table scrolls.
       `.cell-wrap` is for the few cells that hold a sentence, not a value. */
    .table td { white-space: nowrap; }
    .table td.cell-wrap { white-space: normal; min-width: 220px; }
}

@media (max-width: 560px) {
    /* Filters in a card heading share each row between them instead of
       leaving a gap at the end; a lone button keeps its own size. */
    .card-head .page-actions > .input,
    .card-head .page-actions > .select { flex: 1 1 150px; min-width: 0; }

    /* Schedule screens: one filter per row, all the same width, and the week
       stepper breaks before "This week" rather than pushing it off the card. */
    .selection-grid .field { flex: 1 1 100%; min-width: 0; }
    .week-nav { flex-wrap: wrap; }
    .week-nav .week-label { flex: 1 1 auto; justify-content: center; }
    .week-nav > .btn { flex: 1 1 100%; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Reports -> Product Report
   -------------------------------------------------------------------------- */
.pa-filters { grid-template-columns: 150px 150px 190px repeat(2, minmax(0, 1fr)) 200px 152px auto; }

@media (max-width: 1450px) {
    .pa-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .pa-actions { grid-column: 1 / -1; justify-content: flex-start; }
}

@media (max-width: 700px) {
    .pa-filters { grid-template-columns: minmax(0, 1fr); }
}

.pa-actions { flex-direction: row; align-items: end; gap: 8px; justify-content: flex-end; }

.pa-head-right { display: flex; align-items: center; gap: 14px; margin-left: auto; }
/* Availability picker beside the table title: All / Missing / Mapped, the
   three ways to read the same grid. Sits on the head line, so it is sized
   down from a full filter-bar control. */
.pa-mode {
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
}
.pa-mode .select { width: auto; padding: 5px 10px; font-size: 12.5px; font-weight: 500; }

.pa-wrap { max-height: 660px; }
.pa-table td, .pa-table th { vertical-align: middle; }
.pa-code { font-family: var(--mono, ui-monospace, monospace); font-size: 11.5px; color: var(--text-2); }

/* A region header carries its name over the two counts that make a column of
   crosses readable — no sales across 13 outlets is a distribution gap, across
   one outlet it is noise. */
.pa-table th.pa-col { text-align: center; min-width: 122px; }
.pa-col-name { display: block; font-weight: 700; }
.pa-col-sub {
    display: block;
    font-weight: 400;
    font-size: 9.5px;
    color: var(--text-3, var(--text-2));
    margin-top: 2px;
    text-transform: none;
    letter-spacing: 0;
}

/* The answer cell. Colour does the scanning and the glyph does the saying —
   neither alone, because colour is unreadable to some people and a bare tick
   in a 63-row grid is hard to count down a column. */
.pa-cell { text-align: center; padding: 0; }
.pa-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
}
/* Deeper than the shared `--green-soft` — that tint is pale by design for
   badges and hovers, and washed out under a whole column of ticks. */
.pa-cell.is-yes { background: #bdedd0; }
[data-theme="dark"] .pa-cell.is-yes { background: #1c4a2c; }
.pa-cell.is-yes .pa-mark { color: var(--green); }
.pa-cell.is-no .pa-mark { color: var(--red); opacity: .55; }

/* A product available nowhere is the row worth acting on, so it is marked
   once on the left rather than by its five identical crosses. */
.pa-table tr.pa-nowhere td.row-title { color: var(--red); }
.pa-table tr.pa-nowhere td.cat-date {
    box-shadow: inset 3px 0 0 var(--red);
}

.pa-tally { color: var(--text-2); white-space: nowrap; font-size: 11.5px; }

/* The generic `.table tbody tr:hover` fill (rule near the top of this file)
   repaints every row as the mouse crosses it. On a grid read column by column
   rather than row by row, that band is a distraction rather than a cue, so
   this table opts out. The tick cells need their own restatement: the
   generic rule's selector (.table tbody tr:hover td) out-specifies
   `.pa-cell.is-yes` by element count even though this one has more classes,
   so without it hovering wipes the green fill back to plain grey. */
.pa-table tbody tr:hover td { background: transparent; }
.pa-table tbody tr:hover td.pa-cell.is-yes { background: #bdedd0; }
[data-theme="dark"] .pa-table tbody tr:hover td.pa-cell.is-yes { background: #1c4a2c; }

/* --------------------------------------------------------------------------
   Reports -> Unassigned Outlets
   -------------------------------------------------------------------------- */
.uo-filters { grid-template-columns: 150px repeat(3, minmax(0, 1fr)) 140px 180px auto; }

@media (max-width: 1550px) {
    .uo-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .uo-actions { grid-column: 1 / -1; justify-content: flex-start; }
}

@media (max-width: 700px) {
    .uo-filters { grid-template-columns: minmax(0, 1fr); }
}

.uo-actions { flex-direction: row; align-items: end; gap: 8px; justify-content: flex-end; }

/* A day-by-day grid: Outlet + City fixed, then a Promoter/Merchandiser pair
   of columns per day of the month (up to 31 days = 62 data columns), so the
   frame scrolls horizontally with Outlet pinned — the one column worth
   always having in view while scrolling through the month. City is worth
   seeing but not worth a second stacked sticky column's complexity, so it
   scrolls with the grid rather than staying pinned beside Outlet. */
.uo-wrap { max-height: 640px; }
.uo-grid th, .uo-grid td { white-space: nowrap; }
.uo-grid th { font-weight: 700; }

.uo-col-outlet {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--surface);
    min-width: 160px;
    max-width: 160px;
    font-weight: 700;
}
.uo-grid thead .uo-col-outlet { z-index: 4; background: var(--surface-2); }
.uo-col-city { min-width: 110px; max-width: 110px; }

/* Each day's two sub-columns are as narrow as a tick/cross needs — dense on
   purpose, since up to 31 of these sit side by side. A right border on the
   second (M) column marks where one day ends and the next begins, since
   nothing else in a grid this dense would show it. */
.uo-day-head { text-align: center; height: 36px; }
.uo-role-head {
    min-width: 30px;
    max-width: 30px;
    text-align: center;
    font-size: 10.5px;
    padding: 4px 2px;
    height: 28px;
}
.uo-role-head.is-last, .uo-cell.is-last { border-right: 2px solid var(--border-strong); }

/* Both header rows freeze while scrolling down through up to 322 outlets.
   .uo-wrap is its own scroll container (it sets max-height, and the
   .table-wrap base class's overflow-x: auto silently computes overflow-y:
   auto too, per the CSS overflow spec's "one axis visible + one not ->
   both auto" rule) -- so these stick to the TOP OF .uo-wrap's scrollport,
   the same convention already used for .dash-grid .table th, not to the
   page or the app's sticky top bar. Two rows means two `top` offsets: the
   day-number row sits at 0, the P/M row sits right under it at 36px --
   .uo-day-head's own height, set explicitly above so this arithmetic has
   something fixed to add to. The Outlet header is frozen in both
   directions at once (left AND top), so it needs the highest z-index of
   anything on the screen or a plain sticky-left body cell scrolling past
   underneath would cover half of it. */
.uo-grid thead th {
    position: sticky;
    z-index: 2;
}
.uo-grid thead tr:first-child th { top: 0; }
.uo-grid thead tr:last-child th { top: 36px; }

.uo-cell {
    text-align: center;
    padding: 4px 2px;
    font-size: 12.5px;
    font-weight: 700;
    cursor: default;
}

/* A background tint per cell, not just a coloured glyph — at this density
   (up to 62 of these across one row) a bare tick/cross reads as one grey
   texture until you look closely. Deeper than the shared --green-soft/
   --red-soft tokens: those are tuned pale for a badge sitting on its own,
   and wash out tiled edge-to-edge across a whole row the same way
   --surface-2 did on the Category Sales totals row before. */
.uo-cell.is-yes { background: #c8f0d7; color: var(--green); }
.uo-cell.is-no  { background: #fbd7d7; color: var(--red); }
[data-theme="dark"] .uo-cell.is-yes { background: #1c4a2c; }
[data-theme="dark"] .uo-cell.is-no  { background: #4a1c1c; }

/* The generic `.table tbody tr:hover` rule (near the top of this file)
   repaints every cell in the row as the mouse crosses it — on a grid this
   dense that reads as the whole row flashing grey, and it washes out the
   tick/cross tint along with it. #uo-table's id gives these enough
   specificity to win outright, no source-order games needed. The sticky
   Outlet column is the one cell that must keep an opaque background
   regardless of hover — it hides whatever day columns have scrolled
   underneath it, and turning transparent there would let them show through. */
#uo-table tbody tr:hover td { background: transparent; }
#uo-table tbody tr:hover td.uo-col-outlet { background: var(--surface); }
#uo-table tbody tr:hover td.uo-cell.is-yes { background: #c8f0d7; }
#uo-table tbody tr:hover td.uo-cell.is-no { background: #fbd7d7; }
[data-theme="dark"] #uo-table tbody tr:hover td.uo-cell.is-yes { background: #1c4a2c; }
[data-theme="dark"] #uo-table tbody tr:hover td.uo-cell.is-no { background: #4a1c1c; }


/* ------------------------------------------------------------------ tour */
/* The "?" guide, app.tour in main.js. The lit part is the page itself: one
   element sits over it and its huge shadow dims everything else, so what the
   reader sees highlighted is the live screen, not a picture of it. The layer
   under it takes every click while the guide is open. Above the chat dock
   (70) and modals (80). The card is outside `.content`, so it uses --text-2
   for secondary text: --muted is only lifted to 4.5:1 inside `.content`. */
.tour-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 28px;
    height: 28px;
    border: 1.5px solid var(--accent);
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent);
    font: inherit;
    font-size: 15px;
    font-weight: 800;
    line-height: 1;
    cursor: pointer;
}
.tour-btn:hover { background: var(--accent); color: var(--accent-text); }
.tour-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-soft), 0 0 0 5px var(--accent); }

.tour-layer { position: fixed; inset: 0; z-index: 90; }
.tour-spot {
    position: fixed;
    z-index: 91;
    pointer-events: none;
    border-radius: var(--radius-sm);
    box-shadow: 0 0 0 2px var(--accent), 0 0 0 200vmax rgba(10, 10, 16, .6);
}
.tour-card {
    position: fixed;
    z-index: 92;
    width: min(400px, calc(100vw - 24px));
    max-height: calc(100vh - 24px);
    display: flex;
    flex-direction: column;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
}
.tour-card.is-sheet {
    top: auto; left: 0; right: 0; bottom: 0;
    width: auto;
    max-height: 52vh;
    border-bottom: 0;
    border-radius: var(--radius) var(--radius) 0 0;
}
.tour-top { display: flex; align-items: center; justify-content: space-between; padding: 12px 12px 0 18px; }
.tour-count { font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--accent); }
.tour-close {
    display: inline-flex; padding: 4px; border: 0; border-radius: 6px;
    background: none; color: var(--text-2); cursor: pointer;
}
.tour-close:hover { background: var(--surface-2); color: var(--text); }
.tour-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tour-close .ico { width: 18px; height: 18px; }
.tour-main { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.tour-title { margin: 2px 18px 0; font-size: 17px; font-weight: 800; line-height: 1.3; color: var(--text); }
.tour-body { padding: 8px 18px 2px; overflow-y: auto; font-size: 14px; line-height: 1.55; color: var(--text); }
.tour-body p { margin: 0 0 8px; }
.tour-body ul { margin: 0 0 8px; padding-left: 18px; }
.tour-body li { margin: 3px 0; }
.tour-body .tour-f {
    margin: 2px 0 10px; padding: 8px 11px; border-radius: var(--radius-sm);
    background: var(--accent-soft); font-weight: 600;
}
.tour-body .tour-ex {
    margin: 2px 0 10px; padding: 9px 11px; border-radius: var(--radius-sm);
    background: var(--surface-2); border: 1px solid var(--border);
}
.tour-body .tour-ex-h {
    display: block; margin-bottom: 3px; font-size: 11.5px; font-weight: 700;
    letter-spacing: .04em; text-transform: uppercase; color: var(--text-2);
}
.tour-body .tour-ex div + div { margin-top: 3px; }
.tour-body .tour-note { font-size: 12.5px; color: var(--text-2); }
.tour-body .up { color: var(--green); font-weight: 700; }
.tour-body .down { color: var(--red); font-weight: 700; }
.tour-body .acc { color: var(--accent); font-weight: 700; }
.tour-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 18px 16px; }
/* A cell the current step is talking about, inside the lit part. */
.tour-mark { outline: 2px solid var(--accent); outline-offset: -2px; }
.tour-mark:not(td):not(th) { outline-offset: 4px; border-radius: var(--radius-sm); }
/* Glides from one step to the next only. Always on, it would trail behind
   the page while the reader scrolls. */
@media (prefers-reduced-motion: no-preference) {
    .tour-spot.is-step { transition: top .25s ease, left .25s ease, width .25s ease, height .25s ease; }
    .tour-card.is-step:not(.is-sheet) { transition: top .25s ease, left .25s ease; }
}
@media print { .tour-btn { display: none; } }

/* ------------------------------------------------------ snack penetration */
/* Snack Penetration and Promoter & Merchandiser Penetration, which share one
   layout: `snp-` prefixed, because .kpi, .seg, .tabs, .pager and .chip are
   already taken above. Every colour is a token, so both themes follow. */
.snp-filters { --snp-ctl: 40px; display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.snp-filters .field { flex: 0 0 190px; }
.snp-filters .field .input { min-width: 190px; height: var(--snp-ctl); }
/* The month is a face reading "Sep 2026", not the native control's own
   "September, 2026" — it has no 190px floor to work around, and the room it
   gives back goes to the controls beside it. */
.snp-filters #f-month { flex: 0 0 116px; }
.snp-filters #f-month .input { min-width: 0; }
/* The buttons flow on after the filters and wrap with them. Pushed to the far
   right they end up alone on a line, with a gap beside the filters (2026-09-23). */
.snp-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.snp-actions .btn { white-space: nowrap; height: var(--snp-ctl); }

.snp-switch {
    display: inline-flex; align-items: center; gap: 8px; height: var(--snp-ctl);
    padding: 0 12px 0 9px; border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); background: var(--surface);
    color: var(--text-2); font: inherit; font-size: 13.5px; font-weight: 600;
    white-space: nowrap; cursor: pointer;
}
.snp-switch:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }
.snp-switch-track { position: relative; flex: none; width: 30px; height: 18px;
    border-radius: 9px; background: var(--border-strong); transition: background .15s; }
.snp-switch-knob { position: absolute; top: 2px; left: 2px; width: 14px; height: 14px;
    border-radius: 50%; background: var(--surface); box-shadow: 0 1px 2px rgba(0,0,0,.25);
    transition: transform .15s; }
.snp-switch.is-on { border-color: var(--accent); color: var(--accent); }
.snp-switch.is-on .snp-switch-track { background: var(--accent); }
.snp-switch.is-on .snp-switch-knob { transform: translateX(12px); }

.snp-head { margin: 2px 2px 14px; }
.snp-title { display: flex; align-items: center; gap: 10px; }
.snp-head + .card { margin-bottom: 14px; }
.snp-head h2 { margin: 0; font-size: clamp(20px, 2.6vw, 28px); font-weight: 800;
    line-height: 1.15; letter-spacing: -.01em; color: var(--text); }
.snp-head p { margin: 6px 0 0; color: var(--muted); font-size: 13.5px; }
.snp-head .snp-bak { color: var(--accent); font-weight: 600; }

.snp-seg { display: flex; border: 1px solid var(--border); border-radius: var(--radius-sm);
    overflow: hidden; }
.snp-seg button { flex: 1; border: 0; background: transparent; padding: 7px 12px;
    cursor: pointer; font: inherit; font-size: 13.5px; color: var(--text); }
.snp-seg button[aria-pressed="true"] { background: var(--accent); color: var(--accent-text);
    font-weight: 600; }
/* Period and date share the filter row with Month and Bakery, at the
   height of the inputs beside them. */
.snp-filters .snp-fauto { flex: 0 0 auto; }
.snp-filters .snp-seg { height: var(--snp-ctl); min-width: 230px; border-color: var(--border-strong); }
.snp-filters .snp-seg button { white-space: nowrap; padding: 0 14px; }
.snp-day { display: flex; gap: 6px; }
.snp-day .select { width: auto; height: var(--snp-ctl); font-weight: 700; }
.snp-step { height: var(--snp-ctl); padding: 0 12px; font-size: 18px; line-height: 1; }
.snp-pnote { flex: 1 1 100%; margin: -4px 0 0; color: var(--muted); font-size: 12.5px; }

.snp-tabs { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 10px; margin-bottom: 14px; }
.snp-tab { text-align: left; border: 1px solid var(--border); background: var(--surface);
    border-radius: var(--radius); padding: 12px 14px; cursor: pointer; font: inherit;
    color: var(--text); }
.snp-tab .rn { font-weight: 700; font-size: 16px; }
.snp-tab .ro { font-size: 13px; color: var(--muted); }
.snp-tab .ra { font-weight: 700; color: var(--red); font-size: 15px; margin-top: 4px; }
.snp-tab[aria-selected="true"] { border: 2px solid var(--accent); padding: 11px 13px;
    box-shadow: inset 0 -4px 0 var(--amber); }

.snp-cities { display: none; flex-wrap: wrap; gap: 8px; margin: -4px 0 14px; }
.snp-chip { border: 1px solid var(--border); background: var(--surface); border-radius: 99px;
    padding: 6px 14px; cursor: pointer; font: inherit; font-size: 13.5px; font-weight: 600;
    color: var(--text); }
.snp-chip span { color: var(--muted); font-weight: 500; font-size: 12.5px; }
.snp-chip b { color: var(--red); margin-left: 4px; }
.snp-chip[aria-pressed="true"] { background: var(--accent); color: var(--accent-text);
    border-color: var(--accent); }
.snp-chip[aria-pressed="true"] span, .snp-chip[aria-pressed="true"] b { color: var(--accent-text); }

.snp-grid { display: flex; flex-direction: column; gap: 16px; }
.snp-panel { background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); min-width: 0; }
.snp-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    border-bottom: 1px solid var(--border); }
.snp-kpi { padding: 14px 16px; border-right: 1px solid var(--border); min-width: 0; }
.snp-kpi:last-child { border-right: 0; }
.snp-kpi .l { font-size: 12.5px; color: var(--muted); }
.snp-kpi .v { font-weight: 700; font-size: 21px; line-height: 1.25; color: var(--text); }
.snp-kpi.opp .v { color: var(--red); }
.snp-kpi.bm .v { color: var(--green); }

.snp-toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
    padding: 12px 16px; border-bottom: 1px solid var(--border); }
.snp-toolbar .input { flex: 1 1 220px; width: auto; }
.snp-toolbar label { font-size: 13px; color: var(--muted); display: flex; gap: 6px;
    align-items: center; }
.snp-toolbar .select { width: auto; }
.snp-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12.5px; color: var(--muted);
    padding: 10px 16px 0; align-items: center; }
.snp-legend i { display: inline-block; width: 14px; height: 8px; border-radius: 3px;
    vertical-align: middle; margin-right: 5px; }
/* Table exports (2026-09-24; labelled 2026-09-24 so a bare icon is never
   ambiguous between "PDF" and "image"): pushed to the far right of the
   legend row so it sits beside "Click any outlet for the talking points…". */
.snp-legend .snp-icobtn:first-of-type { margin-left: auto; }
.snp-icobtn { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px 0 10px;
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    background: var(--surface); color: var(--text-2); cursor: pointer; flex: none;
    font: inherit; font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.snp-icobtn:hover { color: var(--accent); border-color: var(--accent); }
.snp-icobtn:disabled { opacity: .5; cursor: default; color: var(--text-2); border-color: var(--border-strong); }
.snp-icobtn svg { width: 15px; height: 15px; flex: none; }
/* The per-outlet "save this as an image" button, top-right of its detail panel. */
.snp-dhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.snp-dhead h4 { margin: 0; }
.snp-dtools { display: flex; justify-content: flex-end; margin-bottom: 8px; }

.snp-scroll { overflow-x: auto; }
.snp-table { width: 100%; border-collapse: collapse; min-width: 980px;
    font-variant-numeric: tabular-nums; }
.snp-table th { font-size: 12.5px; font-weight: 600; color: var(--muted); text-align: right;
    padding: 10px 12px; border-bottom: 1px solid var(--border); white-space: nowrap;
    cursor: pointer; user-select: none; background: var(--surface); position: sticky; top: 0; }
.snp-table th.l, .snp-table td.l { text-align: left; }
.snp-table th[aria-sort="descending"]::after { content: " \25BC"; color: var(--accent); }
.snp-table th[aria-sort="ascending"]::after { content: " \25B2"; color: var(--accent); }
.snp-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); text-align: right;
    white-space: nowrap; color: var(--text); font-size: 14px; }
.snp-table tr.row { cursor: pointer; }
.snp-table tr.row:hover td { background: var(--amber-soft); }
.snp-table tr.row.open td { background: var(--amber-soft); }
.snp-table td.rank { color: var(--muted); width: 40px; }
.snp-table td.name { font-weight: 600; white-space: normal; min-width: 180px; }
.snp-reg { font-size: 12px; color: var(--muted); font-weight: 500; }
.snp-tag { display: inline-block; font-size: 11.5px; font-weight: 700; padding: 1px 7px;
    border-radius: 99px; margin-left: 6px; vertical-align: 1px; }
.snp-tag.bm, .snp-tag.ab { background: var(--green-soft); color: var(--green); }
.snp-tag.nil { background: var(--red-soft); color: var(--red); }
/* Outlet to Focus sync tags (sss_outlet_sync): red = Penguin Sync Issue
   (.nil), amber = Sync is in progress, grey = Nothing Received. */
.snp-tag.prog { background: var(--amber-soft); color: var(--amber); }
.snp-tag.nr { background: var(--surface-2); color: var(--text-2); box-shadow: inset 0 0 0 1px var(--border-strong); }
.snp-table td.opp { font-weight: 700; color: var(--red); }
.snp-table td.gp { color: var(--red); }
.snp-table td.zero { color: var(--green); font-weight: 600; }

/* The gap bar: outlet penetration against its city high. */
.snp-share { display: flex; align-items: center; gap: 10px; justify-content: flex-end; }
.snp-bar { position: relative; width: 120px; height: 10px; background: var(--surface-2);
    border-radius: 5px; flex: none; }
.snp-bar .fill { position: absolute; left: 0; top: 0; bottom: 0; background: var(--accent);
    border-radius: 5px; }
.snp-bar .gap { position: absolute; top: 0; bottom: 0; opacity: .75;
    background: repeating-linear-gradient(135deg, var(--red) 0 3px, transparent 3px 6px); }
.snp-bar .mark { position: absolute; top: -4px; bottom: -4px; width: 3px; background: var(--green);
    border-radius: 2px; }
.snp-share .pct { min-width: 48px; font-weight: 600; }

.snp-table tr.detail > td { background: var(--bg); text-align: left; white-space: normal;
    padding: 16px 18px; }
.snp-dgrid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 22px; }
.snp-dgrid h4 { margin: 0 0 6px; font-size: 15px; color: var(--text); }
.snp-pitch { margin: 0; max-width: 60ch; color: var(--text); line-height: 1.55; }
.snp-pitch b { color: var(--red); }
.snp-sku { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.snp-sku td, .snp-sku th { padding: 6px 8px; border-bottom: 1px solid var(--border);
    text-align: right; color: var(--text); font-weight: 500; }
.snp-sku th { color: var(--muted); font-size: 12.5px; font-weight: 600; }
.snp-sku .l { text-align: left; white-space: normal; }
.snp-sku td.gp { color: var(--red); }
.snp-note { font-size: 12.5px; color: var(--muted); margin: 6px 0 0; }
.snp-trend { width: 100%; height: auto; display: block; margin-top: 4px; }
.snp-trend text { font-size: 10px; fill: var(--muted); }

.snp-pager { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
    gap: 10px; padding: 12px 16px; }
.snp-pager .info { color: var(--muted); font-size: 13.5px; }
.snp-pager .pg { display: flex; gap: 6px; flex-wrap: wrap; }
.snp-pager button { min-width: 36px; padding: 6px 10px; border: 1px solid var(--border);
    background: var(--surface); border-radius: 7px; cursor: pointer; font: inherit;
    color: var(--text); }
.snp-pager button[aria-current="page"] { background: var(--accent); color: var(--accent-text);
    border-color: var(--accent); font-weight: 700; }
.snp-pager button:disabled { opacity: .4; cursor: default; }

.snp-aside { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 16px; }
.snp-aside .snp-panel { padding: 16px 18px; }
.snp-aside h3 { font-size: 16px; margin: 0 0 4px; color: var(--text); }
.snp-aside p.h { color: var(--muted); font-size: 13px; margin: 0 0 12px; }
#snp-leaders.multi { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 4px 18px; }
.snp-leader { display: grid; grid-template-columns: 22px 1fr auto; gap: 4px 10px; padding: 8px 0;
    border-top: 1px solid var(--border); align-items: baseline; }
.snp-leader:first-of-type { border-top: 0; }
.snp-leader .n { color: var(--muted); font-size: 13px; }
.snp-leader .o { font-weight: 600; color: var(--text); }
.snp-leader .p { font-weight: 700; color: var(--green); }
.snp-leader .s { grid-column: 2 / 4; font-size: 12.5px; color: var(--muted); }
.snp-ghead { font-weight: 700; font-size: 13px; color: var(--muted); margin-top: 6px; }
.snp-cats label { display: flex; justify-content: space-between; gap: 10px; padding: 5px 0;
    font-size: 14px; cursor: pointer; color: var(--text); }
.snp-cats label span:last-child { color: var(--muted); font-size: 13px; }
.snp-cats .fixed { cursor: default; }
.snp-cats em { color: var(--muted); font-style: normal; font-size: 12.5px; }
.snp-aside .snp-seg { margin-top: 6px; }
.snp-empty { padding: 40px; text-align: center; color: var(--muted); }

@media (prefers-reduced-motion: no-preference) {
    .snp-bar .fill, .snp-bar .gap { transition: width .35s ease, left .35s ease; }
}
@media (max-width: 900px) {
    .snp-aside { grid-template-columns: 1fr; }
    .snp-dgrid { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
    .snp-filters .field { flex: 1 1 100%; }
    .snp-filters .field .input { min-width: 0; }
    .snp-actions { margin-left: 0; flex: 1 1 100%; }
    .snp-actions .btn { flex: 1 1 0; justify-content: center; }
    .snp-filters .snp-seg { min-width: 0; }
    .snp-day .select { flex: 1 1 0; min-width: 0; }
    .snp-kpi { border-right: 0; border-bottom: 1px solid var(--border); }
}
/* Promoter & Merchandiser Penetration: a person's row opens into a talking
   point and one row per outlet, held together by a rail down the left. */
.snp-caret { display: inline-block; width: 16px; color: var(--muted); font-size: 12px; }
.snp-chiplbl { align-self: center; margin-right: 2px; font-size: 12.5px; color: var(--muted); }
.snp-table tr.talk > td { background: var(--amber-soft); border-bottom: 0; text-align: left;
    white-space: normal; padding: 4px 12px 12px; }
.snp-table tr.talk .snp-pitch { max-width: 110ch; }
.snp-table tr.sub > td { background: var(--bg); font-size: 13.5px; padding-top: 8px; padding-bottom: 8px; }
.snp-table tr.sub > td.name { position: relative; padding-left: 28px; font-weight: 500; }
.snp-table tr.sub > td.name::before { content: ""; position: absolute; left: 14px; top: 0; bottom: 0;
    border-left: 2px solid var(--amber); }
.snp-table tr.sub.last > td { border-bottom: 2px solid var(--amber); }
.snp-table td.sn { color: var(--muted); font-size: 12px; }
.snp-table td.st .snp-tag { margin-left: 0; max-width: 92px; padding: 2px 8px; border-radius: 8px;
    white-space: normal; text-align: center; line-height: 1.25; }
.snp-hl + .snp-hl { margin-top: 4px; }
@media print {
    .snp-filters, .snp-toolbar, .snp-pager, .snp-cfg { display: none; }
}

/* ------------------------------------------------- daily command centre */
/* The MIS screen: `dcc-` prefixed. The filter bar, the switch and the table
   are Snack Penetration's (`snp-`), because they are the same controls; only
   the MIS's own blocks — the hero, the target cells, the status pills, the
   bar lists and the action list — are new. Every colour is a token. */
/* The MIS filter bar carries six controls and three buttons. They flow left
   to right and wrap together, instead of the buttons being pushed to the far
   right, where they end up alone on a line with a gap beside the filters.
   Sized so the whole bar holds one row: 168px a field left the three Download
   buttons no room once the Non-Core switch arrived (2026-09-29), and they
   dropped to a line of their own. The note keeps its own line — it is prose,
   not a control. */
.dcc-bar .field { flex: 0 0 142px; }
/* The month is a face reading "Sep 2026", not the native control's own
   "September, 2026", so it needs far less than a picker beside it. */
.dcc-bar #f-month { flex: 0 0 116px; }
/* The day stepper sizes to its own content: pinned to the field width above,
   its select clipped "28 Sep" down to "28" between the two arrows. */
.dcc-bar #f-date { flex: 0 0 auto; }
.dcc-bar .field .input, .dcc-bar .field .select { min-width: 0; width: 100%; }
.dcc-bar .snp-actions { margin-left: auto; flex-wrap: nowrap; }
.dcc-bar .snp-actions .btn { height: 40px; }
/* Below this the three buttons cannot sit side by side — their labels do not
   wrap ("Download image"), so holding them on one line pushed the bar wider
   than the card and put a sideways scroll on a phone. They stack instead, and
   the fields go back to sharing the width rather than holding 142px each. */
@media (max-width: 620px) {
    .dcc-bar .snp-actions { margin-left: 0; flex-wrap: wrap; }
    .dcc-bar .field, .dcc-bar #f-month, .dcc-bar #f-date { flex: 1 1 100%; }
}

.dcc-wrap { display: flex; flex-direction: column; gap: 16px; }
.dcc-two { display: grid; grid-template-columns: minmax(280px, 1fr) minmax(420px, 2fr); gap: 16px; }
.dcc-mid { display: grid; grid-template-columns: minmax(380px, 1fr) minmax(360px, 1fr); gap: 16px;
    align-items: start; }
.dcc-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
@media (max-width: 1080px) { .dcc-two, .dcc-mid { grid-template-columns: 1fr; } }

.dcc-ch { display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 12px 16px; border-bottom: 1px solid var(--border); }
.dcc-ch h3 { margin: 0; font-size: 15px; font-weight: 700; color: var(--text); }
.dcc-ch .n { font-size: 12.5px; color: var(--muted); }

.dcc-hero { display: flex; gap: 16px; align-items: center; padding: 18px 16px 12px; }
.dcc-hero .cap { font-size: 12px; font-weight: 700; color: var(--muted); letter-spacing: .3px; }
.dcc-hero .big { font-size: clamp(30px, 4.4vw, 42px); font-weight: 800; color: var(--green);
    line-height: 1.05; letter-spacing: -.02em; }
.dcc-hero .sub { font-size: 12.5px; color: var(--muted); margin-top: 4px; }
.dcc-chips { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding: 0 16px 16px; }
.dcc-chip { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 9px 10px;
    background: var(--surface-2); min-width: 0; }
.dcc-chip .v { font-size: 15px; font-weight: 700; }
.dcc-chip .l { font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.dcc-up { color: var(--green); }
.dcc-down { color: var(--red); }
.dcc-flat { color: var(--muted); }

.dcc-cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.dcc-cell { padding: 14px 16px; border-right: 1px solid var(--border); min-width: 0; }
.dcc-cell:last-child { border-right: 0; }
.dcc-cell .l { font-size: 11.5px; font-weight: 600; color: var(--muted); letter-spacing: .2px; }
.dcc-cell .v { font-size: 22px; font-weight: 800; color: var(--text); letter-spacing: -.02em;
    margin-top: 3px; }
.dcc-cell .s { font-size: 11.5px; color: var(--muted); margin-top: 3px; }
.dcc-meter { height: 5px; border-radius: 99px; background: var(--border); overflow: hidden;
    margin-top: 8px; }
.dcc-meter i { display: block; height: 100%; border-radius: 99px; background: var(--green); }
.dcc-meter.r i { background: var(--red); }
.dcc-meter.a i { background: var(--amber); }

/* status: a coloured dot and a word, used down the pulse and traction tables */
.dcc-sc { display: flex; align-items: center; justify-content: flex-end; gap: 6px;
    white-space: nowrap; font-weight: 600; }
/* the action list is the one table that reads left to right */
.dcc-table td.what, .dcc-table td.act, .dcc-table td.l .dcc-sc { justify-content: flex-start; }
.dcc-pill { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: none; }
.dcc-pill.g { background: var(--green); }
.dcc-pill.a { background: var(--amber); }
.dcc-pill.r { background: var(--red); }
.dcc-pill.n { background: #b9c7d4; }
.dcc-table tr.today td { background: var(--accent-soft); }

.dcc-table { min-width: 0; }
.dcc-table th { cursor: default; }
.dcc-table td.b { font-weight: 700; }
.dcc-table tr.tot td { background: var(--surface-2); font-weight: 700; border-top: 1px solid var(--border-strong); }
.dcc-table .m { color: var(--muted); }

.dcc-bars { padding: 14px 16px; }
.dcc-bars .row { display: flex; align-items: center; gap: 10px; padding: 7px 0; }
.dcc-bars .lb { width: 104px; font-size: 13px; font-weight: 600; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dcc-bars .tk { flex: 1; height: 8px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.dcc-bars .tk i { display: block; height: 100%; border-radius: 99px; background: var(--accent); }
.dcc-bars .vl { width: 82px; text-align: right; font-size: 13px; font-weight: 600; color: var(--text); }

.dcc-chart { width: 100%; height: auto; display: block; }
.dcc-chart text { font-size: 10px; fill: var(--muted); }
.dcc-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12.5px; color: var(--muted);
    padding: 10px 16px 0; }
.dcc-legend i { display: inline-block; width: 14px; height: 8px; border-radius: 3px;
    vertical-align: middle; margin-right: 5px; }

/* action list */
.dcc-acthead { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start;
    justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.dcc-actchips { display: flex; flex-wrap: wrap; gap: 8px; }
.dcc-actchip { border: 1px solid var(--border); background: var(--surface); border-radius: 99px;
    padding: 5px 12px; cursor: pointer; font: inherit; font-size: 13px; font-weight: 600; color: var(--text); }
.dcc-actchip b { color: var(--muted); margin-left: 4px; }
.dcc-actchip[aria-pressed="true"] { background: var(--accent); border-color: var(--accent);
    color: var(--accent-text); }
.dcc-actchip[aria-pressed="true"] b { color: var(--accent-text); }
.dcc-pri { display: inline-block; font-size: 11.5px; font-weight: 700; padding: 2px 9px;
    border-radius: 99px; }
.dcc-pri.high { background: var(--red-soft); color: var(--red); }
.dcc-pri.medium { background: var(--amber-soft); color: var(--amber); }
.dcc-pri.low { background: var(--surface-2); color: var(--muted); }
.dcc-type { display: inline-block; font-size: 11.5px; font-weight: 700; padding: 2px 9px;
    border-radius: 99px; background: var(--accent-soft); color: var(--accent); white-space: nowrap; }
.dcc-table td.what, .dcc-table td.act { white-space: normal; min-width: 200px; font-size: 13.5px; }
.dcc-table td.act { color: var(--text-2); }
.dcc-more { padding: 12px 16px; }
.dcc-note { padding: 0 16px 14px; font-size: 12.5px; color: var(--muted); }
.dcc-foot { margin: 2px 2px 8px; font-size: 12px; color: var(--muted); }

/* on a phone the controls go full width, as on the other dashboards */
@media (max-width: 560px) {
    .dcc-bar .field { flex: 1 1 100%; }
}
@media (max-width: 720px) {
    .dcc-chips { grid-template-columns: 1fr; }
    .dcc-cell { border-right: 0; border-bottom: 1px solid var(--border); }
    .dcc-bars .lb { width: 84px; }
}
@media print {
    .snp-filters, .dcc-actchips, .dcc-more { display: none; }
}


/* ------------------------------------------------- stock command centre */
/* Dashboards -> Stock Command Centre. `scc-` prefixed; it borrows the Outlet
   to Focus head, switch and segmented buttons (`snp-`) and the other
   dashboards' pickers (`msel`). Every colour is a token, so both themes
   follow — the replenishment sheet's preview included; only the PDF itself
   is always white. */
.scc-rules .scc-num { flex: 0 0 auto; }
.scc-rules .scc-num .input { min-width: 0; width: 132px; }
.scc-rules .field select.input { min-width: 180px; height: var(--snp-ctl); }
/* The reset sits in the note under the row, so the row keeps room for the
   two views beside the rules (user, 2026-10-14). */
.scc-link { border: 0; background: none; padding: 0; margin-left: 6px; font: inherit; font-weight: 600;
    color: var(--accent); cursor: pointer; }
.scc-link:hover { text-decoration: underline; }

/* The two views, a pair of tabs in the rules row, at the height of the
   controls beside them. */
.scc-tabs { display: flex; height: var(--snp-ctl, 40px); border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); overflow: hidden; }
.scc-tabs button { border: 0; background: var(--surface); color: var(--text); padding: 0 14px; font: inherit;
    font-size: 13.5px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.scc-tabs button + button { border-left: 1px solid var(--border-strong); }
.scc-tabs button:hover { background: var(--surface-2); }
.scc-tabs button[aria-selected="true"] { background: var(--accent); color: var(--accent-text); }

.scc-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px;
    margin-bottom: 14px; }
.scc-kpi { display: flex; flex-direction: column; gap: 2px; text-align: left; min-width: 0;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 12px 14px; font: inherit; color: var(--text); cursor: pointer; }
.scc-kpi:hover { border-color: var(--accent); }
.scc-kpi .l { font-size: 12.5px; color: var(--muted); display: flex; align-items: center; gap: 6px; }
.scc-kpi .v { font-size: 22px; font-weight: 800; line-height: 1.2; }
.scc-kpi .v small { font-size: 13px; color: var(--muted); font-weight: 600; }
.scc-kpi .s { font-size: 12px; color: var(--muted); }
.scc-kpi.hero { background: var(--accent-soft); border-color: var(--accent); }
.scc-kpi.hero .v { color: var(--accent); }
.scc-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; }
.scc-dot.red { background: var(--red); }
.scc-dot.amber { background: var(--amber); }

.scc-panel { padding: 14px 16px 16px; margin-bottom: 16px; }
.scc-panel-h { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
    flex-wrap: wrap; margin-bottom: 10px; }
.scc-panel-h h3 { margin: 0; font-size: 16px; font-weight: 700; }
.scc-sub { margin: 3px 0 0; color: var(--muted); font-size: 12.5px; }
.scc-view, .scc-mode { min-width: 0; }
/* The cell switch and the matrix's Download Excel, side by side. */
.scc-head-tools { display: flex; flex-wrap: wrap; align-items: stretch; gap: 8px; }
.scc-head-tools .btn { white-space: nowrap; }
.scc-view button, .scc-mode button { white-space: nowrap; }

.scc-filters { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 12px 14px;
    align-items: end; margin-bottom: 12px; }
.scc-filters .field { min-width: 0; }
.scc-filters .select, .scc-filters .input { height: 40px; width: 100%; min-width: 0; }
.scc-filters .msel-toggle { height: 40px; }
/* A picker near the panel's right edge opens its list leftwards. */
.scc-filters .msel.scc-flip .msel-panel { left: auto; right: 0; }
.scc-filters .scc-fbtn { align-items: flex-start; }
.scc-filters .scc-fbtn .btn { height: 40px; white-space: nowrap; }
/* Two tracks for the controls whose text needs it. */
.scc-filters .scc-fwide { grid-column: span 2; }
.scc-filters .scc-mode { height: 40px; border-color: var(--border-strong); }

.scc-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 12px; color: var(--muted);
    margin-bottom: 10px; align-items: center; }
.scc-legend span { display: inline-flex; align-items: center; gap: 6px; }
.scc-sw { display: inline-block; width: 12px; height: 12px; border-radius: 3px; vertical-align: -1px; }
.scc-sw.red { background: var(--red-soft); box-shadow: inset 0 0 0 1px var(--red); }
.scc-sw.amber { background: var(--amber-soft); box-shadow: inset 0 0 0 1px var(--amber); }
.scc-sw.green { background: var(--green-soft); box-shadow: inset 0 0 0 1px var(--green); }
.scc-sw.grey { background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--border-strong); }
.scc-sw.avg12 { height: 3px; background: var(--text); }
.scc-sw.avg2 { height: 3px; background: var(--accent); }
/* The key on the left, the matrix's two switches on the right. */
.scc-hm-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin-bottom: 10px; }
.scc-hm-tools .scc-legend { margin: 0 auto 0 0; }

/* The matrix: header and first column stay put while it scrolls. */
.scc-hm-wrap { overflow: auto; max-height: 72vh; border: 1px solid var(--border);
    border-radius: var(--radius-sm); background: var(--surface); }
.scc-empty { padding: 40px 16px; text-align: center; color: var(--muted); }
.scc-hm { border-collapse: separate; border-spacing: 0; font-size: 12px; }
.scc-hm thead th { position: sticky; z-index: 2; background: var(--surface-2); color: var(--text);
    font-weight: 700; border-bottom: 1px solid var(--border); padding: 6px; text-align: center; }
.scc-hm thead tr.r1 th { top: 0; height: 30px; box-sizing: border-box; font-size: 11px; text-transform: uppercase;
    letter-spacing: .04em; color: var(--muted); border-left: 1px solid var(--border); text-align: left;
    white-space: nowrap; }
/* A category can span thirty columns: its name sits at the left of the span,
   where the eye starts, not in the middle off-screen. */
.scc-hm thead tr.r2 th { top: 30px; }
.scc-hm thead th.corner { left: 0; z-index: 4; text-align: left; min-width: 220px; vertical-align: bottom;
    font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.scc-hm th.skuh { min-width: 88px; max-width: 88px; vertical-align: top; font-weight: 600; }
.scc-hm th.skuh .sn { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden; font-size: 11px; line-height: 1.25; }
.scc-hm th.skuh .sc { display: block; font-size: 10px; color: var(--muted); font-weight: 500; margin-top: 2px; }
.scc-hm tbody th, .scc-hm tfoot th { position: sticky; left: 0; z-index: 1; background: var(--surface);
    text-align: left; border-bottom: 1px solid var(--border); padding: 6px 8px; min-width: 220px;
    max-width: 260px; font-weight: 600; }
.scc-hm td { min-width: 88px; max-width: 88px; height: 48px; text-align: center; vertical-align: middle;
    border-bottom: 1px solid var(--border); border-left: 1px solid var(--border); position: relative;
    padding: 2px 4px; }
.scc-hm td.c { cursor: pointer; }
.scc-hm td.c:hover { outline: 2px solid var(--accent); outline-offset: -2px; }
.scc-hm td .v { font-weight: 700; font-size: 13px; }
.scc-hm td .v small { font-size: 10px; margin-left: 1px; font-weight: 600; }
.scc-hm td .s { font-size: 10.5px; opacity: .8; }
.scc-hm td.st-red { background: var(--red-soft); color: var(--red); }
.scc-hm td.st-amber { background: var(--amber-soft); color: var(--amber); }
.scc-hm td.st-green { background: var(--green-soft); color: var(--green); }
.scc-hm td.st-grey { background: var(--surface-2); color: var(--muted); }
.scc-hm td.dim { opacity: .28; }
.scc-hm td.nl { color: var(--border-strong); cursor: default; }
.scc-hm td .nd { position: absolute; top: 2px; right: 2px; background: var(--text); color: var(--bg);
    border-radius: 4px; padding: 0 3px; font-size: 9.5px; font-weight: 700; line-height: 14px; }
.scc-hm td .nd.rv { background: var(--muted); }
.scc-hm .oc .on { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.scc-hm .oc .on .snp-tag { font-size: 10px; padding: 0 5px; vertical-align: 1px; }
.scc-hm .oc .om { display: flex; gap: 8px; font-size: 11px; font-weight: 500; color: var(--muted); }
.scc-hm .oc .oos { color: var(--red); font-weight: 700; }
.scc-hm .mono { font-family: var(--mono); }
.scc-hm tr.grp th { padding: 0; }
.scc-hm tr.grp td { font-size: 11px; font-weight: 700; color: var(--muted); height: 32px; }
.scc-hm tr.grp.region th, .scc-hm tr.grp.region td { background: var(--surface-2); }
.scc-hm .gbtn { display: flex; align-items: center; gap: 6px; width: 100%; border: 0; background: none;
    padding: 7px 8px; font: inherit; font-weight: 700; color: var(--text); cursor: pointer; text-align: left; }
.scc-hm tr.grp.city .gbtn { padding-left: 20px; font-weight: 600; }
.scc-hm .gbtn .chev { font-size: 9px; color: var(--muted); transition: transform .15s; }
.scc-hm .gbtn[aria-expanded="false"] .chev { transform: rotate(-90deg); }
.scc-hm .gbtn .gm { margin-left: auto; font-size: 11px; color: var(--muted); font-weight: 500; white-space: nowrap; }
.scc-hm tfoot th, .scc-hm tfoot td { position: sticky; bottom: 0; background: var(--surface-2);
    font-weight: 700; z-index: 2; border-top: 1px solid var(--border-strong); }
.scc-hm tfoot th { z-index: 3; }
.scc-hm tfoot td small { display: block; font-size: 10px; color: var(--muted); font-weight: 500; }
/* Compact: shorter rows, narrower columns, the main figure only. */
.scc-hm.compact th.skuh, .scc-hm.compact td { min-width: 62px; max-width: 62px; }
.scc-hm.compact th.skuh .sn { -webkit-line-clamp: 2; font-size: 10.5px; }
.scc-hm.compact th.skuh .sc, .scc-hm.compact td .s, .scc-hm.compact .oc .om,
.scc-hm.compact tfoot td small { display: none; }
.scc-hm.compact td { height: 30px; padding: 1px 3px; }
.scc-hm.compact td .v { font-size: 12px; }
.scc-hm.compact tbody th, .scc-hm.compact tfoot th { padding-top: 4px; padding-bottom: 4px; }
.scc-hm.compact tr.grp th { padding: 0; }
.scc-hm.compact tr.grp td { height: 26px; }
.scc-hm.compact .gbtn { padding: 4px 8px; }
.scc-hm.compact tr.grp.city .gbtn { padding-left: 20px; }

/* The hover card on a matrix cell, as in the design. Fixed to the window and
   never in the pointer's way. */
.scc-tip { position: fixed; z-index: 60; pointer-events: none; min-width: 220px; max-width: 300px;
    background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); padding: 10px 12px; font-size: 12.5px; }
.scc-tip h4 { margin: 0 0 7px; font-size: 13px; font-weight: 700; line-height: 1.3; }
.scc-tip h4 span { color: var(--muted); font-weight: 500; }
.scc-tip dl { display: grid; grid-template-columns: 1fr auto; gap: 3px 18px; margin: 0; }
.scc-tip dt { color: var(--text-2); }
.scc-tip dd { margin: 0; text-align: right; font-weight: 600; white-space: nowrap; }
.scc-tip dd.b { font-weight: 800; }
.scc-tip dd.st-red { color: var(--red); }
.scc-tip dd.st-amber { color: var(--amber); }
.scc-tip dd.st-green { color: var(--green); }
.scc-tip dd.st-grey { color: var(--muted); }
.scc-tip-note { margin: 7px 0 0; color: var(--muted); font-size: 11.5px; }
.scc-tip-note.warn { color: var(--amber); }

/* Cell detail: a panel from the right; a full sheet on a phone. */
body.scc-locked { overflow: hidden; }
.scc-drawer-back { position: fixed; inset: 0; background: rgba(20, 20, 30, .45); z-index: 70; }
.scc-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(760px, 100%); z-index: 71;
    background: var(--bg); box-shadow: var(--shadow-lg); overflow-y: auto; }
.scc-dr-h { position: sticky; top: 0; background: var(--surface); border-bottom: 1px solid var(--border);
    padding: 14px 18px; z-index: 1; }
.scc-crumb { font-size: 12px; color: var(--muted); }
.scc-dr-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
.scc-dr-row h3 { margin: 0; flex: 1; font-size: 17px; line-height: 1.3; }
.scc-dr-row h3 .mono { font-size: 12px; color: var(--muted); font-weight: 500; font-family: var(--mono); }
.scc-dr-row h3 small { font-size: 13px; color: var(--muted); font-weight: 500; }
/* The detail's outlet and product switches: the msel look, one choice. */
.scc-dr-row .scc-pick { flex: 1 1 220px; min-width: 0; }
.scc-dr-row .scc-pick .msel-toggle { height: 40px; }
.scc-pick-row { border: 0; background: none; color: var(--text); font: inherit; font-size: 13px;
    text-align: left; cursor: pointer; align-items: center; }
.scc-pick-row.is-current { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.scc-dr-b { padding: 16px 18px 28px; display: flex; flex-direction: column; gap: 12px; }
.scc-quads { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.scc-quad { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
    padding: 12px 14px; min-width: 0; }
.scc-quad .lbl { font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
    font-weight: 700; }
.scc-quad .big { font-size: 26px; font-weight: 800; margin: 4px 0 6px; }
.scc-quad .big small { font-size: 12px; color: var(--muted); font-weight: 600; margin-left: 5px; }
.scc-quad .big.st-red { color: var(--red); }
.scc-quad .big.st-amber { color: var(--amber); }
.scc-quad .big.st-green { color: var(--green); }
.scc-quad .big.st-grey { color: var(--muted); }
.scc-quad .kv { display: grid; grid-template-columns: 1fr auto; gap: 3px 12px; margin: 0; font-size: 12.5px; }
.scc-quad .kv dt { color: var(--muted); }
.scc-quad .kv dd { margin: 0; font-weight: 600; text-align: right; }
.scc-quad .up { color: var(--green); }
.scc-quad .down { color: var(--red); }
.scc-note { margin: 8px 0 0; font-size: 12px; color: var(--muted); }
.scc-note.warn { color: var(--amber); }
.scc-dr-b > .scc-note.warn { margin: 0; padding: 10px 12px; border-radius: var(--radius-sm);
    background: var(--amber-soft); }
.scc-pill { display: inline-block; border-radius: 99px; padding: 2px 10px; font-size: 12px; font-weight: 700; }
.scc-pill.st-red { background: var(--red-soft); color: var(--red); }
.scc-pill.st-amber { background: var(--amber-soft); color: var(--amber); }
.scc-pill.st-green { background: var(--green-soft); color: var(--green); }
.scc-pill.st-grey { background: var(--surface-2); color: var(--muted); }
.scc-cov { position: relative; height: 30px; margin: 4px 0 8px; }
.scc-cov .track { position: absolute; left: 0; right: 0; top: 6px; height: 8px; border-radius: 4px;
    background: var(--surface-2); }
.scc-cov .fill { position: absolute; left: 0; top: 6px; height: 8px; border-radius: 4px; }
.scc-cov .tk { position: absolute; top: 2px; width: 2px; height: 16px; background: var(--text); }
.scc-cov .tl { position: absolute; top: 18px; transform: translateX(-50%); font-size: 10px; color: var(--muted);
    white-space: nowrap; }
.fill-red { background: var(--red); fill: var(--red); }
.fill-amber { background: var(--amber); fill: var(--amber); }
.fill-green { background: var(--green); fill: var(--green); }
.fill-grey { background: var(--muted); fill: var(--muted); }
.scc-chart { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
    padding: 10px 12px; }
.scc-chart-h { display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 12.5px;
    font-weight: 700; margin-bottom: 4px; }
.scc-chart svg { width: 100%; height: auto; display: block; }
.scc-chart .track { fill: var(--surface-2); }
.scc-chart .gap { fill: var(--border-strong); opacity: .6; }
.scc-chart .tline { stroke: var(--text); stroke-width: 2; }
.scc-chart .grid { stroke: var(--border); stroke-width: 1; }
.scc-chart .t1 { font-size: 10px; fill: var(--muted); }
.scc-chart .t2 { font-size: 11px; fill: var(--text); font-weight: 600; }
.scc-chart .bar { fill: var(--border-strong); }
.scc-chart .bar.recent { fill: var(--accent); }
.scc-chart .bar.off { fill: var(--surface-2); }
.scc-chart .avg12 { stroke: var(--text); stroke-width: 1.5; stroke-dasharray: 5 4; }
.scc-chart .avg2 { stroke: var(--accent); stroke-width: 2; }

/* The tour's colour words. */
.scc-chip { display: inline-block; border-radius: 4px; padding: 0 5px; font-weight: 700; font-size: 12px; }
.scc-chip.red { background: var(--red-soft); color: var(--red); }
.scc-chip.amber { background: var(--amber-soft); color: var(--amber); }
.scc-chip.green { background: var(--green-soft); color: var(--green); }
.scc-chip.grey { background: var(--surface-2); color: var(--muted); }

/* The replenishment sheet: a page in the reader's theme. The PDF it
   downloads is always white. */
.scc-sheet-bar { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap;
    margin-bottom: 10px; }
.scc-sheet-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.scc-paper-wrap { background: var(--surface-2); border-radius: var(--radius-sm); padding: 16px; overflow-x: auto; }
.scc-paper { background: var(--surface); color: var(--text); border: 1px solid var(--border); max-width: 820px;
    margin: 0 auto; padding: 28px 30px 34px; box-shadow: var(--shadow-sm); font-size: 12px; }
.scc-paper.wide { max-width: none; }
.scc-paper .p-top { display: flex; justify-content: space-between; font-size: 10px; letter-spacing: .08em;
    color: var(--muted); font-weight: 700; }
.scc-paper h3 { margin: 8px 0 4px; font-size: 19px; }
.scc-paper .p-meta, .scc-paper .p-rules { color: var(--text-2); font-size: 11.5px; margin-top: 2px; }
.scc-paper .p-boxes { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 12px 0 8px; }
.scc-paper .p-box { border: 1px solid var(--border); border-radius: 6px; padding: 7px 9px; background: var(--surface-2); }
.scc-paper .p-box span { display: block; font-size: 10px; color: var(--muted); }
.scc-paper .p-box b { font-size: 17px; }
.scc-paper .p-legend { font-size: 11px; color: var(--text-2); margin: 6px 0; }
.scc-paper .p-note { font-size: 11px; color: var(--amber); margin: 3px 0; }
.scc-paper .p-empty { padding: 26px 0; text-align: center; color: var(--muted); font-weight: 600; }
.scc-paper table { width: 100%; border-collapse: collapse; font-size: 11.5px; }
.scc-paper th { text-align: right; font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
    background: var(--surface-2); padding: 5px 6px; border-bottom: 1px solid var(--border); }
.scc-paper td { text-align: right; padding: 4px 6px; border-bottom: 1px solid var(--border); }
.scc-paper .l { text-align: left; }
.scc-paper .code { color: var(--muted); font-family: var(--mono); font-size: 10.5px; }
.scc-paper .muted { color: var(--muted); }
.scc-paper .send { font-weight: 800; }
.scc-paper .oosd { color: var(--red); font-weight: 700; }
.scc-paper .pr { font-size: 10px; font-weight: 800; letter-spacing: .03em; }
.scc-paper .pr.oos { color: var(--red); }
.scc-paper .pr.low { color: var(--text); }
.scc-paper .pr.top { color: var(--text); }
.scc-paper .box { display: inline-block; width: 11px; height: 11px; border: 1px solid var(--text-2); border-radius: 2px; }
.scc-paper .p-grp { margin-top: 14px; page-break-inside: avoid; }
.scc-paper .p-grp tr[data-k] { cursor: pointer; }
.scc-paper .p-grp tr[data-k]:hover td { background: var(--accent-soft); }
.scc-paper .p-gh { display: flex; align-items: baseline; gap: 8px; border-bottom: 1.5px solid var(--text);
    padding-bottom: 3px; margin-bottom: 2px; }
.scc-paper .p-gh small { color: var(--muted); }
.scc-paper .p-gh em { margin-left: auto; font-style: normal; font-weight: 700; }
.scc-paper .p-tot { margin-top: 18px; }
.scc-paper .p-tot h4 { margin: 0 0 4px; font-size: 13px; }
.scc-paper tr.ft td { font-weight: 800; border-top: 1.5px solid var(--text); }
.scc-paper .p-sign { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 46px;
    font-size: 11px; color: var(--text-2); }
.scc-paper .p-sign div { border-top: 1px solid var(--text-2); padding-top: 4px; }
.scc-paper .p-scroll { overflow-x: auto; margin-top: 12px; }
.scc-paper table.mx { width: auto; min-width: 100%; }
/* SKU split: outlets down the side, products across under their category.
   The outlet column stays put while the products scroll past. */
.scc-paper table.mx th { text-align: center; min-width: 64px; max-width: 96px; vertical-align: bottom;
    text-transform: none; letter-spacing: 0; font-size: 10px; color: var(--text); }
.scc-paper table.mx th small { display: block; color: var(--muted); font-weight: 500; }
.scc-paper table.mx tr.cats th.cat { text-align: left; text-transform: uppercase; letter-spacing: .04em;
    color: var(--muted); border-left: 1px solid var(--border); max-width: none; }
.scc-paper table.mx th.l { min-width: 190px; max-width: none; text-align: left; }
.scc-paper table.mx th.tot { max-width: none; }
.scc-paper table.mx td { text-align: center; }
.scc-paper table.mx td.l { text-align: left; white-space: nowrap; }
.scc-paper table.mx th.l, .scc-paper table.mx td.l { position: sticky; left: 0; z-index: 1;
    background: var(--surface); }
.scc-paper table.mx th.l { background: var(--surface-2); }
.scc-paper table.mx tr.cat td.l { background: var(--surface-2); }
.scc-paper table.mx .tot { font-weight: 800; background: var(--surface-2); }
.scc-paper table.mx tr.cat td { background: var(--surface-2); font-weight: 800; }
.scc-paper table.mx td.q-oos { color: var(--red); font-weight: 800; }
.scc-paper table.mx td.q-low { color: var(--amber); font-weight: 800; }
.scc-paper table.mx td.q-top { color: var(--text); }

/* SKU split in the testers' design (2026-10-15): cards, note tags, a toolbar
   (category and region tabs, search, Hide empty columns, Compact, the key)
   and an outlets x products table. One colour only (management): red for a
   line out of stock now, plain for the rest. */
.scc-paper .sx { --sx-ow: 220px; --sx-tw: 112px; }
.scc-paper .sx.compact { --sx-ow: 176px; --sx-tw: 84px; }

.sx-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 14px 0 10px; }
.sx-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 14px; background: var(--surface-2); }
.sx-card span { display: block; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.sx-card b { display: block; margin-top: 2px; font-size: 22px; font-weight: 800; }
.sx-card.hero b { color: var(--accent); }

.sx-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.sx-pills:empty { display: none; }
.sx-pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border: 1px solid; border-radius: 99px;
    font-size: 12.5px; font-weight: 600; }
.sx-pill svg { width: 14px; height: 14px; flex: none; }
.sx-pill.warn { color: var(--amber); background: var(--amber-soft); border-color: var(--amber); }
.sx-pill.bad { color: var(--red); background: var(--red-soft); border-color: var(--red); }
.sx-pill.info { color: var(--blue); background: var(--surface-2); border-color: var(--blue); }

.sx-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding: 10px 12px;
    border: 1px solid var(--border); border-bottom: 0; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: var(--surface-2); }
.sx-tabs { display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; border: 1px solid var(--border);
    border-radius: var(--radius-sm); background: var(--surface); }
.sx-tabs:empty { display: none; }
.sx-tabs button { border: 0; background: none; padding: 5px 12px; border-radius: 7px; font: inherit; font-size: 13px;
    font-weight: 600; color: var(--text-2); cursor: pointer; white-space: nowrap; }
.sx-tabs button:hover { color: var(--text); }
.sx-tabs button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); }
.sx-search.input { height: 34px; width: 220px; max-width: 100%; font-size: 13px; }
.sx-tg { display: inline-flex; align-items: center; gap: 8px; padding: 0; border: 0; background: none; font: inherit;
    font-size: 13px; color: var(--text-2); cursor: pointer; white-space: nowrap; }
.sx-tg-track { position: relative; width: 30px; height: 18px; border-radius: 9px; background: var(--border-strong);
    transition: background .15s; flex: none; }
.sx-tg-track span { position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%;
    background: var(--surface); box-shadow: 0 1px 2px rgba(0, 0, 0, .25); transition: transform .15s; }
.sx-tg[aria-checked="true"] { color: var(--text); }
.sx-tg[aria-checked="true"] .sx-tg-track { background: var(--accent); }
.sx-tg[aria-checked="true"] .sx-tg-track span { transform: translateX(12px); }
.sx-key { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin-left: auto; font-size: 12.5px;
    color: var(--text-2); }
.sx-key-item { display: inline-flex; align-items: center; gap: 7px; }
/* The key's two samples: a red box and a plain one, no figure in them. */
.sx-sw { display: inline-block; flex: none; width: 16px; height: 16px; border-radius: 4px;
    border: 1px solid var(--border-strong); }
.sx-sw.oos { background: var(--red-soft); border-color: var(--red); }

.sx-body .p-scroll { margin-top: 0; border: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm); }
.sx-body .p-empty { border: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm); }
.scc-paper .sx-tbl { width: auto; min-width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
.scc-paper .sx-tbl th, .scc-paper .sx-tbl td { padding: 7px 8px; text-align: center; vertical-align: middle;
    border-bottom: 1px solid var(--border); background: var(--surface); }
.scc-paper .sx-tbl thead th { background: var(--surface-2); color: var(--text); vertical-align: bottom; text-transform: none;
    letter-spacing: 0; font-size: 12px; }
.scc-paper .sx-tbl .sx-o { position: sticky; left: 0; z-index: 2; box-sizing: border-box; width: var(--sx-ow);
    min-width: var(--sx-ow); max-width: var(--sx-ow); text-align: left; }
.scc-paper .sx-tbl .sx-t { position: sticky; left: var(--sx-ow); z-index: 2; box-sizing: border-box; width: var(--sx-tw);
    min-width: var(--sx-tw); text-align: right; border-right: 1px solid var(--border); }
.scc-paper .sx-tbl thead .sx-o, .scc-paper .sx-tbl thead .sx-t { z-index: 3; font-size: 11px; font-weight: 700;
    letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.scc-paper .sx-tbl .sx-band th:not(.sx-o):not(.sx-t) { text-align: left; font-size: 11px; font-weight: 700;
    letter-spacing: .05em; text-transform: uppercase; color: var(--muted); border-left: 1px solid var(--border); }
.scc-paper .sx-tbl th.sx-p { min-width: 92px; max-width: 112px; line-height: 1.25; }
.sx-p .b { display: block; font-size: 10.5px; font-weight: 500; color: var(--muted); }
.sx-p .n { display: block; font-weight: 700; }
.sx-p .k { display: inline-block; margin-top: 4px; padding: 1px 7px; border-radius: 99px; background: var(--accent-soft);
    color: var(--accent); font-size: 10.5px; font-weight: 700; }
.scc-paper .sx-tbl tr.sx-g td { background: var(--accent-soft); font-weight: 700; }
.sx-fold { display: inline-flex; align-items: baseline; gap: 7px; padding: 0; border: 0; background: none; font: inherit;
    font-weight: 700; color: var(--text); cursor: pointer; white-space: nowrap; }
.sx-fold small { font-weight: 500; font-size: 12px; color: var(--muted); }
.sx-fold .chev { display: inline-block; font-size: 9px; color: var(--muted); transition: transform .15s; }
.sx-fold[aria-expanded="false"] .chev { transform: rotate(-90deg); }
.sx-on { display: flex; align-items: center; gap: 6px; min-width: 0; }
.sx-on .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.sx-id { flex: none; padding: 0 6px; border: 1px solid var(--border); border-radius: 99px; font-size: 11px; color: var(--muted); }
.sx-on .snp-tag { flex: none; font-size: 10.5px; }
.sx-t b { display: block; font-weight: 800; }
.sx-bar { display: block; height: 3px; margin-top: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.sx-bar i { display: block; height: 100%; border-radius: 2px; background: var(--accent); }
.sx-c { display: inline-block; min-width: 30px; padding: 3px 8px; border-radius: 6px; font-size: 12.5px; font-weight: 600;
    color: var(--text); }
.sx-c.oos { background: var(--red-soft); color: var(--red); font-weight: 800; box-shadow: inset 0 0 0 1px var(--red); }
.scc-paper .sx-tbl td[data-k] { cursor: pointer; }
.scc-paper .sx-tbl td[data-k]:hover .sx-c { outline: 2px solid var(--accent); outline-offset: 1px; }
.sx-dot { color: var(--border-strong); }
.scc-paper .sx-tbl tfoot td { background: var(--surface-2); font-weight: 800; border-top: 1px solid var(--border-strong); border-bottom: 0; }
/* Compact: shorter rows, narrower columns, the name and pack only. */
.sx.compact .sx-tbl { font-size: 12px; }
.scc-paper .sx.compact .sx-tbl th, .scc-paper .sx.compact .sx-tbl td { padding: 3px 5px; }
.scc-paper .sx.compact .sx-tbl th.sx-p { min-width: 66px; max-width: 84px; }
.sx.compact .sx-p .b, .sx.compact .sx-bar { display: none; }
.sx.compact .sx-c { min-width: 24px; padding: 1px 6px; font-size: 11.5px; }

/* On a phone the controls go full width, as on the other dashboards. */
@media (max-width: 640px) {
    .scc-rules .scc-num, .scc-rules .field { flex: 1 1 100%; }
    .scc-rules .field select.input, .scc-rules .scc-num .input { min-width: 0; width: 100%; }
    .scc-tabs button { flex: 1 1 50%; }
    .scc-filters { grid-template-columns: minmax(0, 1fr); }
    .scc-filters .scc-fwide { grid-column: auto; }
    .scc-filters .scc-fbtn .btn { width: 100%; }
    .scc-panel { padding: 12px; }
    .scc-quads { grid-template-columns: 1fr; }
    .scc-hm thead th.corner, .scc-hm tbody th, .scc-hm tfoot th { min-width: 150px; max-width: 170px; }
    .scc-paper { padding: 18px 14px 22px; }
    .scc-paper .p-boxes { grid-template-columns: repeat(2, 1fr); }
    .scc-paper .p-sign { grid-template-columns: 1fr; gap: 28px; }
    .scc-paper .sx, .scc-paper .sx.compact { --sx-ow: 140px; --sx-tw: 72px; }
    .sx-key { margin-left: 0; }
    .sx-search.input { width: 100%; }
    .scc-hm-wrap { max-height: 68vh; }
}

/* ===================================================================== */
/* Stock -> Stock to Sales (hidden page, 2026-10-22). sts-: this page alone;
   the cards, filters and pills are the Stock Command Centre's (scc-) and
   Outlet to Focus's (snp-). Colours are tokens, so dark mode follows.    */
/* ===================================================================== */
.scc-pill.st-blue { background: var(--accent-soft); color: var(--accent); }
.sts-kpi[disabled] { cursor: default; }
.sts-kpi[disabled]:hover { border-color: var(--border); }
.sts-kpi.up .v { color: var(--green); }
.sts-kpi.down .v { color: var(--red); }
.sts-crumb { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 14px; margin-bottom: 10px; }
.sts-crumb button { font: inherit; border: 0; background: none; padding: 0; color: var(--accent); cursor: pointer;
    text-decoration: underline; text-underline-offset: 2px; }
.sts-crumb .sep { color: var(--muted); }
.sts-story { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
    padding: 12px 14px; font-size: 13.5px; line-height: 1.55; margin-bottom: 14px; color: var(--text); }
.sts-story p { margin: 0 0 4px; }
.sts-story p:last-child { margin-bottom: 0; }
.sts-tw { overflow: auto; max-height: 68vh; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.sts-table { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; }
.sts-table th { position: sticky; top: 0; z-index: 1; background: var(--surface-2); color: var(--text-2);
    font-size: 12px; font-weight: 700; text-align: right; padding: 8px 10px; border-bottom: 1px solid var(--border);
    white-space: nowrap; cursor: pointer; user-select: none; }
.sts-table tr.grp th { position: static; cursor: default; color: var(--text); border-bottom: 0; padding-bottom: 0; }
.sts-table th.yh, .sts-table td.yh { color: var(--muted); }
.sts-table th[aria-sort="ascending"]::after { content: " ▲"; font-size: 10px; }
.sts-table th[aria-sort="descending"]::after { content: " ▼"; font-size: 10px; }
.sts-table td { text-align: right; padding: 8px 10px; border-bottom: 1px solid var(--border); white-space: nowrap;
    vertical-align: middle; color: var(--text); }
.sts-table th.l, .sts-table td.l { text-align: left; }
.sts-table td.l:first-child { white-space: normal; min-width: 170px; font-weight: 600; }
.sts-table td.down { color: var(--red); font-weight: 600; }
.sts-table td.up { color: var(--green); font-weight: 600; }
.sts-table tr.click { cursor: pointer; }
.sts-table tr.click:hover td, .sts-table tr.click:focus-visible td { background: var(--accent-soft); }
.sts-table .snp-tag { margin-left: 6px; }
.sts-bar { display: inline-block; width: 64px; height: 7px; border-radius: 99px; background: var(--surface-2);
    box-shadow: inset 0 0 0 1px var(--border); overflow: hidden; vertical-align: 1px; margin-right: 8px; }
.sts-bar i { display: block; height: 100%; border-radius: 99px; }
.sts-spark { display: block; }
.sts-chart { margin: 0 0 12px; }
.sts-chart text { fill: var(--muted); font-size: 11px; }
.sts-legend { margin: 12px 0 0; }
.sts-top .snp-pnote { flex-basis: 100%; }
@media (max-width: 720px) {
    .sts-table { font-size: 12px; }
    .sts-table td.l:first-child { min-width: 130px; }
}
/* A display rule on these beats the `hidden` attribute; this puts it back. */
.scc-filters[hidden], .sts-top .field[hidden], .sts-chart[hidden] { display: none; }

/* ===================================================================== */
/* Stock -> Stock to Sales (3 Days) (2026-10-22). s3-: this page alone;
   products across, three days under each, outlets down the side.        */
/* ===================================================================== */
.s3-top .snp-pnote { flex-basis: 100%; }
.s3-top #f-region-w { min-width: 170px; }
.s3-top #f-city-w { min-width: 200px; }
.s3-kpi { cursor: default; }
.s3-kpi:hover { border-color: var(--border); }
.s3-tools { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.s3-cats { flex-wrap: wrap; height: auto; }
.s3-cats button { white-space: nowrap; }
.s3-q { width: 260px; max-width: 100%; height: 40px; }
.s3-wrap { overflow: auto; max-height: 72vh; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.s3-grid { border-collapse: separate; border-spacing: 0; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.s3-grid th, .s3-grid td { border-bottom: 1px solid var(--border); padding: 6px 8px; white-space: nowrap; }
.s3-grid thead th { position: sticky; top: 0; z-index: 2; background: var(--surface-2); color: var(--text-2);
    font-weight: 700; text-align: center; }
.s3-grid th.s3-p { color: var(--text); font-size: 12.5px; border-left: 2px solid var(--border-strong);
    max-width: 260px; overflow: hidden; text-overflow: ellipsis; }
.s3-grid th.s3-p span { display: block; font-size: 11px; font-weight: 600; color: var(--muted); }
.s3-grid th.s3-d { font-size: 11.5px; border-left: 1px solid var(--border); }
.s3-grid th.s3-m { font-size: 11px; font-weight: 600; color: var(--muted); }
.s3-grid th.s3-first { position: sticky; left: 0; z-index: 3; text-align: left; background: var(--surface-2);
    min-width: 190px; max-width: 240px; white-space: normal; border-right: 1px solid var(--border-strong); }
.s3-grid thead th.s3-first { z-index: 4; vertical-align: bottom; }
.s3-grid tbody th.s3-first { background: var(--surface); font-weight: 600; color: var(--text); }
.s3-grid tbody tr.s3-tot th.s3-first, .s3-grid tbody tr.s3-tot td { background: var(--accent-soft); font-weight: 700; }
.s3-grid tbody tr.s3-reg th.s3-first, .s3-grid tbody tr.s3-reg td { font-weight: 700; }
.s3-grid tbody tr.s3-out th.s3-first { font-weight: 500; padding-left: 18px; }
.s3-oc { display: block; font-size: 11px; font-weight: 500; color: var(--muted); }
.s3-o .snp-tag { margin-left: 6px; }
.s3-grid td.s3-c { text-align: right; color: var(--text); cursor: pointer; }
.s3-grid tr.s3-out td.s3-c { cursor: default; }
.s3-grid td.s3-c:hover { background: var(--accent-soft); }
.s3-grid td.s3-st { color: var(--accent); font-weight: 700; }
.s3-grid td.zero { color: var(--red); }
.s3-grid td.hl { background: var(--amber-soft); }
.s3-grid td.last, .s3-grid th.last { border-right: 2px solid var(--border-strong); }
.s3-grid.compact td.s3-cmp { text-align: center; line-height: 1.25; min-width: 86px; }
.s3-grid.compact td.s3-cmp b { display: block; color: var(--accent); }
.s3-grid.compact td.s3-cmp span { display: block; font-size: 11px; color: var(--muted); }
@media (max-width: 720px) {
    .s3-grid th.s3-first { min-width: 130px; max-width: 150px; }
    .s3-q { width: 100%; }
}
/* A product spans 9 columns: its name starts where they do, not in the middle off screen. */
.s3-grid th.s3-p { text-align: left; padding-left: 12px; }

/* ---- Product Analysis (rebuilt 2026-10-27 from management's design) ----
   `pda-`: `pa-` is Reports -> Product Report's and `pan-` was the old screen's.
   The filter bar wraps like Outlet to Focus's: twelve controls cannot share
   one row, and a fixed grid would squeeze the pickers to nothing. */
.pda-filters { --pda-ctl: 38px; display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.pda-f-month { flex: 0 0 168px; }
.pda-f-auto { flex: 0 0 auto; }
.pda-f-pick { flex: 1 1 150px; min-width: 150px; }
.pda-actions { display: flex; gap: 10px; margin-left: auto; }
.pda-actions .btn { white-space: nowrap; height: var(--pda-ctl); }
.pda-seg { display: flex; height: var(--pda-ctl); border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); overflow: hidden; background: var(--surface); }
.pda-seg button { border: 0; background: transparent; padding: 0 14px; cursor: pointer;
    font: inherit; font-size: 13.5px; color: var(--text-2); white-space: nowrap; }
.pda-seg button + button { border-left: 1px solid var(--border-strong); }
.pda-seg button[aria-pressed="true"] { background: var(--accent); color: var(--accent-text); font-weight: 600; }
.pda-day { display: flex; gap: 6px; }
.pda-day .select { width: auto; height: var(--pda-ctl); font-weight: 600; }
.pda-step { height: var(--pda-ctl); padding: 0 12px; font-size: 18px; line-height: 1; }

.pda-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-end;
    gap: 12px; margin: 2px 0 14px; }
.pda-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; }
.pda-crumb { border: 0; background: none; padding: 4px 8px; border-radius: 8px; font: inherit;
    font-size: 15px; font-weight: 600; color: var(--accent); cursor: pointer; }
button.pda-crumb:hover { background: var(--accent-soft); }
span.pda-crumb { color: var(--text); font-weight: 700; cursor: default; }
.pda-chev { color: var(--muted); font-size: 16px; }
.pda-sub { margin: 2px 0 0 8px; color: var(--text-2); font-size: 13px; }
.pda-show { display: flex; align-items: center; gap: 8px; color: var(--text-2); font-size: 13px; }
.pda-show .pda-seg { height: 34px; }
.pda-kpi-sub { margin-top: 4px; font-size: 12px; color: var(--text-2); }

.pda-sec { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.pda-sh, .pda-card-head { display: flex; flex-wrap: wrap; justify-content: space-between;
    align-items: center; gap: 10px; }
.pda-card-head { padding: 14px 18px 10px; }
.pda-h { margin: 0; font-size: 16px; font-weight: 700; color: var(--text); }
.pda-hint { font-size: 12.5px; color: var(--text-2); }

/* The decision cards. A button each: it filters the ranking below. */
.pda-board { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
.pda-dcard { display: flex; flex-direction: column; gap: 6px; text-align: left; padding: 13px 15px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
    box-shadow: var(--shadow-sm); cursor: pointer; font: inherit; color: var(--text); }
.pda-dcard:hover { border-color: var(--border-strong); }
.pda-dcard.is-on { border: 2px solid var(--accent); padding: 12px 14px; box-shadow: 0 0 0 3px var(--accent-soft); }
.pda-dcard-top { display: flex; justify-content: space-between; align-items: center; }
.pda-dcard-n { font-size: 24px; font-weight: 800; }
.pda-dcard-act { font-size: 13px; font-weight: 600; color: var(--text-2); }
.pda-dcard-rule { font-size: 12px; color: var(--muted); }
.pda-dcard-names { font-size: 12px; border-top: 1px solid var(--border); padding-top: 8px; margin-top: 2px; }

/* The design's decision colours, with darker pairs for the dark theme. */
.pda-chip { display: inline-block; font-size: 12px; font-weight: 700; padding: 3px 10px;
    border-radius: 999px; white-space: nowrap; }
.pda-chip.is-push   { background: #dcfae6; color: #067647; }
.pda-chip.is-expand { background: #dbeafe; color: #1e40af; }
.pda-chip.is-watch  { background: #fef0c7; color: #93370d; }
.pda-chip.is-fix    { background: #fee4e2; color: #b42318; }
.pda-chip.is-steady { background: #f2f4f7; color: #344054; }
.pda-chip.is-new    { background: #ede9fe; color: #5b21b6; }
[data-theme="dark"] .pda-chip.is-push   { background: #14301d; color: #4ade80; }
[data-theme="dark"] .pda-chip.is-expand { background: #172554; color: #93c5fd; }
[data-theme="dark"] .pda-chip.is-watch  { background: #302510; color: #fbbf24; }
[data-theme="dark"] .pda-chip.is-fix    { background: #331a1a; color: #f87171; }
[data-theme="dark"] .pda-chip.is-steady { background: #262626; color: #d4d4d4; }
[data-theme="dark"] .pda-chip.is-new    { background: #241a3d; color: #c4b5fd; }

.pda-sorts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.pda-pill { border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-2);
    border-radius: 999px; padding: 6px 13px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.pda-pill.is-on { border-color: var(--accent); background: var(--accent); color: var(--accent-text); }

.pda-card { margin-bottom: 18px; }
.pda-table { min-width: 1080px; }
.pda-table th, .pda-table td { padding: 10px 14px; vertical-align: middle; }
.pda-table tr.is-sel td { background: var(--accent-soft); }
.pda-rk { color: var(--muted); font-weight: 600; width: 1%; }
.pda-lnk { border: 0; background: none; padding: 0; text-align: left; font: inherit; font-weight: 600;
    color: var(--text); cursor: pointer; text-decoration: underline;
    text-decoration-color: var(--border-strong); text-underline-offset: 3px; }
.pda-lnk:hover { color: var(--accent); }
.pda-lnk.is-blue { color: var(--accent); text-decoration: none; }
.pda-code { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.pda-mtd { min-width: 140px; }
.pda-mtd-top { display: flex; justify-content: space-between; gap: 8px; }
.pda-bar { height: 6px; background: var(--border); border-radius: 99px; margin-top: 5px; }
.pda-bar i { display: block; height: 6px; border-radius: 99px; background: var(--accent); }
.pda-xs { font-size: 11.5px; }
/* `.table td` sets its own colour, so the cell forms are named too. */
.pda-dim, .pda-table td.pda-dim { color: var(--text-2); }
.pda-up, .pda-table td.pda-up { color: var(--green); }
.pda-down, .pda-table td.pda-down { color: var(--red); }
.pda-new, .pda-table td.pda-new { color: var(--accent); }
.pda-muted, .pda-table td.pda-muted { color: var(--muted); }
.pda-warn { color: var(--amber); }
.pda-table td { color: var(--text); }
.pda-spark { display: block; }
.pda-spark polyline { stroke: var(--accent); }
.pda-spark circle { fill: var(--accent); }
.pda-total td { font-weight: 700; background: var(--surface-2); border-top: 2px solid var(--text); }
.pda-table tbody tr.pda-total:hover td { background: var(--surface-2); }
.pda-note { margin: 0; padding: 10px 18px 14px; font-size: 12px; color: var(--muted); }
.pda-scroll { max-height: 560px; overflow: auto; }
.pda-scroll thead th { position: sticky; top: 0; z-index: 1; }
.pda-scroll .pda-table { min-width: 900px; }
.pda-open { white-space: nowrap; }

/* The open product. */
.pda-panel { border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--surface);
    padding: 18px 20px; margin-bottom: 18px; display: flex; flex-direction: column; gap: 12px; }
.pda-panel-head { display: flex; justify-content: space-between; gap: 12px; }
.pda-eb { font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--accent); }
.pda-panel-name { font-size: 20px; font-weight: 800; margin: 2px 0 6px; }
.pda-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.pda-x { flex: none; width: 36px; height: 36px; border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); background: var(--surface); color: var(--text-2); cursor: pointer; font-size: 15px; }
.pda-panel .pda-table { min-width: 760px; }
.pda-gaps { display: flex; flex-direction: column; gap: 8px; }
.pda-gap { border: 1px dashed var(--amber); background: var(--amber-soft); color: var(--amber);
    border-radius: 999px; padding: 5px 12px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.pda-miss { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.pda-miss-card { border: 1px solid var(--amber); background: var(--amber-soft); border-radius: var(--radius);
    padding: 12px 14px; display: flex; flex-direction: column; gap: 4px; }
.pda-allgood { border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px;
    color: var(--green); font-weight: 600; }

@media (max-width: 640px) {
    .pda-actions { margin-left: 0; }
    .pda-f-pick { flex-basis: 100%; }
}
