/* ===========================================================================
   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; }
.mono { font-variant-numeric: tabular-nums; }

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

.mode-strip {
    border-radius: var(--radius);
    padding: .8rem 1rem .8rem 1.1rem;
    margin-bottom: .9rem;
    border-left: 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-left-color: var(--accent);  background: var(--accent-soft); }
.mode-store .caption { color: var(--accent); }
.mode-remove { border-left-color: var(--warning); background: var(--warning-soft); }
.mode-remove .caption { color: var(--warning); }
.mode-switch { border-left-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;
}

/* contain, not cover: with cover the browser crops the frame to fill the box,
   so part of what the camera sees is never shown - and the operator ends up
   aiming at a region the decoder is not looking at. contain guarantees that
   whatever is visible is exactly what gets decoded. */
.scanner video { width: 100%; height: 100%; object-fit: contain; }

/* The whole frame is decoded, so the reticle is an aiming aid rather than a
   boundary - drawn light, with no darkened surround implying "only in here". */
.scanner .reticle {
    position: absolute;
    inset: 12% 16%;
    border: 2px dashed rgba(255, 255, 255, .55);
    border-radius: 10px;
    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: right; 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;
    left: 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); }
.toggle:checked::after { transform: translateX(20px); }
.toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

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