/* Sellex admin UI — design system.
   Single stylesheet for every page under /admin (login included). Served at
   /static/admin.css with a mtime cache-buster (see app/web/templating.py).

   Rules of the road:
   - Every colour comes from a token below. No literal hex outside :root.
   - Templates style themselves with the component classes here, not with
     inline style= attributes.
   - Legacy aliases at the end of the token block keep pre-redesign markup
     working while pages migrate. */

/* ---------------------------------------------------------------- tokens */

:root {
    /* neutrals (warm stone) */
    --n0: #ffffff;
    --n50: #fafaf9;
    --n100: #f5f5f4;
    --n200: #e7e5e4;
    --n300: #d6d3d1;
    --n400: #a8a29e;
    --n500: #78716c;
    --n600: #57534e;
    --n700: #44403c;
    --n800: #292524;
    --n900: #1c1917;

    /* accent (amber / copper) */
    --accent: #b45309;
    --accent-hover: #92400e;
    --accent-soft: #fffbeb;
    --accent-soft-border: #fde68a;
    --accent-ink: #ffffff;

    /* categorical series — charts only, assigned in this fixed order and never
       cycled (app/web/charts.py::series_colour). Validated as a set for
       colour-vision separation and contrast on a white panel: every pair is
       distinguishable under protanopia and deuteranopia, which is why the ramp
       stops at five and a sixth slice folds into --series-other rather than
       reusing --series-1. Deliberately NOT the semantic colours below: a slice
       that happens to be green must not read as "good".
       Re-validate the whole set before changing any one of them. */
    --series-1: #b45309;
    --series-2: #1d4ed8;
    --series-3: #0d9488;
    --series-4: #a21caf;
    --series-5: #7c2d12;
    --series-other: var(--n400);

    /* semantic */
    --success: #15803d;
    --success-soft: #f0fdf4;
    --success-border: #bbf7d0;
    --warning: #a16207;
    --warning-soft: #fefce8;
    --warning-border: #fde68a;
    --danger: #b91c1c;
    --danger-soft: #fef2f2;
    --danger-border: #fecaca;
    --info: #1d4ed8;
    --info-soft: #eff6ff;
    --info-border: #bfdbfe;

    /* spacing (4px scale) */
    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 16px;
    --s5: 20px;
    --s6: 24px;
    --s8: 32px;
    --s10: 40px;
    --s12: 48px;

    /* radius */
    --r-sm: 6px;
    --r-md: 10px;
    --r-lg: 14px;
    --r-full: 999px;

    /* elevation */
    --sh1: 0 1px 2px rgba(28, 25, 23, .05);
    --sh2: 0 1px 3px rgba(28, 25, 23, .06), 0 4px 12px rgba(28, 25, 23, .06);
    --sh3: 0 4px 6px rgba(28, 25, 23, .05), 0 12px 32px rgba(28, 25, 23, .14);

    /* type scale */
    --fs-xs: 12px;
    --fs-sm: 13px;
    --fs-base: 14px;
    --fs-md: 15px;
    --fs-lg: 18px;
    --fs-xl: 22px;
    --fs-2xl: 28px;

    /* layout */
    --sidebar-w: 248px;
    --topbar-h: 56px;
    --content-max: 1280px;

    /* legacy aliases — pre-redesign markup still references these */
    --ink: var(--n900);
    --muted: var(--n500);
    --line: var(--n200);
    --bg: var(--n50);
    --good: var(--success);
    --panel-2: var(--n100);
}

/* Inter, self-hosted (SIL Open Font License 1.1 — see fonts/LICENSE.txt).
   font-display:swap so a cold cache paints in the system stack first. */
@font-face {
    font-family: "InterVar";
    src: url("/static/fonts/InterVariable.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

/* ------------------------------------------------------------------ base */

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--n800);
    font-family: "InterVar", system-ui, -apple-system, "Segoe UI", sans-serif;
    font-size: var(--fs-base);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

h1 {
    font-size: var(--fs-xl);
    font-weight: 650;
    letter-spacing: -0.01em;
    color: var(--n900);
    margin: 0 0 var(--s2);
}

h2 {
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--n900);
    margin: var(--s6) 0 var(--s3);
}

h3 {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--n800);
    margin: var(--s4) 0 var(--s2);
}

a {
    color: var(--accent);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

.muted {
    color: var(--n500);
}

small,
.small {
    font-size: var(--fs-sm);
}

/* One focus treatment for the whole app. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-sm);
}

/* ----------------------------------------------------------------- shell */

.nav-toggle-cb {
    display: none;
}

.sidebar {
    position: fixed;
    top: 0;
    inset-inline-start: 0;
    bottom: 0;
    width: var(--sidebar-w);
    background: var(--n0);
    border-inline-end: 1px solid var(--n200);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 40;
}

.sidebar .brand {
    display: flex;
    align-items: center;
    gap: var(--s3);
    height: var(--topbar-h);
    flex: 0 0 var(--topbar-h);
    padding: 0 var(--s4);
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--n900);
    letter-spacing: -0.01em;
    text-decoration: none;
    border-bottom: 1px solid var(--n200);
}

.sidebar .brand:hover {
    text-decoration: none;
}

.brandmark {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    border-radius: 8px;
    background: var(--accent);
    color: var(--accent-ink);
    font-size: var(--fs-sm);
    font-weight: 700;
}

.sidebar nav {
    padding: var(--s3) var(--s3) var(--s8);
}

/* A nav section: a clickable label that opens its items. Collapsed by default
   (the server marks the current page's section `open`), so every section stays
   reachable without scrolling the rail. */
.navgroup {
    margin-top: var(--s2);
}

.navlabel {
    display: flex;
    align-items: center;
    gap: var(--s2);
    list-style: none;
    cursor: pointer;
    height: 30px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--n400);
    padding: 0 var(--s3);
    border-radius: var(--r-sm);
}

.navlabel::-webkit-details-marker {
    display: none;
}

.navlabel:hover {
    background: var(--n100);
    color: var(--n600);
}

.navlabel .icon {
    margin-inline-start: auto;
    transition: transform .15s;
}

.navgroup[open]>.navlabel .icon {
    transform: rotate(180deg);
}

/* `nowrap` on a fixed-height row means a long label cannot wrap — without the
   overflow pair it runs straight out of the 248px rail instead. Keep labels
   short (that is the real fix); this only stops one from breaking the shell. */
.navitem {
    display: flex;
    align-items: center;
    gap: var(--s3);
    height: 36px;
    padding: 0 var(--s3);
    border-radius: var(--r-sm);
    font-size: var(--fs-base);
    font-weight: 500;
    color: var(--n600);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background .12s, color .12s;
}

.navitem:hover {
    background: var(--n100);
    color: var(--n900);
    text-decoration: none;
}

.navitem .icon {
    flex: 0 0 18px;
    color: var(--n400);
}

.navitem:hover .icon {
    color: var(--n600);
}

.navitem.on {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

.navitem.on .icon {
    color: var(--accent);
}

.shell {
    margin-inline-start: var(--sidebar-w);
    min-height: 100vh;
}

.topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    height: var(--topbar-h);
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: 0 var(--s8);
    background: rgba(250, 250, 249, .85);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--n200);
}

.topbar .crumb {
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--n900);
}

.navburger {
    display: none;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    margin-inline-start: calc(var(--s3) * -1);
    border-radius: var(--r-sm);
    color: var(--n600);
    cursor: pointer;
}

.navburger:hover {
    background: var(--n100);
}

.nav-scrim {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(28, 25, 23, .4);
    z-index: 35;
}

/* User menu (topbar right) */
.usermenu {
    position: relative;
    margin-inline-start: auto;
    flex: 0 0 auto;
}

.usermenu>summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--s2);
    height: 36px;
    padding: 0 var(--s2) 0 var(--s2);
    border-radius: var(--r-sm);
    color: var(--n600);
    font-size: var(--fs-sm);
}

.usermenu>summary::-webkit-details-marker {
    display: none;
}

.usermenu>summary:hover {
    background: var(--n100);
}

.avatar {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    flex: 0 0 26px;
    border-radius: var(--r-full);
    background: var(--n800);
    color: var(--n0);
    font-size: var(--fs-xs);
    font-weight: 650;
    text-transform: uppercase;
}

.usermenu[open]>.usermenu-panel {
    display: block;
}

.usermenu-panel {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-end: 0;
    min-width: 240px;
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-md);
    box-shadow: var(--sh3);
    padding: var(--s2);
    z-index: 60;
}

.usermenu-who {
    padding: var(--s2) var(--s3) var(--s3);
    border-bottom: 1px solid var(--n100);
    margin-bottom: var(--s2);
}

.usermenu-who .email {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--n900);
    word-break: break-all;
}

.usermenu-panel a,
.usermenu-panel button {
    display: flex;
    align-items: center;
    gap: var(--s3);
    width: 100%;
    height: 34px;
    padding: 0 var(--s3);
    border: none;
    border-radius: var(--r-sm);
    background: none;
    font: inherit;
    font-size: var(--fs-base);
    color: var(--n700);
    text-align: start;
    text-decoration: none;
    cursor: pointer;
}

.usermenu-panel a:hover,
.usermenu-panel button:hover {
    background: var(--n100);
    text-decoration: none;
}

.usermenu-panel .icon {
    color: var(--n400);
}

main {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: var(--s6) var(--s8) var(--s12);
}

/* --------------------------------------------------------- page header */

.pagehead {
    display: flex;
    align-items: flex-start;
    gap: var(--s4);
    flex-wrap: wrap;
    margin-bottom: var(--s6);
}

.pagehead .titles {
    flex: 1 1 auto;
    min-width: 0;
}

.pagehead .sub {
    font-size: var(--fs-sm);
    color: var(--n500);
    margin: 0;
}

.pagehead .actions {
    display: flex;
    align-items: center;
    gap: var(--s2);
    flex-wrap: wrap;
}

/* -------------------------------------------------------------- buttons */

button,
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    height: 36px;
    padding: 0 var(--s4);
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    font: inherit;
    font-size: var(--fs-base);
    font-weight: 550;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
    background: var(--accent);
    color: var(--accent-ink);
    box-shadow: var(--sh1);
    transition: background .12s, border-color .12s, color .12s;
}

button:hover,
.btn:hover {
    background: var(--accent-hover);
    color: var(--accent-ink);
    text-decoration: none;
}

button.quiet,
.btn.quiet {
    background: var(--n0);
    border-color: var(--n300);
    color: var(--n700);
}

button.quiet:hover,
.btn.quiet:hover {
    background: var(--n100);
    color: var(--n900);
}

button.ghost,
.btn.ghost {
    background: none;
    border-color: transparent;
    color: var(--n600);
    box-shadow: none;
}

button.ghost:hover,
.btn.ghost:hover {
    background: var(--n100);
    color: var(--n900);
}

button.danger,
.btn.danger {
    background: var(--n0);
    border-color: var(--danger-border);
    color: var(--danger);
}

button.danger:hover,
.btn.danger:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

button.sm,
.btn.sm {
    height: 30px;
    padding: 0 var(--s3);
    font-size: var(--fs-sm);
}

.btn.disabled,
.btn.quiet.disabled {
    opacity: .45;
    cursor: default;
    pointer-events: none;
}

button:disabled {
    opacity: .5;
    cursor: not-allowed;
}

/* --------------------------------------------------------------- inputs */

input,
select,
textarea {
    height: 36px;
    padding: 0 var(--s3);
    border: 1px solid var(--n300);
    border-radius: var(--r-sm);
    font: inherit;
    font-size: var(--fs-base);
    color: var(--n900);
    background: var(--n0);
    transition: border-color .12s, box-shadow .12s;
}

textarea {
    height: auto;
    padding: var(--s2) var(--s3);
    line-height: 1.5;
}

select {
    /* room for the native arrow, which the padding above would crowd — and
       which the browser moves to the other side under `dir="rtl"` */
    padding-inline-end: var(--s2);
}

input[type="checkbox"],
input[type="radio"] {
    height: auto;
    accent-color: var(--accent);
}

input:focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(180, 83, 9, .12);
}

input::placeholder {
    color: var(--n400);
}

input:disabled,
select:disabled,
textarea:disabled {
    background: var(--n100);
    color: var(--n500);
}

/* An input the product fixes the tail of — the account email, which takes the
   part before the @ and shows the domain beside it. Rendered as one control so
   what is being typed is never ambiguous; the suffix is decoration, not a
   field, so it submits nothing. Logical properties, so it mirrors under RTL. */
.suffixed {
    display: flex;
    align-items: stretch;
    min-width: 0;
}

.suffixed input {
    flex: 1;
    min-width: 0;
    border-start-end-radius: 0;
    border-end-end-radius: 0;
}

.suffixed .suffix {
    display: flex;
    align-items: center;
    padding: 0 var(--s3);
    border: 1px solid var(--n300);
    border-inline-start: 0;
    border-start-end-radius: var(--r-sm);
    border-end-end-radius: var(--r-sm);
    background: var(--n100);
    color: var(--n600);
    font-size: var(--fs-sm);
    white-space: nowrap;
}

/* Set by admin.js from ?field= on a validation redirect. */
.field-error {
    border-color: var(--danger) !important;
    box-shadow: 0 0 0 3px rgba(185, 28, 28, .12);
}

label {
    color: var(--n700);
}

/* --------------------------------------------------------------- panels */

.panel {
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-lg);
    box-shadow: var(--sh1);
    padding: var(--s5) var(--s6);
    margin-bottom: var(--s5);
}

.panel h2:first-child,
.panel h3:first-child {
    margin-top: 0;
}

.panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s3);
    flex-wrap: wrap;
    margin-bottom: var(--s4);
}

.panel-head h2,
.panel-head h3 {
    margin: 0;
}

.grid2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s5);
}

/* --------------------------------------------------------------- tables */

.tablewrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-lg);
    box-shadow: var(--sh1);
}

/* Inside a panel the panel already draws the card. */
.panel .tablewrap {
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: none;
}

table {
    width: 100%;
    border-collapse: collapse;
    background: var(--n0);
}

th,
td {
    text-align: start;
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--n100);
}

th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--n50);
    border-bottom: 1px solid var(--n200);
    font-size: var(--fs-xs);
    font-weight: 550;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--n500);
    white-space: nowrap;
}

tbody tr:last-child td {
    border-bottom: none;
}

tbody tr:hover td {
    background: var(--n50);
}

td.num,
th.num {
    /* `end`, not `right`: a figures column hugs the end of the row, which is
       the left edge once the table is mirrored. The digits themselves stay
       Western and LTR — `num()`/`money()` are untouched, as in the till. */
    text-align: end;
    font-variant-numeric: tabular-nums;
}

/* A statement's closing row (trial balance, per-branch P&L). */
tr.total td {
    border-top: 2px solid var(--n300);
    font-weight: 600;
}

th a.sort {
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

th a.sort:hover {
    color: var(--accent);
}

th a.sort.on {
    color: var(--accent);
}

th a.sort .arrow {
    font-size: .7em;
    padding-inline-start: var(--s1);
}

/* --------------------------------------------------------------- badges */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--s1);
    padding: 2px 10px;
    border: 1px solid transparent;
    border-radius: var(--r-full);
    font-size: var(--fs-xs);
    font-weight: 550;
    line-height: 1.6;
    white-space: nowrap;
    text-transform: uppercase;
    letter-spacing: .02em;
    background: var(--n100);
    color: var(--n600);
    border-color: var(--n200);
}

.badge.draft,
.badge.neutral {
    background: var(--n100);
    color: var(--n600);
    border-color: var(--n200);
}

.badge.submitted,
.badge.warn {
    background: var(--warning-soft);
    color: var(--warning);
    border-color: var(--warning-border);
}

.badge.received,
.badge.on,
.badge.ok {
    background: var(--success-soft);
    color: var(--success);
    border-color: var(--success-border);
}

.badge.cancelled,
.badge.off,
.badge.bad {
    background: var(--danger-soft);
    color: var(--danger);
    border-color: var(--danger-border);
}

.badge.info {
    background: var(--info-soft);
    color: var(--info);
    border-color: var(--info-border);
}

/* ---------------------------------------------------------------- flash */

.flash {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    border: 1px solid transparent;
    border-radius: var(--r-md);
    margin-bottom: var(--s5);
    font-size: var(--fs-base);
}

.flash .icon {
    flex: 0 0 18px;
    margin-top: 1px;
}

.flash.error {
    background: var(--danger-soft);
    color: var(--danger);
    border-color: var(--danger-border);
}

.flash.ok {
    background: var(--success-soft);
    color: var(--success);
    border-color: var(--success-border);
}

.flash.warn {
    background: var(--warning-soft);
    color: var(--warning);
    border-color: var(--warning-border);
}

.flash.info {
    background: var(--info-soft);
    color: var(--info);
    border-color: var(--info-border);
}

/* ------------------------------------------------------- stats & cards */

/* The min column width is set by the longest KPI label, not by taste: at 180px
   a card had 138px of content and "Revenue (net, excl. VAT)" (150px) wrapped to
   two lines. 210px clears every label in the app with a little room to spare. */
.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: var(--s4);
    margin-bottom: var(--s5);
}

/* Five tiles that belong together (the sales overview's money row). At the
   210px floor five need 1050px and the content column is ~1032px, so they
   broke 4 + 1 — one lonely card under four, reading as a second group rather
   than the tail of one. A lower floor fits all five; auto-fit still wraps them
   on a narrow screen. */
.cards.cards-5 {
    grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
}

.card,
.stat {
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-lg);
    box-shadow: var(--sh1);
    padding: var(--s5);
}

.stat-label,
.card .t {
    font-size: var(--fs-sm);
    color: var(--n500);
    margin-bottom: var(--s1);
}

.stat-value,
.card .n {
    font-size: var(--fs-2xl);
    font-weight: 650;
    line-height: 1.15;
    letter-spacing: -0.02em;
    color: var(--n900);
    font-variant-numeric: tabular-nums;
}

/* the legacy .card markup puts the number first */
.card .n {
    margin-bottom: var(--s1);
}

.stat-delta {
    display: inline-flex;
    align-items: center;
    gap: var(--s1);
    margin-top: var(--s2);
    font-size: var(--fs-xs);
    font-weight: 600;
}

.stat-delta.up {
    color: var(--success);
}

.stat-delta.down {
    color: var(--danger);
}

.stat-delta.flat {
    color: var(--n500);
}

.stat-spark {
    margin-top: var(--s3);
}

/* ---------------------------------------------------------- dashboard */

.chart {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

.stat-spark .spark {
    display: block;
    width: 100%;
    height: 32px;
}

/* --- sales overview (the ?view=sales tab) --- */

/* Tabs across the top of a page whose views are separate server renders. Each
   is a plain link, so the current view lives in the URL and is shareable. */
.viewtabs {
    display: flex;
    gap: var(--s1);
    border-bottom: 1px solid var(--n200);
    margin-bottom: var(--s5);
}

.viewtabs a {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
    color: var(--n500);
    font-weight: 500;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

.viewtabs a:hover {
    color: var(--n800);
}

.viewtabs a.on {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* Slicer strips: year / month / category. Chips are links that rewrite the
   window in the query string, so the whole filter state stays in the URL. */
.slicer {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s2);
    margin-bottom: var(--s3);
}

.slicer:last-child {
    margin-bottom: 0;
}

.slicer .slicer-label {
    min-width: 68px;
    color: var(--n500);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: .04em;
}

.slicer .chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1);
}

.chip {
    display: inline-flex;
    align-items: center;
    padding: var(--s1) var(--s3);
    border: 1px solid var(--n200);
    border-radius: var(--r-sm);
    background: var(--n0);
    color: var(--n600);
    font-size: var(--fs-sm);
    font-weight: 500;
    white-space: nowrap;
}

.chip:hover {
    border-color: var(--n300);
    background: var(--n50);
    color: var(--n800);
}

.chip.on {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
}

/* A period that cannot be chosen — a month still in the future. Shown rather
   than hidden so the strip keeps its shape and Jan..Dec stay where the eye
   expects them; it is a <span>, so there is nothing to click. */
.chip.off {
    background: var(--n50);
    border-style: dashed;
    color: var(--n400);
    cursor: default;
}

.chip.on:hover {
    background: var(--accent-hover);
    color: var(--accent-ink);
}

/* Two charts side by side, stacking on a narrow screen. */
.chartgrid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: var(--s4);
}

.chartgrid > .panel {
    margin: 0;
    min-width: 0;
}

/* A donut is only readable with its key beside it. */
.donutwrap {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s5);
}

.donut {
    flex: none;
}

.legend {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    flex: 1 1 180px;
    min-width: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

.legend li {
    display: flex;
    align-items: baseline;
    gap: var(--s2);
    font-size: var(--fs-sm);
}

/* The swatch carries identity; the text stays in ink tokens so a label is
   never legible only to someone who can separate the hues. */
.legend .swatch {
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: 3px;
    background: var(--n400);
}

/* The name is the row's flexible part: it truncates so a long category can
   never push the value and share off the panel (min-width:0 is what lets a
   flex item shrink below its content at all). */
.legend .name {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--n700);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.legend .value {
    flex: none;
    color: var(--n900);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.legend .share {
    flex: none;
    color: var(--n500);
    font-variant-numeric: tabular-nums;
}

/* Inline key for a multi-series line chart. */
.serieskey {
    display: flex;
    gap: var(--s4);
    flex-wrap: wrap;
}

.serieskey span {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    color: var(--n600);
    font-size: var(--fs-sm);
}

.serieskey .swatch {
    width: 12px;
    height: 3px;
    border-radius: 2px;
    background: var(--n400);
}

.swatch.s1 { background: var(--series-1); }
.swatch.s2 { background: var(--series-2); }
.swatch.s3 { background: var(--series-3); }
.swatch.s4 { background: var(--series-4); }
.swatch.s5 { background: var(--series-5); }
.swatch.s6 { background: var(--series-other); }

/* First-run setup checklist. Namespaced: `.checklist` is already taken by the
   product picker on the discounts page. */
.setup .panel-head h2,
.panel-head h2 {
    display: flex;
    align-items: center;
    gap: var(--s2);
}

.setuplist {
    list-style: none;
    margin: 0;
    padding: 0;
}

.setuplist li {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s2) 0;
    border-bottom: 1px solid var(--n100);
}

.setuplist li:last-child {
    border-bottom: none;
}

.setuplist li .icon {
    color: var(--n300);
}

.setuplist li.done .icon {
    color: var(--success);
}

.setuplist li a {
    color: var(--n800);
    font-weight: 500;
}

.setuplist li.done a {
    color: var(--n500);
    font-weight: 400;
}

/* "Needs attention" — one prioritised list instead of four look-alike tables. */
.alertlist {
    list-style: none;
    margin: 0;
    padding: 0;
}

.alertlist li {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) 0;
    border-bottom: 1px solid var(--n100);
}

.alertlist li:last-child {
    border-bottom: none;
}

.alertlist li a {
    color: var(--n800);
    font-weight: 500;
}

.alertlist li a:hover {
    color: var(--accent);
}

.alertlist li.danger>.icon {
    color: var(--danger);
}

.alertlist li.warn>.icon {
    color: var(--warning);
}

.alertlist li.info>.icon {
    color: var(--info);
}

/* ------------------------------------------------------------ pill nav */

.pills {
    display: flex;
    align-items: center;
    gap: var(--s1);
    flex-wrap: wrap;
    margin-bottom: var(--s5);
}

.pills a {
    padding: 6px 14px;
    border-radius: var(--r-full);
    font-size: var(--fs-sm);
    font-weight: 550;
    color: var(--n600);
    text-decoration: none;
}

.pills a:hover {
    background: var(--n100);
    color: var(--n900);
    text-decoration: none;
}

.pills a.on {
    background: var(--n900);
    color: var(--n0);
}

/* --------------------------------------------------------- empty state */

.empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: var(--s10) var(--s6);
}

.empty .bubble {
    display: grid;
    place-items: center;
    width: 56px;
    height: 56px;
    border-radius: var(--r-full);
    background: var(--n100);
    color: var(--n400);
    margin-bottom: var(--s4);
}

.empty .title {
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--n700);
}

.empty .hint {
    font-size: var(--fs-sm);
    color: var(--n500);
    margin-top: var(--s1);
    max-width: 42ch;
}

.empty .btn {
    margin-top: var(--s5);
}

/* ------------------------------------------------------ list controls */

.searchbar {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-bottom: var(--s4);
    flex-wrap: wrap;
}

.searchwrap {
    position: relative;
    flex: 1 1 240px;
    min-width: 0;
    max-width: 340px;
}

.searchwrap .icon {
    position: absolute;
    inset-inline-start: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--n400);
    pointer-events: none;
}

.searchbar input[type="search"] {
    width: 100%;
    padding-inline-start: 34px;
}

.searchbar select,
.searchbar input[type="date"] {
    flex: 0 0 auto;
    max-width: 180px;
    font-size: var(--fs-sm);
}

/* A facet that is actually filtering shouldn't look like the ones that
   aren't — otherwise a narrowed list reads as an empty one. */
.searchbar select.picked {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s3);
    flex-wrap: wrap;
    margin-top: var(--s4);
    font-size: var(--fs-sm);
    color: var(--n500);
}

.pager-nav {
    display: flex;
    align-items: center;
    gap: var(--s2);
}

.pagesize a {
    padding: 0 var(--s1);
    text-decoration: none;
    color: var(--n500);
}

.pagesize a.on {
    font-weight: 700;
    color: var(--accent);
}

/* -------------------------------------------------- collapsible cards */

details.card {
    padding: var(--s3) var(--s5);
    margin-bottom: var(--s3);
}

details.card>summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--s3);
    flex-wrap: wrap;
    min-height: 40px;
    font-weight: 600;
    color: var(--n900);
}

details.card>summary::-webkit-details-marker {
    display: none;
}

/* Disclosure caret drawn in CSS, so a card needs no icon markup of its own. */
details.card>summary::before {
    content: "";
    flex: 0 0 auto;
    width: 6px;
    height: 6px;
    border-right: 1.5px solid var(--n400);
    border-bottom: 1.5px solid var(--n400);
    transform: rotate(-45deg);
    transition: transform .15s;
}

details.card[open]>summary::before {
    transform: rotate(45deg);
}

/* A rotation is not mirrored by `dir`, so the closed caret has to be turned
   round by hand or it points away from the text in Arabic. Open points down
   either way, but it is restated so the closed override cannot win over it. */
[dir="rtl"] details.card>summary::before {
    transform: rotate(135deg);
}

[dir="rtl"] details.card[open]>summary::before {
    transform: rotate(45deg);
}

details.card>summary:hover::before {
    border-color: var(--accent);
}

details.card>summary:hover {
    color: var(--accent);
}

details.card>summary:hover .icon {
    color: var(--accent);
}

details.card>summary .sub {
    font-weight: 400;
    font-size: var(--fs-sm);
    color: var(--n500);
}

details.card[open]>summary {
    margin-bottom: var(--s4);
    padding-bottom: var(--s3);
    border-bottom: 1px solid var(--n200);
}

/* ----------------------------------------------------------------forms */

form.inline {
    display: inline;
}

/* Inline one-row forms (filters, row actions). */
.formrow {
    display: flex;
    gap: var(--s3);
    flex-wrap: wrap;
    align-items: flex-end;
}

.formrow label {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    font-size: var(--fs-sm);
    font-weight: 550;
    color: var(--n700);
}

/* Full form pages: an aligned two-column grid. */
.formgrid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s4) var(--s5);
    align-items: start;
}

.formgrid label,
.formgrid .field {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    font-size: var(--fs-sm);
    font-weight: 550;
    color: var(--n700);
    min-width: 0;
}

.formgrid .span2 {
    grid-column: 1 / -1;
}

.formgrid .help {
    font-weight: 400;
    font-size: var(--fs-xs);
    color: var(--n500);
}

.formsection {
    margin: var(--s6) 0 var(--s4);
    padding-bottom: var(--s2);
    border-bottom: 1px solid var(--n200);
}

.formsection:first-child {
    margin-top: 0;
}

.formsection h2 {
    margin: 0;
}

/* A set of checkboxes that should wrap into columns rather than one long row
   (product ↔ modifier groups). */
.checkgrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--s2) var(--s4);
}

.checkgrid label {
    font-size: var(--fs-base);
    font-weight: 400;
}

.formactions {
    display: flex;
    align-items: center;
    gap: var(--s2);
    flex-wrap: wrap;
    margin-top: var(--s5);
}

/* --------------------------------------------------- permission grid (IAM)

   One row per permission area on /admin/users/{id}: what it covers on the
   left, the three mutually exclusive levels on the right. Radios rather than
   a <select> because the whole point of the page is reading a person's access
   at a glance, and a column of closed dropdowns cannot be skimmed. */

.permgrid {
    display: flex;
    flex-direction: column;
    gap: 0;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow: hidden;
}

.permrow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s4);
    padding: var(--s3) var(--s4);
    flex-wrap: wrap;
}

.permrow+.permrow {
    border-top: 1px solid var(--line);
}

.permrow.granted {
    background: var(--accent-soft);
}

.permrow .what {
    min-width: 0;
    flex: 1 1 22rem;
}

.permrow .what strong {
    display: block;
}

.permrow .what span {
    color: var(--muted);
    font-size: var(--fs-sm);
}

/* The level choices: a fixed-width strip so every row's radios line up
   however long the description above them wrapped. */
.permlevels {
    display: flex;
    align-items: center;
    gap: var(--s3);
    flex: 0 0 auto;
}

.permlevels label {
    display: flex;
    align-items: center;
    gap: var(--s1);
    font-size: var(--fs-sm);
    white-space: nowrap;
    cursor: pointer;
}

/* Read-only rendering, for someone who may look at access but not change it. */
.permlevels .fixed {
    color: var(--muted);
}

/* ------------------------------------------------------------ utilities */

.row {
    display: flex;
    align-items: center;
    gap: var(--s3);
    flex-wrap: wrap;
}

/* A heading with a `.right` action in it lays out as a flex row, so a page or
   panel title can carry its button without the old float:right. */
h1:has(> .right),
h2:has(> .right),
h3:has(> .right) {
    display: flex;
    align-items: center;
    gap: var(--s3);
    flex-wrap: wrap;
}

/* A label wrapping a checkbox reads across, not down. */
.formrow label.check,
.formgrid label.check,
label.check {
    flex-direction: row;
    align-items: center;
    gap: var(--s2);
}

.spread {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s3);
    flex-wrap: wrap;
}

.grow {
    flex: 1 1 auto;
    min-width: 0;
}

.right {
    margin-inline-start: auto;
}

.txt-danger {
    color: var(--danger);
}

.txt-success {
    color: var(--success);
}

.txt-warning {
    color: var(--warning);
}

.txt-muted {
    color: var(--n500);
}

.nowrap {
    white-space: nowrap;
}

.items-start {
    align-items: flex-start;
}

.items-end {
    align-items: flex-end;
}

.fs-sm {
    font-size: var(--fs-sm);
}

.fs-lg {
    font-size: var(--fs-lg);
}

a.plain {
    text-decoration: none;
}

.bold {
    font-weight: 600;
}

.normal {
    font-weight: 400;
}

.hidden {
    display: none;
}

.w-full {
    width: 100%;
}

/* Field-width hints for the inline `.formrow` layout; the mobile rule below
   still stretches every field to full width. */
.w-xs {
    width: 6rem;
}

.w-sm {
    width: 9rem;
}

.w-md {
    width: 14rem;
}

.w-lg {
    width: 20rem;
}

.m-0 {
    margin: 0;
}

.mt-0 {
    margin-top: 0;
}

.mt-2 {
    margin-top: var(--s2);
}

.mt-3 {
    margin-top: var(--s3);
}

.mt-4 {
    margin-top: var(--s4);
}

.mt-6 {
    margin-top: var(--s6);
}

.mb-0 {
    margin-bottom: 0;
}

.mb-2 {
    margin-bottom: var(--s2);
}

.mb-3 {
    margin-bottom: var(--s3);
}

.mb-4 {
    margin-bottom: var(--s4);
}

.mb-6 {
    margin-bottom: var(--s6);
}

.icon {
    display: inline-block;
    vertical-align: middle;
    flex-shrink: 0;
}

/* ---------------------------------------------------- searchable select */

.combo {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.combo-input {
    width: 100%;
}

/* The real <select> stays in the form (a display:none control is still
   submitted) — the combobox is only a filter in front of it, so the page
   keeps working with JS off. */
.combo-native {
    display: none;
}

.combo-list {
    position: absolute;
    top: calc(100% + 4px);
    inset-inline: 0;
    max-height: 280px;
    overflow-y: auto;
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-md);
    box-shadow: var(--sh3);
    padding: var(--s1);
    z-index: 70;
}

.combo-list div {
    padding: var(--s2) var(--s3);
    border-radius: var(--r-sm);
    font-size: var(--fs-base);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.combo-list div:hover,
.combo-list div.on {
    background: var(--n100);
}

.combo-list .none {
    color: var(--n500);
    cursor: default;
}

/* ------------------------------------------------------- odds and ends */

/* Product image preview on the product form. */
.thumb {
    width: 72px;
    height: 72px;
    object-fit: cover;
    border-radius: var(--r-md);
    background: var(--n100);
    border: 1px solid var(--n200);
}

/* A one-time device activation code — read aloud, so give it room. */
code.pin {
    font-size: var(--fs-lg);
    letter-spacing: 2px;
    font-weight: 600;
    color: var(--n900);
}

/* The screen-only toolbar above a printable sheet (payslips). */
.printbar {
    display: flex;
    justify-content: flex-end;
    gap: var(--s2);
    max-width: 640px;
    margin: 0 auto var(--s3);
}

/* ZATCA tax-invoice panel on the sale detail page: seller identity beside
   the QR the customer scans. */
.taxpanel {
    display: flex;
    gap: var(--s6);
    flex-wrap: wrap;
    align-items: flex-start;
}

.taxpanel .who {
    flex: 1 1 16rem;
    min-width: 0;
}

.taxpanel .who p {
    margin: 0 0 2px;
}

.taxpanel .qr {
    width: 150px;
    text-align: center;
}

.taxpanel .qr img,
.taxpanel .qr svg {
    width: 100%;
    height: auto;
}

.taxpanel .qr .cap {
    font-size: var(--fs-xs);
    margin: var(--s1) 0 0;
}

/* --------------------------------------------------------- report library */

.reportgrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--s4);
}

.reportcard {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    padding: var(--s5);
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-lg);
    box-shadow: var(--sh1);
    text-decoration: none;
    transition: border-color .12s, box-shadow .12s, transform .12s;
}

.reportcard:hover {
    border-color: var(--accent-soft-border);
    box-shadow: var(--sh2);
    text-decoration: none;
}

.rc-title {
    display: flex;
    align-items: center;
    gap: var(--s2);
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--n900);
}

.rc-title .icon {
    margin-inline-start: auto;
    color: var(--n300);
}

.reportcard:hover .rc-title {
    color: var(--accent);
}

.reportcard:hover .rc-title .icon {
    color: var(--accent);
}

.rc-blurb {
    font-size: var(--fs-sm);
    color: var(--n500);
    line-height: 1.45;
}

/* --------------------------------------------------------- language toggle */

/* Sits beside the palette trigger and borrows its shape. Shows the language
   tapping it switches *to*, the same convention as the till's 🌐 button. */
.langtoggle {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    flex: 0 0 auto;
    height: 32px;
    padding-block: 0;
    padding-inline: var(--s3);
    margin-inline-start: var(--s2);
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-full);
    color: var(--n500);
    font-size: var(--fs-sm);
    text-decoration: none;
    white-space: nowrap;
}

.langtoggle:hover {
    color: var(--accent);
    border-color: var(--accent);
}

/* The login page has no topbar to sit in. */
.loginlang {
    position: absolute;
    top: var(--s4);
    inset-inline-end: var(--s4);
    z-index: 2;
}

/* The label is the other language's own name, so it renders in that script
   regardless of the page direction — keep it isolated from the surrounding
   bidi run or the parenthesis-free label can still reorder oddly. */
.langtoggle span {
    unicode-bidi: isolate;
}

@media (max-width: 700px) {
    .langtoggle span {
        display: none;
    }
}

/* ------------------------------------------------------- jump-to palette */

/* The topbar affordance — the shortcut alone would never be discovered. */
.palette-trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    height: 32px;
    padding-block: 0;
    padding-inline: var(--s3) var(--s2);
    margin-inline-start: var(--s3);
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-full);
    box-shadow: none;
    color: var(--n500);
    font-size: var(--fs-sm);
    font-weight: 450;
    cursor: pointer;
}

.palette-trigger:hover {
    background: var(--n100);
    color: var(--n700);
}

.palette-trigger kbd {
    font: inherit;
    font-size: 11px;
    padding: 1px 6px;
    border: 1px solid var(--n200);
    border-radius: var(--r-sm);
    background: var(--n50);
    color: var(--n500);
}

.palette {
    position: fixed;
    inset: 0;
    z-index: 80;
    background: rgba(28, 25, 23, .35);
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: 12vh var(--s4) var(--s4);
}

.palette[hidden] {
    display: none;
}

.palette-box {
    width: 100%;
    max-width: 520px;
    background: var(--n0);
    border: 1px solid var(--n200);
    border-radius: var(--r-lg);
    box-shadow: var(--sh3);
    overflow: hidden;
}

.palette-input {
    width: 100%;
    height: 48px;
    border: none;
    border-bottom: 1px solid var(--n200);
    border-radius: 0;
    padding: 0 var(--s5);
    font-size: var(--fs-md);
}

.palette-input:focus {
    outline: none;
    box-shadow: none;
    border-color: var(--n200);
}

.palette-list {
    max-height: 320px;
    overflow-y: auto;
    padding: var(--s2);
}

.palette-row {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s2) var(--s3);
    border-radius: var(--r-sm);
    color: var(--n800);
    text-decoration: none;
    font-size: var(--fs-base);
}

.palette-row:hover {
    text-decoration: none;
}

.palette-row.on {
    background: var(--accent-soft);
    color: var(--accent);
}

.palette-row .icon {
    color: var(--n400);
}

.palette-row.on .icon {
    color: var(--accent);
}

.palette-group {
    margin-inline-start: auto;
    font-size: var(--fs-xs);
    color: var(--n400);
    text-transform: uppercase;
    letter-spacing: .04em;
}

.palette-none {
    padding: var(--s4) var(--s3);
    color: var(--n500);
    font-size: var(--fs-sm);
    text-align: center;
}

@media (max-width: 720px) {
    .palette-trigger span {
        display: none;
    }

    .palette-trigger kbd {
        display: none;
    }
}

/* --------------------------------------------------------------- login */

.loginpage {
    display: grid;
    grid-template-columns: 45% 1fr;
    min-height: 100vh;
}

.loginpage .brandside {
    background: var(--n900);
    color: var(--n0);
    padding: var(--s12) var(--s10);
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--s4);
}

.loginpage .brandside .mark {
    display: flex;
    align-items: center;
    gap: var(--s3);
    font-size: var(--fs-xl);
    font-weight: 700;
}

.loginpage .brandside .tagline {
    font-size: var(--fs-lg);
    color: var(--n400);
    max-width: 26ch;
    line-height: 1.4;
}

.loginpage .formside {
    display: grid;
    place-items: center;
    padding: var(--s8) var(--s6);
}

.loginpage .box {
    width: 100%;
    max-width: 360px;
}

.loginpage .box h1 {
    margin-bottom: var(--s1);
}

.loginpage .box .sub {
    font-size: var(--fs-sm);
    color: var(--n500);
    margin: 0 0 var(--s6);
}

.loginpage .box label {
    display: block;
    font-size: var(--fs-sm);
    font-weight: 550;
    margin-bottom: var(--s4);
}

.loginpage .box input {
    display: block;
    width: 100%;
    margin-top: var(--s1);
}

.loginpage .box button {
    width: 100%;
    margin-top: var(--s2);
}

/* ----------------------------------------------------------- responsive */

@media (max-width: 900px) {
    .sidebar {
        transform: translateX(-100%);
        transition: transform .2s ease;
        box-shadow: var(--sh3);
    }

    /* The drawer sits on the right in Arabic, so it has to slide off to the
       right — translateX is physical and would otherwise hide it across the
       screen instead of behind its own edge. */
    [dir="rtl"] .sidebar {
        transform: translateX(100%);
    }

    .nav-toggle-cb:checked~.sidebar {
        transform: none;
    }

    .nav-toggle-cb:checked~.nav-scrim {
        display: block;
    }

    .navburger {
        display: inline-flex;
    }

    .shell {
        margin-inline-start: 0;
    }

    .topbar {
        padding: 0 var(--s4);
    }

    main {
        padding: var(--s5) var(--s4) var(--s10);
    }
}

@media (max-width: 800px) {
    .grid2 {
        grid-template-columns: 1fr;
    }

    .formgrid {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 720px) {
    body {
        overflow-x: hidden;
    }

    .formrow {
        flex-direction: column;
        align-items: stretch;
    }

    .formrow label,
    .formrow input,
    .formrow select,
    .formrow textarea {
        width: 100%;
    }

    .searchwrap {
        max-width: none;
        flex-basis: 100%;
    }

    .loginpage {
        grid-template-columns: 1fr;
    }

    .loginpage .brandside {
        display: none;
    }
}

/* --------------------------------------------------------------- print */

@media print {

    .sidebar,
    .topbar,
    .nav-scrim,
    .searchbar,
    .pager,
    .pagehead .actions,
    .formactions {
        display: none !important;
    }

    body {
        background: var(--n0);
    }

    .shell {
        margin-inline-start: 0;
    }

    main {
        max-width: none;
        padding: 0;
    }

    .panel,
    .card,
    .stat,
    .tablewrap {
        border: none;
        box-shadow: none;
        padding: 0;
    }

    th {
        position: static;
    }
}
