/* ===========================================================================
   StoreIT mobile.

   Mobile-first and sized in relative units throughout, because this runs on
   anything from a 320px-wide phone to a 13" tablet, and on notched/rounded
   screens. Three things do the heavy lifting:

     - safe-area insets, so nothing hides under a notch or a home indicator
     - dvh rather than vh, so the layout does not jump when mobile browser
       chrome slides away
     - a single fluid grid that reflows to columns on wide screens instead of
       a separate tablet layout

   Colours mirror the desktop app's Theme.cs so the two read as one product.
   =========================================================================== */

:root {
    --page: #f4f6f9;
    --surface: #ffffff;
    --border: #e2e8f0;
    --text: #161d27;
    --text-muted: #64707d;

    --accent: #2563eb;
    --accent-soft: #e8f0fe;
    --danger: #dc2626;
    --danger-soft: #fee9e9;
    --success: #16a34a;
    --success-soft: #e6f7ec;
    --warning: #d97706;
    --warning-soft: #fef3e0;
    --neutral: #64707d;
    --neutral-soft: #eef1f5;

    --radius: 12px;
    --shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .1);

    /* Big enough to hit with a gloved thumb in a warehouse. */
    --tap: 48px;

    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --page: #11161d;
        --surface: #1a212b;
        --border: #2b3542;
        --text: #e8edf3;
        --text-muted: #97a3b2;
        --accent-soft: #17233a;
        --danger-soft: #2e1a1c;
        --success-soft: #14261c;
        --warning-soft: #2c2113;
        --neutral-soft: #222b36;
        --shadow: 0 1px 3px rgba(0, 0, 0, .4);
    }
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--page);
    color: var(--text);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
    font-size: 16px;            /* never below 16px: iOS zooms the page otherwise */
    -webkit-text-size-adjust: 100%;
    overscroll-behavior-y: none;
}

/* --- app shell ---------------------------------------------------------- */

.app {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

.app-header {
    position: sticky;
    top: 0;
    z-index: 20;
    padding: calc(var(--safe-top) + .7rem) calc(var(--safe-right) + 1rem) .7rem calc(var(--safe-left) + 1rem);
    background: #181f2a;
    color: #fff;
    display: flex;
    align-items: center;
    gap: .7rem;
}

.app-header .brand { font-weight: 600; font-size: 1.05rem; letter-spacing: .2px; }
.app-header .spacer { flex: 1; }

/* Deliberately loud. Being in ADMIN without noticing is how a part type gets
   deleted by accident. */
.app-header .admin-flag {
    font-size: .64rem;
    font-weight: 800;
    letter-spacing: .8px;
    padding: .2rem .45rem;
    border-radius: 4px;
    background: var(--danger);
    color: #fff;
}
.app-header .conn { font-size: .72rem; opacity: .75; }

.app-main {
    flex: 1;
    padding: 1rem calc(var(--safe-right) + 1rem) 1.2rem calc(var(--safe-left) + 1rem);
    /* clear the fixed tab bar */
    padding-bottom: calc(var(--tap) + var(--safe-bottom) + 2.2rem);
    max-width: 1100px;
    width: 100%;
    margin-inline: auto;
}

/* --- bottom tab bar ----------------------------------------------------- */

.tabs {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 30;
    display: flex;
    background: var(--surface);
    border-top: 1px solid var(--border);
    padding: .25rem calc(var(--safe-right)) calc(var(--safe-bottom)) calc(var(--safe-left));
}

.tabs a {
    flex: 1;
    min-height: var(--tap);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .15rem;
    padding: .3rem .1rem;
    text-decoration: none;
    color: var(--text-muted);
    font-size: .68rem;
    line-height: 1.1;
    text-align: center;
}

.tabs a.active { color: var(--accent); font-weight: 600; }
.tabs a svg { width: 21px; height: 21px; }

/* On a wide screen the tab bar becomes a normal top nav row. */
@media (min-width: 820px) {
    .tabs {
        position: sticky;
        top: 0;
        border-top: none;
        border-bottom: 1px solid var(--border);
        max-width: 1100px;
        margin-inline: auto;
    }
    .tabs a { flex-direction: row; gap: .45rem; font-size: .85rem; }
    .app-main { padding-bottom: 2rem; }
}

/* --- cards, text -------------------------------------------------------- */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1rem;
    margin-bottom: .9rem;
}

.card h2 { margin: 0 0 .6rem; font-size: 1rem; }
.page-title { margin: .2rem 0 .9rem; font-size: 1.3rem; font-weight: 650; }
.muted { color: var(--text-muted); font-size: .85rem; }
/*
   Codes and numbers inside Hebrew text.

   unicode-bidi: isolate stops a run like "PT-100001" or "IPE-200" from being
   reordered by the surrounding right-to-left text - without it a trailing
   hyphen or digit group jumps to the wrong end of the code, which looks like
   a data error rather than a typography one. isolate is enough on its own;
   forcing direction:ltr here would wreck the Hebrew words that share these
   lines ("100014 · יחידה 3312").
*/
.mono { font-variant-numeric: tabular-nums; unicode-bidi: isolate; }

/* For a code standing alone, where the whole run really is left-to-right. */
.ltr { direction: ltr; unicode-bidi: isolate; text-align: start; }

/* Mirror the one directional icon in the app: the arrow leaving the box on
   the Deliver tab. Text and layout flip themselves; SVG paths do not. */
[dir="rtl"] .tab-deliver svg { transform: scaleX(-1); }

/* --- the mode banner, mirroring the desktop's ModeStrip ----------------- */

.mode-strip {
    border-radius: var(--radius);
    /* Logical padding: the wider edge follows the coloured stripe, which sits
       on the right in Hebrew and the left in a left-to-right language. */
    padding: .8rem 1rem .8rem 1.1rem;
    padding-inline: 1.1rem 1rem;
    margin-bottom: .9rem;
    border-inline-start: 7px solid var(--neutral);
    background: var(--neutral-soft);
}

.mode-strip .caption {
    font-weight: 700;
    font-size: .95rem;
    letter-spacing: .3px;
    color: var(--neutral);
}

.mode-strip .detail { font-size: .8rem; color: var(--text-muted); margin-top: .15rem; }

.mode-store  { border-inline-start-color: var(--accent);  background: var(--accent-soft); }
.mode-store .caption { color: var(--accent); }
.mode-remove { border-inline-start-color: var(--warning); background: var(--warning-soft); }
.mode-remove .caption { color: var(--warning); }
.mode-switch { border-inline-start-color: var(--success); background: var(--success-soft); }
.mode-switch .caption { color: var(--success); }

/* --- buttons ------------------------------------------------------------ */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    min-height: var(--tap);
    padding: .6rem 1rem;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: .95rem;
    font-weight: 550;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}

.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .5; cursor: default; transform: none; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-danger  { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-ghost   { background: transparent; border-color: transparent; color: var(--accent); }
.btn-block   { display: flex; width: 100%; }
.btn-lg      { min-height: 56px; font-size: 1.02rem; }

.btn-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.btn-row > .btn { flex: 1 1 auto; }

/* --- status banner ------------------------------------------------------ */

.status {
    border-radius: 10px;
    padding: .65rem .8rem;
    font-size: .88rem;
    font-weight: 550;
    margin-bottom: .8rem;
    background: var(--neutral-soft);
    color: var(--text-muted);
}

.status-good { background: var(--success-soft); color: var(--success); }
.status-warn { background: var(--warning-soft); color: var(--warning); }
.status-bad  { background: var(--danger-soft);  color: var(--danger); }

/* --- scanner ------------------------------------------------------------ */

.scanner {
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 3;
    max-height: 45dvh;
    background: #12161d;
    border-radius: var(--radius);
    overflow: hidden;
    display: grid;
    place-items: center;
}

/* ABSOLUTELY positioned, so the element's box is exactly the panel's box.
   As a grid item with height:100% it was ambiguous: a landscape stream resolved
   to the panel height, but a PORTRAIT one fell back to its intrinsic ratio and
   laid out 577px tall inside a 244px panel, overflowing and being clipped. The
   square was then centred on the element - whose centre was below the visible
   area - which is exactly the "square is not in the middle" report from a phone.

   cover, not contain: the picture fills the panel instead of showing as a
   narrow strip when the camera hands back a portrait frame. Nothing is lost by
   cropping, because the decode region is a square inside the visible area and
   is mapped through this same fit - see reticleGeometry() in scanner.js. */
.scanner video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The square IS the decode region - nothing outside it is read. So it is drawn
   as a real boundary: solid and bright, with the surround dimmed, rather than
   the old dashed aiming hint that only suggested where to point.

   Deliberately NO inset here. scanner.js positions and sizes it from the crop
   it actually decodes (see reticleRect/syncReticle); a CSS percentage would be
   a percentage of the PANEL, which the letterboxed frame does not fill, so the
   border would sit off the region it claims to mark. Invisible until JS has
   placed it once, or it would flash at the wrong size on the first frame. */
.scanner .reticle {
    position: absolute;
    border: 2px solid rgba(255, 255, 255, .9);
    border-radius: 10px;
    pointer-events: none;
    opacity: 0;
    /* Dims everything outside the square without needing a second element:
       the huge spread is clipped by .scanner's overflow:hidden. */
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .45);
}

.scanner .reticle.ready { opacity: 1; }

/* Paused: the camera is still live, so the square has to stop looking like a
   target or the operator will keep presenting labels to it. Dashed and faint
   is the same language the reticle used before it meant anything. */
.scanner .reticle.paused {
    border-style: dashed;
    border-color: rgba(255, 255, 255, .4);
}

/* Covers the whole preview while decoding is suspended. Painted over the
   reticle (later in the DOM, both positioned) so the square reads as inactive,
   and pointer-events:none so it can never swallow a tap meant for the panel. */
.scanner .scan-paused {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(9, 12, 18, .74);
    color: #e2e8f0;
    font-size: .9rem;
    line-height: 1.45;
    text-align: center;
    pointer-events: none;
}

/* Says why nothing happens when the label is off to one side. Anchored to the
   panel, not to the square, so it cannot be pushed off the bottom edge. */
.scanner .aim-hint {
    position: absolute;
    inset-inline: 0;
    bottom: .45rem;
    text-align: center;
    color: #e2e8f0;
    font-size: .78rem;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .9);
    pointer-events: none;
}

.scanner .hint {
    position: absolute;
    color: #cbd5e1;
    font-size: .85rem;
    text-align: center;
    padding: 1rem;
}

/* The camera panel doubles as the start button, so the tap that raises the
   browser's permission popup is the most obvious thing on the screen. */
.scanner-tappable { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.scanner-tappable:active { filter: brightness(1.25); }

.tap-to-scan {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    color: #e2e8f0;
    padding: 1rem;
    text-align: center;
}

.tap-to-scan svg { width: 52px; height: 52px; opacity: .9; }
.tap-to-scan .tap-title { font-size: 1.05rem; font-weight: 650; }
.tap-to-scan .tap-sub { font-size: .82rem; color: #94a3b8; max-width: 22rem; }

/* --- scanner diagnostics ------------------------------------------------ */

.diag {
    margin-top: .5rem;
    padding: .7rem .8rem;
    background: var(--neutral-soft);
    border-radius: 10px;
    font-size: .78rem;
    font-family: ui-monospace, Consolas, monospace;
}

.diag-row {
    display: flex;
    justify-content: space-between;
    gap: .8rem;
    padding: .18rem 0;
    border-bottom: 1px solid var(--border);
}

.diag-row:last-of-type { border-bottom: none; }
.diag-row .k { color: var(--text-muted); }
.diag-row .v { color: var(--text); font-weight: 600; text-align: end; word-break: break-all; }

/* attempts stuck at 0 is the headline fault - make it impossible to miss */
.diag-row .v.bad { color: var(--danger); }

/* --- manual entry ------------------------------------------------------- */

.field { display: flex; gap: .5rem; margin-top: .7rem; }

.field input, .field select {
    flex: 1;
    min-width: 0;
    min-height: var(--tap);
    padding: .55rem .7rem;
    font: inherit;
    font-size: 1rem;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
}

select.full { width: 100%; min-height: var(--tap); padding: .55rem .7rem;
    font: inherit; font-size: 1rem; color: var(--text);
    background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }

/* --- settings switches --------------------------------------------------- */

/* The whole row is a <label>, so the hit area is the full width rather than
   just the switch - the difference between easy and fiddly with a gloved thumb. */
.setting-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: var(--tap);
    cursor: pointer;
}

.setting-text { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.setting-title { font-weight: 600; }
.setting-sub { font-size: .85rem; color: var(--text-muted); }

/* Built from the checkbox itself rather than from extra markup, so it stays
   keyboard focusable and still announces as a checkbox to a screen reader. */
.toggle {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    flex: 0 0 auto;
    width: 52px;
    height: 32px;
    margin: 0;
    background: var(--neutral-soft);
    border: 1px solid var(--border);
    border-radius: 999px;
    cursor: pointer;
    transition: background .18s ease, border-color .18s ease;
}

/* The control is only 32px tall for looks, so its hit area is grown back to
   the 48px minimum instead - nothing here should be under a thumb's width. */
.toggle::before {
    content: "";
    position: absolute;
    inset: -8px -4px;
}

.toggle::after {
    content: "";
    position: absolute;
    top: 3px;
    /* Starts at the inline start, so the knob sits on the right in Hebrew and
       travels left - the mirror of the English behaviour, which is what a
       right-to-left reader expects. */
    inset-inline-start: 3px;
    width: 24px;
    height: 24px;
    background: #ffffff;        /* white in both themes, as on iOS */
    border-radius: 50%;
    box-shadow: var(--shadow);
    transition: transform .18s ease;
}

.toggle:checked { background: var(--accent); border-color: var(--accent); }

/* transform has no logical form, so the direction is spelled out per side. */
.toggle:checked::after { transform: translateX(20px); }
[dir="rtl"] .toggle:checked::after { transform: translateX(-20px); }
.toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- delivery receipt ---------------------------------------------------- */

/*
   Printed as often as it is read on screen, so it is built for paper first:
   a white ground, black text and hairline rules, all of which survive a phone's
   "Save as PDF" without the operator changing any print setting.
*/
.receipt {
    background: #fff;
    color: #111;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.1rem;
}

.receipt-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: .8rem;
    border-bottom: 2px solid #111;
}

.receipt-brand { font-size: 1.35rem; font-weight: 800; letter-spacing: .5px; }
.receipt-sub { font-size: .95rem; color: #444; margin-top: .1rem; }
.receipt-no { text-align: end; }
.receipt-no-label { font-size: .72rem; color: #444; text-transform: uppercase; }
.receipt-no-value { font-size: 1.5rem; font-weight: 800; line-height: 1.1; }

.receipt-meta {
    display: flex;
    justify-content: space-between;
    gap: 1.2rem;
    flex-wrap: wrap;
    margin: .9rem 0 1rem;
}

.receipt-label { font-size: .72rem; color: #555; margin-bottom: .15rem; }
.receipt-value { font-size: .95rem; font-weight: 600; }
.receipt-value.muted { font-weight: 400; color: #555; font-size: .82rem; }

.receipt-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.receipt-table th,
.receipt-table td { padding: .45rem .3rem; border-bottom: 1px solid #ddd; text-align: start; }
.receipt-table th { font-size: .75rem; color: #555; border-bottom: 1px solid #111; }
/* Numeric columns hug the far edge, which is the LEFT in a right-to-left page. */
.receipt-table .num { text-align: end; white-space: nowrap; }
.receipt-table tfoot td { font-weight: 800; border-bottom: none; border-top: 2px solid #111; }

.receipt-signatures {
    display: flex;
    gap: 1.5rem;
    margin-top: 2.2rem;
}

.receipt-signatures > div { flex: 1; }
.receipt-sig-line { border-bottom: 1px solid #111; height: 2rem; }

/* --- printable documents: barcode labels and catalogue tables ------------ */

/*
   Same paper-first treatment as the receipt above - white ground, black text,
   hairline rules - but under its own names rather than borrowing .receipt-*,
   which would be a lie about what these are.
*/
.print-doc,
.print-label {
    background: #fff;
    color: #111;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.1rem;
}

.print-label { text-align: center; }

/*
   The barcode's printed size is the whole point of the page: a Data Matrix
   below about 20mm is unreliable to scan off paper, so it is sized in
   MILLIMETRES rather than pixels and given a comfortable margin over that.
*/
.print-label-code {
    width: 58mm;
    max-width: 100%;
    margin: 0 auto .9rem;
}

.print-label-code svg { width: 100%; height: auto; display: block; }

/* text-align is restated because .ltr sets `start`, and `start` inside an
   ltr box is LEFT - which knocked the number out of the centre while the
   barcode above and the name below stayed centred. */
.print-label-id {
    font-size: 1.6rem;
    font-weight: 800;
    letter-spacing: 1px;
    text-align: center;
}
.print-label-name { font-size: 1.1rem; font-weight: 600; margin-top: .35rem; }
.print-label-sub { font-size: .85rem; color: #555; margin-top: .25rem; }

.print-doc-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: .8rem;
    margin-bottom: .9rem;
    border-bottom: 2px solid #111;
}

.print-doc-brand { font-size: 1.35rem; font-weight: 800; letter-spacing: .5px; }
.print-doc-title { font-size: .95rem; color: #444; margin-top: .1rem; }
.print-doc-meta { text-align: end; font-size: .82rem; color: #444; }

.print-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.print-table th,
.print-table td { padding: .45rem .3rem; border-bottom: 1px solid #ddd; text-align: start; }
.print-table th { font-size: .75rem; color: #555; border-bottom: 1px solid #111; }
/*
   The barcode column. Two things fight here and both have to be settled by
   hand:

     - the cell is LTR, because "PT-100001" is Latin and the hyphen lands
       between a letter run and a digit run, which bidi is entitled to reorder;
     - but `text-align: end` inside an LTR cell means RIGHT, while the column
       belongs against the far edge of a Hebrew page, which is the physical
       LEFT. That mismatch left the numbers right-aligned under a left-aligned
       heading.

   So the alignment is stated physically, on the heading as well as the cells.
*/
.print-table .code {
    direction: ltr;
    unicode-bidi: isolate;
    text-align: left;
    white-space: nowrap;
}

/* Quantities stay in the reading direction and hug the far edge of the page,
   so the eye can run down the column against a physical count. */
.print-table .qty { text-align: end; white-space: nowrap; width: 4.5rem; }

/*
   The scannable symbol, one per row, so printing the list prints the labels.

   Sized in MILLIMETRES like the single label, for the same reason: a Data
   Matrix below about 20mm is unreliable to read off paper. 25mm is comfortably
   over that and still leaves roughly ten rows on an A4 sheet. The padding is a
   quiet zone in its own right - a symbol butted against a table rule often
   will not decode.
*/
.print-table .bc { width: 27mm; padding: .35rem .3rem; }

.print-table .bc svg {
    width: 25mm;
    height: auto;
    display: block;
}
.print-table tfoot td { font-weight: 800; border-bottom: none; border-top: 2px solid #111; }

/*
   Printing. The app shell, the tab bar and the print button itself are controls,
   not document, so they go; the receipt drops its card styling and fills the
   sheet. color-adjust keeps the rules black if the browser decides to save ink.
*/
@media print {
    .no-print,
    .app-header,
    .tabs { display: none !important; }

    html, body, .app, .app-main {
        background: #fff !important;
        padding: 0 !important;
        margin: 0 !important;
        display: block !important;
    }

    .receipt {
        border: none;
        border-radius: 0;
        padding: 0;
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* A delivery note should not spill one line onto a second sheet. */
    .receipt-table { page-break-inside: auto; }
    .receipt-table tr { page-break-inside: avoid; }
    .receipt-signatures { page-break-inside: avoid; }

    .print-doc,
    .print-label {
        border: none;
        border-radius: 0;
        padding: 0;
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* Slightly smaller on paper than on screen and still far above the ~20mm
       a Data Matrix needs to read reliably. */
    .print-label-code { width: 45mm; }

    /* A catalogue runs to several sheets, so the column headings have to come
       back at the top of each one - otherwise page two is two bare columns of
       text and numbers with nothing saying which is which. */
    .print-table thead { display: table-header-group; }
    .print-table tr { page-break-inside: avoid; }
}

/* --- scanned list ------------------------------------------------------- */

.rows { display: flex; flex-direction: column; gap: .45rem; }

.row {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .6rem .7rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}

.row .grow { flex: 1; min-width: 0; }
.row .name { font-weight: 550; font-size: .92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row .sub { font-size: .76rem; color: var(--text-muted); }

.row .drop {
    min-height: 38px; min-width: 38px;
    border: none; background: transparent;
    color: var(--danger); font-size: 1.3rem; line-height: 1;
    cursor: pointer; border-radius: 8px;
}

.pill {
    font-size: .68rem;
    font-weight: 700;
    padding: .2rem .45rem;
    border-radius: 999px;
    background: var(--neutral-soft);
    color: var(--text-muted);
    white-space: nowrap;
}

.pill-dismantled { background: var(--danger-soft); color: var(--danger); }
.pill-storage    { background: var(--accent-soft); color: var(--accent); }
.pill-constructor{ background: var(--warning-soft); color: var(--warning); }
.pill-installed  { background: var(--success-soft); color: var(--success); }

/* --- stats grid: reflows on its own, no breakpoints needed -------------- */

.stats { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .8rem;
    /* Tiles are links; keep them looking like tiles, not blue underlined text. */
    display: block;
    text-decoration: none;
    color: inherit;
    min-height: var(--tap);
    -webkit-tap-highlight-color: transparent;
}

a.stat:active { background: var(--neutral-soft); }
.stat .n { font-size: 1.6rem; font-weight: 650; line-height: 1.1; }
.stat .l { font-size: .78rem; color: var(--text-muted); margin-top: .15rem; }

/* --- sign in ------------------------------------------------------------ */

.login-wrap {
    min-height: 70dvh;
    display: flex;
    align-items: center;
    justify-content: center;
}

.login-wrap .card { width: 100%; max-width: 380px; }

.login-brand {
    display: flex;
    align-items: center;
    gap: .55rem;
    font-size: 1.25rem;
    font-weight: 650;
    margin-bottom: .5rem;
}

/* --- dialog ------------------------------------------------------------- */

.sheet-backdrop {
    position: fixed; inset: 0; z-index: 50;
    background: rgba(8, 12, 18, .5);
    display: flex; align-items: flex-end; justify-content: center;
}

.sheet {
    width: 100%; max-width: 520px;
    background: var(--surface);
    border-radius: var(--radius) var(--radius) 0 0;
    padding: 1.1rem 1.1rem calc(1.1rem + var(--safe-bottom));
    box-shadow: 0 -8px 30px rgba(0, 0, 0, .25);
}

@media (min-width: 640px) {
    .sheet-backdrop { align-items: center; }
    .sheet { border-radius: var(--radius); }
}

.sheet h3 { margin: 0 0 .5rem; font-size: 1.05rem; }
.sheet p { margin: 0 0 1rem; font-size: .92rem; color: var(--text-muted); }

/*
   Title bar for a scrolling sheet, with its own way out.
   Sticky rather than merely first: a shelf can hold hundreds of units, and a
   Close button at the top is no use once you have scrolled past it. This one
   stays put wherever you are in the list.
*/
.sheet-head {
    position: sticky;

    /* NOT top:0. A sticky element anchors to the scrollport, whose top edge is
       already inside the sheet's 1.1rem padding - so top:0 leaves a gap that
       the list scrolls visibly through. Pulling the anchor up by that same
       padding pins the bar flush to the sheet's real top edge. The negative
       margin does the matching job horizontally. */
    top: -1.1rem;
    z-index: 2;

    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;

    margin: -1.1rem -1.1rem .7rem;
    padding: .85rem 1.1rem .7rem;

    background: var(--surface);
    border-bottom: 1px solid var(--border);
    /* Full-bleed, so it must not square off the sheet's rounded top. */
    border-radius: var(--radius) var(--radius) 0 0;
}

.sheet-head h3 { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* Never let a long shelf name squeeze the way out. */
.sheet-head .btn { flex: 0 0 auto; min-height: 44px; padding: .5rem .9rem; }
