/*
 * 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 */
}

/* ------------------------------------------------------------------ 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);
}

.topbar-title {
    font-size: 15.5px;
    font-weight: 500;
    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 */
.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: block;
    color: var(--accent);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    margin-bottom: 3px;
}

.page-title {
    font-size: 25px;
    font-weight: 700;
    letter-spacing: -.01em;
    text-transform: uppercase;
}

.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 + tracked label from the reference screens. */
.card-title {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text);
}

.card-title::before {
    content: "";
    width: 7px;
    height: 7px;
    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);
}

.stat-label {
    color: var(--muted);
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-weight: 700;
}

.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);
}

.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; }

.msel-actions {
    display: flex;
    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; }
.content.is-dense .card-title { font-size: 11px; }
.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: 9.5px; margin-bottom: 3px; }
.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); }
}

/* ---- 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;
}

.kpi-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.kpi-value { font-size: 21px; 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); }

/* ---- 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, and the two buttons in one cell. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, .85fr)
                           minmax(0, .85fr) auto auto;
    gap: 12px;
    align-items: end;
    padding: 0;
}

@media (max-width: 1150px) {
    .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; }
.month-pick .input { flex: 1 1 auto; min-width: 0; padding: 7px 9px; }
.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); }

.cat-table .row-subtotal td,
.cat-table .row-total td {
    font-weight: 700;
    color: var(--text);
    background: var(--surface-2);
    border-top: 2px solid var(--border-strong);
}

/* 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)) 82px; }

@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); }
}

.pv-actions { flex-direction: row; align-items: end; justify-content: flex-end; }

/* 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); }

/* ---- "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 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 ---- */
.cs-filters { grid-template-columns: 190px 160px 160px minmax(0, 1fr) 82px; }

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

@media (max-width: 620px) {
    .cs-filters { 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; }

/* 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); }

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

@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 ---- */
.cm-filters { grid-template-columns: 160px repeat(4, minmax(0, 1fr)); }

@media (max-width: 1100px) {
    .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: Month and Type need their own width
   (a two-option radio row 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: 190px 205px repeat(4, minmax(0, 1fr)) 152px; }

@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: 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 column per product, so the frame scrolls in both directions with the
   header and the S.No column pinned — the same pattern as Category Sales.
   The header carries the whole product name sideways; keeping the column
   narrow means it reads as a label, not a block wasting the row's height. */
.sr-wrap { max-height: 640px; }
.sr-table th.sr-col {
    min-width: 40px;
    max-width: 40px;
    writing-mode: vertical-rl;
    text-align: left;
    font-size: 10.5px;
    padding: 8px 4px;
}
.sr-table td.row-title { white-space: nowrap; }

/* ---- Product Analysis ---- */
/* The month column is fixed and wide enough for "August, 2026" between its two
   arrows — a month input that ellipsises its own value is worse than useless. */
.pa-filters { grid-template-columns: 232px repeat(5, minmax(0, 1fr)); }

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

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

.pa-matrix-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* One line under the card head saying how to read the table, because the same
   frame shows two different things depending on the switch beside it. */
.pa-note { margin: -2px 0 8px; padding: 0 14px; font-size: 11px; color: var(--muted); }

/* The city header spans its columns; the periods sit under it. */
.cat-table th.pa-group {
    text-align: center;
    background: var(--accent-soft);
    color: var(--accent);
}

.cat-table th.pa-sub { font-size: 10.5px; font-weight: 600; }

/* Ranked view: the row is a position, not a product, so the first column is a
   number and each city carries its own name column. */
.cat-table .rank-col { min-width: 44px; width: 44px; }
.cat-table.is-ranked td.rank-name { min-width: 138px; font-weight: 600; color: var(--text); }
.cat-table.is-ranked td.rank-name .row-sub { font-weight: 400; }

/* 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); }

/* Stock summary: one proportion and four counts, and they are not the same
   kind of thing. The proportion gets a bar, because a percentage is read
   against its own scale; the counts get a plain grid, so every label sits on
   one baseline and every figure in one column. The earlier version stacked a
   centred hero on four full-width rows, and nothing lined up with anything. */
.stock-avail {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    padding: 10px 12px;
    margin-bottom: 10px;
}

.stock-avail-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.stock-avail-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--muted);
}

.stock-pct { font-size: 22px; font-weight: 700; color: var(--accent); line-height: 1.1; }
.stock-avail-foot { font-size: 10.5px; color: var(--muted); margin-top: 5px; }

.stock-bar {
    height: 6px;
    border-radius: 999px;
    background: var(--border);
    overflow: hidden;
    margin-top: 7px;
}

.stock-bar > span { display: block; height: 100%; background: var(--accent); }

.stock-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
}

.stock-cell {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
}

.stock-cell-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted);
}

.stock-cell-value { font-size: 15px; font-weight: 700; margin-top: 2px; }
.stock-cell-sub { font-size: 10.5px; color: var(--muted); margin-top: 1px; }

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

.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 { height: 380px; }
.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(330px, 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: 175px; display: block; }

/* The all-in-one chart carries 20 stacked bars and two rows of labels, so it
   needs the height the others do not. */
.cg-tall { height: 470px; }

/* 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;
}

.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-submit { width: 100%; margin-top: 22px; 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%; }
}
