/* Household Shopping — design tokens and shared vocabulary.
 *
 * Deliberate divergence from the five sibling sites: they define a single dark
 * palette, tuned for an ops dashboard read indoors at a desk. This app is used on
 * a phone in a supermarket under 800–1500 lux, where a dark screen becomes a
 * mirror and low-contrast text washes out. So light is the reference design and
 * dark is offered for late-night list building at home.
 *
 * The variable NAMES and the class vocabulary are kept identical to the siblings,
 * so a developer moving between projects reads the same CSS. Only the values move.
 */

:root {
    /* LIGHT — the reference */
    --bg:       #f6f7f9;
    --panel:    #ffffff;
    --panel-2:  #eceff3;
    --text:     #12151b;
    --text-2:   #3d4552;
    --muted:    #6b7280;   /* 4.83:1 on white */
    --border:   #d8dde5;

    /* The one sibling value that could not be inherited: #4a9eff is 2.2:1 on
     * white, which fails AA for link text, focus rings, and the button carrying
     * the app's primary action. #1668d6 clears 4.5:1 and reads as the same blue. */
    --accent:   #1668d6;

    --green:    #17803d;
    --red:      #c02626;
    --orange:   #b45309;
    --yellow:   #a16207;
    --purple:   #6d28d9;

    --shadow-1: 0 1px 2px rgba(0,0,0,.06), 0 1px 3px rgba(0,0,0,.10);
    --shadow-2: 0 8px 24px rgba(0,0,0,.18);

    /* Type scale — six steps, never below 13px.
     * --t-lg is 16px and is a HARD FLOOR for any text input: iOS auto-zooms the
     * viewport on focus for anything smaller, which wrecks the layout mid-trip. */
    --t-xs:  11px;
    --t-sm:  13px;
    --t-md:  15px;
    --t-lg:  16px;
    --t-xl:  17px;
    --t-2xl: 22px;

    /* Spacing — 4px base. Row gutters are 12px, tighter than the siblings' 20/16,
     * because at 320px every pixel of item-name width is contested. */
    --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;

    --r-sm: 4px; --r-md: 8px; --r-pill: 999px;
}

/* Dark — the sibling palette, verbatim. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg:      #0f1117;
        --panel:   #171a21;
        --panel-2: #1f232c;
        --text:    #e7ebf0;
        --text-2:  #cdd3db;
        --muted:   #8b93a1;   /* 5.4:1 on --panel */
        --border:  #2a2f3a;
        --accent:  #4a9eff;
        --green:   #4ade80;
        --red:     #ff5858;
        --orange:  #ff9844;
        --yellow:  #ffd460;
        --purple:  #a78bfa;
        --shadow-1: 0 1px 2px rgba(0,0,0,.30);
        --shadow-2: 0 8px 24px rgba(0,0,0,.45);
    }
}

/* Explicit override wins in both directions, so the Settings toggle works
 * regardless of the OS setting. */
:root[data-theme="dark"] {
    --bg:      #0f1117; --panel:  #171a21; --panel-2: #1f232c;
    --text:    #e7ebf0; --text-2: #cdd3db; --muted:   #8b93a1;
    --border:  #2a2f3a; --accent: #4a9eff;
    --green:   #4ade80; --red:    #ff5858; --orange:  #ff9844;
    --yellow:  #ffd460; --purple: #a78bfa;
    --shadow-1: 0 1px 2px rgba(0,0,0,.30);
    --shadow-2: 0 8px 24px rgba(0,0,0,.45);
}

@media (prefers-contrast: more) {
    :root { --muted: var(--text-2); --border: #9aa3b0; }
}

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

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    /* No weight 500 anywhere: system-ui renders it inconsistently across
     * Android, iOS and Windows. 400 / 600 / 700 only. */
    font: 400 var(--t-lg)/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* Counts and quantities must not reflow the line when 9 becomes 10 — which,
 * on this app's main screen, happens on every single tap. */
.tnum, .count, .qty { font-variant-numeric: tabular-nums; }

.muted { color: var(--muted); }
.small { font-size: var(--t-sm); }

a { color: var(--accent); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.skip-link {
    position: absolute; left: -9999px;
    background: var(--panel); color: var(--text);
    padding: var(--s2) var(--s3); border-radius: var(--r-sm); z-index: 100;
}
.skip-link:focus { left: var(--s2); top: var(--s2); }

.visually-hidden {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
}

/* --- M0 placeholder. Replaced by the app shell in M1. --- */
.boot { min-height: 100vh; display: grid; place-items: center; padding: var(--s5); }
.boot main { max-width: 32rem; }
.boot h1 { font-size: var(--t-2xl); margin: 0 0 var(--s3); }
.boot p { margin: 0 0 var(--s3); color: var(--text-2); }

/* --- App shell: two layouts, one component tree ------------------------------
 *
 * Phones get the layout the app was designed for: a single column, a bottom tab
 * bar under the thumb, and a quick-add bar pinned above it.
 *
 * Desktops get a different SHAPE, not the phone stretched or letterboxed. A
 * bottom tab bar on a 27" monitor is a long way from the cursor and reads as a
 * phone app in a frame; a mouse has no thumb-reach problem and a keyboard is
 * already at the top of the screen. So above 900px the nav becomes a sidebar and
 * the quick-add moves to the top of the list where the caret goes.
 *
 * Same DOM, same components — only CSS differs, so there is no second UI to
 * maintain and no divergence between them.
 */
:root { --app-w: 640px; --nav-w: 232px; --content-w: 760px; }

.app { min-height: 100vh; background: var(--bg); }

/* Desktop-only furniture, hidden by default. */
.nav-brand { display: none; }

/* Base tab styling, shared by both layouts. Only orientation and sizing differ
 * per breakpoint, below. */
.tab {
    display: flex; align-items: center; justify-content: center; gap: 2px;
    padding: 0;
    background: none; border: 0; color: var(--muted);
    font-family: inherit; font-size: var(--t-xs); font-weight: 600;
    cursor: pointer;
}
/* Explicit dimensions are required: an SVG with no intrinsic size stretches to
 * fill its flex container, which turns a 22px icon into a full-width one. */
.tab-glyph { width: 22px; height: 22px; display: block; flex: 0 0 auto; }
.tab-label { white-space: nowrap; }

/* ---------- Phone: single column, bottom tab bar ---------- */
@media (max-width: 899px) {
    .app { max-width: var(--app-w); margin: 0 auto; }

    /* Fixed bars follow the column, not the window: on a tablet they would
     * otherwise span the full width while the content sits in a narrow strip. */
    .qa, .tabbar, .undo-bar {
        left: 50%; transform: translateX(-50%);
        width: 100%; max-width: var(--app-w);
    }
    /* A floating bar, not a docked one: a gutter each side. */
    .undo-bar { width: calc(100% - var(--s4)); max-width: calc(var(--app-w) - var(--s4)); }

    .tabbar {
        position: fixed; right: auto; bottom: 0; z-index: 35;
        display: grid; grid-template-columns: repeat(4, 1fr);
        height: calc(56px + env(safe-area-inset-bottom));
        padding-bottom: env(safe-area-inset-bottom);
        background: var(--panel);
        border-top: 1px solid var(--border);
    }
    .tab { flex-direction: column; height: 56px; }

    .list { padding-bottom: calc(56px + env(safe-area-inset-bottom) + var(--s4)); }
    .app[data-route="shop"] .list {
        padding-bottom: calc(56px + 64px + env(safe-area-inset-bottom) + var(--s4));
    }
}

@media (min-width: 680px) and (max-width: 899px) {
    body { background: var(--panel-2); }
    .app {
        border-left: 1px solid var(--border);
        border-right: 1px solid var(--border);
        box-shadow: var(--shadow-1);
    }
}

/* ---------- Desktop: sidebar + content ---------- */
@media (min-width: 900px) {
    body { background: var(--panel-2); }

    .app {
        display: grid;
        grid-template-columns: var(--nav-w) minmax(0, 1fr);
        max-width: calc(var(--nav-w) + var(--content-w));
        margin: 0 auto;
        border-left: 1px solid var(--border);
        border-right: 1px solid var(--border);
        box-shadow: var(--shadow-1);
    }

    .tabbar {
        position: sticky; top: 0; align-self: start;
        display: flex; flex-direction: column; gap: 2px;
        height: 100vh; padding: var(--s3) var(--s2);
        background: var(--panel-2);
        border-right: 1px solid var(--border);
    }

    .nav-brand {
        display: grid; gap: var(--s2);
        padding: var(--s2) var(--s2) var(--s4);
    }
    .nav-brand-name {
        font-size: var(--t-md); font-weight: 700;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .nav-brand .sync-chip { justify-self: start; }

    .tab {
        flex-direction: row; justify-content: flex-start; gap: var(--s3);
        height: 44px; padding: 0 var(--s3);
        border-radius: var(--r-sm);
        font-size: var(--t-md);
    }
    .tab:hover { background: var(--panel); color: var(--text); }
    .tab[aria-current="page"] {
        background: var(--panel); color: var(--accent); box-shadow: none;
    }
    .tab-label { font-weight: 600; }

    .screen {
        display: flex; flex-direction: column; min-width: 0;
        background: var(--bg);
    }
    /* The quick-add moves ABOVE the list: on desktop the caret and the keyboard
     * are already at the top, and a bar welded to the bottom of a tall window is
     * just far away. */
    .screen > .hdr { order: 1; }
    .screen > .qa  { order: 2; position: static; transform: none;
                     width: auto; max-width: none;
                     border-top: 0; border-bottom: 1px solid var(--border); }
    .screen > .list { order: 3; padding-bottom: var(--s6); }

    /* Rows gain a hover affordance, which a touch screen has no use for. */
    .row:hover, .mrow:hover, .meal:hover { background: var(--panel); }
    .row-tick { cursor: pointer; }

    .undo-bar {
        left: auto; right: var(--s4); bottom: var(--s4);
        transform: none; width: auto; min-width: 280px; max-width: 420px;
    }

    /* The chip is RENDERED in the sidebar on desktop, not hidden here — see
     * useMedia() in ui/app.js. A hidden duplicate would still be announced by a
     * screen reader and would break any locator expecting one. */
}

/* --- Shared row/section furniture ------------------------------------------ */

.sec-h {
    margin: var(--s5) 0 var(--s1);
    padding: 0 var(--s3);
    font-size: var(--t-xs); font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
    color: var(--muted);
}
.sec-h--warn { color: var(--red); }

.srow {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
    min-height: 48px; padding: 0 var(--s3);
    border-bottom: 1px solid var(--border);
    font-size: var(--t-md);
}
.srow > span:last-child { text-align: right; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.srow--warn { color: var(--red); }

.link-btn {
    min-height: 44px; padding: 0;
    background: none; border: 0; color: var(--accent);
    font-family: inherit; font-size: var(--t-md); font-weight: 600;
    cursor: pointer; text-align: left;
}
.link-btn:disabled { color: var(--muted); cursor: not-allowed; }
.link-btn--danger { color: var(--red); }
/* A short word at the end of a row ("Send") still gets a whole fingertip. */
.srow > .link-btn:last-child { min-width: 44px; text-align: right; }

/* On/off: a button with role="switch", its state in aria-checked, so the look and
   what a screen reader announces come from one attribute and cannot disagree. The
   track and knob are drawn inside a 52 × 44 hit area. */
.switch {
    position: relative; flex: none;
    width: 52px; min-height: 44px; padding: 0;
    margin-right: -4px;   /* the track, not the hit area, lines up with the text above */
    background: none; border: 0; cursor: pointer;
}
.switch::before {
    content: ""; position: absolute; left: 4px; right: 4px; top: 50%;
    height: 28px; margin-top: -14px; border-radius: var(--r-pill);
    background: var(--muted);
    transition: background-color .15s;
}
.switch::after {
    content: ""; position: absolute; left: 7px; top: 50%;
    width: 22px; height: 22px; margin-top: -11px; border-radius: 50%;
    background: #fff; box-shadow: var(--shadow-1);
    transition: transform .15s;
}
.switch[aria-checked="true"]::before { background: var(--accent); }
.switch[aria-checked="true"]::after { transform: translateX(16px); }

.pill {
    display: inline-block; margin-left: var(--s2);
    padding: 1px var(--s2); border-radius: var(--r-pill);
    background: var(--panel-2); color: var(--muted);
    font-size: var(--t-xs); font-weight: 600;
}

.hint { padding: var(--s2) var(--s3) 0; }

/* Shown only where the sidebar is absent, i.e. phones. Used for things the
 * sidebar already says on desktop. */
@media (min-width: 900px) { .hide-desktop { display: none; } }

/* --- Update prompt ---------------------------------------------------------
 * Non-modal by design: an update is never urgent enough to interrupt someone
 * mid-aisle, and a modal here would be. It also only appears when nothing is
 * unsent — see ui/app.js.
 */
.update-bar {
    position: fixed; z-index: 45;
    left: 50%; transform: translateX(-50%);
    bottom: calc(56px + 64px + env(safe-area-inset-bottom) + var(--s2));
    width: calc(100% - var(--s4)); max-width: calc(var(--app-w) - var(--s4));
    display: flex; align-items: center; gap: var(--s2);
    min-height: 44px; padding: 0 var(--s2) 0 var(--s3);
    background: var(--accent); color: #fff;
    border-radius: var(--r-md); box-shadow: var(--shadow-2);
}
.update-text { flex: 1 1 auto; font-size: var(--t-sm); font-weight: 600; }
.update-btn {
    flex: 0 0 auto; min-height: 44px; padding: 0 var(--s3);
    background: none; border: 0; color: #fff;
    font-family: inherit; font-size: var(--t-md); font-weight: 700;
    text-decoration: underline; cursor: pointer;
}
.update-x {
    flex: 0 0 auto; width: 44px; min-height: 44px;
    background: none; border: 0; color: #fff;
    font-size: 20px; line-height: 1; cursor: pointer; opacity: .8;
}
@media (min-width: 900px) {
    /* Sits above the undo bar in the content column, clear of the sidebar. */
    .update-bar { left: auto; right: var(--s4); transform: none;
                  bottom: calc(var(--s4) + 56px); width: auto; min-width: 300px; }
}

/* --- Sheets -----------------------------------------------------------------
 * A native <dialog> in the top layer (see Sheet in ui/bits.js), so there is no
 * z-index to fight with the fixed bars. On a phone it spans the column, hanging
 * from the top or rising from the bottom; on a desktop it is a centred panel.
 *
 * The dialog itself has no padding. A tap anywhere outside its content is then a
 * tap on the dialog element, which is how a tap on the backdrop closes it.
 */
.sheet {
    width: 100%; max-width: var(--app-w);
    max-height: calc(100vh - var(--s6));
    max-height: calc(100dvh - var(--s6));
    margin: auto; padding: 0; border: 0;
    background: var(--panel); color: var(--text);
    box-shadow: var(--shadow-2);
    overflow: auto; overscroll-behavior: contain;
}
.sheet::backdrop { background: rgba(0, 0, 0, .4); }
.sheet--top {
    margin: 0 auto auto;
    padding-top: env(safe-area-inset-top);
    border-radius: 0 0 var(--r-md) var(--r-md);
}
.sheet--bottom {
    margin: auto auto 0;
    padding-bottom: env(safe-area-inset-bottom);
    border-radius: var(--r-md) var(--r-md) 0 0;
}

.sheet-hdr {
    display: flex; align-items: center; gap: var(--s2);
    min-height: 52px; padding: 0 var(--s1) 0 var(--s4);
    border-bottom: 1px solid var(--border);
}
.sheet-title {
    flex: 1 1 auto; min-width: 0; margin: 0;
    font-size: var(--t-lg); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Focused on open so the sheet is announced, but it is not a control: nothing to
 * press, so no ring. Every control after it keeps its own. */
.sheet-title:focus { outline: none; }
.sheet-x {
    flex: 0 0 auto; width: 44px; height: 44px;
    background: none; border: 0; color: var(--muted);
    font-size: 26px; line-height: 1; cursor: pointer;
}

@media (min-width: 900px) {
    .sheet, .sheet--top, .sheet--bottom {
        max-width: 440px; margin: 12vh auto auto;
        padding: 0; border-radius: var(--r-md);
    }
}

@media (max-width: 899px) {
    /* Manage has no quick-add bar for the undo bar to clear. */
    .app[data-route="manage"] .undo-bar { bottom: calc(56px + env(safe-area-inset-bottom) + var(--s2)); }
}
