/* ============================================================================
   AIEShop — storefront design system
   Brand: Art-Deco parchment + deli amber. Four themes driven by [data-theme].
   "Místo, kde se zastavil čas."
   ========================================================================== */

:root {
    --font-display: "Fraunces", "Georgia", serif;
    --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

    --radius: 16px;
    --radius-sm: 10px;
    --gap: clamp(14px, 2vw, 24px);
    --ease: cubic-bezier(.22, .61, .36, 1);
}

/* ---- Theme: Light / parchment (default) ---- */
.shop,
.shop[data-theme="light"] {
    --bg: #ece2cf;
    --bg-2: #f6efe2;
    --panel: #fbf7ee;
    --card: #fffdf7;
    --ink: #1b1714;
    --muted: #796c59;
    --line: rgba(27, 23, 20, .12);
    --accent: #e0772e;
    --accent-press: #c4641f;
    --accent-ink: #fff8ef;
    --green: #34604b;
    --ochre: #d6a24a;
    --chip-bg: rgba(52, 96, 75, .10);
    --chip-ink: #34604b;
    /* Semantic status pair (the desk's overdue/credential-broken red and its due/attention amber).
       Defined PER THEME like every other token — never hard-coded at a use site. */
    --danger: #b3261e;
    --warn: #b45309;
    --shadow: 0 14px 38px rgba(27, 23, 20, .14);
    --shadow-sm: 0 4px 14px rgba(27, 23, 20, .10);
    --user-bubble: #34604b;
    --user-ink: #f6efe2;
    --scale: 1;
}

/* ---- Theme: Dark / espresso ---- */
.shop[data-theme="dark"] {
    --bg: #18120e;
    --bg-2: #201913;
    --panel: #271f18;
    --card: #2c241d;
    --ink: #f3e7d4;
    --muted: #b6a48d;
    --line: rgba(243, 231, 212, .14);
    --accent: #ea9356;
    --accent-press: #d97e3c;
    --accent-ink: #1b1714;
    --green: #84bd9f;
    --ochre: #e0b264;
    --chip-bg: rgba(132, 189, 159, .14);
    --chip-ink: #a9d6c0;
    --danger: #f28b82;
    --warn: #f0b566;
    --shadow: 0 16px 40px rgba(0, 0, 0, .55);
    --shadow-sm: 0 6px 18px rgba(0, 0, 0, .45);
    --user-bubble: #84bd9f;
    --user-ink: #18120e;
    --scale: 1;
}

/* ---- Theme: Contrast / colourblind-safe (Okabe–Ito) ---- */
.shop[data-theme="contrast"] {
    --bg: #ffffff;
    --bg-2: #ffffff;
    --panel: #ffffff;
    --card: #ffffff;
    --ink: #000000;
    --muted: #1a1a1a;
    --line: #000000;
    --accent: #d55e00;
    --accent-press: #a84a00;
    --accent-ink: #ffffff;
    --green: #0072b2;
    --ochre: #e69f00;
    --chip-bg: #ffffff;
    --chip-ink: #000000;
    /* Okabe–Ito: vermillion for danger, orange for warning — colourblind-safe like the rest. */
    --danger: #d55e00;
    --warn: #e69f00;
    --shadow: none;
    --shadow-sm: none;
    --user-bubble: #0072b2;
    --user-ink: #ffffff;
    --scale: 1.02;
}

/* ---- Theme: Elder / large print ---- */
.shop[data-theme="elder"] {
    --bg: #efe6d3;
    --bg-2: #f7f1e5;
    --panel: #fcf8ef;
    --card: #fffdf7;
    --ink: #16120f;
    --muted: #5d5142;
    --line: rgba(22, 18, 15, .2);
    --accent: #c4641f;
    --accent-press: #a85316;
    --accent-ink: #fff8ef;
    --green: #2c5340;
    --ochre: #c08e34;
    --chip-bg: rgba(44, 83, 64, .14);
    --chip-ink: #2c5340;
    --danger: #a3170f;
    --warn: #9a4207;
    --shadow: 0 14px 38px rgba(27, 23, 20, .16);
    --shadow-sm: 0 4px 14px rgba(27, 23, 20, .12);
    --user-bubble: #2c5340;
    --user-ink: #f7f1e5;
    --scale: 1.28;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
}

/* THE PAGE SCROLLS WITHOUT A DRAWN BAR (Frank, 25 Oct 2026: "get rid of this appalling scroll bar"): the wheel, touch and keys still scroll it;
   only the page's own bar goes — a panel that scrolls inside the page keeps its own. */
html, body { scrollbar-width: none; }
html::-webkit-scrollbar, body::-webkit-scrollbar { display: none; }

body {
    font-family: var(--font-body);
    background: var(--bg);
    color: var(--ink);
}

/* ============================================================================
   Layout
   ========================================================================== */

.shop {
    min-height: 100vh;
    background:
        radial-gradient(1200px 600px at 100% -10%, color-mix(in srgb, var(--ochre) 18%, transparent), transparent 60%),
        radial-gradient(900px 500px at -10% 110%, color-mix(in srgb, var(--green) 16%, transparent), transparent 55%),
        var(--bg);
    color: var(--ink);
    font-size: calc(16px * var(--scale));
    display: grid;
    grid-template-columns: minmax(340px, 30%) 1fr;
    transition: background .6s var(--ease), color .35s var(--ease);
}

@media (max-width: 980px) {
    /* minmax(0,1fr), not 1fr: a bare 1fr track refuses to shrink below its content's min-content width,
       so a wide child (the topbar/cards) would force the whole column — and the page — wider than the
       phone. minmax(0,…) lets the single column track collapse to the viewport so the storefront fits. */
    .shop { grid-template-columns: minmax(0, 1fr); }
}

/* Left rail: the conversation with the shopkeeper */
.aisle {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    max-height: 100vh;
    border-right: 1px solid var(--line);
    background: linear-gradient(180deg, color-mix(in srgb, var(--panel) 92%, transparent), var(--panel));
    backdrop-filter: blur(4px);
    position: sticky;
    top: 0;
}

/* Mobile chat-dock toggle (#2) — hidden on desktop, where the chat is a full side rail. */
.chat-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    gap: .4em;
    width: 100%;
    padding: 7px;
    border: none;
    border-bottom: 1px solid var(--line);
    background: color-mix(in srgb, var(--panel) 86%, transparent);
    color: var(--muted);
    font: inherit;
    font-size: .85em;
    letter-spacing: .02em;
    cursor: pointer;
}

/* Desktop collapse (client, 2026-07-29: the assistant panel takes screen estate — make it hideable;
   AssistantCollapsed starts a tenant with it folded). The rail narrows to the grip strip and the window takes the
   full width. FOLDING THE ASSISTANT FOLDS THE CONVERSATION, NEVER THE SHOP'S SECTIONS (the owner, 27 Sep 2026: folded,
   the buyer had no way to Objednávky or Doklady): the folder tabs switch the window, not the chat, so they stay on the
   rail — at its divider edge, in its flow above the grip, which takes what is left (never less than a thumb's square),
   so the two cannot overlap. The phone's folded dock keeps them the same way, in one row with the grip. */
@media (min-width: 981px) {
    .chat-toggle { display: flex; }
    .shop.chat-collapsed { grid-template-columns: 44px 1fr; }
    .shop.chat-collapsed .aisle > :not(.chat-toggle):not(.folder-tabs) { display: none; }
    .shop.chat-collapsed .aisle > .folder-tabs { position: static; order: -1; align-items: flex-end; padding: 16px 0 12px; }
    .shop.chat-collapsed .chat-toggle {
        flex: 1 1 auto;
        min-height: 44px;
        overflow: hidden;
        border-bottom: none;
        writing-mode: vertical-rl;
        letter-spacing: .08em;
        font-size: .9em;
    }
}

@media (max-width: 980px) {
    /* Point 2 (Frank, 2026-06-20): keep the conversation always reachable on a phone — dock it to the
       bottom of the screen, capped, so the výloha above stays fully visible; the grip collapses it to a
       single composer row when the customer wants the whole screen for browsing. */
    .aisle {
        position: fixed;
        left: 0;
        right: 0;
        top: auto;            /* reset the desktop sticky top:0 — dock to the bottom, not stretch full height */
        bottom: 0;
        min-height: 0;
        max-height: 46vh;
        border-right: none;
        border-top: 1px solid var(--line);
        box-shadow: 0 -10px 30px rgba(0, 0, 0, .18);
        z-index: 40;
    }
    .aisle .persona-stage { display: none; }            /* the big portrait doesn't fit the dock */
    .chat-toggle { display: flex; }
    .shop.chat-collapsed .aisle { max-height: none; }   /* collapsed → only the toggle + composer show */
    .shop.chat-collapsed .chat-scroll { display: none; }
}

/* Standard/Premium brain toggle (Frank, 2026-07-01): a clickable mini-avatar pill, top-right of the chat
   aisle (which is position:sticky, so it anchors here). Same persona, different engine — Standard = vLLM/Qwen,
   Premium ("+") = Anthropic/Claude. Gold = the premium signal, deliberately theme-independent. */
.brain-toggle {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 30;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px 4px 4px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: color-mix(in srgb, var(--panel) 90%, transparent);
    color: var(--ink);
    font: inherit;
    font-size: .82em;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .10);
    transition: border-color .15s, box-shadow .15s, background .15s;
}
.brain-toggle:hover { border-color: #d4af37; box-shadow: 0 3px 12px rgba(0, 0, 0, .16); }
.brain-mini { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex: none; background: var(--panel); }
.brain-name { font-weight: 600; white-space: nowrap; }
.brain-plus { color: #b8860b; font-weight: 800; }
.brain-mode {
    font-size: .8em; letter-spacing: .04em; text-transform: uppercase;
    color: var(--muted); padding-left: 6px; margin-left: 2px; border-left: 1px solid var(--line);
}
/* Premium: a gold ring + tint so the "+" clearly reads as the upgraded experience. */
.brain-toggle.premium { border-color: #d4af37; background: linear-gradient(180deg, color-mix(in srgb, #d4af37 16%, var(--panel)), var(--panel)); box-shadow: 0 2px 10px rgba(212, 175, 55, .30); }
.brain-toggle.premium .brain-mini { box-shadow: 0 0 0 2px #d4af37; }
.brain-toggle.premium .brain-mode { color: #b8860b; }

@media (max-width: 980px) {
    /* On the phone dock the portrait is hidden and the grip text is centred, so the right edge is free —
       keep the pill compact there (drop the mode word; the "+" already signals premium). */
    .brain-toggle { top: 4px; right: 6px; padding: 3px 9px 3px 3px; font-size: .76em; }
    .brain-toggle .brain-mode { display: none; }
    .brain-mini { width: 22px; height: 22px; }
}

/* Right: the shop window (výloha). A fixed viewport-height flex column so ONLY the gallery scrolls
   inside it — its old min-height:100vh + padding + topbar pushed the page itself ~70px taller than
   the viewport, giving a second (outer) scrollbar beside the gallery's own. */
.window {
    position: relative;
    height: 100vh;
    padding: var(--gap);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

@media (max-width: 980px) {
    /* Stacked on a phone: the page scrolls as one, no inner viewport cap. Bottom room so the last products
       rise above the bottom-docked chat — EXACTLY the dock's height (--dock-h, measured by storefront.js), so the
       page scrolls all the way down whether the dock is open or collapsed, and whatever it holds. The fallbacks
       stand only until the dock has been measured. */
    .window { height: auto; min-height: 100vh; overflow: visible; display: block; padding-bottom: calc(var(--dock-h, 46vh) + 24px); }
    .shop.chat-collapsed .window { padding-bottom: calc(var(--dock-h, 170px) + 24px); }
}

.window::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: url("/assets/brand/interior.jpg");
    background-size: cover;
    background-position: center;
    opacity: .06;
    filter: saturate(.8);
    pointer-events: none;
}

/* ============================================================================
   Persona stage — logo fades in, portrait fades in, tagline settles
   ========================================================================== */

.persona-stage {
    padding: clamp(18px, 3vw, 30px) var(--gap) var(--gap);
    text-align: center;
    border-bottom: 1px solid var(--line);
}

.brand-logo {
    width: min(86%, 320px);
    height: auto;
    filter: drop-shadow(0 2px 0 color-mix(in srgb, var(--ink) 8%, transparent));
    opacity: 0;
    animation: logoIn 1.1s var(--ease) .1s forwards;
}

.shop[data-theme="dark"] .brand-logo { filter: invert(1) brightness(1.4) drop-shadow(0 1px 6px rgba(0,0,0,.5)); }
.shop[data-theme="contrast"] .brand-logo { filter: contrast(2); }

.portrait-wrap {
    position: relative;
    width: clamp(110px, 16vw, 150px);
    aspect-ratio: 1;
    margin: clamp(12px, 2vw, 18px) auto 10px;
}

.portrait, .portrait-fallback {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
    border: 3px solid color-mix(in srgb, var(--ochre) 70%, var(--panel));
    box-shadow: var(--shadow), inset 0 0 0 6px color-mix(in srgb, var(--panel) 70%, transparent);
    opacity: 0;
    animation: portraitIn 1.2s var(--ease) .6s forwards;
}

.portrait-fallback {
    display: grid;
    place-items: center;
    background:
        radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--ochre) 35%, var(--panel)), var(--panel));
    font-family: var(--font-display);
    font-size: 2.6rem;
    color: var(--green);
    animation-delay: .6s;
}

.persona-name {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 1.5rem;
    margin: 2px 0 0;
    opacity: 0;
    animation: riseIn .8s var(--ease) 1s forwards;
}

.persona-tagline {
    color: var(--muted);
    font-style: italic;
    margin: 4px 0 0;
    opacity: 0;
    animation: riseIn .8s var(--ease) 1.15s forwards;
}

/* ============================================================================
   Chat
   ========================================================================== */

.chat {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.chat-scroll {
    flex: 1;
    overflow-y: auto;
    padding: var(--gap);
    display: flex;
    flex-direction: column;
    gap: 12px;
    scroll-behavior: smooth;
}

.msg {
    max-width: 86%;
    padding: 11px 15px;
    border-radius: 18px;
    line-height: 1.45;
    box-shadow: var(--shadow-sm);
    animation: riseIn .45s var(--ease) both;
    white-space: pre-wrap;
}

.msg.assistant {
    align-self: flex-start;
    background: var(--card);
    border: 1px solid var(--line);
    border-bottom-left-radius: 6px;
}

.msg.user {
    align-self: flex-end;
    background: var(--user-bubble);
    color: var(--user-ink);
    border-bottom-right-radius: 6px;
}

.msg-who {
    display: block;
    font-size: .72em;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 3px;
}

.msg.user .msg-who { color: color-mix(in srgb, var(--user-ink) 75%, transparent); }

.typing {
    align-self: flex-start;
    display: inline-flex;
    gap: 5px;
    padding: 14px 16px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 18px;
    border-bottom-left-radius: 6px;
}

.typing span {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--muted);
    animation: blink 1.2s infinite both;
}

.typing span:nth-child(2) { animation-delay: .2s; }
.typing span:nth-child(3) { animation-delay: .4s; }

/* ── The category wall: the instant, no-AI pathway on an empty výloha ───────────────────────────
   Painted shelf tiles in the village-grocer style. The wall OWNS the window: on a PC the canonical
   shelves divide the whole display area into even rows of equal blocks (the Unihobby move, done
   village-grocer); on a phone they go 2-up, big and tappable. */
.gallery:has(.cat-wall) > div { height: 100%; }

.cat-wall {
    height: 100%;
    display: flex;
    flex-direction: column;
    padding: 4px 2px;
}

.cat-wall-lead {
    font-family: var(--font-display);
    font-size: 1.2rem;
    color: var(--ink-soft);
    margin: 2px 2px 14px;
    flex: 0 0 auto;
}

.cat-grid {
    display: grid;
    grid-template-columns: repeat(var(--cols-1, 1), 1fr);
    grid-auto-rows: 1fr;       /* equal rows: 9 shelves = a clean 3×3 division of the area */
    gap: 16px;
}

/* A WALL DIVIDES INTO EVEN ROWS (Frank, 25 Sep 2026: three departments stood two and one). How many tiles a row
   CAN hold is the width's to say — a tile no narrower than 150px, a row no wider than four tiles
   (CategoryLanding.MostAcross); how many it DOES hold is the component's, which states the columns for each
   capacity as --cols-1 … --cols-4 (CategoryLanding.ColumnsFor). These rules only read the width, and they read the
   frame round the grid, not the screen: the assistant's rail takes a share of the screen that changes (open,
   folded, docked). Each step is that many 150px tiles and the 16px gaps between them. */
.cat-grid-frame { flex: 0 0 auto; container: cat-wall / inline-size; }
@container cat-wall (min-width: 316px) { .cat-grid { grid-template-columns: repeat(var(--cols-2, 2), 1fr); } }
@container cat-wall (min-width: 482px) { .cat-grid { grid-template-columns: repeat(var(--cols-3, 3), 1fr); } }
@container cat-wall (min-width: 648px) { .cat-grid { grid-template-columns: repeat(var(--cols-4, 4), 1fr); } }

/* Mobile-primary: generous tap targets (a phone's width holds two across), natural scroll inside the window. */
@media (max-width: 760px) {
    .cat-wall { height: auto; }
    .cat-grid {
        grid-auto-rows: auto;
        gap: 12px;
    }
    .cat-tile { aspect-ratio: 16 / 11; }
    .cat-wall-lead { font-size: 1.05rem; }
    .cat-tile .cat-name { font-size: 1.05rem; padding: 20px 10px 8px; }
}

/* Each shelf gets its own accent — the border (and the count beside the name) colour-codes the wall,
   so the categories separate even at a squint. Hues sit inside the brand's warm palette, with
   drogerie's cool blue as the one deliberate outlier (exactly like a real shop aisle). */
.cat-tile { --cat-accent: var(--ochre); }
.cat-tile[data-cat="pecivo"]         { --cat-accent: #D6A24A; }  /* baked gold */
.cat-tile[data-cat="mlecne"]         { --cat-accent: #9FB7C6; }  /* milk-cool blue-grey */
.cat-tile[data-cat="maso"]           { --cat-accent: #A4452F; }  /* smoked paprika red */
.cat-tile[data-cat="ovoce-zelenina"] { --cat-accent: #6E8C5A; }  /* market green */
.cat-tile[data-cat="potraviny"]      { --cat-accent: #B9743B; }  /* terracotta pantry */
.cat-tile[data-cat="napoje"]         { --cat-accent: #7A5A8C; }  /* wine violet */
.cat-tile[data-cat="drogerie"]       { --cat-accent: #4E8FA8; }  /* fresh soap blue */
.cat-tile[data-cat="domacnost"]      { --cat-accent: #6B4A30; }  /* deep wood */
.cat-tile[data-cat="ostatni"]        { --cat-accent: #857868; }  /* newsprint grey */

.cat-tile {
    position: relative;
    /* width:100% pins the inline size to the grid column. Without it the tile's aspect-ratio (set on a
       phone) disables grid stretch and derives WIDTH from the 130px min-height (≈189px) — wider than the
       column, so neighbouring tiles overlapped. With a definite width, aspect-ratio derives height instead. */
    width: 100%;
    min-width: 0;
    min-height: 130px;
    /* A confident 4px separator in the shelf's own near-primary accent — the wall reads as nine
       clearly framed departments, not nine photos that happen to sit together. */
    border: 4px solid color-mix(in srgb, var(--cat-accent) 88%, var(--card));
    border-radius: 16px;
    overflow: hidden;
    padding: 0;
    cursor: pointer;
    background:
        radial-gradient(circle at 50% 35%, color-mix(in srgb, var(--ochre) 25%, var(--card)), var(--card));
    box-shadow: 0 8px 24px rgba(60, 40, 20, .10);
    animation: riseIn .5s var(--ease) both;
    animation-delay: calc(var(--i) * 70ms);
    transition: transform .25s var(--ease), box-shadow .25s var(--ease), border-color .25s var(--ease);
}

.cat-tile:hover, .cat-tile:focus-visible {
    transform: translateY(-4px);
    border-color: color-mix(in srgb, var(--cat-accent) 80%, white);
    box-shadow: 0 14px 32px color-mix(in srgb, var(--cat-accent) 28%, rgba(60, 40, 20, .12));
}

.cat-tile img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;   /* ambience tiles fill the frame — unlike product pack shots */
}

/* The member-product fallback (no curated tile art yet): a pack shot presented as a product tile —
   contained on a clean white ground, breathing room around the tin, never stretched to fill. */
.cat-tile img.cat-tile-product {
    object-fit: contain;
    background: #fff;
    padding: 10% 12% 26%;   /* bottom clears the name band */
}

.cat-tile .cat-glyph {
    position: absolute;
    inset: 0;
    place-items: center;
    font-size: 3.4rem;
    opacity: .85;
}

.cat-tile .cat-name {
    position: absolute;
    inset: auto 0 0 0;
    padding: 26px 14px 10px;
    font-family: var(--font-display);
    font-size: 1.2rem;
    font-weight: 600;
    color: #F6EFE2;
    text-align: left;
    text-shadow: 0 1px 3px rgba(20, 12, 6, .65);
    background: linear-gradient(to top, rgba(27, 23, 20, .72), rgba(27, 23, 20, 0));
    /* No accent flag at the name's left edge: laid against the tile's own 4px border it read as that border
       doubling in thickness from the name down (Frank, 25 Sep 2026). The count carries the accent instead. */
}

@media (prefers-reduced-motion: reduce) {
    .cat-tile { animation: none; }
}

/* ── Category landing (ADR-0011): department cards that drill into sub-categories, with the
   promotional "Akce" products shown by default underneath. The landing scrolls within the window
   (unlike the old fill-the-frame wall), so the cards size to content and the Akce wall sits below.
   These come AFTER the .cat-wall base so they win on equal specificity. */
.gallery:has(.cat-landing) > div { height: auto; }
.cat-landing { height: auto; min-height: 100%; }

/* Department cards size to a comfortable tile rather than stretching to fill — leaving room for Akce. */
.cat-landing .cat-grid {
    grid-auto-rows: minmax(160px, 24vh);
}

/* Mobile: the landing tiles were wide-and-short (16/11 ≈ 120px) sitting inside tall fixed rows
   (.cat-landing .cat-grid forces minmax(160px,24vh) even on phones), so each squat tile left a fat gap
   below it. Square them up — a bigger, cleaner tap target — and let the rows hug them so the inter-row
   gap disappears. Placed after the .cat-landing rules so it wins the cascade (Frank, 2026-06-25). A square is
   never taller than the desktop's row, though: where the wall stands one across (a lone tile, or a screen too
   narrow for two) it would be as tall as the phone is wide. */
@media (max-width: 760px) {
    .cat-landing .cat-grid { grid-auto-rows: auto; }
    .cat-landing .cat-tile { aspect-ratio: 1 / 1; min-height: 0; max-height: max(160px, 24vh); }
}

/* A PRODUCT'S OWN PAGE (ProductPage) — how a shelf that exists to be read opens a product: laid out after the maker's
   product pages (picture beside the name, subtitle and key points; the detail and the files beneath), in this shop's
   own colours and type. */
.pp { max-width: 1120px; margin: 0 auto; padding: 4px 4px 32px; }
.pp-hero {
    display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: clamp(20px, 4vw, 56px);
    align-items: center;
}
.pp-media {
    background: var(--card); border-radius: var(--radius); box-shadow: var(--shadow-sm);
    aspect-ratio: 1 / 1; display: grid; place-items: center; padding: clamp(14px, 3vw, 32px); overflow: hidden;
}
.pp-media img { width: 100%; height: 100%; object-fit: contain; }
.pp-glyph { font-size: 5rem; }
.pp-summary { display: flex; flex-direction: column; gap: .9rem; min-width: 0; }
.pp-title {
    margin: 0; font-family: var(--font-display); font-weight: 700; line-height: 1.1;
    font-size: clamp(1.7rem, 3.2vw, 2.5rem); color: var(--ink); text-wrap: balance;
}
.pp-subtitle { margin: 0; font-size: 1.15rem; font-weight: 600; line-height: 1.4; color: var(--muted); max-width: 46ch; }
.pp-points { list-style: none; margin: .4rem 0 0; padding: 0; display: grid; gap: .6rem; }
.pp-points li { position: relative; padding-left: 2rem; line-height: 1.45; max-width: 60ch; }
.pp-points li::before {
    content: "✓"; position: absolute; left: 0; top: .05em;
    width: 1.35rem; height: 1.35rem; border-radius: 50%; display: grid; place-items: center;
    border: 1.5px solid var(--accent); color: var(--accent); font-size: .78rem; font-weight: 700;
}
.pp-details {
    display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); gap: clamp(20px, 4vw, 56px);
    align-items: start; margin-top: clamp(28px, 5vw, 56px); padding-top: clamp(20px, 3vw, 32px);
    border-top: 1px solid var(--line);
}
.pp-section + .pp-section { margin-top: 1.4rem; }
.pp-section h2 { margin: 0 0 .45rem; font-family: var(--font-display); font-size: 1.25rem; font-weight: 700; color: var(--ink); }
.pp-section p { margin: 0; line-height: 1.6; max-width: 68ch; }
.pp-files {
    background: color-mix(in srgb, var(--accent) 7%, var(--card)); border-radius: var(--radius);
    padding: 1.3rem 1.4rem;
}
.pp-files h2 { margin: 0 0 .8rem; font-family: var(--font-display); font-size: 1.15rem; font-weight: 700; }
.pp-files ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.pp-files a { display: inline-flex; align-items: center; gap: .6rem; color: var(--ink); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.pp-files a:hover, .pp-files a:focus-visible { color: var(--accent-press); }
.pp-file-ico {
    flex: none; font-size: .62rem; font-weight: 800; letter-spacing: .04em; color: var(--danger);
    border: 1.5px solid currentColor; border-radius: 4px; padding: .12rem .22rem; text-decoration: none;
}
@media (max-width: 760px) {
    .pp-hero, .pp-details { grid-template-columns: minmax(0, 1fr); }
    .pp-media { aspect-ratio: 4 / 3; }
}

/* THE PAGE'S HEAD — breadcrumbs to where the shopper stands, and the page's title under them (BrowsePlace.Trail). */
.page-head { display: flex; flex-direction: column; gap: .2rem; margin: 0 2px; min-width: 0; flex: 1 1 320px; }
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: .15rem .4rem; font-size: .85rem; color: var(--muted); }
.crumb {
    background: none; border: none; padding: 0; cursor: pointer;
    font: inherit; color: var(--muted); text-align: left;
}
.crumb:hover, .crumb:focus-visible { color: var(--ink); text-decoration: underline; }
.crumb.is-up { color: var(--ink); font-weight: 600; }
.crumb-sep { opacity: .55; }
.page-title {
    margin: 0; font-family: var(--font-display); font-size: 1.6rem; font-weight: 700; line-height: 1.2;
    color: var(--ink); text-wrap: balance;
}
@media (max-width: 760px) {
    /* A phone shows the way up alone — „‹ DEKORATIVA" — the trail's other crumbs would wrap over three lines. */
    .crumbs .crumb:not(.is-up), .crumbs .crumb-sep { display: none; }
    .crumbs .crumb.is-up::before { content: "‹ "; }
    .page-title { font-size: 1.3rem; }
    /* The header row keeps the logo and the menu; the page's head takes its own line under them. */
    .topbar > .page-head { order: 10; flex-basis: 100%; }
}

.cat-landing-head {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 2px 2px 14px;
    flex: 0 0 auto;
}

.cat-landing-title {
    font-family: var(--font-display);
    font-size: 1.35rem;
    font-weight: 700;
    margin: 0;
    color: var(--ink);
}

.cat-landing-back { white-space: nowrap; }

/* A small count beside the department name — the assortment's size at a glance. */
/* A department kept for one backend's own customers (Category.SignIn): the tile stays itself, dimmed, under a
   padlock — choosing it opens the sign-in, and the tile opens once the buyer is signed in. */
.cat-tile-locked img { filter: grayscale(.55) brightness(.92); }
.cat-lock {
    position: absolute; top: 10px; right: 10px; z-index: 2;
    display: grid; place-items: center;
    width: 38px; height: 38px; border-radius: 50%;
    background: color-mix(in srgb, var(--card) 92%, transparent);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
    font-size: 1.1rem; line-height: 1;
}

.cat-tile .cat-name .cat-count {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 600;
    color: #F6EFE2;
    background: color-mix(in srgb, var(--cat-accent) 55%, rgba(20, 12, 6, .35));
    vertical-align: middle;
}

/* The "Akce" promotional default view, underneath the department cards. */
.akce-section {
    flex: 0 0 auto;
    margin-top: 22px;
    padding-top: 14px;
    border-top: 2px dashed color-mix(in srgb, var(--brand, var(--ochre)) 45%, var(--line));
}

.akce-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 2px 12px;
}

.akce-flag {
    font-family: var(--font-display);
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    font-size: .82rem;
    color: #fff;
    background: var(--brand, #c8102e);
    padding: 3px 12px;
    border-radius: 6px;
    box-shadow: 0 2px 8px color-mix(in srgb, var(--brand, #c8102e) 35%, transparent);
}

.akce-title {
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 700;
    margin: 0;
    color: var(--ink);
}

/* BAL department accents (the four top-level cards). The grocer's shelves keep their own
   accents above; an unknown id falls back to the brand ochre default. */
.cat-tile[data-cat="BL"] { --cat-accent: #0a6cb5; }  /* paint blue */
.cat-tile[data-cat="DU"] { --cat-accent: #4E8FA8; }  /* fresh soap blue */
.cat-tile[data-cat="KZ"] { --cat-accent: #C06A9E; }  /* cosmetic rose */
.cat-tile[data-cat="DH"] { --cat-accent: #6E8C5A; }  /* garden green */

/* Sold out: a stocked line with no shelf stock right now — muted badge, dead add button. */
.badge.soldout {
    background: color-mix(in srgb, var(--muted) 18%, transparent);
    color: var(--muted);
    border: 1px solid var(--line);
}

.pc-add:disabled {
    opacity: .35;
    cursor: not-allowed;
}

/* The live utterance: the reply typing itself out as tokens stream in. The entry animation is
   suppressed — the bubble re-renders on every delta and must not pulse; the caret carries the life. */
.msg.assistant.streaming { animation: none; }

.msg.assistant.streaming .caret {
    display: inline-block;
    width: 2px;
    height: 1em;
    margin-left: 2px;
    vertical-align: text-bottom;
    background: var(--ink, currentColor);
    animation: blink 1.0s steps(1) infinite;
}

/* Document-reading interstitial — the slower "I'm reading the sheets" moment */
.reading {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 18px;
    border-bottom-left-radius: 6px;
    color: var(--muted);
    font-size: .92em;
}

.reading-spin {
    flex: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid var(--line);
    border-top-color: var(--accent, #b8742a);
    animation: spin .8s linear infinite;
}

.reading-label { line-height: 1.3; }

@keyframes spin { to { transform: rotate(360deg); } }

/* Shelf header — item count + a manual reset of the search funnel */
.shelf-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;                 /* on a phone the tools drop to their own row instead of colliding */
    gap: 10px;
    padding: 8px var(--gap) 14px;   /* breathing room before the gallery below (was 0 — too cramped) */
}

.shelf-count {
    font-size: .9em;
    color: var(--muted);
    letter-spacing: .02em;
}

.shelf-back { padding: 6px 12px; font-size: .88em; flex: none; }   /* #1 back to the previous výloha */

/* On-screen filter + sort over the shown products — plus the category/tag narrowers (Frank, 2026-08-21:
   the same view row the assistant drives). Wraps so four controls never overflow the head. */
.shelf-tools { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; justify-content: center; min-width: 0; flex-wrap: wrap; }
.shelf-cat, .shelf-tag { max-width: 11rem; text-overflow: ellipsis; overflow: hidden; }
.shelf-filter, .shelf-sort {
    padding: 6px 10px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--card);
    color: inherit;
    font-size: .85em;
}
.shelf-filter { flex: 0 1 220px; min-width: 110px; }
.shelf-filter::placeholder { color: var(--muted); }
.shelf-sort { flex: none; cursor: pointer; }

/* THE SHOP'S FOOT — Kontakty and the legal pages as small print at the bottom of the window (Frank, 25 Sep 2026). */
.shop-foot {
    position: relative; z-index: 1;
    display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: .15rem 1.1rem;
    padding: .45rem .5rem .1rem; margin-top: .4rem;
    border-top: 1px solid var(--line);
    font-size: .8rem;
}
.shop-foot-link {
    background: none; border: none; padding: .2rem 0; cursor: pointer;
    font: inherit; color: var(--muted); text-decoration: none;
}
.shop-foot-link:hover, .shop-foot-link:focus-visible { color: var(--ink); text-decoration: underline; }

@media (max-width: 760px) {
    /* The shelf head can't fit count + filter + sort + "Začít znovu" on one phone row — they overlapped and
       the filter / reset became unusable. Drop the filter+sort onto their own full-width row below the
       count/back/reset line (Frank, 2026-06-27). */
    .shelf-tools { order: 9; flex: 1 1 100%; justify-content: stretch; }
    .shelf-filter { flex: 1 1 auto; }
}

/* Composer */
.composer {
    display: flex;
    gap: 10px;
    padding: var(--gap);
    border-top: 1px solid var(--line);
    background: color-mix(in srgb, var(--panel) 80%, transparent);
}

/* Background-task chips (delegation fan-out + datasheet filter) — running jobs with progress + cancel. */
.bg-tasks { display: flex; flex-direction: column; gap: 6px; padding: 6px var(--gap) 0; }
.bg-chip {
    display: flex; align-items: center; gap: 8px;
    padding: 7px 10px; border-radius: 12px; font-size: .82rem; color: var(--ink);
    background: color-mix(in srgb, var(--accent) 12%, var(--card));
    border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
}
.bg-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bg-spin {
    flex: none; width: 13px; height: 13px; border-radius: 50%;
    border: 2px solid color-mix(in srgb, var(--accent) 35%, transparent); border-top-color: var(--accent);
    animation: bgspin .8s linear infinite;
}
@keyframes bgspin { to { transform: rotate(360deg); } }
.bg-cancel {
    flex: none; border: none; background: transparent; cursor: pointer; color: var(--muted);
    font-size: 1rem; line-height: 1; padding: 2px 4px; border-radius: 6px;
}
.bg-cancel:hover { color: var(--brand, #c8102e); background: color-mix(in srgb, var(--brand, #c8102e) 10%, transparent); }

.composer input {
    flex: 1;
    min-width: 0;            /* let the input shrink so the Send button never overflows the rail */
    padding: 13px 16px;
    font: inherit;
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 999px;
    outline: none;
    transition: border-color .2s, box-shadow .2s;
}

.composer input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* Keep the Send button at its natural width (it must not be squeezed or pushed past the rail edge). */
.composer .btn { flex: none; white-space: nowrap; }

.btn {
    font: inherit;
    font-weight: 600;
    border: none;
    border-radius: 999px;
    padding: 13px 20px;
    cursor: pointer;
    background: var(--accent);
    color: var(--accent-ink);
    transition: transform .12s var(--ease), background .2s, box-shadow .2s;
    box-shadow: var(--shadow-sm);
}

.btn:hover { background: var(--accent-press); }
.btn:active { transform: translateY(1px) scale(.99); }
.btn:disabled { opacity: .5; cursor: default; }

.shop[data-theme="contrast"] .btn,
.shop[data-theme="contrast"] .composer input,
.shop[data-theme="contrast"] .msg,
.shop[data-theme="contrast"] .product-card,
.shop[data-theme="contrast"] .theme-btn { border: 2px solid #000; box-shadow: none; }

/* ============================================================================
   Top bar + theme switcher
   ========================================================================== */

.topbar {
    position: relative;
    /* 300, not 1: the burger dropdown lives inside THIS stacking context, so the topbar itself must sit
       above the mobile chat dock (z 40) and shelf chrome — at z 1 the open menu painted BEHIND them
       (Frank, 2026-08-14). Dialogs (z 1000) still cover everything here. */
    z-index: 300;
    display: flex;
    align-items: center;
    flex-wrap: wrap;          /* on a phone the brand + buyer badge + theme switcher can't share one row;
                                wrap instead of forcing the header (and the page) wider than the viewport */
    gap: var(--gap);
    margin-bottom: var(--gap);
}

/* (No .shopname block any more — Frank #6, 2026-08-13: the aisle's PersonaStage is the identity's one
   home; the topbar shows at most the logo, and only while the assistant rail is collapsed.) */

.theme-switcher { display: inline-flex; gap: 8px; }

.theme-btn {
    width: 40px;
    height: 40px;
    border-radius: 12px;
    border: 1px solid var(--line);
    background: var(--card);
    color: var(--ink);
    cursor: pointer;
    font-size: 1.05rem;
    display: grid;
    place-items: center;
    transition: transform .12s var(--ease), box-shadow .2s, border-color .2s;
}

.theme-btn:hover { transform: translateY(-2px); }
.theme-btn.active {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent);
}

.tray-btn { position: relative; }
/* Attention cue (the grocery skin's rendering of an off-screen Focus): the tray button bounces and pulses
   an accent ring when the agent changed an order while the shopper is browsing — the same decision the
   BAL folder tab renders as a poke-and-glow. */
.tray-btn.attention { animation: trayAttention 1s var(--ease, ease-in-out) infinite; }
@keyframes trayAttention {
    0%, 100% { transform: translateY(0); box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 50%, transparent); }
    30% { transform: translateY(-5px); box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 30%, transparent); }
    60% { transform: translateY(0); box-shadow: 0 0 0 9px color-mix(in srgb, var(--accent) 0%, transparent); }
}
.tray-btn .count {
    position: absolute;
    top: -6px;
    right: -6px;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--accent);
    color: var(--accent-ink);
    font-size: .72rem;
    font-weight: 700;
    display: grid;
    place-items: center;
}

/* ============================================================================
   Gallery — the výloha
   ========================================================================== */

.gallery {
    position: relative;
    z-index: 1;
    overflow-y: auto;
    /* Fill the space left under the topbar/shelf-head and scroll within it (min-height:0 lets a flex
       child actually shrink below its content so the scroll lands here, not on the page). */
    flex: 1 1 auto;
    min-height: 0;
    padding-right: 4px;
}

@media (max-width: 980px) {
    .gallery { flex: none; max-height: none; overflow-y: visible; }
}

.group-title {
    font-family: var(--font-display);
    font-size: 1.1rem;
    color: var(--green);
    margin: 18px 2px 10px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.group-title::after { content: ""; flex: 1; height: 1px; background: var(--line); }

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: var(--gap);
}

.gallery-more {
    display: flex;
    justify-content: center;
    margin: 14px 0 6px;
}

.product-card {
    position: relative;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    animation: riseIn .55s var(--ease) both;
    animation-delay: calc(var(--i, 0) * 55ms);
    transition: transform .18s var(--ease), box-shadow .25s, border-color .25s;
}

.product-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow);
    border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
}

.product-card.is-highlight {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent), var(--shadow);
    animation: glow 1.6s var(--ease) infinite alternate;
}

.pc-media {
    aspect-ratio: 4 / 3;
    position: relative;
    display: grid;
    place-items: center;
    background:
        radial-gradient(circle at 50% 35%, color-mix(in srgb, var(--ochre) 22%, var(--card)), var(--card));
    overflow: hidden;
}

/* Real product photos are typically tall pack shots on a white studio background — cover-cropping
   chopped bottles in half. Contain shows the whole product on a clean light tile (the img's own
   background, so the glyph fallback keeps the ochre backdrop), matching the lightbox.
   Absolutely positioned because as a grid item the img's percentage height resolves against an
   implicit track sized by the photo's intrinsic height — it overflowed the 4:3 box and the
   overflow clip looked like a crop even with contain. */
.pc-media img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 10px;
    background: #fff;
}

.pc-glyph { font-size: 3rem; opacity: .8; filter: saturate(.9); }

.pc-badges {
    position: absolute;
    top: 8px;
    left: 8px;
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.badge {
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .03em;
    text-transform: uppercase;
    padding: 3px 8px;
    border-radius: 999px;
    color: #fff;
    box-shadow: var(--shadow-sm);
}

.badge.fresh { background: var(--green); }
.badge.promo { background: var(--accent); }
.badge.supplier { background: #4d6a8a; }

.pc-supply {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: .8rem;
    font-weight: 600;
    color: color-mix(in srgb, var(--ink) 72%, #4d6a8a);
    margin-top: -2px;
}

.pc-body { padding: 12px 13px 14px; display: flex; flex-direction: column; gap: 6px; flex: 1; }

.pc-name { font-weight: 600; font-size: 1.02rem; line-height: 1.25; }

.pc-pack { color: var(--muted); font-size: .82rem; margin-top: -2px; }
/* The OLDBAL one-liner under the name (client, 2026-07-29) — quiet, two lines max. */
.pc-sub {
    color: var(--muted);
    font-size: .82rem;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* The card's basket glyph on configured-to-order lines — same slot as the quick "+". */
.pc-add.pc-basket { font-size: 1.05rem; line-height: 1; }

/* The brand mark in the topbar — only while PersonaStage (the identity's one home) is off screen:
   desktop with the rail OPEN hides it (the aisle shows logo + tagline; showing both read as duplicated
   branding — Frank #6, 2026-08-13); the collapsed rail and phones (where the docked aisle hides the
   stage) keep it. */
.topbar-logo { height: 44px; width: auto; max-width: 220px; object-fit: contain; }
@media (min-width: 981px) {
    .shop:not(.chat-collapsed) .topbar-logo { display: none; }
}

/* The header's actions sit at its RIGHT, as a shop's account and settings do (the client, 25 Sep 2026: a centred
   sign-in beside four theme icons did not read as a finished header). The group takes the free space and aligns its
   contents to the end, so the appearance, basket and burger buttons that follow it line up on the right too. */
.topbar-actions {
    flex: 0 1 auto;
    margin-left: auto;            /* at the right whether or not a page head stands to its left */
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}
.topbar-actions .btn.topbar-action {
    font-size: .95rem;
    padding: 8px 16px;
    border-radius: 999px;
}
.topbar-appearance { font-size: 1.15rem; }
/* One visual language for the whole row (Frank, 2026-08-13): the signed-out sign-in IS a
   .btn topbar-action (see TradeSignInCard); the signed-in buyer chip keeps its readable
   name+seller+odhlásit content but sits on the same pill height and type size as its neighbours. */
.topbar-actions .user-chip {
    font-size: .92rem;
    padding: .38rem .55rem .38rem .95rem;
}

/* ---- #7 narrow-screen topbar (Frank, 2026-08-13) ----
   Below the breakpoint the primary actions get their OWN full-width row (they wrapped awkwardly around
   the identity), and the ThemeSwitcher + buyer chip fold into a burger dropdown. Desktop is untouched:
   the burger and its menu exist only under the breakpoint, the .topbar-wide homes only above it. */
.topbar-burger { display: none; margin-left: auto; }
.topbar-menu {
    position: absolute;
    top: 100%;
    right: 0;
    /* Above the mobile chat dock (z 40) and the shelf chrome — the dropdown rendered BEHIND the
       docked aisle and tiles (Frank, 2026-08-14). Dialogs (z 1000) still cover it. */
    z-index: 300;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
    padding: 12px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
.topbar-menu .user-chip { margin-left: 0; }
/* UNIFORM menu rows (Frank, 2026-08-14): every entry a standard left-aligned item; the account chip
   sits apart under a divider. */
.topbar-menu { align-items: stretch; min-width: 240px; gap: 2px; }
.topbar-menu .menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    text-align: left;
    padding: 10px 12px;
    border: none;
    background: none;
    border-radius: 8px;
    font: inherit;
    font-size: .95em;
    color: var(--ink);
    cursor: pointer;
}
.topbar-menu .menu-item:hover { background: color-mix(in srgb, var(--panel) 60%, var(--line)); }
/* Fixed-width icon column: emoji widths differ, so labels aligned only through this. */
.topbar-menu .menu-ico { width: 1.5em; text-align: center; flex: none; }
.topbar-menu .menu-account { border-top: 1px solid var(--line); margin-top: 6px; padding-top: 10px; }

/* The Vzhled dialog: the four skins as labeled rows stacked above a full-width Zavřít — the popup
   must be a COLUMN (Frank, 2026-08-14: the button floated beside the first skin row). */
.appearance-popup { width: min(340px, 92vw); display: flex; flex-direction: column; }
.appearance-popup > .btn { align-self: stretch; margin-top: .4rem; }
.theme-switcher.labeled { width: 100%; flex-direction: column; align-items: stretch; gap: 6px; margin-bottom: 1rem; }
.theme-switcher.labeled .theme-btn {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 12px;
    width: 100%;
    padding: 12px 14px;
    font-size: 1em;
}
.theme-switcher.labeled .theme-glyph { width: 1.4em; text-align: center; }

/* THE INSTALLABLE APP: the header's pill keeps its words on one line; the iPhone's steps (it has no install API) are the Vzhled
   dialog's column with a numbered list, the share glyph drawn in the text's own colour. */
.topbar-install { white-space: nowrap; }
.install-popup { display: flex; flex-direction: column; }
.install-popup > .btn { align-self: stretch; }
.install-steps { margin: 0 0 1.2rem; padding-left: 1.3rem; display: flex; flex-direction: column; gap: .6rem; line-height: 1.5; }
.install-share {
    width: 1.15em; height: 1.15em; vertical-align: -.2em;
    fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

/* Kontakty + legal documents as full PAGES in the window (Frank, 2026-08-14 — not dialogs). */
.doc-page {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 22px clamp(16px, 4vw, 40px) 34px;
    overflow: auto;
}
.doc-page-head { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
.doc-page-head h2 { margin: 0; font-size: 1.35rem; }
.doc-page-body { max-width: 72ch; }
.doc-page-body h3 { margin: 1.1rem 0 .3rem; font-size: 1rem; }
.doc-page-body h3:first-child { margin-top: 0; }
.doc-page-body p { margin: 0 0 .45rem; font-size: .95rem; color: var(--ink); line-height: 1.55; }
.doc-page-links { margin-top: 1.4rem; padding-top: 1rem; border-top: 1px solid var(--line); }
/* The legal pills on the desktop top row: present but quieter than the primary trio. */
.topbar-action-quiet { opacity: .78; font-size: .88em; }
.topbar-action-quiet:hover { opacity: 1; }
@media (max-width: 980px) {
    .topbar-wide { display: none; }                       /* the switcher + chip live in the burger here */
    .topbar-burger { display: grid; }
    /* Frank, 2026-08-14: the whole actions row folds into the burger — the topbar keeps one tidy
       row (brand + burger) and Vyhledávání/Kontakty/podmínky live in the dropdown. */
    .topbar-actions { display: none; }
}
@media (min-width: 981px) {
    .topbar-menu { display: none; }                       /* a stale open dropdown never haunts a widened window */
}


/* Landing quick actions (Vyhledávání · Přihlášení · Kontakty — client, 2026-07-29). */
.cat-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
    margin: 4px 0 14px;
}

.pc-foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: 8px; padding-top: 6px; }

.pc-price { font-family: var(--font-display); font-weight: 600; font-size: 1.18rem; }

.pc-add {
    border: none;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent-press);
    font: inherit;
    font-weight: 700;
    width: 38px;
    height: 38px;
    border-radius: 11px;
    cursor: pointer;
    transition: background .18s, transform .12s var(--ease);
}

.pc-add:hover { background: var(--accent); color: var(--accent-ink); transform: rotate(90deg); }

.chips { display: flex; flex-wrap: wrap; gap: 5px; }

.chip {
    font-size: .72rem;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--chip-bg);
    color: var(--chip-ink);
    border: 1px solid color-mix(in srgb, var(--chip-ink) 22%, transparent);
}

.chip.free { background: color-mix(in srgb, var(--green) 16%, transparent); }
/* B2B stock chips: in-stock at the BAL warehouse (green) vs out (muted) */
.chip.stock { background: color-mix(in srgb, var(--green) 18%, transparent); color: color-mix(in srgb, var(--green) 70%, var(--ink)); font-weight: 600; }
.chip.out { background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--muted); }

.pc-details {
    border-top: 1px dashed var(--line);
    padding: 10px 13px 13px;
    font-size: .84rem;
    color: var(--muted);
    animation: riseIn .3s var(--ease);
}

.pc-details b { color: var(--ink); font-weight: 600; }

/* Downloadable product paperwork (safety/technical sheets, certificates) */
.pc-docs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
}

.pc-docs-label {
    font-size: .76rem;
    font-weight: 600;
    color: var(--ink);
    margin-right: 2px;
}

.doc-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: .74rem;
    line-height: 1;
    padding: 5px 10px;
    border-radius: 9px;
    text-decoration: none;
    color: var(--accent-press);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
    transition: background .16s, transform .12s var(--ease), border-color .16s;
}

.doc-btn:hover {
    background: var(--accent);
    color: var(--accent-ink);
    border-color: var(--accent);
    transform: translateY(-1px);
}

.doc-btn-ico { font-size: .82rem; }

/* Empty window */
.empty {
    position: relative;
    z-index: 1;
    text-align: center;
    color: var(--muted);
    padding: 12vh 10%;
    font-style: italic;
    font-size: 1.05rem;
}

.empty .big { font-size: 3rem; font-style: normal; display: block; margin-bottom: 10px; opacity: .7; }

/* ============================================================================
   Order tray
   ========================================================================== */

/* 400/401: ABOVE the topbar (z 300) — at 40/41 the header's appearance and basket buttons painted over the tray's own
   head and its ×, on a wide screen as on a phone (the phone pass, 26 Sep 2026). Dialogs (1000+) still cover it. */
.tray-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .35);
    z-index: 400;
    animation: fadeIn .25s var(--ease);
}

.order-tray {
    position: fixed;
    top: 0;
    right: 0;
    height: 100vh;
    width: min(420px, 92vw);
    background: var(--panel);
    border-left: 1px solid var(--line);
    box-shadow: var(--shadow);
    z-index: 401;
    display: flex;
    flex-direction: column;
    animation: slideIn .35s var(--ease);
}

.tray-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--gap);
    border-bottom: 1px solid var(--line);
}

.tray-head h2 { font-family: var(--font-display); font-size: 1.25rem; margin: 0; }

.tray-close { background: none; border: none; font-size: 1.5rem; cursor: pointer; color: var(--muted); line-height: 1; }

.tray-lines { flex: 1; overflow-y: auto; padding: var(--gap); display: flex; flex-direction: column; gap: 10px; }
/* The scrollable region holding the per-order cards — head and foot stay pinned; any number of
   concurrent orders scrolls instead of cropping off the bottom of the screen. */
.tray-orders { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--gap); }
.tray-orders .tray-lines { flex: none; overflow: visible; padding: 0; }

.tray-line { display: flex; align-items: center; gap: 12px; }
.tray-line .tl-name { flex: 1; min-width: 0; }
.tray-line .tl-name small { display: block; color: var(--muted); }
.tray-line .tl-sum { font-family: var(--font-display); font-weight: 600; min-width: 4.5ch; text-align: right; }

/* #6: per-line quantity stepper + remove (the tray was read-only before) */
.tl-stepper { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.tl-stepper .tl-qty { font-variant-numeric: tabular-nums; min-width: 1.5ch; text-align: center; }
.tl-step, .tl-del {
    width: 26px; height: 26px; line-height: 1; padding: 0;
    border: 1px solid var(--line); border-radius: var(--radius-sm, 8px);
    background: var(--card); color: inherit; cursor: pointer;
    font-size: 1rem; display: inline-flex; align-items: center; justify-content: center;
}
.tl-step:hover, .tl-del:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.tl-del { color: var(--muted); margin-left: 4px; }

.tray-foot { padding: var(--gap); border-top: 1px solid var(--line); }
.tray-total { display: flex; justify-content: space-between; align-items: baseline; font-size: 1.2rem; margin-bottom: 12px; }
.tray-total b { font-family: var(--font-display); font-size: 1.5rem; }
.tray-foot .btn { width: 100%; }
/* What the send asks for beyond the session (IOrderSink.Asks — a contact the deployment's sink needs): the tray asks it
   above its button exactly as the workbench's form does, so both skins send through the one door. */
.tray-asks { display: grid; gap: .5rem; margin-bottom: 12px; }
.tray-asks label { display: grid; gap: .2rem; font-size: .85rem; color: var(--muted); }
.tray-asks input { min-height: 40px; width: 100%; }
.tray-empty { color: var(--muted); text-align: center; padding: 8vh 0; font-style: italic; }

/* ============================================================================
   Ghost button (used by the shelf-reset "Začít znovu" control)
   ========================================================================== */

.btn.ghost {
    background: transparent;
    color: var(--accent-press);
    border: 1px solid color-mix(in srgb, var(--accent) 50%, var(--line));
    box-shadow: none;
    padding: 9px 16px;
}

.btn.ghost:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

.shop[data-theme="dark"] .btn.ghost { color: var(--accent); }

/* #4: transient "added to Můj nákup" confirmation flash after a product-card "+" */
.toast {
    position: fixed;
    left: 50%;
    bottom: 28px;
    transform: translateX(-50%);
    z-index: 1100;   /* above the product lightbox (z-1000) so "Dát stranou" confirms over the modal, not behind its blur */
    padding: 10px 18px;
    background: var(--accent, #8a6d3b);
    color: #fff;
    border-radius: 999px;
    font-weight: 600;
    box-shadow: 0 6px 22px rgba(0, 0, 0, .22);
    pointer-events: none;
    animation: toast-pop 1.9s ease both;
}
@keyframes toast-pop {
    0%   { opacity: 0; transform: translate(-50%, 12px); }
    12%  { opacity: 1; transform: translate(-50%, 0); }
    82%  { opacity: 1; transform: translate(-50%, 0); }
    100% { opacity: 0; transform: translate(-50%, 8px); }
}

/* ============================================================================
   Keyframes
   ========================================================================== */

@keyframes riseIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes logoIn { from { opacity: 0; transform: translateY(-10px) scale(.98); letter-spacing: .1em; } to { opacity: 1; transform: none; } }
@keyframes portraitIn { from { opacity: 0; transform: scale(.86) translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes slideIn { from { transform: translateX(100%); } to { transform: none; } }
@keyframes blink { 0%, 100% { opacity: .25; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-3px); } }
@keyframes glow {
    from { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent), var(--shadow-sm); }
    to { box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 40%, transparent), var(--shadow); }
}

/* ── Product lightbox: a rising detail modal above the storefront, with an image carousel ── */
/* The product lightbox is a full-screen overlay, but it's rendered INSIDE .gallery, whose
   `position:relative; z-index:1` makes a stacking context — which trapped the z-1000 overlay BELOW the
   mobile chat dock (z-index 40), so the dock painted over the dialog's lower half and cropped the details +
   add button. While a lightbox is open, drop the gallery's stacking context so the overlay rises to the top
   and covers the whole screen, dock included (Frank, 2026-06-26). */
.shop:has(.pc-lightbox) .gallery { z-index: auto; }
/* Same trap, same cure (Frank, 2026-08-28): the dense grid's „Sloupce" picker is absolutely positioned
   inside .gallery, so its z-61 only ever competed INSIDE .gallery's z-1 layer and the assistant panel
   painted over it. Drop the stacking context while the picker is open so it rises above the chat. */
.shop:has(.dg-cols) .gallery { z-index: auto; }

.pc-lightbox {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: grid;
    place-items: center;
    padding: clamp(12px, 4vw, 48px);
    background: color-mix(in srgb, #100c09 64%, transparent);
    backdrop-filter: blur(7px);
    animation: lbFade .25s var(--ease) both;
    outline: none;
}

.pc-lightbox-dialog {
    position: relative;
    width: min(980px, 100%);
    max-height: 92vh;
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    /* The ROW must be capped too, not just the dialog (Frank, 2026-08-29). max-height alone leaves the
       row auto-sized to its tallest item, so a long panel simply grew (measured: 1562px of panel in a
       570px dialog) and overflow:hidden clipped the surplus — taking the sticky price/CTA with it.
       minmax(0, 1fr) lets the row resolve to the dialog's capped height and shrink below its content,
       which is what finally allows .lb-panel's own overflow-y to become a real scrollbar. */
    grid-template-rows: minmax(0, 1fr);
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: calc(var(--radius) + 6px);
    overflow: hidden;
    box-shadow: 0 44px 130px rgba(0, 0, 0, .55), 0 14px 44px rgba(0, 0, 0, .38);
    /* the "rising up out of the screen" motion */
    animation: lbRise .5s var(--ease) both;
}

@media (max-width: 760px) {
    .pc-lightbox-dialog { grid-template-columns: 1fr; max-height: 94vh; overflow-y: auto; }
    /* stacked: the black divider moves under the image, between it and the details */
    [data-brand="bal"] .lb-stage { border-right: none; border-bottom: 2px solid #181512; }
}

/* ADR-0016 → ADR-0025 §13 — the CONFIGURATOR dialog (Frank, 2026-07-24): a single-column overlay that hosts
   whatever configurator the purchase experience renders (a tinting extension's carousel, the generic picker);
   the configurator owns its own scrolling, so the dialog itself is a fixed-height frame. */
.pc-lightbox-dialog.has-configurator {
    display: block;
    width: min(760px, 100%);
    height: min(88vh, 780px);
    grid-template-columns: none;
    overflow: hidden;
}
@media (max-width: 760px) {
    .pc-lightbox-dialog.has-configurator { width: 100%; height: 92vh; }
}

/* ── the base's GENERIC configurator (ADR-0025 §13): one picker per axis, a price line, one save ── */
.vp { height: 100%; display: flex; flex-direction: column; min-height: 0; }
.vp-hero { display: flex; gap: 14px; align-items: center; padding: 16px 18px 6px; }
.vp-hero img { width: 96px; height: 96px; object-fit: contain; border-radius: 10px; background: var(--panel); flex: 0 0 auto; }
.vp-glyph { width: 96px; height: 96px; display: grid; place-items: center; font-size: 2.4rem; background: var(--panel); border-radius: 10px; flex: 0 0 auto; }
.vp-title { font-family: var(--font-display); font-weight: 600; font-size: 1.25rem; line-height: 1.16; }
.vp-sub { color: var(--muted); font-size: .9rem; margin-top: 2px; }
.vp-desc { font-size: .9rem; line-height: 1.5; color: color-mix(in srgb, var(--ink) 86%, var(--muted)); margin: 0 18px 8px; }
.vp-axis { padding: 8px 18px; }
.vp-axis-label { font-family: var(--font-display); font-weight: 600; font-size: 1.02rem; margin-bottom: 8px; }
.vp-options { display: flex; flex-wrap: wrap; gap: 8px; }
.vp-option {
    display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; font: inherit;
    border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--ink); cursor: pointer;
    transition: border-color .15s, background .15s;
}
.vp-option:hover { border-color: var(--accent); }
.vp-option.selected { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.vp-dot { width: 16px; height: 16px; border-radius: 5px; display: inline-block; border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent); }
.vp-none, .vp-refusal { font-size: .85rem; color: var(--muted); padding: 4px 18px; }
.vp-refusal { color: var(--ochre); }
.vp-foot {
    margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
    padding: 12px 18px; border-top: 1px solid var(--line); background: var(--card);
}
.vp-price { font-family: var(--font-display); display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.vp-price b { font-size: 1.3rem; }
.vp-price small { font-size: .78rem; color: var(--muted); font-weight: 500; }
.vp-price.muted { color: var(--muted); }
.vp-price-cta { font-size: inherit; font-weight: 600; }
.vp-save { display: flex; align-items: center; gap: 12px; }
.vp-qty { display: inline-flex; align-items: center; gap: 4px; }
.vp-qty input { width: 56px; text-align: center; padding: 5px 4px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); color: var(--ink); font: inherit; }
.vp-step { width: 28px; height: 28px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); color: var(--ink); font-size: 1rem; line-height: 1; cursor: pointer; }
.vp-step:disabled { opacity: .4; cursor: default; }
.vp-save-btn { padding: 9px 20px; font-weight: 600; }
.vp-save-btn:disabled { opacity: .5; cursor: default; }


/* Carousel stage (left) — shows the whole photo, never cropped */
.lb-stage {
    position: relative;
    min-height: 320px;
    max-height: 64vh;          /* cap the panel so a tall pack shot can't push the dialog to full height */
    padding: 1rem;
    display: grid;
    place-items: center;
    background: radial-gradient(circle at 50% 35%, color-mix(in srgb, var(--ochre, #caa46a) 16%, #0c0a08), #0c0a08);
    overflow: hidden;
}

.lb-stage img {
    /* Natural size, capped both ways — so a tall spray/shampoo doesn't tower over the dialog and a low-res
       pack shot is NEVER upscaled into a blurry giant (Frank, 2026-06-24); nudged a few % larger. */
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: min(58vh, 496px);
    object-fit: contain;
    display: block;
    animation: lbFade .3s var(--ease);
}

/* BAL pack-shots are opaque JPEGs on a near-white studio backdrop — a dark stage framed each one in an ugly
   white box. Match the stage to that ~near-white edge (sampled mean ≈ rgb(240,241,244)) so the photo blends
   in, and keep a black divider between the image and the details panel (Frank, 2026-06-24). Scoped to the BAL
   brand; the grocery skin keeps its warm dark stage. */
[data-brand="bal"] .lb-stage {
    background: #f1f2f4;
    border-right: 2px solid #181512;
}
[data-brand="bal"] .lb-stage .lb-dot { background: color-mix(in srgb, #181512 26%, transparent); }
[data-brand="bal"] .lb-stage .lb-dot.is-active { background: #181512; }

.lb-stage .pc-glyph { font-size: 5rem; }

.lb-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    border: none;
    border-radius: 999px;
    display: grid;
    place-items: center;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    color: #1b1714;
    background: color-mix(in srgb, #fff 82%, transparent);
    box-shadow: var(--shadow-sm);
    transition: background .15s, transform .12s var(--ease);
}

.lb-nav:hover { background: #fff; transform: translateY(-50%) scale(1.08); }
.lb-nav.prev { left: 12px; }
.lb-nav.next { right: 12px; }

.lb-count {
    position: absolute;
    top: 12px;
    left: 12px;
    font-size: .74rem;
    font-weight: 600;
    color: #fff;
    background: color-mix(in srgb, #000 46%, transparent);
    padding: 3px 9px;
    border-radius: 999px;
}

.lb-dots {
    position: absolute;
    bottom: 12px;
    left: 0;
    right: 0;
    display: flex;
    justify-content: center;
    gap: 7px;
}

.lb-dot {
    width: 8px;
    height: 8px;
    padding: 0;
    border: none;
    border-radius: 999px;
    cursor: pointer;
    background: color-mix(in srgb, #fff 45%, transparent);
    transition: background .15s, transform .15s var(--ease);
}

.lb-dot.is-active { background: #fff; transform: scale(1.4); }

/* Detail panel (right) */
.lb-panel {
    padding: 24px 26px 22px;
    overflow-y: auto;
    /* LOAD-BEARING (Frank, 2026-08-29): a grid item's automatic minimum size is its CONTENT, so without
       this the panel grew taller than the dialog instead of scrolling — the dialog's overflow:hidden then
       clipped the overrun and the sticky .lb-actions stuck to a bottom outside the visible box, which is
       why long products (TELSOL: three §5 sections) ran past the edge and „Přihlásit se pro ceny"
       disappeared. min-height:0 lets the panel actually become the scroller it declares itself to be. */
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.lb-badges { display: flex; gap: 6px; flex-wrap: wrap; }

.lb-title {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 1.5rem;
    line-height: 1.16;
    padding-right: 36px;
}

.lb-pack { color: var(--muted); font-size: .9rem; margin-top: -4px; }

.lb-desc {
    font-size: .92rem;
    line-height: 1.55;
    color: color-mix(in srgb, var(--ink) 86%, var(--muted));
}

.lb-meta { font-size: .86rem; color: var(--muted); }
.lb-meta b { color: var(--ink); font-weight: 600; }

.lb-actions {
    margin-top: auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-top: 10px;
    /* STICKY footer of whichever container scrolls (the panel on desktop, the whole stacked dialog
       under 760px): with the §5 section texts the actions sat at the bottom of a long scroll and the
       add/price/vzorník controls read as MISSING (Frank, 2026-08-14). */
    position: sticky;
    bottom: 0;
    z-index: 3;
    background: var(--card);
    border-top: 1px solid var(--line);
    padding-bottom: 8px;
}

.lb-price { font-family: var(--font-display); font-weight: 600; font-size: 1.7rem; }

.lb-add {
    border: none;
    border-radius: 12px;
    padding: 12px 20px;
    font: inherit;
    font-weight: 700;
    cursor: pointer;
    color: var(--accent-ink);
    background: var(--accent);
    transition: background .15s, transform .12s var(--ease);
}

.lb-add:hover { background: var(--accent-press); transform: translateY(-1px); }
/* Disabled = no live price yet (quoting, or the quote failed). The onclick already can't fire; this
   makes that VISIBLE so the button doesn't look clickable before the price lands (Frank, 2026-08-09). */
.lb-add:disabled { opacity: .45; cursor: progress; background: var(--accent); transform: none; }
.lb-add:disabled:hover { background: var(--accent); transform: none; }

.lb-close {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 2;
    width: 38px;
    height: 38px;
    border: none;
    border-radius: 999px;
    display: grid;
    place-items: center;
    font-size: 1rem;
    cursor: pointer;
    color: #1b1714;
    background: color-mix(in srgb, #fff 82%, transparent);
    box-shadow: var(--shadow-sm);
    transition: background .15s, transform .12s var(--ease);
}

.lb-close:hover { background: #fff; transform: rotate(90deg); }

@keyframes lbFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes lbRise {
    from { opacity: 0; transform: translateY(54px) scale(.92); }
    to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    * { animation: none !important; transition: none !important; }
}

/* ── Multi-order tray (ADR-0008): each order is its own card ─────────────────────────────── */
.tray-order {
    border: 1px solid var(--line, rgba(0,0,0,.12));
    border-radius: 12px;
    padding: .6rem .75rem;
    margin: .5rem 0;
    background: var(--paper, var(--panel, #fffdf7));
}
.tray-order.active { border-color: var(--accent, #8a6d3b); box-shadow: 0 0 0 1px var(--accent, #8a6d3b) inset; }
.tray-order-head { display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; margin-bottom: .35rem; }
.tray-order-name { font-weight: 600; }
.tray-order-plan { opacity: .8; font-size: .85em; white-space: nowrap; }
.tray-order-del {
    margin-left: auto; flex: none;
    width: 24px; height: 24px; line-height: 1; padding: 0;
    border: none; background: none; cursor: pointer;
    color: var(--muted); font-size: 1.15rem; border-radius: var(--radius-sm, 8px);
}
.tray-order-del:hover { color: var(--accent-press, inherit); background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* The active-order marker + the flip button (the UI twin of the agent's select_order). */
.tray-order-active { flex: none; color: var(--accent, #8a6d3b); font-size: .8em; font-weight: 600; white-space: nowrap; }
.tray-order-select {
    flex: none; padding: .05rem .5rem;
    border: 1px solid var(--line, rgba(0,0,0,.18)); background: none; cursor: pointer;
    color: var(--muted); font-size: .8em; border-radius: var(--radius-sm, 8px); white-space: nowrap;
}
.tray-order-select:hover { color: var(--accent-press, inherit); border-color: var(--accent, #8a6d3b); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.tray-order-total { display: flex; justify-content: space-between; margin-top: .35rem; padding-top: .35rem; border-top: 1px dashed var(--line, rgba(0,0,0,.12)); opacity: .9; }
/* ---- Trade-buyer discounts (ADR-0016): per line, per order and for the whole basket ---- */
.tray-line .tl-sum .tl-disc { display: block; font-family: var(--font-body, inherit); font-weight: 500; font-size: .72rem; color: #2f8f4e; }
.tray-order-disc, .tray-disc { display: flex; justify-content: space-between; align-items: baseline; color: #2f8f4e; font-size: .85rem; }
.tray-order-disc { margin-top: .35rem; }
.tray-disc { margin-bottom: .35rem; }
.tray-order-disc b, .tray-disc b { font-family: var(--font-display); font-weight: 600; }

/* ── Delivery-address map (ADR-0008): Leaflet/OSM in the window ──────────────────────────── */
.map-panel { display: flex; flex-direction: column; gap: .6rem; padding: .5rem; }
.map-head { display: flex; justify-content: space-between; align-items: center; }
.map-title { font-weight: 600; font-size: 1.05rem; }
.map-canvas { width: 100%; height: 52vh; min-height: 320px; border-radius: 14px; overflow: hidden; border: 1px solid var(--line, rgba(0,0,0,.12)); z-index: 0; }
.map-actions { display: flex; justify-content: space-between; align-items: center; gap: .75rem; flex-wrap: wrap; }
.map-hint { opacity: .75; font-size: .9em; }

/* ── Registration consent card (ADR-0008) — centred modal ───────────────────────────────── */
.reg-card {
    /* 1200, not 60: the sign-in (and its siblings) must rise ABOVE the product lightbox/configurator
       (z 1000) — signing in mid-configuration keeps the wizard underneath, alive (Frank, 2026-08-14). */
    position: fixed; z-index: 1200; left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: min(92vw, 460px); max-height: 86vh; overflow: auto;
    background: var(--paper, var(--panel, #fffdf7)); color: var(--ink, #2a2118);
    border: 1px solid var(--line, rgba(0,0,0,.14)); border-radius: 16px;
    padding: 1.1rem 1.25rem; box-shadow: 0 24px 60px rgba(0,0,0,.28);
}
.reg-head { display: flex; justify-content: space-between; align-items: baseline; }
.reg-lede { margin: .5rem 0 .75rem; }
.reg-tnc { font-size: .85rem; opacity: .8; border: 1px solid var(--line, rgba(0,0,0,.1)); border-radius: 10px; padding: .6rem .7rem; max-height: 9rem; overflow: auto; }
.reg-actions { display: flex; gap: .6rem; margin-top: 1rem; }

/* The PIN row — shared by the consent card (setting it) and the identity-claim prompt (proving it). */
.reg-pin { margin: .25rem 0 .9rem; display: flex; flex-direction: column; gap: .35rem; }
.reg-pin label { font-size: .9rem; font-weight: 600; }
.reg-pin input {
    width: 9rem; font-size: 1.35rem; letter-spacing: .45em; text-align: center;
    padding: .4rem .5rem; border: 1px solid var(--line, rgba(0,0,0,.2)); border-radius: 10px;
    background: var(--paper, var(--panel, #fff)); color: var(--ink, #2a2118);
}
.reg-pin input.reg-name { width: 100%; font-size: 1rem; letter-spacing: normal; text-align: left; }
.reg-pin-hint { margin: 0; font-size: .85rem; color: #b3261e; }

/* The MULTI-CREDENTIAL sign-in (ITradeAccount.Components): one section per backend component of the
   composite. A single-component deployment renders one bare section; a composite renders several, each
   its own bordered group with the component's name and its own success/failure line. */
.signin-component { border: 1px solid var(--line, rgba(0,0,0,.1)); border-radius: 12px; padding: .7rem .8rem .2rem; margin: 0 0 .8rem; }
.signin-component-name { margin: 0 0 .6rem; font-size: .95rem; font-weight: 700; }
.signin-ok { margin: 0 0 .6rem; font-size: .85rem; color: #1b7a3d; font-weight: 600; }
/* A section's note — how a buyer with no account on that backend becomes its customer (TradeComponent.Note). */
.signin-component-note {
    margin: .1rem 0 .75rem; padding: .55rem .7rem; border-radius: 10px;
    background: color-mix(in srgb, var(--ochre) 12%, transparent);
    font-size: .88rem; line-height: 1.4;
}
.signin-component-note strong { display: block; margin-bottom: .15rem; }
.signin-component-note p { margin: 0; }
.signin-component-note a { font-weight: 600; }

/* The sign-in FITS WITHOUT A SCROLLBAR (Frank, 25 Sep 2026): a composite's sections stand side by side where the
   screen is wide enough — the dialog widens to hold them — and its spacing is tighter than the consent card's it
   shares .reg-card with. On a phone the sections stack, and the card scrolls only where the screen cannot hold it. */
.reg-card.signin-card { padding: 1rem 1.2rem; }
.reg-card.signin-card.signin-multi { width: min(94vw, 780px); }
.signin-components {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
    gap: .8rem; align-items: start;
}
.signin-components .signin-component { margin: 0; padding: .6rem .75rem .1rem; }
.signin-card .reg-lede { margin: .3rem 0 .4rem; }
.signin-card .reg-demo-note { margin-bottom: .7rem; }
.signin-card .reg-pin { margin: .1rem 0 .55rem; gap: .2rem; }
.signin-card .reg-pin input.reg-name { padding: .35rem .55rem; }
.signin-card .reg-actions { margin-top: .8rem; }
/* On a phone the sections stack, so the card may use more of the screen's height — the sign-in button must not
   sit below the fold of a dialog that has room to show it. */
@media (max-width: 600px) {
    .reg-card.signin-card { max-height: 94vh; padding: .9rem 1rem; }
    .signin-card .reg-head h2 { font-size: 1.3rem; margin: 0; }
}

/* ============================================================================
   The back-office DESK (ADR-0019 §Surfaces, ADR-0023): every admin page renders
   INSIDE the shop window, so this is ONE visual system — one table treatment
   (.desk-table), one form layout (.desk-form), one button hierarchy
   (.btn / .btn.ghost / .btn.mini / .btn.danger), status colours from the theme
   tokens (--danger/--warn/--green), never inline hex from C#.
   (The retired standalone /admin + /prodejna pages took .admin-gate/.admin/
   .admin-head with them.)
   ========================================================================== */

/* ---- THE BACK OFFICE'S SIGN-IN (Frank, 2026-08-17). A quiet cool gate palette under the tenant's own sign
        and logo (the back office is the tenant's — chunk 3, the Architect's note 10 §7 S-B), written
        literally because this page has no .shop wrapper to inherit the tokens from. ---- */
.gate-page {
    min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1.5rem;
    background: linear-gradient(160deg, #eef1f6 0%, #f7f9fc 55%, #e7ecf5 100%);
}
.gate-card {
    width: 100%; max-width: 23rem; display: flex; flex-direction: column;
    padding: 2rem 1.9rem 1.8rem; gap: .35rem;
    background: #fff; border-radius: 18px;
    box-shadow: 0 18px 48px rgba(26, 31, 43, .13), 0 2px 6px rgba(26, 31, 43, .06);
}
.gate-logo { height: 30px; align-self: flex-start; margin-bottom: .1rem; color: #1a1f2b; }
.gate-lead { margin: 0 0 1.1rem; color: #5b6474; font-size: .95rem; }
.gate-label { font-size: .8rem; font-weight: 600; letter-spacing: .02em; color: #5b6474; margin-top: .55rem; }
.gate-input {
    font-size: 1rem; padding: .6rem .7rem; border-radius: 10px;
    border: 1px solid rgba(26, 31, 43, .2); background: #fff; color: #1a1f2b;
}
.gate-input:focus { outline: none; border-color: #1d4ed8; box-shadow: 0 0 0 3px rgba(29, 78, 216, .15); }
.gate-submit {
    margin-top: 1.3rem; padding: .65rem 1rem; border: 0; border-radius: 10px; cursor: pointer;
    background: #1d4ed8; color: #fff; font-size: 1rem; font-weight: 600;
}
.gate-submit:hover { background: #1740ad; }
/* The door through the konzol (the Architect's MEDIUM-2 on chunk 2, 15 Oct 2026): first on an attached site's sign-in card, the break-glass
   form below it. */
.gate-konzol {
    display: block; text-align: center; text-decoration: none; margin-top: .3rem; padding: .65rem 1rem; border-radius: 10px;
    background: #1d4ed8; color: #fff; font-size: 1rem; font-weight: 600;
}
.gate-konzol:hover { background: #1740ad; }
.gate-note { margin: .35rem 0 .6rem; color: #5b6474; font-size: .85rem; }
.gate-error {
    margin: .2rem 0 .4rem; padding: .5rem .65rem; border-radius: 8px; font-size: .88rem;
    background: rgba(179, 38, 30, .08); color: #b3261e;
}
/* Failure/attention prose. The fallback serves the rare render outside a .shop wrapper. */
.admin-warn { color: var(--danger, #b3261e); margin: 0; }

/* ---- THE DESK MENU (Frank, 2026-08-18: "i don't like the top black nav… the menu items should all go
        there [beside the logo], and collapse to the Burger menu"). It reuses the topbar's own actions row
        — the same pills, the same fold-into-burger breakpoint — so the back office has ONE navigation,
        drawn by the shop's own chrome. The active section is the filled pill. ---- */
.topbar-nav .btn.topbar-action { font-size: .86rem; padding: 6px 12px; white-space: nowrap; }
.topbar-nav { gap: 8px; }
/* Shrink-to-content, centred in the slack (measured 2026-08-18: as flex:1 the nav grew to 756px for
   ~560px of pills and pushed Odhlásit onto a second row). Five sections either fit on one row or fold. */
.topbar-nav { flex: 0 1 auto; flex-wrap: nowrap; margin: 0 auto; }

/* WHEN THE MENU FOLDS (Frank, 2026-08-18: "top menu not folding into burger on narrow width"). The
   storefront's burger appears at 980px; the desk has five sections plus the theme icons and Odhlásit,
   which need roughly 1100px of WINDOW — and the window is the viewport minus the assistant rail while
   the rail is open. So the desk folds earlier, and earlier still with the rail open. The burger then
   carries every entry (see .topbar-menu). */
@media (max-width: 1300px) {
    .shop.desk.chat-collapsed .topbar-nav, .shop.desk.chat-collapsed .topbar-wide { display: none; }
    .shop.desk.chat-collapsed .topbar-burger { display: grid; }
}
@media (max-width: 1680px) {
    .shop.desk:not(.chat-collapsed) .topbar-nav, .shop.desk:not(.chat-collapsed) .topbar-wide { display: none; }
    .shop.desk:not(.chat-collapsed) .topbar-burger { display: grid; }
}
/* The desk's burger menu may exist ABOVE the storefront breakpoint (the generic rule hides it there) —
   but ONLY in the band where the desk's burger itself is showing, so a dropdown left open while the
   window was narrow does not haunt the widened window beside the inline menu. */
@media (min-width: 981px) and (max-width: 1300px) {
    .shop.desk.chat-collapsed .topbar-menu { display: flex; }
}
@media (min-width: 981px) and (max-width: 1680px) {
    .shop.desk:not(.chat-collapsed) .topbar-menu { display: flex; }
}
.topbar-nav .btn.topbar-action.active {
    background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
}
.topbar-desk-actions { margin-left: .25rem; }
.topbar-menu .menu-item.active { font-weight: 700; }

/* A DESK PAGE lives inside the shop window like any other view — the window's own padding, the skin's
   panel and ink, no separate reading column. Sections inside it are the shop's ordinary cards. */
.desk-page { padding: 0 .25rem 3rem; }
.desk-page h2 { margin: .2rem 0 .8rem; font-size: 1.35rem; }
.desk-page h3 { margin: 1.3rem 0 .5rem; font-size: 1.05rem; border-bottom: 1px solid var(--line); padding-bottom: .3rem; }
.desk-page h4 { margin: .9rem 0 .35rem; font-size: .95rem; color: var(--muted); }
.desk-page input, .desk-page select, .desk-page textarea {
    font: inherit; background: var(--card); color: var(--ink);
    border: 1px solid var(--line); border-radius: 8px; padding: .3rem .5rem;
}
.desk-page input[type="checkbox"] { accent-color: var(--accent); }
.desk-page .desk-note { color: var(--muted); font-size: .9rem; }
.desk-page .desk-dim { color: var(--muted); }
.desk-page .desk-chip { display: inline-block; padding: .1rem .5rem; border-radius: 999px; background: var(--chip-bg); color: var(--chip-ink); font-size: .8rem; }
.desk-page .desk-two { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1.2rem; align-items: start; }
@media (max-width: 980px) { .desk-page .desk-two { grid-template-columns: 1fr; } }

/* ---- The desk BUTTON hierarchy: the base .btn (solid accent) is the committing act, .btn.ghost is
   everything reversible, .btn.mini is a row-level action inside a table, .btn.danger is the stated
   destructive confirm. Desk buttons are workbench-sized — the storefront's 13px pills read clownish
   between table rows. ---- */
.desk-page .btn { padding: 8px 15px; font-size: .92rem; }
.desk-page .btn.ghost { padding: 7px 14px; }
.btn.mini, .desk-page .btn.mini { padding: .1rem .5rem; font-size: .85em; white-space: nowrap; box-shadow: none; }
.btn.danger { background: var(--danger, #b3261e); color: var(--bg, #fff); }
.btn.danger:hover { background: color-mix(in srgb, var(--danger, #b3261e) 82%, var(--ink)); }

/* ---- Status words carry the theme's semantic tokens — the credential health chip, the deadline
   escalation (ADR-0009 upcoming → due → OVERDUE). A missed supplier deadline must never look like
   an ordinary row. ---- */
.chip-ok { color: var(--green); }
.chip-warn { color: var(--warn, #b45309); font-weight: 600; }
.chip-bad { color: var(--danger, #b3261e); font-weight: 700; }
.chip-dim { color: var(--muted); }
.admin-order.is-due { border-left: 4px solid var(--warn, #b45309); }
.admin-order.is-overdue { border-left: 4px solid var(--danger, #b3261e); }
.admin-when.is-due { color: var(--warn, #b45309); font-weight: 600; }
.admin-when.is-overdue { color: var(--danger, #b3261e); font-weight: 700; }

/* ---- Toolbars + action rows: one flex idiom. ---- */
.admin-catbar, .admin-actions { display: flex; gap: .5rem .6rem; align-items: center; flex-wrap: wrap; margin: .5rem 0; }
.desk-page .flex-spacer { flex: 1 1 auto; }
.desk-page .in-xs { width: 5em; }
.desk-page .in-sm { width: 6.5em; }
.desk-page .in-md { width: 9em; }
.desk-page .in-lg { width: 16em; }
.desk-page .in-grow { flex: 1 1 12em; min-width: 12em; }
.desk-inline { display: inline; }

/* ---- The order/worklist CARD. ---- */
.admin-order { border: 1px solid var(--line); border-radius: 12px; padding: .7rem .9rem; margin: .5rem 0; background: var(--card); color: var(--ink); }
.admin-order-head { display: flex; gap: .3rem 1rem; flex-wrap: wrap; align-items: baseline; }
/* An order's deposit on the desk (ADR-0026, amended): still due, or stated paid. */
.desk-due { color: var(--warn, #b45309); font-weight: 600; }
.desk-paid { color: var(--ok, #15803d); }
.admin-when { color: var(--muted); font-size: .9rem; }
.admin-order ul { margin: .4rem 0; padding-left: 1.2rem; }

/* ---- ONE table treatment for every desk data table. Real headers over typed columns; .num columns
   right-aligned with tabular numerals; .cell-note is the muted evidence column; .cell-actions keeps a
   row's buttons on one line. ---- */
.desk-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.desk-table th {
    text-align: left; padding: .3rem .6rem .3rem 0; color: var(--muted);
    font-weight: 600; font-size: .85em; border-bottom: 2px solid var(--line); white-space: nowrap;
}
.desk-table td { padding: .26rem .6rem .26rem 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.desk-table tbody tr:hover td { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.desk-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.desk-table .cell-note { font-size: .85em; color: var(--muted); }
.desk-table .cell-actions { white-space: nowrap; }
/* The Katalog: thousands of rows — denser type, the header pinned while they scroll under it. */
.desk-table.desk-cat { font-size: .9rem; }
.desk-table.desk-cat th { position: sticky; top: 0; z-index: 2; background: var(--panel); padding: .3rem .5rem .3rem 0; }
.desk-table.desk-cat td { padding: .22rem .5rem .22rem 0; }
.desk-table.desk-cat td .btn.mini { margin-left: .35rem; }
/* The day book / unassigned lists: a packing list — the quantity column bold, right-aligned. */
.desk-table.desk-daybook { max-width: 32rem; }
.desk-table.desk-daybook td:first-child { font-weight: 700; text-align: right; width: 3.5em; font-variant-numeric: tabular-nums; }

/* ---- ONE form layout: a quiet inset panel; each row is <label>caption <control></label> laid on a
   two-column grid so every control lines up under the one before it; checkbox vocabularies sit in a
   .check-row. ---- */
.desk-form {
    display: flex; flex-direction: column; gap: .55rem; padding: .85rem .95rem; margin-top: .6rem;
    background: color-mix(in srgb, var(--ink) 4%, transparent);
    border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.desk-form h4, .desk-form h5, .desk-form p { margin: 0; }
.desk-form > label, .desk-stack > label {
    display: grid; grid-template-columns: minmax(10rem, 15rem) minmax(0, 1fr);
    gap: .25rem .8rem; align-items: center; max-width: 46rem;
}
@media (max-width: 620px) { .desk-form > label, .desk-stack > label { grid-template-columns: 1fr; } }
.desk-stack { display: flex; flex-direction: column; gap: .5rem; }
/* The rulebook editor's product picker (ADR-0026, amended: a rule names the tenant's own products by id). */
.desk-picks { display: flex; flex-wrap: wrap; gap: .3rem .9rem; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: .4rem .7rem; max-width: 46rem; }
.desk-pick { display: inline-flex; align-items: center; gap: .3em; white-space: nowrap; }
.desk-confirm { display: flex; flex-direction: column; gap: .4rem; }
.check-row { display: flex; flex-wrap: wrap; gap: .35rem .9rem; align-items: center; }
.check-row .lead { font-weight: 600; }
.check-row label { display: inline-flex; align-items: center; gap: .3em; cursor: pointer; white-space: nowrap; }

/* ---- The per-row ITEM DETAIL fold (Frank, 2026-08-21) + its documents. ---- */
.desk-detail {
    display: flex; flex-wrap: wrap; gap: 1rem; padding: .7rem; align-items: flex-start;
    background: color-mix(in srgb, var(--ink) 4%, transparent); border-radius: var(--radius-sm);
}
.desk-detail-img { max-width: 9rem; max-height: 9rem; object-fit: contain; border-radius: 4px; }
.desk-detail-col { flex: 1; min-width: 14rem; display: flex; flex-direction: column; gap: .3rem; }
.desk-docs { display: flex; flex-direction: column; gap: .3rem; padding: .5rem .7rem; }
.desk-doc-row { display: flex; flex-wrap: wrap; gap: .6rem; align-items: baseline; }
.doc-excerpt {
    white-space: pre-wrap; font-size: .8em; color: var(--muted); max-height: 14rem; overflow: auto;
    background: color-mix(in srgb, var(--ink) 4%, transparent); padding: .5rem; border-radius: 4px; margin: .2rem 0 0;
}
.desk-pre { white-space: pre-wrap; margin: .3rem 0; font-size: .85em; }

/* ---- The folded review/rules sections (state-driven <details> — the host renders `open` from its
   own field, the summary click flips only that field): a real disclosure card, not a bare browser
   triangle over unstyled prose. ---- */
.desk-details { margin: 1.1rem 0; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--card); }
.desk-details > summary { cursor: pointer; padding: .55rem .8rem; font-weight: 600; border-radius: var(--radius-sm); }
.desk-details > summary:hover { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.desk-details[open] > summary { border-bottom: 1px solid var(--line); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.desk-details .desk-review { padding: .1rem .8rem .8rem; }
.desk-details .desk-review > section > h3:first-child { margin-top: .6rem; }

/* The legacy /admin + /prodejna routes flash one line while redirecting to the portal. */
.redirect-note { padding: 2rem; font-family: system-ui, sans-serif; }

/* ============================================================================
   BAL B2B deployment (ADR-0011): a red/white brand skin, the async Search
   Window, the order workbench, invoices, the single-user bar + research popup.
   ========================================================================== */

/* ---- Brand skin: BAL (white / blue / red, matching bal.cz). Overrides ONLY the accent family + brand
        colours + fonts, so the four themes (light/dark/contrast/elder) still drive bg/ink and the theme
        switcher keeps working. The light theme — BAL's default — additionally gets a clean white/cool-grey
        palette (its parchment is wrong for a trade portal). ---- */
.shop[data-brand="bal"] {
    --font-display: var(--font-body);
    --accent: #0a6cb5;            /* BAL blue — primary actions/links */
    --accent-press: #08568f;
    --accent-ink: #ffffff;
    --brand-blue: #0095db;        /* bright logo blue */
    --brand-red: #e30613;         /* logo red — badges, checkout CTA, orders tab */
    --ochre: #0095db;             /* recolour the warm ambient glow to brand blue */
    --user-bubble: #0a6cb5;
    --user-ink: #ffffff;
}
.shop[data-brand="bal"][data-theme="light"] {
    --bg: #eaf0f5;
    --bg-2: #f5f8fb;
    --panel: #ffffff;
    --card: #ffffff;
    --ink: #16202b;
    --muted: #5a6675;
    --line: rgba(16, 32, 43, .12);
    --green: #2f8f4e;
    --chip-bg: rgba(10, 108, 181, .08);
    --chip-ink: #0a6cb5;
    --shadow: 0 12px 32px rgba(16, 32, 43, .12);
    --shadow-sm: 0 4px 14px rgba(16, 32, 43, .08);
}
.shop[data-brand="bal"] .brand-logo { background: #fff; border-radius: 8px; padding: 4px 10px; }

/* (THE PLATFORM'S OWN SKIN, [data-brand="streamcharge"], went in chunk 3: the back office wears the tenant's own sign and brand —
   the Architect's note 10 §7 S-B — and no package names that brand.) */
/* Replace the grocery-interior backdrop with the BAL warehouse hero. */
.shop[data-brand="bal"] .window::before { background-image: url("/assets/bal-brand/hero.png"); opacity: .06; }

/* ---- Document-folder side tabs: they hang off the LEFT of the aisle↔window divider, beside the persona,
        like the tabs on a folder. Positioned against the aisle's right edge (the aisle's backdrop-filter
        makes it the containing block); the protruding (left) side is rounded, the divider side flush. ---- */
.folder-tabs {
    position: absolute; right: 0; top: 122px;
    display: flex; flex-direction: column; gap: 9px; z-index: 30;
}
.folder-tab {
    /* vertical-rl alone = conventional book-spine reading (top→bottom). NO 180° spin: it would
       mirror the rounded lip to the divider side, flip the top-down gradient and the drop shadow
       upward, and read bottom→top — the "tab mounted backwards" look. Authored as-is: left-rounded
       protruding lip, light-from-above gradient, downward shadow. */
    writing-mode: vertical-rl;
    border: none; border-radius: 11px 0 0 11px;
    padding: 17px 11px; font: inherit; font-weight: 700; letter-spacing: .09em;
    text-transform: uppercase; font-size: .92em; color: #fff; cursor: pointer;
    box-shadow: 0 3px 10px rgba(20, 32, 43, .22), inset 0 0 0 1px rgba(255, 255, 255, .18);
    text-shadow: 0 1px 1px rgba(0, 0, 0, .22);
    opacity: .6; transition: opacity .2s var(--ease), padding .2s var(--ease);
}
/* A skin that states no brand pair falls back to its accent and its green: without the fallback the gradient was
   invalid, the tab had no background at all, and its white label stood on the cream rail — unreadable. */
.folder-tab.vyloha { background: linear-gradient(180deg, color-mix(in srgb, var(--brand-blue, var(--accent)) 85%, #fff), var(--brand-blue, var(--accent)) 52%, color-mix(in srgb, var(--brand-blue, var(--accent)) 85%, #000)); }
.folder-tab.orders { background: linear-gradient(180deg, color-mix(in srgb, var(--brand-red, var(--green)) 85%, #fff), var(--brand-red, var(--green)) 52%, color-mix(in srgb, var(--brand-red, var(--green)) 85%, #000)); }
.folder-tab.docs { --tab: color-mix(in srgb, var(--brand-blue, var(--accent)) 45%, #1f262e);
    background: linear-gradient(180deg, color-mix(in srgb, var(--tab) 85%, #fff), var(--tab) 52%, color-mix(in srgb, var(--tab) 85%, #000)); }
.folder-tab:hover { opacity: .88; }
.folder-tab.active { opacity: 1; padding-left: 15px; }
/* Attention cue (BAL skin's rendering of an off-screen Focus): the folder tab pokes out and pulses a
   brand-red ring so the eye catches the path to the change the agent just made. */
.folder-tab.attention { opacity: 1; animation: tabAttention 1.15s var(--ease, ease-in-out) infinite; }
@keyframes tabAttention {
    0%, 100% { transform: translateX(0); box-shadow: 0 3px 10px rgba(20, 32, 43, .22), inset 0 0 0 1px rgba(255, 255, 255, .18); }
    50% { transform: translateX(-5px); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-red, #e30613) 60%, transparent), 0 4px 16px rgba(20, 32, 43, .34), inset 0 0 0 1px rgba(255, 255, 255, .22); }
}
/* Accessibility: no motion — the cue stays, as a steady ring rather than a pulse. */
@media (prefers-reduced-motion: reduce) {
    .folder-tab.attention { animation: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-red, #e30613) 60%, transparent), 0 3px 10px rgba(20, 32, 43, .28); }
    .tray-btn.attention { animation: none; box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 40%, transparent); }
}

/* ---- Orders panel: compact summary list + full-detail page (spec: handle 100s of rows) ---- */
.orders-panel { height: 100%; overflow: auto; padding: 4px 14px 24px; }
.os-section { margin-bottom: 1.4rem; }
.os-head {
    display: flex; align-items: center; gap: .5rem; margin: .4rem 0 .6rem;
    font-size: .82rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
}
.os-empty { color: var(--muted); padding: .3rem .2rem 0; font-size: .9rem; }
.os-row {
    display: grid; grid-template-columns: 1fr auto auto; grid-template-areas: "label total go" "meta total go";
    gap: 2px 14px; align-items: center; width: 100%; text-align: left;
    background: var(--card); border: 1px solid var(--line); border-radius: 12px;
    padding: .7rem .95rem; margin-bottom: .55rem; cursor: pointer; font: inherit; color: var(--ink);
    transition: border-color .15s, box-shadow .15s, transform .05s;
}
.os-row:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
.os-row:active { transform: translateY(1px); }
.os-label { grid-area: label; font-weight: 700; }
.os-meta { grid-area: meta; font-size: .82rem; color: var(--muted); }
.os-total { grid-area: total; font-weight: 700; white-space: nowrap; }
.os-go { grid-area: go; color: var(--accent); font-size: .82rem; white-space: nowrap; }

/* Delete-order: a row carries its own trash button next to the open button (no nested <button>). */
.os-row-wrap { display: flex; align-items: stretch; gap: 6px; margin-bottom: .55rem; }
.os-row-wrap .os-row { margin-bottom: 0; flex: 1; }
.os-del {
    flex: none; width: 42px; border: 1px solid var(--line); border-radius: 12px; background: var(--card);
    cursor: pointer; font-size: 1rem; line-height: 1; color: var(--muted);
    transition: border-color .15s, color .15s, background .15s;
}
.os-del:hover {
    border-color: var(--brand, #c8102e); color: var(--brand, #c8102e);
    background: color-mix(in srgb, var(--brand, #c8102e) 8%, var(--card));
}
.od-delete { color: var(--muted); margin-right: auto; }   /* detail-view delete sits left of the checkout */
.od-delete:hover { border-color: var(--brand, #c8102e); color: var(--brand, #c8102e); }

/* Delete-order confirmation ("are you sure") — only for a non-empty order. */
.os-modal-backdrop {
    position: fixed; inset: 0; z-index: 400; background: rgba(0, 0, 0, .45);
    display: flex; align-items: center; justify-content: center; padding: 1rem;
}
.os-modal {
    background: var(--card); border: 1px solid var(--line); border-radius: 16px; padding: 1.3rem 1.5rem;
    width: min(420px, 94vw); box-shadow: 0 14px 48px rgba(0, 0, 0, .28); color: var(--ink);
}
.os-modal h3 { margin: 0 0 .55rem; font-size: 1.1rem; }
.os-modal p { margin: 0 0 1.2rem; color: var(--muted); font-size: .92rem; }
.os-modal-actions { display: flex; gap: .6rem; justify-content: flex-end; }
.os-danger { background: var(--brand, #c8102e); color: #fff; border-color: var(--brand, #c8102e); }
.os-danger:hover { filter: brightness(1.08); }

.od-head { display: flex; align-items: center; gap: .9rem; flex-wrap: wrap; margin-bottom: .8rem; }
.od-back { flex: none; }
.od-title { flex: 1; min-width: 0; }
.od-title h2 { margin: 0; }
.od-meta { font-size: .85rem; color: var(--muted); }

@media (max-width: 760px) {
    /* The header jammed the back button, the big order title, the status badge and the meta into one row that
       doesn't fit a phone — the title got squeezed into a sliver and wrapped. Stack it: back button on its own
       line, then the title at full width, then the meta (Frank, 2026-06-26). */
    .od-head { flex-direction: column; align-items: stretch; gap: .4rem; }
    .od-back { align-self: flex-start; }
    .od-title { flex: none; }
}
.od-lines { display: flex; flex-direction: column; }
.od-line {
    display: grid; grid-template-columns: 1fr auto auto; gap: 14px; align-items: center;
    padding: .55rem .2rem; border-bottom: 1px solid var(--line);
}
.od-line-name { background: none; border: 0; text-align: left; cursor: pointer; color: var(--ink); font: inherit; padding: 0; }
.od-line-name small { display: block; color: var(--muted); font-size: .78rem; }
.od-line-name:hover { text-decoration: underline; }
.od-stepper { display: inline-flex; align-items: center; gap: 4px; }
.od-qty { color: var(--muted); white-space: nowrap; }
.od-sum { font-weight: 600; white-space: nowrap; min-width: 84px; text-align: right; }
.od-foot {
    position: sticky; bottom: 0; display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap; margin-top: 1rem; padding: .8rem 0 0; border-top: 2px solid var(--line);
    background: linear-gradient(180deg, transparent, var(--bg) 40%);
}
.od-total { font-size: 1.05rem; }
.od-total b { margin-left: .5rem; }
/* Trade-buyer saving in the order panel: per line, and the order's total above "Celkem". */
.od-sum .od-disc { display: block; font-weight: 500; font-size: .72rem; color: #2f8f4e; }
.od-total-disc { color: #2f8f4e; font-size: .9rem; }
/* The shared <DiscountLine> full-row default, for any surface that doesn't skin it. */
.discount-row { display: flex; justify-content: space-between; align-items: baseline; color: #2f8f4e; font-size: .85rem; }
.discount-row b { font-family: var(--font-display); font-weight: 600; }
/* A configured-to-order tile advertises no number: the price exists only once its variant is chosen. */
.pc-price.pc-price-config { font-family: inherit; font-size: .8rem; font-weight: 500; color: var(--muted); }

/* ---- Signed-out price CTA (Frank, 2026-08-13): every locked price placeholder is a live
   „Přihlásit se pro ceny" button — it opens the sign-in dialog and flashes the topbar chip. ---- */
.pc-price-cta, .lb-price-cta, .vp-price-cta {
    border: none; background: none; padding: 0; cursor: pointer; font: inherit;
    color: var(--accent); text-decoration: underline; text-underline-offset: 3px;
    text-align: left;
}
.pc-price-cta:hover, .lb-price-cta:hover, .vp-price-cta:hover { color: var(--ink); }
.pc-price-cta { font-family: inherit; font-size: .8rem; font-weight: 600; }
.lb-price-cta { font-size: 1rem; font-weight: 600; }
/* The chip flash: a one-shot pulse on the topbar sign-in control when a CTA fires (the wrapper is
   re-keyed per request, so the animation restarts on every click; never loops on its own). Targets the
   signed-out `.user-signin` pill AND the signed-in chip — whichever the wrapper holds. */
.signin-flash .user-chip, .signin-flash .user-signin { animation: signinFlash 1.3s var(--ease, ease-in-out) 2; }
@keyframes signinFlash {
    0%, 100% { transform: translateY(0); box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
    30% { transform: translateY(-3px); box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 35%, transparent); }
    65% { transform: translateY(0); box-shadow: 0 0 0 9px color-mix(in srgb, var(--accent) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
    .signin-flash .user-chip, .signin-flash .user-signin { animation: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); }
}
/* Gate mode: the sign-in IS the page, so it sits on the shop's own surface rather than floating over an
   empty grid — there is nothing behind it to see through to. */
.shop:has(> .signin-gate) { display: block; background: var(--bg); min-height: 100vh; }
.reg-card.signin-gate { box-shadow: var(--shadow-lg, 0 24px 60px rgba(0,0,0,.18)); }
/* The sign-in dialog's demo caveat — quieter than the lede it follows. */
.reg-demo-note { font-size: .85rem; opacity: .8; }
/* The brand pair's fallbacks (accent / blue-grey) keep these legible on a skin that states no brand pair — without
   them the send button and the order badges had no background at all: white words on the page. */
.od-checkout { background: var(--brand-red, var(--accent)); }
.od-checkout:hover { background: var(--brand-red, var(--accent)); filter: brightness(.92); }
/* The vendor client's own two buttons beside the send — Přepočet (the backend's own price and stock) and the draft
   save — and what a Přepočet asks for and says. */
.od-quote, .od-save { white-space: nowrap; }
.od-quote-ask {
    display: flex; flex-wrap: wrap; align-items: end; gap: .5rem .75rem; margin-top: .6rem; padding: .7rem .8rem;
    border: 1px solid var(--line); border-radius: 8px; background: var(--bg);
}
.od-quote-ask label { display: grid; gap: .2rem; font-size: .85rem; color: var(--muted); }
.od-quote-ask input { min-width: 14rem; min-height: 40px; }
.od-quote-why { flex: 1 1 100%; font-size: .85rem; color: var(--muted); }
.od-quote-note { margin: .6rem 0 0; font-size: .88rem; color: var(--ink); }
@media (max-width: 760px) {
    .shop .od-quote-ask label { flex: 1 1 100%; }
    .shop .od-quote-ask input { min-width: 0; width: 100%; }
    .shop .od-foot > .od-quote, .shop .od-foot > .od-save { flex: 1 1 auto; min-height: 44px; }
}

/* Shared order badges/labels (also used by the summary + detail) */
.wb-badge { font-size: .72rem; padding: .05rem .5rem; border-radius: 999px; color: #fff; font-weight: 700; }
.wb-badge.open { background: var(--brand-red, var(--accent)); }
.wb-badge.sent { background: var(--brand-blue, #4d6a8a); }
.wb-badge.done { background: #2f8f4e; }
.wb-fiction { font-size: .8rem; color: var(--muted); margin: 0 0 .6rem; font-style: italic; }
.wb-state { font-size: .68rem; padding: .1rem .5rem; border-radius: 999px; }
.wb-state.open { background: rgba(227, 6, 19, .12); color: var(--brand-red, var(--danger)); }
.wb-state.sent { background: rgba(0, 149, 219, .14); color: #0a6cb5; }
.wb-state.done { background: rgba(47, 143, 78, .16); color: #2f8f4e; }
.wb-state.prepared { background: rgba(214, 137, 0, .16); color: #8a5a00; }

/* ---- Single-user bar (spec #5): the fixed trade buyer + a disabled logout ---- */
.user-chip {
    display: inline-flex; align-items: center; gap: .5rem; margin-left: auto;
    padding: .3rem .6rem; border: 1px solid var(--line); border-radius: 999px;
    background: var(--panel); font-size: .85rem;
}
.user-chip .user-name { font-weight: 600; color: var(--ink); display: inline-flex; flex-direction: column; line-height: 1.15; }
.user-chip .user-name small { font-weight: 500; font-size: .72rem; color: var(--muted); }
.user-chip .user-ico { opacity: .7; }
.user-logout {
    border: 1px solid var(--line); background: transparent; color: var(--muted);
    border-radius: 999px; padding: .15rem .55rem; font: inherit; font-size: .8rem;
    cursor: not-allowed; opacity: .55;
}
/* The BAL demo's logout is permanently disabled; the toning sign-in/out is a real, live control. */
.user-logout:enabled { cursor: pointer; opacity: 1; color: var(--ink); }
.user-logout:enabled:hover { background: var(--panel-2, rgba(0,0,0,.04)); }
/* The topbar uses margin-left:auto on the user-chip; keep the tray button after it tidy. */
.shop[data-brand="bal"] .topbar .theme-switcher { margin-left: .5rem; }

/* ---- Async Search Window (spec #7) ---- */
.search-window {
    display: flex; flex-direction: column; height: 100%; min-height: 0;
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
    box-shadow: var(--shadow-sm); overflow: hidden;
}

@media (max-width: 980px) {
    /* On a phone the window grew unbounded (height:100% → auto in the auto-height gallery), so its internal
       list-scroll never engaged and the foot ("Přidat do objednávky") + in-window chat fell below the viewport
       and behind the chat dock — unreachable. Make it a full-screen overlay so it has a DEFINED height (the
       flex column then scrolls .sw-list internally and pins the foot/chat), and drop .gallery's z-context so
       the overlay actually rises above the dock (same trap as the lightbox; Frank, 2026-06-26). */
    .shop:has(.search-window) .gallery { z-index: auto; }
    .search-window { position: fixed; inset: 0; z-index: 50; border: none; border-radius: 0; }
}
.sw-head {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
    padding: .9rem 1rem; border-bottom: 1px solid var(--line); background: var(--bg-2);
}
.sw-title { display: flex; align-items: flex-start; gap: .6rem; }
.sw-title h2 { margin: 0; font-size: 1.05rem; }
.sw-criteria { margin: .2rem 0 0; font-size: .82rem; color: var(--muted); }
.sw-spin {
    width: 16px; height: 16px; margin-top: 3px; border-radius: 50%; flex: none;
    border: 2px solid var(--line); border-top-color: var(--accent);
}
.sw-spin.on { animation: sw-rot .8s linear infinite; }
@keyframes sw-rot { to { transform: rotate(360deg); } }
.sw-controls { display: flex; gap: .35rem; flex-wrap: wrap; }
.sw-controls .btn { padding: .3rem .6rem; font-size: .82rem; }
.sw-cancel { color: var(--accent); }
.sw-status {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    padding: .5rem 1rem; font-size: .85rem; border-bottom: 1px solid var(--line);
}
.sw-phase { font-weight: 600; }
.sw-phase.run { color: var(--accent); }
.sw-phase.done { color: var(--green); }
.sw-phase.fail { color: #b00020; }
.sw-phase.stopped { color: var(--muted); }
.sw-detail { color: var(--muted); }
.sw-progress { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }
.sw-modify {
    display: flex; gap: .5rem; align-items: end; flex-wrap: wrap;
    padding: .6rem 1rem; border-bottom: 1px solid var(--line); background: var(--bg-2);
}
.sw-modify label { display: flex; flex-direction: column; font-size: .75rem; color: var(--muted); gap: .15rem; }
.sw-modify input { padding: .35rem .5rem; border: 1px solid var(--line); border-radius: 8px; background: var(--card); color: var(--ink); }
.sw-tools { display: flex; align-items: center; gap: .5rem; padding: .5rem 1rem; }
.sw-count { font-size: .85rem; color: var(--muted); }
.sw-tools-sp { flex: 1; }
.sw-mini { padding: .25rem .5rem; font-size: .78rem; }
.sw-list { flex: 1; min-height: 0; overflow: auto; padding: .25rem .5rem; }
.sw-empty { color: var(--muted); padding: 1rem; text-align: center; }
.sw-row {
    display: flex; align-items: center; gap: .55rem; padding: .4rem .5rem;
    border-radius: 10px; border: 1px solid transparent;
}
.sw-row + .sw-row { margin-top: 2px; }
.sw-row.ok { background: rgba(31, 111, 235, .07); border-color: rgba(31, 111, 235, .25); }
.sw-row.no { opacity: .55; }
.sw-row.unk { background: rgba(0, 0, 0, .03); }
.sw-check { width: 18px; height: 18px; flex: none; accent-color: var(--accent); }
.sw-row-main {
    flex: 1; display: flex; justify-content: space-between; gap: .8rem; align-items: baseline;
    background: none; border: 0; text-align: left; cursor: pointer; color: var(--ink); font: inherit; padding: 0;
}
.sw-row-main:hover .sw-name { text-decoration: underline; }
.sw-name { font-weight: 500; }
.sw-price { color: var(--muted); white-space: nowrap; }
.sw-verdict { font-size: .76rem; white-space: nowrap; }
.sw-row.ok .sw-verdict { color: var(--green); font-weight: 600; }
.sw-row.no .sw-verdict { color: #b00020; }
.sw-row.unk .sw-verdict { color: var(--muted); }
.sw-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: .6rem 1rem; border-top: 1px solid var(--line);
}
.sw-sel { font-size: .85rem; color: var(--muted); }
.sw-chat { border-top: 1px solid var(--line); background: var(--bg-2); }
.sw-chat-log { max-height: 9rem; overflow: auto; padding: .6rem 1rem; display: flex; flex-direction: column; gap: .35rem; }
.sw-msg { padding: .35rem .6rem; border-radius: 12px; font-size: .85rem; max-width: 85%; }
.sw-msg.user { align-self: flex-end; background: var(--user-bubble); color: var(--user-ink); }
.sw-msg.bot { align-self: flex-start; background: var(--card); border: 1px solid var(--line); color: var(--ink); }
.sw-chat-composer { display: flex; gap: .5rem; padding: .5rem 1rem .7rem; }
.sw-chat-composer input { flex: 1; padding: .45rem .6rem; border: 1px solid var(--line); border-radius: 10px; background: var(--card); color: var(--ink); }

/* (Order workbench is now the inline orders panel — see .orders-panel / .os-* / .od-* above.) */

/* ---- Dialog backdrop (the product lightbox, the appearance and note dialogs) ---- */
/* 400/401 like the tray: a dialog stands above the topbar (z 300), whose logo and burger otherwise paint over its head. */
.lb-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, .5); z-index: 400; }

/* ---- The buyer's documents and saldo (the Doklady panel): the SERVER's rows, read through the ledger port ----
   A document the supplier's system keeps is a row the buyer reads, never a paper composed here — so there is no document
   view of our own any more: a row opens as the server prints it („Zobrazit doklad", the server's file). */
.tdocs-locked { display: flex; flex-direction: column; align-items: flex-start; gap: .8rem; margin: 1rem 0;
    padding: 1rem 1.1rem; background: var(--bg-2); border-radius: 10px; color: var(--ink); max-width: 60ch; }
.tdocs-locked p { margin: 0; line-height: 1.5; }
.tdocs { display: flex; flex-direction: column; gap: .8rem; padding: .2rem 0 1rem; }
.tdocs-tabs { display: flex; gap: .4rem; border-bottom: 1px solid var(--line); }
.tdocs-tab { background: none; border: 0; border-bottom: 3px solid transparent; padding: .55rem .9rem; font: inherit; font-weight: 600;
    color: var(--muted); cursor: pointer; display: inline-flex; align-items: center; gap: .45rem; }
.tdocs-tab.active { color: var(--ink); border-bottom-color: var(--brand-blue, var(--accent)); }
.tdocs-count { font-size: .75rem; background: var(--bg-2); border-radius: 999px; padding: .05rem .5rem; }
.tdocs-alert { font-size: .75rem; background: color-mix(in srgb, var(--brand-red, #b42318) 16%, transparent); color: var(--brand-red, #b42318);
    border-radius: 999px; padding: .05rem .5rem; font-weight: 700; }
/* eKlient's filter row: the kind, the issue-date range, and „Obnovit" — the server is asked again, never a local filter */
.tdocs-filters { display: flex; flex-wrap: wrap; gap: .6rem; align-items: flex-end; }
.tdocs-filters label { display: flex; flex-direction: column; gap: .2rem; font-size: .75rem; color: var(--muted); }
.tdocs-filters select, .tdocs-filters input { padding: .45rem .55rem; border: 1px solid var(--line); border-radius: 8px;
    background: var(--card); color: var(--ink); font: inherit; font-size: .9rem; min-height: 38px; }
.tdocs-refresh { min-height: 38px; }
/* the server's own words (a refusal, the saldo's heading and message) — printed as it worded them */
.tdocs-note { margin: 0; padding: .6rem .8rem; border-radius: 8px; background: var(--bg-2); color: var(--ink); font-size: .88rem; line-height: 1.45; }
.tdocs-note.blocking { background: color-mix(in srgb, var(--brand-red, #b42318) 12%, transparent); }
.tdocs-list { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.tdocs-row { display: grid; gap: .6rem; align-items: center; padding: .55rem .8rem; border-bottom: 1px solid var(--line);
    background: var(--card); color: var(--ink); font-size: .88rem; }
/* the columns each kind carries — an order (number · issued · state · payment · net · created by · open), an invoice
   (number · issued · due · state · payment · net · gross · outstanding · open), an open item of the saldo */
.tdocs-row.orders { grid-template-columns: minmax(110px, 1.1fr) minmax(84px, .8fr) minmax(110px, 1.1fr) minmax(84px, .8fr) minmax(96px, .9fr) minmax(80px, .8fr) max-content; }
.tdocs-row.invoices { grid-template-columns: minmax(96px, 1fr) minmax(84px, .8fr) minmax(84px, .8fr) minmax(110px, 1.1fr) minmax(84px, .8fr) minmax(90px, .9fr) minmax(90px, .9fr) minmax(96px, 1fr) max-content; }
.tdocs-row.saldo { grid-template-columns: minmax(96px, 1fr) minmax(84px, .8fr) minmax(84px, .8fr) minmax(84px, .8fr) minmax(96px, 1fr) minmax(96px, 1fr) max-content; }
.tdocs-row:last-child { border-bottom: 0; }
.tdocs-row.overdue { box-shadow: inset 3px 0 0 var(--brand-red, #b42318); }
.tdocs-head { background: var(--bg-2); font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 700; }
.tdocs-row .num { text-align: right; font-variant-numeric: tabular-nums; }
.tdocs-number { font-weight: 700; font-variant-numeric: tabular-nums; }
.tdocs-date { font-variant-numeric: tabular-nums; color: var(--muted); }
.tdocs-total { font-weight: 600; }
.tdocs-pay, .tdocs-ref { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tdocs-state-cell { font-size: .82rem; }
.tdocs-open { padding: .3rem .6rem; font-size: .8rem; white-space: nowrap; justify-self: end; }
.tdocs-balance { display: flex; flex-direction: column; gap: .8rem; }
.tdocs-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
.tdocs-card { background: var(--bg-2); border-radius: 10px; padding: .7rem .9rem; display: flex; flex-direction: column; gap: .2rem; }
.tdocs-card span { font-size: .75rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.tdocs-card b { font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.tdocs-card.overdue b { color: var(--brand-red, #b42318); }

/* A phone (the owner tests from his phone): a document row is a card — the number and the amount on the first line, the
   dates, the payment and the state on the next, „Zobrazit doklad" across the bottom at thumb height; the filters stack,
   the two dates side by side, „Obnovit" full width. */
@media (max-width: 760px) {
    .tdocs-row, .tdocs-row.orders, .tdocs-row.invoices, .tdocs-row.saldo { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .6rem; padding: .7rem .8rem; }
    .tdocs-row.tdocs-head { display: none; }
    .tdocs-row::after { content: ""; flex-basis: 100%; order: 3; }
    .tdocs-row > * { order: 4; font-size: .82rem; }
    .tdocs-row > .tdocs-number { order: 1; font-size: .92rem; }
    .tdocs-row > .tdocs-total { order: 2; margin-left: auto; font-size: .95rem; }
    .tdocs-row > .tdocs-state-cell { order: 5; flex-basis: 100%; }
    .tdocs-row > .tdocs-open { order: 6; flex-basis: 100%; margin-top: .35rem; min-height: 40px; }
    .tdocs-row [data-label]:not(:empty)::before { content: attr(data-label) " "; color: var(--muted); font-weight: 400; }
    .tdocs-row > :empty { display: none; }
    .tdocs-cards { grid-template-columns: 1fr 1fr; }
    .tdocs-filters label { flex: 1 1 100%; }
    .tdocs-filters label:nth-child(2), .tdocs-filters label:nth-child(3) { flex: 1 1 0; min-width: 0; }
    .tdocs-filters select, .tdocs-filters input { width: 100%; min-width: 0; }
    .tdocs-refresh { flex: 1 1 100%; min-height: 42px; }
}

/* ---- Checkout research-preview popup (spec #6) ---- */
.research-popup {
    position: fixed; z-index: 401; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: min(420px, 92vw); background: var(--card); border-radius: var(--radius);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .3); padding: 1.4rem 1.5rem;
}
.research-popup h2 { margin: 0 0 .6rem; }
.research-popup p { margin: 0 0 1.1rem; color: var(--ink); line-height: 1.5; }

/* ---- Tenant NOTE panel (Kontakty / Ochrana osobních údajů / Obchodní podmínky) ----
   One generic renderer over blank-line-separated tenant TEXT (Frank, 2026-08-13): a multi-line
   section's first line is its heading. Wider + scrollable — the legal pages are long. */
.note-popup { width: min(620px, 94vw); max-height: 86vh; display: flex; flex-direction: column; }
.note-popup .note-body { overflow: auto; margin-bottom: 1rem; }
.note-popup .note-body h3 { margin: .9rem 0 .25rem; font-size: .95rem; }
.note-popup .note-body h3:first-child { margin-top: 0; }
.note-popup .note-body p { margin: 0 0 .35rem; font-size: .92rem; color: var(--ink); line-height: 1.5; }
.note-popup > .btn { align-self: flex-start; }
/* The legal links: quiet text links (the Kontakty footer + the burger menu), never competing pills. */
.note-links { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.note-link {
    border: none; background: none; padding: 0; cursor: pointer; font: inherit; font-size: .88rem;
    color: var(--accent); text-decoration: underline; text-underline-offset: 3px; text-align: left;
}
.note-link:hover { color: var(--ink); }

@media (max-width: 980px) {
    /* On a phone the tabs become a horizontal row at the top of the docked rail (no absolute hang-off). */
    .folder-tabs { position: static; flex-direction: row; gap: 8px; padding: 8px var(--gap) 0; }
    .folder-tab { writing-mode: horizontal-tb; transform: none; border-radius: 10px; padding: 8px 14px; flex: 1; text-align: center; box-shadow: var(--shadow-sm); }
    .folder-tab.active { padding-left: 14px; }
    .orders-panel { height: auto; overflow: visible; }
}

.lb-add.ghost { background: transparent; color: var(--ink); border: 1px solid var(--line); font-weight: 600; }
.lb-add.ghost:hover { background: var(--panel); border-color: var(--accent); }

/* A configured order line's VARIANT chip (ADR-0025 §13): the experience's own chip or the default, inside
   the base's wrapper — the missing-axis and estimate warnings are the line's, rendered here on every skin. */
.od-variant { display: flex; align-items: center; gap: 6px; font-size: .8rem; color: var(--muted); margin-top: 2px; }
.od-variant-dot {
    width: 13px; height: 13px; border-radius: 4px; display: inline-block; flex: 0 0 auto;
    border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
}
.od-variant-warn { font-style: normal; color: var(--ochre); font-weight: 600; }

/* flex-shrink: 0 stays load-bearing: the panel is a flex column and was SQUEEZING the description to
   ~2 unreadable lines (Frank, 2026-07-23). But the per-section max-height + overflow that came with it
   gave every §5 section its OWN scrollbar — three nested scrollers inside the panel's own, which read as
   panes spilling past the dialog (Frank, 2026-08-29, TELSOL). The text now flows at its natural height
   and THE PANEL is the single scroller; nothing is squeezed, and there is one scrollbar, not four. */
.lb-desc { padding-right: 6px; flex-shrink: 0; }

/* The sign-in dialog's backdrop dims OVER an open lightbox/configurator (z 1000) — the flow underneath
   survives the login and resumes with prices (Frank, 2026-08-14). */
.signin-backdrop { z-index: 1190; }

/* ── dense grid v2 ── */
/* THE DENSE MODE, second cut. The owner reviewed the deployed grid (2026-08-18): "awful UI - columns not
   aligned, ugly vertical scroll bar with no next page, whereas really we should have just a reactive
   scroller that pulls in more as demanded. Columns have no sort options. No category column - no column
   selector … The topic of the page should be a particular order". So: the order bar on top (the topic),
   ONE scroll box that <Virtualize> windows (no pager, no cap) with a sticky header, table-layout: fixed
   + <col> widths so every cell sits under its header, sortable headers, a column picker. Only the skin
   tokens for colour — this grid renders under four themes and three brand skins. Appended as one block
   on purpose: nothing above is touched. Row height 34px is shared with DenseOrderGrid.RowHeight. */
.dense-grid.dg {
    display: flex; flex-direction: column; gap: .45rem; min-height: 0;
    /* Fill the gallery (a definite-height flex item on desktop) so the scroll box below takes exactly
       the space left — ONE scrollbar, the grid's, never the gallery's on top of it. On a phone the
       gallery is auto-height and this resolves to auto; the scroll box then falls back to its own
       viewport-based height. */
    height: 100%;
    padding: .4rem .5rem .5rem; font-size: .92em; color: var(--ink);
}
.dg .btn { padding: 5px 12px; font-size: .9em; white-space: nowrap; }
.dg .btn.mini { padding: .05rem .5rem; font-size: .85em; }
.dg-spacer { flex: 1 1 auto; }

/* the topic bar: which order this grid writes into */
.dg-order { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.dg-order-pick { display: inline-flex; align-items: center; gap: .4rem; color: var(--muted); font-size: .9em; white-space: nowrap; }
.dg-order-select { max-width: 24rem; }
.dg-order-title { margin: 0; font-family: var(--font-display); font-size: 1.2em; font-weight: 600; white-space: nowrap; display: flex; align-items: baseline; gap: .6rem; }
.dg-order-title small { font-family: var(--font-body); font-size: .7em; font-weight: 500; color: var(--muted); }
.dg-order-title small b { color: var(--ink); font-weight: 700; }
/* The order's delivery date on the grid header (2026-09-03): the same FulfilmentPlan.Date column the
   assistant writes; on a buying desk it is half of the (supplier × date) key, so a missing one is said
   out loud beside the field rather than discovered at Objednat. */
.dg-date .dg-date-in { padding: .25rem .4rem; border: 1px solid var(--line); border-radius: var(--radius-sm, 8px); background: var(--card); color: var(--ink); font: inherit; }
.dg-date .dg-date-in:required:invalid { border-color: var(--warn, #b45309); }
.dg-date-hint { color: var(--warn, #b45309); font-size: .85em; }

/* THE DESK's Objednávky u dodavatelů (ADR-0024 §7b.11): the purchase book folded by the row's own
   status — draft cards and purchase cards share .admin-order; the folders share the workbench's
   .os-section/.os-head/.wb-badge so both order surfaces read as one family. */
.desk-orders { padding: .4rem 14px 3rem; }
.desk-orders-head h2 { margin-bottom: .2rem; }
.desk-orders-head .desk-note { margin: 0 0 1rem; }
.desk-orders .os-section { margin-bottom: 1.6rem; }
.desk-orders .admin-order-head .desk-dim { font-size: .85em; }
.desk-orders .admin-order ul .desk-dim { font-size: .85em; margin-left: .3rem; }
.desk-orders .os-head small { font-weight: 500; font-size: .8em; }


/* filters + quick-add */
.dg-tools { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.dg select, .dg input:not([type="checkbox"]) {
    padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius-sm);
    background: var(--card); color: var(--ink); font: inherit;
}
.dg input::placeholder { color: var(--muted); }
.dg-quick { flex: 2 1 12em; min-width: 10em; }
.dg-qty { width: 4.5em; }
.dg-cat-pick { max-width: 16rem; }
/* tag + supplier filters beside the category one (Frank, 2026-08-21) — same look, AND-composed */
.dg-tag-pick, .dg-sup-pick { max-width: 13rem; }
.dg-check { display: inline-flex; align-items: center; gap: .3em; white-space: nowrap; cursor: pointer; }
.dg-check input, .dg-cols input { accent-color: var(--accent); }

/* the column picker: a small popover under its button, closed by the transparent backdrop */
.dg-colpick { position: relative; }
.dg-cols-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; }
.dg-cols {
    position: absolute; right: 0; top: calc(100% + 4px); z-index: 61; min-width: 12rem;
    display: flex; flex-direction: column; gap: .3rem; padding: .55rem .75rem;
    background: var(--card); color: var(--ink); border: 1px solid var(--line);
    border-radius: var(--radius-sm); box-shadow: var(--shadow-sm);
}
.dg-cols label { display: flex; align-items: center; gap: .45rem; white-space: nowrap; cursor: pointer; }
.dg-cols-fixed { color: var(--muted); font-size: .8em; margin-top: .2rem; max-width: 16rem; }
/* one row per column, in the buyer's order: its box (Název and Zadáno always on), and for a column on show the
   arrows that move it — the keyboard's way to do what dragging a header does */
.dg-col-row { display: flex; align-items: center; gap: .5rem; }
.dg-col-row > label, .dg-col-always { flex: 1 1 auto; min-width: 0; }
.dg-col-always { display: flex; align-items: center; gap: .45rem; white-space: nowrap; color: var(--muted); }
.dg-col-move { display: inline-flex; gap: .15rem; margin-left: auto; }
.dg-col-move button {
    all: unset; cursor: pointer; display: grid; place-items: center; width: 1.6rem; height: 1.5rem;
    border-radius: 6px; color: var(--muted); font-size: .9em;
}
.dg-col-move button:hover, .dg-col-move button:focus-visible { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--ink); }
.dg-col-move button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dg-col-move button[aria-disabled="true"] { opacity: .3; cursor: default; background: none; }

/* the scroll box: the ONE place this grid scrolls; Virtualize finds it as the nearest overflow ancestor.
   position: relative makes it the containing block of the stock cells' visually-hidden words (.dg-sr, absolute):
   without it they belonged to .gallery, escaped this box's clipping, and on a landscape phone widened the whole
   page — the docked assistant then ran off the right edge with its Poslat. */
.dg-scroll {
    position: relative;
    flex: 1 1 auto; min-height: 18rem; height: calc(100vh - 20rem);
    overflow: auto; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--card);
}
.dg-table {
    /* --dg-minw is computed by the component from the VISIBLE columns (fixed widths + the Název floor).
       Desktop keeps the 44rem floor exactly as before; ≤760px drops to the computed width alone, so the
       narrow DEFAULT set (Název+Zadáno+Cena) fits a 375px screen with NO horizontal body scroll while a
       picker-restored wider set still scrolls the box sideways honestly (Frank, 2026-08-21: the 375px
       grid showed Kód|Kategorie|Balení and no Název). */
    width: 100%; min-width: max(44rem, var(--dg-minw, 44rem));
    table-layout: fixed;                        /* <col> widths rule — cells line up under their headers */
    border-collapse: separate; border-spacing: 0;
    /* NÁZEV FITTED TO ITS CONTENT (26 Sep 2026): once the component knows Název's width — measured, or dragged by
       the buyer — --dg-fitw is the sum of every visible column at its width, and the table HUGS its columns at it
       while they fit the box; where they do not, min() gives the box's width and Název takes the room there is,
       exactly as before, so the fit never pushes Zadáno off the screen. Unset: the table fills its box as it did. */
    width: min(100%, var(--dg-fitw, 100%));
}
/* The table narrower than its box leaves the header band to carry on across the box, so the header reads as one bar. */
.dg-scroll:has(> .dg-table[style*="--dg-fitw"]) {
    background: linear-gradient(to bottom, var(--panel) 32px, var(--line) 32px, var(--line) 34px, var(--card) 34px) no-repeat, var(--card);
}
/* The hidden copies of the name cells the component measures Název by: laid out at their own width, in the boldest
   weight a row takes, and clipped to nothing so they add no scroll. */
.dg-ruler { position: absolute; top: 0; left: 0; width: 0; height: 0; overflow: hidden; visibility: hidden; pointer-events: none; }
.dg-ruler > .dg-name-in { width: max-content; font-weight: 700; }
@media (max-width: 760px) {
    .dg-table { min-width: var(--dg-minw, 0px); }
}
/* AN ORDER'S NAME, EDITED IN PLACE (item 11). It must LOOK like the heading it replaces — a rename
   affordance that restyles the title would read as a control, not as the name. Hover/focus is the hint. */
.on-name {
    all: unset; cursor: text; font: inherit; color: inherit;
    border-bottom: 1px dashed transparent; transition: border-color .15s;
}
.on-name:hover, .on-name:focus-visible { border-bottom-color: var(--muted); }
.on-name:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }
.on-input { font: inherit; color: inherit; max-width: 100%; width: 22ch; padding: 0 .2em; }

/* THE WORKING-ORDER BAR (item 14): which order this shop window is being filled into. Above the shelf in
   BOTH renderings, because the order you are buying into is a fact about the surface, not about the view. */
.wo-bar {
    display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
    padding: .35rem .5rem; margin: 0 0 .35rem;
    background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
}
.wo-pick { display: inline-flex; align-items: center; gap: .4rem; min-width: 0; }
.wo-pick > span { color: var(--muted); font-size: .85em; white-space: nowrap; }
.wo-select { max-width: min(46ch, 60vw); }
.wo-spacer { flex: 1 1 auto; }
.wo-total { white-space: nowrap; font-size: .9em; color: var(--muted); }
.wo-total b { color: var(--ink); }
@media (max-width: 760px) { .wo-select { max-width: 100%; } .wo-total { width: 100%; text-align: right; } }

/* A TEMPLATE'S ORDER IS DUE (item 21): the reminder above the bar — one sentence and its three answers. */
.wo-due {
    display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
    padding: .45rem .6rem; margin: 0 0 .35rem;
    background: color-mix(in srgb, var(--accent) 12%, var(--panel));
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); border-radius: 10px;
}
.wo-due-text { flex: 1 1 18rem; font-weight: 600; }
/* what the last template / „Jako minule" did — stays until the buyer closes it */
.wo-note {
    display: flex; align-items: flex-start; gap: .5rem; margin: 0 0 .35rem; padding: .35rem .6rem;
    font-size: .9em; background: var(--card); border: 1px dashed var(--line); border-radius: 10px;
}
.wo-note .tray-close { margin-left: auto; font-size: 1.1rem; }

/* WHAT THE ORDER CANNOT HAVE RIGHT NOW (item 20/22): rows waiting for an offer, lines out of stock, their stand-ins */
.oa {
    display: flex; flex-direction: column; gap: .3rem; margin: .35rem 0; padding: .45rem .6rem;
    background: color-mix(in srgb, var(--warn) 7%, var(--card));
    border: 1px solid color-mix(in srgb, var(--warn) 30%, var(--line)); border-radius: 10px; font-size: .9em;
}
.oa-head { margin: 0; font-weight: 600; color: var(--warn); }
.oa-row { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.oa-name { flex: 1 1 12rem; min-width: 0; }
.oa-sub {
    font: inherit; font-size: .9em; cursor: pointer; padding: .15rem .55rem; border-radius: 999px;
    background: var(--chip-bg); color: var(--chip-ink); border: 1px solid color-mix(in srgb, var(--chip-ink) 25%, transparent);
}
.oa-sub small { color: var(--muted); margin-left: .25rem; }
.oa-sub:hover { background: color-mix(in srgb, var(--chip-ink) 18%, transparent); }
.oa-del { margin-left: auto; }
.oa-note { margin: 0; color: var(--muted); }

/* THE TEMPLATES (item 21): opened from the bar, inline below it — the shelf moves down, nothing stacks over it */
.ot {
    display: flex; flex-direction: column; gap: .5rem; margin: 0 0 .5rem; padding: .6rem .75rem;
    background: var(--card); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow-sm);
}
.ot-head { display: flex; align-items: center; justify-content: space-between; }
.ot-head h3 { margin: 0; font-size: 1rem; }
.ot-hint, .ot-empty { margin: 0; color: var(--muted); font-size: .9em; }
.ot-save, .ot-item { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.ot-save { padding-bottom: .5rem; border-bottom: 1px solid var(--line); }
.ot-field { display: inline-flex; flex-direction: column; gap: .2rem; }
.ot-field > span { color: var(--muted); font-size: .85em; }
.ot-name { font: inherit; padding: .25rem .45rem; width: 22ch; max-width: 100%; }
.ot-rhythm-edit { display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.ot-days { display: inline-flex; gap: .35rem; flex-wrap: wrap; }
.ot-days label, .ot-inline { display: inline-flex; align-items: center; gap: .25rem; white-space: nowrap; }
.ot-inline input { width: 4.5em; font: inherit; }
.ot-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.ot-item { padding: .35rem 0; border-top: 1px solid var(--line); }
.ot-item:first-child { border-top: 0; }
.ot-title { flex: 1 1 14rem; }
.ot-rhythm { color: var(--muted); font-size: .9em; }
.ot-actions { display: inline-flex; gap: .35rem; margin-left: auto; flex-wrap: wrap; }

/* the desk's row-action column — fixed, not resizable */
col.dg-col-act { width: 12ch; }
.dg-table th {
    position: sticky; top: 0; z-index: 2;       /* the header stays while the rows scroll under it */
    height: 32px; padding: 0 .45rem; text-align: left; white-space: nowrap;
    background: var(--panel); color: var(--muted); font-weight: 600; font-size: .85em;
    border-bottom: 2px solid var(--line);
    user-select: none;                          /* double-click here RESETS a width — never selects */
}
.dg-table th.dg-num { text-align: right; }
/* Column resizing (Frank, 2026-08-30): the handle rides the header's right edge — drag it, nudge it
   with ←/→ when focused, Home or double-click resets. The <th> is position:sticky (a positioned box),
   so the absolute handle anchors to it; kept fully inside the cell so the next header never paints
   over it. Widths are per-device presentation (localStorage), like the Sloupce picker — never view row. */
.dg-resize {
    position: absolute; top: 0; right: 0; width: 9px; height: 100%;
    cursor: col-resize; z-index: 3; touch-action: none;
    background: linear-gradient(to right, transparent 3px, var(--line) 3px, var(--line) 5px, transparent 5px);
    opacity: 0; transition: opacity .15s;
}
.dg-table th:hover .dg-resize, .dg-resize:focus-visible, .dg-resize.dragging { opacity: 1; }
.dg-resize:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 3px; }
.dg-sort {
    all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: .3em;
    font: inherit; color: inherit; max-width: 100%;
}
.dg-sort:hover, .dg-sort.on { color: var(--ink); }
.dg-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.dg-arrow { font-size: .7em; min-width: 1em; }
/* COLUMN ORDER (26 Sep 2026): a header is picked up and dropped onto another. The one being moved fades; the one
   under the pointer is tinted and carries a bar on the side the column will land. */
.dg-table th[draggable="true"] { cursor: grab; }
.dg-table th.dg-dragging { opacity: .45; }
.dg-table th.dg-drop-before, .dg-table th.dg-drop-after { background: color-mix(in srgb, var(--accent) 16%, var(--panel)); color: var(--ink); }
.dg-table th.dg-drop-before { box-shadow: inset 4px 0 0 var(--accent); }
.dg-table th.dg-drop-after { box-shadow: inset -4px 0 0 var(--accent); }
.dg-table tbody tr { height: 34px; }            /* = DenseOrderGrid.RowHeight; Virtualize spacers assume it */
.dg-table td {
    padding: 0 .45rem; border-bottom: 1px solid var(--line); vertical-align: middle;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dg-table tbody tr:hover td { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.dg-code { font-variant-numeric: tabular-nums; }
.dg-name { font-weight: 500; }
/* THE ROW OPENS THE PRODUCT (Frank, 2026-09-03, item 6). The Detail column is gone: it spent a column of
   width on something the row itself could do. Its removal also removes item 1's „stray dot" — the button
   overflowed its 8ch cell at any width below full and the cell's text-overflow painted an ellipsis after
   it, which at that size reads as a single dot. */
.dg-openable { cursor: pointer; }
.dg-openable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* A cell whose content is a CONTROL must never ellipsise: a clipped button paints a stray „…" beside
   itself. Clip instead — the control has its own width and its own tooltip. (This is the general form of
   item 1, kept so the desk's action column cannot grow the same artefact.) */
.dg-table td.dg-act, .dg-table td:has(> button), .dg-table td:has(> input) { text-overflow: clip; }
.dg-num { text-align: right; font-variant-numeric: tabular-nums; }
/* rows with something Zadáno stand out — the review pass reads the order off the grid at a glance */
.dg-row.on td { font-weight: 600; background: color-mix(in srgb, var(--accent) 9%, transparent); }
.dg-row.on:hover td { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.dg input.dg-in {
    width: 5em; height: 26px; padding: 0 6px; border-radius: 6px; text-align: right;
    font-variant-numeric: tabular-nums;
}
.dg input.dg-in:focus { outline: 2px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 0; border-color: var(--accent); }
.dg-empty td { text-align: center; color: var(--muted); font-style: italic; padding: 1.4rem .5rem; white-space: normal; }

/* footer: the count + „objednat znovu" */
.dg-foot { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.dg-count { color: var(--muted); font-size: .85em; white-space: nowrap; }
.dg-clone { max-width: 20rem; }

/* the Dlaždice/Tabulka view toggle row above the gallery (workbench + desk) */
.dense-toggle-row { display: flex; justify-content: flex-end; padding: 0 .5rem; }

/* the signed-in trade buyer's identity chip (ADR-0020 §3.3): the toast look, parked bottom-right */
.toast.identity-chip { top: auto; bottom: .75rem; right: .75rem; left: auto; opacity: .92; }

/* per-order „Export CSV" in the B2B orders detail header (moved off the grid at the owner's request) */
.od-export { flex: none; white-space: nowrap; }
@media (max-width: 760px) {
    .od-export { align-self: flex-start; }      /* the stacked head must not stretch it full-width */
    .dg-order-title { white-space: normal; }
}

/* ── Per-supplier order forms (ADR-0024 §7b.12): the draft's own delivery date, chosen from the dates
   its source offers; the verdict line names whose word it is. Theme tokens only. ─────────────────── */
.po-date { display: inline-flex; align-items: baseline; gap: .35rem; }
.po-date > span { color: var(--muted); }
.po-date-pick, .po-date-in { font: inherit; padding: .1rem .35rem; border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--ink); }
.po-verdict { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .5rem; }
.po-source { font-weight: 700; }
.po-source-supplier { color: var(--brand-blue); }
.po-source-declared { color: var(--muted); }
.desk-ordering-form .po-live { border-left: 3px solid var(--brand-blue); padding-left: .5rem; }
.check-inline { display: inline-flex; gap: .3rem; align-items: center; font-weight: 400; }

/* ── Supplier-first orders at the desk (ADR-0024 §7b.13): the topic order names ONE supplier and ONE
   date; „＋ Nová objednávka" asks for both before it opens anything, the selected order pins the grid to
   that supplier, and a pick with no order selected is refused with the one prompt. Theme tokens only. ── */
.dg-new-form { display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; padding: .25rem .5rem; border: 1px dashed var(--line); border-radius: var(--radius-sm, 8px); background: color-mix(in srgb, var(--accent) 6%, transparent); }
.dg-new-form select, .dg-new-form input { font: inherit; padding: .2rem .35rem; border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--ink); }
.dg-new-form .dg-new-label { color: var(--muted); font-size: .85em; }
.dg-pin { display: inline-flex; align-items: baseline; gap: .35rem; font-size: .85em; color: var(--muted); }
.dg-pin b { color: var(--brand-blue, var(--ink)); font-weight: 700; }
.dg-order-select:disabled, .dg-sup-pick:disabled { opacity: .7; }
.dg-no-order { color: var(--warn, #b45309); font-size: .9em; }
.dg-close { white-space: nowrap; }

/* the inbound cart on a draft card (ADR-0024 §7b.13): the supplier's own cart, read in */
.po-cart { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .5rem; }
.po-cart .po-source-cart { color: var(--brand-blue); font-weight: 700; }
.po-in-cart { color: var(--brand-blue); font-size: .85em; }
.po-empty-draft { color: var(--muted); font-style: italic; }

/* The supplier lens in the dense grid: the same order seen per supplier; a chip narrows the view,
   it never splits the order. */
.dg-lens { display: inline-flex; flex-wrap: wrap; gap: .3rem; align-items: center; }
.dg-chip { font: inherit; font-size: .82rem; padding: .1rem .55rem; border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--ink); cursor: pointer; display: inline-flex; gap: .35rem; align-items: baseline; }
.dg-chip b { font-weight: 700; }
.dg-chip small { color: var(--muted); font-size: .75em; }
.dg-chip.on { border-color: var(--brand-blue, var(--accent)); box-shadow: inset 0 0 0 1px var(--brand-blue, var(--accent)); }
/* ── The shelf picker: a catalogue's levels, as a control among the filters ──────────────
   It began as a rail down the side of the rows and that was wrong twice: it put a band between
   the order bar and the delivery date that order is FOR — two halves of one act with a navigation
   control wedged between them — and it took 250px from the rows permanently, on the very surface
   whose whole point is rows. Narrowing by level is the same KIND of act as narrowing by štítek, so
   it is the same kind of control: a popover on a button, built exactly like the column picker
   beside it. The button says where the reader is standing, so the closed state answers rather than
   merely offers. */
.cat-tree { position: relative; display: inline-flex; }
.cat-tree-open { max-width: 18rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cat-tree-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; }

.cat-tree-body { display: none; }
.cat-tree.is-shown .cat-tree-body {
    position: absolute;
    left: 0;
    top: calc(100% + 4px);
    z-index: 61;
    display: block;
    width: max-content;
    min-width: 16rem;
    max-width: min(24rem, calc(100vw - 32px));
    max-height: min(60vh, 32rem);
    overflow: auto;
    padding: .55rem .75rem;
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
}
/* No filter row to stand in (the cards surface): the button sits on its own above them. */
.shelf-picker-alone { margin-bottom: var(--gap); }

.cat-tree-find { margin-bottom: 8px; }
.cat-tree-input {
    width: 100%;
    box-sizing: border-box;
    font: inherit;
    font-size: .9rem;
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 6px 9px;
}
.cat-tree-input::placeholder { color: var(--muted); }
.cat-tree-none { color: var(--muted); font-size: .85rem; margin: 6px 2px; }

.cat-tree-list { list-style: none; margin: 0; padding: 0; }
.cat-tree-list ul { list-style: none; margin: 0; padding: 0; }
.cat-tree-row { display: flex; align-items: stretch; gap: 2px; }

.cat-tree-twist {
    flex: 0 0 auto;
    width: 18px;
    margin-left: calc(var(--depth, 0) * 12px);
    background: none;
    border: 0;
    padding: 0;
    color: var(--muted);
    font-size: .7rem;
    line-height: 1;
}
.cat-tree-twist.is-twist { cursor: pointer; transition: transform .15s var(--ease); }
.cat-tree-twist.is-open { transform: rotate(90deg); }

.cat-tree-node {
    flex: 1 1 auto;
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    text-align: left;
    font: inherit;
    font-size: .88rem;
    color: var(--ink);
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    padding: 4px 7px;
    cursor: pointer;
}
.cat-tree-node:hover { background: var(--chip-bg); }
.cat-tree-node.is-on { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.cat-tree-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cat-tree-count {
    margin-left: auto;
    flex: 0 0 auto;
    font-size: .74rem;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}
.cat-tree-node.is-on .cat-tree-count { color: var(--accent-ink); opacity: .85; }

@media (max-width: 700px) {
    /* A phone has no room for a popover beside anything: it fills the width under its button. */
    .cat-tree { display: flex; }
    .cat-tree-open { max-width: none; width: 100%; }
    .cat-tree.is-shown .cat-tree-body { max-width: calc(100vw - 32px); min-width: 0; right: 0; }
}

/* ── THE TRADE BUYER'S TABLE (25 Sep 2026, the vendor-client survey): marks as chips, promotion badges and rows, the
   stock state against what is entered, entry in any unit, the campaigns' strip and panel, the order's measure, saved
   views, the camera. Skin tokens only (--green/--warn/--danger/--ochre per theme), so all four themes and every brand
   skin read it; nothing above is touched. ─────────────────────────────────────────────────────────────────────── */

/* the mark chips: one row, grouped by kind, each kind its own quiet colour */
.dg-marks { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .35rem; }
.dg-marks-kind { color: var(--muted); font-size: .75rem; letter-spacing: .04em; text-transform: uppercase; margin-left: .35rem; }
.dg-marks-kind:first-child { margin-left: 0; }
.dg-chip.mk-line { border-color: color-mix(in srgb, var(--green) 45%, var(--line)); }
.dg-chip.mk-promo { border-color: color-mix(in srgb, var(--ochre) 70%, var(--line)); background: color-mix(in srgb, var(--ochre) 12%, var(--card)); }
.dg-chip.mk-camp { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
.dg-chip.mk-state { border-style: dashed; }
.dg-chip.on.mk-line { box-shadow: inset 0 0 0 1px var(--green); border-color: var(--green); }
.dg-chip.on.mk-promo { box-shadow: inset 0 0 0 1px var(--ochre); border-color: var(--ochre); }
.dg-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* badges beside a name — the name ellipsises, the badges stay */
.dg-name-in { display: flex; align-items: center; gap: .3rem; min-width: 0; }
.dg-name-in .dg-nm { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dg-name-in .dg-badge { flex: none; margin-left: 0; }
.dg-badge {
    display: inline-block; vertical-align: middle; margin-left: .3rem; padding: 0 .4rem; border-radius: 999px;
    font-size: .72em; font-weight: 700; line-height: 1.45; white-space: nowrap; border: 1px solid var(--line);
    color: var(--ink); background: var(--card);
}
.dg-badge.mk-promo { background: color-mix(in srgb, var(--ochre) 30%, var(--card)); border-color: color-mix(in srgb, var(--ochre) 70%, transparent); }
.dg-badge.mk-state { color: var(--muted); }
.dg-badge.mk-line { border-color: color-mix(in srgb, var(--green) 45%, var(--line)); }
.dg-badge.mk-camp { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }

/* a promoted row: the vendor client's „bold on yellow", in the skin's own ochre */
.dg-row.dg-promo td { background: color-mix(in srgb, var(--ochre) 11%, transparent); }
.dg-row.dg-promo td.dg-name { font-weight: 700; }
.dg-row.dg-promo:hover td { background: color-mix(in srgb, var(--ochre) 18%, transparent); }

/* a price with more to read: a small mark after it */
.dg-offer::after { content: "%"; margin-left: .25em; font-size: .72em; font-weight: 800; color: var(--warn); vertical-align: super; }

/* the stock state: a dot, coloured by what the stock does for the entered quantity */
.dg-dot { display: inline-block; width: .55em; height: .55em; border-radius: 50%; margin-right: .35em; vertical-align: middle; background: var(--line); }
.dg-st-enough .dg-dot { background: var(--green); }
.dg-st-partial .dg-dot { background: var(--warn); }
.dg-st-none .dg-dot { background: var(--danger); }
td.dg-st-partial { color: var(--warn); font-weight: 600; }
.dg-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* entry in any unit: the quantity and its unit side by side in one 34px row */
.dg select.dg-unit { height: 26px; padding: 0 2px; margin-left: .2rem; font-size: .85em; max-width: 5.2em; vertical-align: middle; }
.dg td.dg-entered:has(> select.dg-unit) input.dg-in { width: 4.2em; }

/* a phone: the chips scroll sideways in one line, the name keeps its room (the row's tint still says „akce"), and the
   unit picker steps aside so Název + Cena + Zadáno fit the screen — a quantity typed there counts pieces */
@media (max-width: 760px) {
    .dg-marks { flex-wrap: nowrap; overflow-x: auto; padding-bottom: .15rem; scrollbar-width: thin; }
    .dg-marks > * { flex: none; }
    .dg-name-in .dg-badge { display: none; }
    .dg select.dg-unit { display: none; }
    .dg td.dg-entered:has(> select.dg-unit) input.dg-in { width: 5em; }
    .dg-legend { display: none; }
}

/* the order's measure, the legend and the freshness in the footer */
.dg-sum { font-size: .88em; color: var(--muted); font-variant-numeric: tabular-nums; }
.dg-sum b { color: var(--ink); }
.dg-short { color: var(--warn); font-weight: 600; }
.dg-legend { display: inline-flex; gap: .7rem; font-size: .78em; color: var(--muted); white-space: nowrap; }
.dg-fresh { font-size: .8em; color: var(--muted); white-space: nowrap; }
.dg-promos.on { box-shadow: inset 0 0 0 1px var(--ochre); }
.dg-scan { padding-inline: .55rem; }

/* saved views: the column picker's popover, with a name field */
.dg-views { min-width: 15rem; }
.dg-view-row { display: flex; align-items: center; gap: .3rem; }
.dg-view-apply { all: unset; flex: 1 1 auto; cursor: pointer; padding: .15rem .25rem; border-radius: 6px; }
.dg-view-apply:hover, .dg-view-apply:focus-visible { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.dg-view-del { all: unset; cursor: pointer; color: var(--muted); padding: 0 .3rem; }
.dg-view-del:hover, .dg-view-del:focus-visible { color: var(--danger); }
.dg-view-save { display: flex; gap: .3rem; margin-top: .35rem; padding-top: .4rem; border-top: 1px solid var(--line); }
.dg-view-save input { flex: 1 1 auto; min-width: 0; }

/* the campaigns: the strip under the filters, the panel on demand */
.pp-strip { display: flex; flex-direction: column; gap: .2rem; }
.pp-line {
    all: unset; cursor: pointer; display: flex; flex-wrap: wrap; align-items: baseline; gap: .15rem .45rem;
    padding: .2rem .55rem; border-radius: 8px; font-size: .86em;
    background: color-mix(in srgb, var(--ochre) 10%, var(--card)); border: 1px solid color-mix(in srgb, var(--ochre) 45%, var(--line));
}
.pp-line.ok { background: color-mix(in srgb, var(--green) 10%, var(--card)); border-color: color-mix(in srgb, var(--green) 45%, var(--line)); }
.pp-line:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pp-kind { font-size: .72em; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.pp-dim { color: var(--muted); font-size: .9em; }
.pp-soon { color: var(--warn); }
/* THE PANEL IS OPENED TO BE READ (Frank, 26 Sep 2026: „very short"). As a child of the grid's column it was SHRUNK like
   one — to 170 px of a 516 px list at 1400 × 900 — because the rows box below would not give up its 18rem. Now it takes
   its own height, up to a share of the SCREEN (never less than a row of cards), and scrolls inside past that; while it
   is open the rows box gives way to a smaller floor, so the rows „Zobrazit zboží" narrows stay in sight under it. On a
   short screen the grid's gallery scrolls rather than the panel being squeezed shut. (A phone's is a sheet — below.) */
.pp-panel {
    flex: 0 0 auto; border: 1px solid var(--line); border-radius: var(--radius-sm, 8px); background: var(--panel);
    padding: .55rem .7rem; display: flex; flex-direction: column; gap: .55rem;
    max-height: clamp(15rem, 42vh, 34rem); overflow: auto; overscroll-behavior: contain;
}
.dg:has(> .pp-panel) > .dg-scroll { min-height: 9rem; }
.pp-head { display: flex; align-items: center; gap: .5rem; }
.pp-head h3 { margin: 0; font-family: var(--font-display); font-size: 1.1em; }
.pp-groups { display: flex; flex-wrap: wrap; gap: .3rem; }
.pp-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: .55rem; }
.pp-card { border: 1px solid var(--line); border-radius: 10px; background: var(--card); padding: .55rem .65rem; display: flex; flex-direction: column; gap: .3rem; font-size: .88em; }
.pp-card.ok { border-color: color-mix(in srgb, var(--green) 55%, var(--line)); }
.pp-card header { display: flex; flex-direction: column; gap: .1rem; }
.pp-card h4 { margin: 0; font-size: 1em; }
.pp-card p { margin: 0; }
.pp-card footer { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: auto; }
.pp-levels { margin: 0; padding-left: 1.1rem; }
.pp-levels li.on { font-weight: 700; color: var(--green); }
.pp-bar { height: 6px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 10%, transparent); overflow: hidden; }
.pp-bar span { display: block; height: 100%; background: var(--ochre); }
.pp-card.ok .pp-bar span { background: var(--green); }
.pp-status { font-weight: 600; }

/* the product detail's trade facts */
.tf { display: flex; flex-direction: column; gap: .4rem; margin-top: .4rem; }
.tf-facts { display: grid; grid-template-columns: max-content 1fr; gap: .15rem .7rem; margin: 0; font-size: .9em; }
.tf-facts dt { color: var(--muted); }
.tf-facts dd { margin: 0; display: flex; flex-wrap: wrap; gap: .2rem .6rem; }
.tf-code, .tf-ean { font-variant-numeric: tabular-nums; }
.tf-place b { font-variant-numeric: tabular-nums; }
.tf-units { border-collapse: collapse; font-size: .88em; }
.tf-units th, .tf-units td { padding: .15rem .6rem .15rem 0; text-align: left; border-bottom: 1px solid var(--line); }
.tf-units .tf-num { text-align: right; font-variant-numeric: tabular-nums; }
.tf-sub { margin: .3rem 0 0; font-size: .85em; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.tf-story, .tf-camps { margin: 0; padding-left: 1.1rem; font-size: .9em; display: flex; flex-direction: column; gap: .15rem; }
.tf-marks { display: flex; flex-wrap: wrap; gap: .25rem; }
.tf-marks .dg-badge { margin-left: 0; }
.tf-dim { color: var(--muted); }

/* the camera's viewfinder: over everything, closed by its button or Esc */
.dg-scanner {
    position: fixed; inset: 0; z-index: 1000; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .8rem; background: rgba(0, 0, 0, .86); color: #fff;
}
.dg-scanner video { width: min(92vw, 640px); max-height: 70vh; border-radius: 12px; background: #000; object-fit: cover; }
.dg-scanner-frame { position: absolute; width: min(70vw, 420px); height: min(26vw, 160px); border: 2px solid rgba(255, 255, 255, .8); border-radius: 10px; pointer-events: none; }
.dg-scanner p { margin: 0; font-size: 1rem; }
.dg-scanner button { font: inherit; padding: .5rem 1.4rem; border-radius: 999px; border: 0; background: #fff; color: #111; cursor: pointer; }
.dg-scanner button:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

/* ════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   THE PHONE (26 Sep 2026 — the owner could test only on his phone, and found the storefront unusable there).
   Every surface of the shop, laid out for a thumb. Nothing here is a second design: it is the same components
   under a phone's constraints. ONE block, appended, with every selector scoped under .shop, so a wide-screen rule
   added above it later cannot quietly undo it. Nothing in it changes the wide screen — each rule sits under a phone query:
     • ≤980px — the assistant is DOCKED at the bottom (the storefront's own breakpoint);
     • ≤760px — a phone held upright (the storefront's mobile breakpoint);
     • the grid's PHONE query — ≤760px, or ≤980 × ≤500px (a phone held sideways) — DenseOrderGrid.PhoneQuery, which
       storefront.watchNarrow reports to the component, so its column rule and these cards agree on the screen.
   The rules before the first query are the few wide-screen defaults that keep the phone's controls out of the way.
   ════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */

/* A wide screen: the phone's filter sheet is layout-transparent — its filters stand in the toolbar's row as before, and
   the sheet's sort list stays out of it (the headers sort there). */
.dg-filters { display: contents; }
.dg-filters-btn, .dg-filters-head, .dg-filters-backdrop, .pp-backdrop, .dg-sort-pick { display: none; }
/* A dialog or sheet opened from inside the shop window rises above the header and the docked assistant: .gallery's
   own stacking layer (z 1) would otherwise hold it underneath both — the same trap as the lightbox and the pickers. */
.shop:has(.os-modal-backdrop) .gallery, .shop:has(.dg-filters-open) .gallery, .shop:has(.pp-backdrop) .gallery { z-index: auto; }
@keyframes sheetUp { from { opacity: 0; transform: translateY(28px); } to { opacity: 1; transform: none; } }

/* ── Fields never zoom the page: a phone's browser enlarges any field set under 16px when it takes focus. ───── */
@media (max-width: 980px) {
    .shop .shelf-filter, .shop .shelf-sort, .shop .dg input:not([type="checkbox"]):not([type="radio"]), .shop .dg select,
    .shop .cat-tree-input, .shop .vp-qty input, .shop .ot-name, .shop .on-input, .shop .sw-modify input,
    .shop .sw-chat-composer input, .shop .po-date-pick, .shop .po-date-in,
    .shop .desk-page input:not([type="checkbox"]):not([type="radio"]), .shop .desk-page select, .shop .desk-page textarea {
        font-size: 16px;
    }
}

/* ── The docked assistant (≤980px): its grip and the folder tabs share ONE row, the composer is compact, every control
   a thumb's size. Folded it is two short rows (~100px) instead of three (~170px) — the rows above get the screen. ── */
@media (max-width: 980px) {
    .shop .aisle {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-rows: auto minmax(0, 1fr);
        grid-template-areas: "grip tabs" "chat chat";
    }
    .shop .aisle > .chat-toggle {
        grid-area: grip; display: block; min-height: 44px; padding: 0 6px 0 var(--gap); line-height: 44px; text-align: left;
        font-size: .9rem; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
        container-type: inline-size;
    }
    .shop .aisle > .folder-tabs {
        grid-area: tabs; align-items: center; gap: 6px; padding: 0 var(--gap) 0 0;
        border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--panel) 86%, transparent);
    }
    .shop .aisle > .folder-tabs .folder-tab { flex: none; min-height: 36px; padding: 6px 12px; font-size: .78em; letter-spacing: .06em; }
    .shop .aisle > .chat { grid-area: chat; min-height: 0; }
    .shop .composer { gap: 8px; padding: 8px var(--gap); padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px)); }
    .shop .composer input { padding: 11px 14px; }
    .shop .composer .btn { padding: 11px 16px; }
    /* the short confirmations rise above the dock rather than onto its composer; the buyer's name lives in the menu */
    .shop .toast { bottom: calc(var(--dock-h, 110px) + 12px); max-width: calc(100vw - 32px); text-align: center; }
    .shop .toast.identity-chip { display: none; }
    /* the way up — on a phone the only crumb shown — is a thumb's target, not a line of small text */
    .shop .crumbs .crumb.is-up { min-height: 40px; padding: 8px 14px 8px 0; }
    .shop .shop-foot-link { min-height: 36px; }
    .shop .gallery { padding-right: 0; }
}
/* The grip beside three folder tabs on a narrow phone has room for its arrow only: an ellipsis where the assistant's
   name was reads as broken, and the name stands in the composer's placeholder right under it. */
@container (max-width: 110px) {
    .chat-toggle-name { display: none; }
}
/* A phone held sideways has ~375px of height: folded, the dock is ONE row — grip, tabs, composer; opened, the
   conversation may take most of the screen, because the buyer opened it to converse. */
@media (max-width: 980px) and (max-height: 500px) {
    .shop.chat-collapsed .aisle { grid-template-columns: auto auto minmax(0, 1fr); grid-template-rows: auto; grid-template-areas: "grip tabs chat"; }
    .shop.chat-collapsed .aisle > .chat-toggle { max-width: 30vw; border-bottom: 0; container-type: normal; }
    .shop.chat-collapsed .aisle > .folder-tabs { border-bottom: 0; background: none; }
    .shop.chat-collapsed .composer { border-top: 0; padding-top: 6px; padding-bottom: 6px; }
    .shop:not(.chat-collapsed) .aisle { max-height: 80vh; }
    .shop .lb-stage { min-height: 0; }
}

/* ── The working order, the Dlaždice/Tabulka switch, the templates and the reminders (≤760px) ─────────────────── */
@media (max-width: 760px) {
    .shop .wo-bar { gap: .45rem; padding: .45rem; }
    .shop .wo-pick { flex: 1 1 100%; min-width: 0; }
    .shop .wo-pick > span { display: none; }                      /* the select names itself (aria-label) */
    /* min-width: 0, or a long order name (a pre-order's campaign) widened the select and the page past the screen */
    .shop .wo-select { flex: 1 1 auto; width: 100%; min-width: 0; max-width: none; min-height: 44px; }
    .shop .wo-bar > .btn, .shop .wo-due .btn, .shop .ot .btn { min-height: 40px; padding: 8px 12px; font-size: .9rem; }
    /* the running total is the select's own words („… · 6 pol. · 3 290,22 Kč"); a third row repeating it is room lost */
    .shop .wo-spacer, .shop .wo-total { display: none; }
    .shop .wo-note .tray-close, .shop .ot-head .tray-close { min-width: 40px; min-height: 40px; }
    .shop .dense-toggle-row { padding: 0; margin: 0 0 .35rem; }
    .shop .dense-toggle-row .btn { min-height: 38px; padding: 6px 14px; font-size: .88rem; }
    /* the shelf's filter takes its own line; its narrowers and its sort share the next one */
    .shop .shelf-tools .shelf-filter { flex: 1 1 100%; min-height: 40px; }
    .shop .shelf-tools .shelf-sort { flex: 1 1 0; min-width: 0; max-width: none; min-height: 40px; }
}

/* ── THE DENSE GRID ON A PHONE — the trade buyer's table ─────────────────────────────────────────────────────────────
   One scroll, the page's — no box inside a box. The toolbar (search, camera, quantity, „Filtry") is pinned over the
   rows; the other filters, the column and view pickers and the campaigns open as SHEETS from the bottom, where the
   thumb is. Each row is a CARD: the whole name (two lines) and under it what the buyer chose to read (price, code,
   stock…), with the quantity — and its unit — in a column at the right edge. The header row becomes the sort bar.
   Virtualize keeps working: with no overflowing ancestor it watches the viewport. ─────────────────────────────── */
@media (max-width: 760px), (max-width: 980px) and (max-height: 500px) {
    .shop .dense-grid.dg { height: auto; padding: 0; gap: .55rem; font-size: 1rem; }
    .shop .dg .btn { min-height: 40px; }
    .shop .dg-scroll, .shop .dg-scroll:has(> .dg-table) { height: auto; min-height: 0; overflow: visible; border: 0; border-radius: 0; background: none; }
    .shop .dg-order { gap: .5rem; }
    .shop .dg-order-pick { white-space: normal; }
    .shop .dg-date-in { min-height: 40px; }

    /* the toolbar, pinned — no backdrop-filter: it would make the fixed sheet inside position against the bar */
    .shop .dg-tools {
        position: sticky; top: 0; z-index: 6; flex-wrap: nowrap; gap: .4rem;
        margin: 0 calc(-1 * var(--gap)); padding: .5rem var(--gap);
        background: var(--bg); border-bottom: 1px solid var(--line); box-shadow: 0 8px 14px -12px rgba(0, 0, 0, .45);
    }
    .shop .dg-tools > .dg-quick { flex: 1 1 0; min-width: 6rem; height: 44px; }
    .shop .dg-tools > .dg-qty { flex: none; width: 3.4rem; height: 44px; padding: 0 4px; text-align: center; }
    .shop .dg-tools > .dg-scan { flex: none; width: 44px; height: 44px; padding: 0; }
    .shop .dg-tools > .dg-filters-btn { display: inline-flex; align-items: center; flex: none; height: 44px; padding: 0 .85rem; }
    .shop .dg-tools > .dg-filters-btn[aria-expanded="true"] { background: color-mix(in srgb, var(--accent) 14%, transparent); }
    /* the grid's own word (a pack rounded up, a refusal) must be seen where the buyer is — under the pinned bar; a tap
       puts it away (the ×) */
    .shop .dg > p.dg-note {
        position: sticky; top: 64px; z-index: 5; margin: 0; padding: .55rem 2.6rem .55rem .75rem; border-radius: 10px;
        background: color-mix(in srgb, var(--danger) 9%, var(--card)); border: 1px solid color-mix(in srgb, var(--danger) 30%, var(--line));
        box-shadow: var(--shadow-sm); cursor: pointer;
    }
    .shop .dg > p.dg-note::after {
        content: "×"; position: absolute; top: 50%; right: .8rem; transform: translateY(-50%);
        font-size: 1.35rem; line-height: 1; color: var(--muted);
    }

    /* THE SHEET: the rest of the filters, the pickers and the campaigns' button — over the docked assistant and the
       header (z 300), under the dialogs (400+) */
    .shop .dg-tools.dg-filters-open { z-index: 350; }
    .shop .dg-filters { display: none; }
    .shop .dg-filters-open > .dg-filters {
        display: flex; flex-wrap: wrap; align-items: center; align-content: flex-start; gap: .55rem .5rem;
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 1;
        max-height: 80vh; overflow-y: auto; overscroll-behavior: contain;
        padding: .75rem var(--gap) calc(1rem + env(safe-area-inset-bottom, 0px));
        background: var(--panel); color: var(--ink);
        border-top: 1px solid var(--line); border-radius: 18px 18px 0 0; box-shadow: 0 -14px 40px rgba(0, 0, 0, .28);
        animation: sheetUp .22s var(--ease);
    }
    .shop .dg-filters-open > .dg-filters-backdrop { display: block; position: fixed; inset: 0; z-index: 0; background: rgba(0, 0, 0, .38); }
    .shop .dg-filters-head { display: flex; flex: 1 1 100%; align-items: center; justify-content: space-between; gap: .6rem; }
    .shop .dg-filters-head b { font-family: var(--font-display); font-size: 1.1rem; }
    .shop .dg-filters > select, .shop .dg-filters > .cat-tree { flex: 1 1 100%; max-width: none; min-height: 44px; }
    .shop .dg-filters > .dg-check { flex: 1 1 42%; min-height: 44px; }
    .shop .dg-filters > .dg-check input { width: 20px; height: 20px; }
    .shop .dg-filters > .dg-spacer { display: none; }
    .shop .dg-filters > .dg-pin { flex: 1 1 100%; }
    .shop .dg-filters > .btn, .shop .dg-filters > .dg-colpick { flex: 1 1 auto; }
    .shop .dg-filters > .dg-colpick > .btn { width: 100%; }
    /* the sort, by any column: the list and its direction on one line */
    .shop .dg-filters > .dg-sort-pick { display: flex; flex: 1 1 100%; align-items: flex-end; gap: .5rem; }
    .shop .dg-sort-pick > label { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .2rem; }
    .shop .dg-sort-pick > label > span { color: var(--muted); font-size: .8rem; }
    .shop .dg-sort-pick select { width: 100%; max-width: none; min-height: 44px; }
    .shop .dg-sort-pick > .dg-sort-dir { flex: none; min-height: 44px; }

    /* the pickers opened from the sheet are sheets too — their wide-screen placement (inline, beside the button) gives way */
    .shop .dg-cols-backdrop, .shop .cat-tree-backdrop { z-index: 80; background: rgba(0, 0, 0, .3); }
    .shop .dg-cols {
        position: fixed !important; top: auto !important; left: 0 !important; right: 0 !important; bottom: 0 !important;
        z-index: 81; min-width: 0; max-height: 72vh; overflow-y: auto; overscroll-behavior: contain; gap: .15rem;
        padding: .9rem var(--gap) calc(1rem + env(safe-area-inset-bottom, 0px));
        border-radius: 18px 18px 0 0; box-shadow: 0 -14px 40px rgba(0, 0, 0, .3); animation: sheetUp .22s var(--ease);
    }
    .shop .cat-tree.is-shown .cat-tree-body {
        position: fixed; top: auto; left: 0; right: 0; bottom: 0; z-index: 81;
        width: auto; min-width: 0; max-width: none; max-height: 72vh; overscroll-behavior: contain;
        padding: .9rem var(--gap) calc(1rem + env(safe-area-inset-bottom, 0px));
        border-radius: 18px 18px 0 0; box-shadow: 0 -14px 40px rgba(0, 0, 0, .3);
    }
    .shop .dg-cols label, .shop .dg-col-always { min-height: 40px; }
    .shop .dg-cols input[type="checkbox"] { width: 20px; height: 20px; }
    .shop .dg-col-move button { width: 40px; height: 40px; font-size: 1.05rem; }
    .shop .dg-cols-fixed { max-width: none; }
    .shop .dg-view-row { min-height: 44px; }
    .shop .dg-view-apply { padding: .6rem .4rem; }
    .shop .dg-view-del { display: grid; place-items: center; min-width: 44px; min-height: 44px; }
    .shop .dg-view-save input { min-height: 44px; }
    .shop .cat-tree-node { min-height: 40px; align-items: center; }
    .shop .cat-tree-twist { width: 32px; }

    /* the mark chips: one line that scrolls sideways, each chip a thumb's height */
    .shop .dg-marks { gap: .35rem; scrollbar-width: none; }
    .shop .dg-marks::-webkit-scrollbar { display: none; }
    .shop .dg-marks .dg-chip, .shop .dg-marks .btn.mini { min-height: 36px; padding: 0 .75rem; align-items: center; }

    /* the campaigns' panel is a sheet as well; its cards stand one per row */
    .shop .pp-backdrop { display: block; position: fixed; inset: 0; z-index: 360; background: rgba(0, 0, 0, .38); }
    .shop .pp-panel {
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 361; max-height: 84vh; overscroll-behavior: contain;
        padding: .9rem var(--gap) calc(1rem + env(safe-area-inset-bottom, 0px));
        border-radius: 18px 18px 0 0; box-shadow: 0 -14px 40px rgba(0, 0, 0, .3); animation: sheetUp .22s var(--ease);
    }
    .shop .pp-cards { grid-template-columns: minmax(0, 1fr); }
    .shop .pp-line { padding: .45rem .65rem; }
    .shop .pp-card .btn { min-height: 40px; }
    .shop .pp-groups .dg-chip { min-height: 36px; align-items: center; }

    /* THE ROWS AS CARDS */
    .shop .dg-table { display: block; width: 100%; min-width: 0; }
    .shop .dg-table > colgroup { display: none; }
    .shop .dg-table > thead, .shop .dg-table > tbody { display: block; }
    /* the header row is the sort bar: the sortable columns as pills; the rest, and the width grips, step aside */
    .shop .dg-table > thead > tr { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; height: auto; padding: 0 0 .55rem; }
    .shop .dg-table > thead > tr::before { content: "Řadit:"; margin-right: .1rem; color: var(--muted); font-size: .85rem; }
    .shop .dg-table th { position: static; display: block; height: auto; padding: 0; border: 0; background: none; }
    .shop .dg-table th:not(:has(.dg-sort)) { display: none; }
    .shop .dg-resize { display: none; }
    .shop .dg-sort {
        display: inline-flex; align-items: center; min-height: 36px; padding: 0 .75rem;
        border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--muted); font-size: .88rem;
    }
    .shop .dg-sort.on { color: var(--ink); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--card)); }

    .shop .dg-table > tbody > tr { display: block; }             /* Virtualize's spacers keep their stated heights */
    .shop .dg-table > tbody > tr.dg-row {
        position: relative; display: flex; flex-wrap: wrap; align-items: baseline; align-content: flex-start;
        column-gap: .75rem; row-gap: .15rem;
        height: auto; min-height: 76px; margin: 0 0 .5rem; padding: .6rem 6.6rem .6rem .8rem;
        background: var(--card); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow-sm);
    }
    .shop .dg-table > tbody > tr.dg-row:has(select.dg-unit) { min-height: 98px; }
    .shop .dg-table > tbody > tr.dg-row > td {
        display: block; height: auto; padding: 0; border: 0; background: none;
        overflow: visible; white-space: nowrap; text-align: left; font-size: .88rem; line-height: 1.35;
    }
    /* the name cell dissolves into the card: its name heads it (two lines at most), its badges join the line under it */
    .shop .dg-table > tbody > tr.dg-row > td.dg-name, .shop .dg-table > tbody > tr.dg-row .dg-name-in { display: contents; }
    .shop .dg-table > tbody > tr.dg-row .dg-nm {
        order: -2; flex: 1 1 100%; margin-bottom: .15rem; font-size: 1rem; line-height: 1.28;
        white-space: normal; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
    }
    .shop .dg-table > tbody > tr.dg-row .dg-badge { order: -1; display: inline-block; margin: 0; }
    .shop .dg-table > tbody > tr.dg-row > td.dg-text { white-space: normal; }
    .shop .dg-table > tbody > tr.dg-row > td.dg-code, .shop .dg-table > tbody > tr.dg-row > td.dg-text { color: var(--muted); }
    .shop .dg-table > tbody > tr.dg-row > td.dg-price { font-size: .96rem; font-weight: 700; color: var(--ink); }
    /* the quantity — and its unit — in a column at the card's right edge; a tap there is typing, never the detail */
    .shop .dg-table > tbody > tr.dg-row > td.dg-entered {
        position: absolute; top: 0; bottom: 0; right: .6rem; width: 5.4rem;
        display: flex; flex-direction: column; justify-content: center; align-items: stretch; gap: 5px;
    }
    .shop .dg tr.dg-row td.dg-entered input.dg-in {
        width: 100%; height: 44px; padding: 0 6px; border-radius: 10px; text-align: center; font-size: 17px; font-weight: 600;
    }
    .shop .dg tr.dg-row td.dg-entered select.dg-unit { display: block; width: 100%; max-width: none; height: 36px; margin: 0; padding: 0 4px; }
    .shop .dg-table > tbody > tr.dg-row.dg-promo { background: color-mix(in srgb, var(--ochre) 10%, var(--card)); }
    .shop .dg-table > tbody > tr.dg-row.on {
        border-color: color-mix(in srgb, var(--accent) 60%, var(--line)); box-shadow: inset 4px 0 0 var(--accent), var(--shadow-sm);
    }
    .shop .dg-table > tbody > tr.dg-empty > td { display: block; padding: 1.6rem .5rem; border: 0; }

    /* the foot: the order's measure first, then the file, the freshness and the repeat */
    .shop .dg-foot { gap: .5rem .7rem; padding: .2rem 0 .6rem; }
    .shop .dg-foot > .dg-spacer { display: none; }
    .shop .dg-foot > .dg-sum { flex: 1 1 100%; order: -1; font-size: .95rem; }
    .shop .dg-legend { flex-wrap: wrap; white-space: normal; }
    .shop .dg-clone { flex: 1 1 auto; min-width: 0; max-width: none; min-height: 44px; }
}
/* Held sideways the card has width to spare and no height: the quantity and its unit stand side by side, and a card
   is one line of name and one of facts. */
@media (min-width: 761px) and (max-width: 980px) and (max-height: 500px) {
    .shop .dg-table > tbody > tr.dg-row, .shop .dg-table > tbody > tr.dg-row:has(select.dg-unit) { min-height: 62px; padding-right: 12.2rem; }
    .shop .dg-table > tbody > tr.dg-row > td.dg-entered { width: 11.2rem; flex-direction: row; align-items: center; }
    .shop .dg tr.dg-row td.dg-entered input.dg-in { flex: 0 0 5.4rem; width: 5.4rem; }
    .shop .dg tr.dg-row td.dg-entered select.dg-unit { flex: 1 1 auto; width: auto; height: 44px; }
}

/* ── The product's detail (≤760px): the picture stays in view at the top — smaller, and small when there is no
   photo — and the details scroll beneath it with the price and „Dát stranou" pinned at the bottom. It used to be a
   two-row grid whose first row collapsed to nothing, so the picture was drawn OVER the name, the badges and the
   first facts of a long product (the trade facts made most products long). ─────────────────────────────────── */
@media (max-width: 760px) {
    .shop .pc-lightbox { padding: 10px; }
    .shop .pc-lightbox-dialog:not(.has-configurator) {
        display: flex; flex-direction: column; overflow: hidden;
        max-height: 94vh; max-height: calc(100dvh - 20px);
    }
    .shop .pc-lightbox-dialog:not(.has-configurator) > .lb-stage {
        flex: none; min-height: 0; height: min(32vh, 260px); max-height: none; padding: .5rem;
    }
    .shop .pc-lightbox-dialog:not(.has-configurator) > .lb-stage:has(> .pc-glyph) { height: 108px; }
    .shop .pc-lightbox-dialog:not(.has-configurator) > .lb-stage img { max-height: 100%; }
    .shop .pc-lightbox-dialog:not(.has-configurator) > .lb-panel {
        flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 14px 16px 0;
    }
    .shop .lb-title { font-size: 1.28rem; }
    .shop .lb-actions { padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px)); }
    .shop .lb-add { min-height: 48px; }
    .shop .lb-close { width: 44px; height: 44px; top: 8px; right: 8px; }
    .shop .vp-step { width: 40px; height: 40px; }
    .shop .vp-qty input { height: 40px; }
    .shop .vp-option { min-height: 40px; }
}

/* ── A shelf of products (≤760px): two cards across, as every shop on a phone shows them — one card was a whole
   screen tall, so a shelf of ten was ten screens. ─────────────────────────────────────────────────────────────── */
@media (max-width: 760px) {
    .shop .card-grid { grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 10px; }
    .shop .card-grid .pc-body { padding: 9px 10px 11px; gap: 4px; }
    .shop .card-grid .pc-name { font-size: .95rem; overflow-wrap: anywhere; }
    .shop .card-grid .pc-price { font-size: 1.02rem; }
    .shop .card-grid .pc-foot { gap: 6px; }
    .shop .card-grid .pc-add { flex: none; width: 42px; height: 42px; }
}

/* ── The basket tray (≤760px): a line's name takes the whole width, its stepper and sum go under it. ──────────── */
@media (max-width: 760px) {
    .shop .order-tray { width: 100vw; }
    .shop .tray-line { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name name" "step sum"; gap: 6px 10px; }
    .shop .tray-line > .tl-name { grid-area: name; }
    .shop .tray-line > .tl-stepper { grid-area: step; }
    .shop .tray-line > .tl-sum { grid-area: sum; align-self: center; }
    .shop .tl-step, .shop .tl-del { width: 40px; height: 40px; font-size: 1.1rem; }
    .shop .tray-close { min-width: 44px; min-height: 44px; }
    .shop .tray-order-del { width: 40px; height: 40px; }
    .shop .tray-order-select { min-height: 36px; }
}

/* ── The orders panel (≤760px): a row never runs past the screen, a line's name takes the width with its stepper
   under it, and the order's head holds its files on one line. ───────────────────────────────────────────────── */
@media (max-width: 760px) {
    .shop .orders-panel { padding: 4px 0 24px; }
    .shop .os-row-wrap .os-row { min-width: 0; }
    .shop .os-row { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "label total" "meta go"; padding: .7rem .8rem; }
    .shop .os-label, .shop .os-meta { min-width: 0; overflow-wrap: anywhere; }
    .shop .os-del { width: 44px; }
    .shop .od-line { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name name" "qty sum"; gap: .45rem .8rem; padding: .7rem .1rem; }
    .shop .od-line > .od-line-name { grid-area: name; }
    .shop .od-line > .od-stepper, .shop .od-line > .od-qty { grid-area: qty; }
    .shop .od-line > .od-sum { grid-area: sum; }
    .shop .od-stepper .tl-step, .shop .od-stepper .tl-del, .shop .oa .tl-del { width: 40px; height: 40px; font-size: 1.1rem; }
    /* a row the order cannot have: its name with its × on one line, each stand-in a full-width choice under it —
       wrapped as a flex row, the × fell onto a line of its own, far from the row it takes out */
    .shop .oa-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: .35rem .5rem; }
    .shop .oa-row > .oa-name { grid-column: 1; grid-row: 1; }
    .shop .oa-row > .oa-del { grid-column: 2; grid-row: 1; margin: 0; }
    .shop .oa-row > .oa-sub { grid-column: 1 / -1; min-height: 40px; text-align: left; }
    .shop .od-foot > .od-checkout { flex: 1 1 100%; min-height: 48px; }
    .shop .od-delete { margin-right: 0; }
}
@media (max-width: 980px) {
    /* the order's head: the back button, then the name and its count on a line of their own, then the files on one
       line — squeezed between the buttons, „12 položek · rozpracovaná" stood one word per line and ran under
       „Importovat"; stacked in a column (the old phone rule), every button took a row of its own */
    .shop .od-head { flex-direction: row; flex-wrap: wrap; align-items: center; }
    .shop .od-head > .od-title { flex: 1 1 100%; }
}
@media (max-width: 760px), (max-width: 980px) and (max-height: 500px) {
    /* on a phone the foot (total, delete, send) ends the order like a receipt: pinned, it covered a third of the
       screen — held sideways, most of it — and over an open import it hid the lines being reviewed */
    .shop .od-foot { position: static; }
}
@media (min-width: 761px) and (max-width: 980px) and (min-height: 501px) {
    /* a tablet keeps it pinned — above the docked assistant, where it was pinned behind it */
    .shop .od-foot { bottom: var(--dock-h, 110px); z-index: 2; padding-bottom: .7rem; background: var(--bg); }
}
/* A phone held sideways opens a product's detail as a wide sheet: the picture beside the details, smaller, and the
   details' own scroll ending AT the price bar — the panel's bottom padding used to leave a band of text under it. */
@media (max-width: 980px) and (max-height: 500px) {
    .shop .pc-lightbox { padding: 8px; }
    .shop .pc-lightbox-dialog:not(.has-configurator) { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); max-height: calc(100dvh - 16px); }
    .shop .pc-lightbox-dialog:not(.has-configurator) > .lb-panel { padding: 14px 18px 0; }
    .shop .lb-title { font-size: 1.2rem; padding-right: 48px; }
    .shop .lb-actions { padding-bottom: 8px; }
    .shop .pc-lightbox-dialog.has-configurator { height: calc(100dvh - 16px); }
    /* four cards across rather than three screen-tall ones */
    .shop .card-grid { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 12px; }
}

/* THE ASSISTANT SWITCHED OFF ON ITS PLATFORM (Frank, 9 Oct 2026: a feature's surface obeys its switch — off, it is not
   there). The shop keeps selling without a grip, a stage or a conversation: the rail keeps the shop's own sections (the
   folder tabs, as the folded rail keeps them) and goes entirely when there are none (.no-aisle); the phone's dock holds the
   tabs alone, and reserves no room when it is gone. The markup decides what is rendered (FeatureGate); these rules only
   lay out what remains. */
@media (min-width: 981px) {
    .shop.no-aisle { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 980px) {
    .shop.no-assistant .aisle { grid-template-columns: minmax(0, 1fr); grid-template-areas: "tabs"; }
    .shop.no-aisle .window { padding-bottom: 24px; }
}
.desk-contact { color: var(--muted, #555); }

/* THE CONVERSATION ALONE (/asistent — what a static site's bubble opens, StaticSite): the assistant's rail is the whole page, and the
   phone's bottom dock is the page itself — never docked over nothing. The shop's window is drawn only while it holds something the
   conversation put there (.conversation-shows): under the chat, without the shop's chrome, so what the assistant shows is seen. */
.shop.conversation { grid-template-columns: minmax(0, 1fr) !important; }
.shop.conversation:not(.conversation-shows) > .window { display: none !important; }
.shop.conversation.conversation-shows { grid-template-rows: auto auto; }
.shop.conversation.conversation-shows > .window { display: block !important; max-height: 46vh; overflow: auto; padding-bottom: 16px;
    border-top: 1px solid var(--line, rgba(127,127,127,.25)); }
.shop.conversation.conversation-shows > .window > .topbar { display: none !important; }
.shop.conversation.conversation-shows .aisle { min-height: 54vh; max-height: 54vh !important; height: 54vh; }
.shop.conversation .aisle { position: relative !important; inset: auto !important; display: flex !important; flex-direction: column !important;
    min-height: 100vh; max-height: 100vh !important; height: 100vh; border: 0 !important; box-shadow: none !important; }
.shop.conversation .chat-toggle { display: none !important; }
.shop.conversation .chat-scroll { display: block !important; }
.shop.conversation .aisle > .chat { flex: 1 1 auto; min-height: 0; }
