/* ==========================================================================
   U Jabka — Soda design system: COMPONENTS
   Reusable recipes shared by every redesigned page. Page-specific layout lives
   in soda/<page>.css, owned by that page's agent; this file is integrator-owned.

   Geometry, colour and easing are transcribed from the approved reference.
   Additions beyond the reference are marked NEW and exist because the reference
   is a prototype: it has no focus styles, no form states, no disabled states,
   no error surfaces and no empty states.
   ========================================================================== */

/* ============================================================ CTA / buttons */

html.theme-soda .soda-btn {
    display: inline-flex;
    align-items: center;
    gap: 1.4rem;
    width: fit-content;
    padding: .42rem .42rem .42rem 1.6rem;
    border: 1px solid var(--btn-border);
    border-radius: var(--r-control);
    background: var(--btn-bg);
    color: var(--ink);
    font-family: var(--font-display);
    font-size: .92rem;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    -webkit-backdrop-filter: blur(var(--blur-glass));
    backdrop-filter: blur(var(--blur-glass));
    /* NOTHING SCALES AND NOTHING LIFTS. A control that grows or rises under the
       cursor is a tell that the motion was added for its own sake, and it
       shoulders whatever sits beside it. Vesper responds in colour and weight
       only: the surface takes light, the border firms up, the ink shifts. The
       box never moves, so nothing reflows. (docs/vesper/src/lib/springs/
       interaction.ts — "Nothing scales".) */
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

html.theme-soda .soda-btn:hover {
    background: var(--btn-bg-hover);
    border-color: var(--line-hi);
}

html.theme-soda .soda-btn:active {
    /* A press is a confirmation, not an animation — Vesper's press spring is
       near-instant (t700/f26), so this resolves in --dur-fast. */
    transition-duration: var(--dur-fast);
}

/* The accent circle-arrow. Decorative glyph — the label carries the meaning. */
/* The Vesper CTA is a block plus a SQUARE tile beside it, never a pill with an
   inline circle. The 50% here predated the radius token and had to go by hand. */
html.theme-soda .soda-btn__arrow {
    display: grid;
    place-items: center;
    flex: none;
    width: 38px;
    height: 38px;
    border-radius: var(--r-mark);
    background: var(--accent);
    color: var(--accent-ink);
    font-size: 1.05rem;
    font-weight: 500;
    line-height: 1;
    transition: background var(--dur) var(--ease);
}

/* Ghost variant */
html.theme-soda .soda-btn--ghost {
    padding: .3rem .3rem .3rem 1.25rem;
    gap: .9rem;
    font-size: .85rem;
    background: var(--panel);
    border: 1px solid var(--glass-border);
    -webkit-backdrop-filter: blur(var(--blur-sm));
    backdrop-filter: blur(var(--blur-sm));
}

html.theme-soda .soda-btn--ghost:hover {
    background: var(--panel-hi);
    border-color: var(--line-hi);
}

html.theme-soda .soda-btn--ghost .soda-btn__arrow {
    width: 30px;
    height: 30px;
    font-size: .85rem;
}

/* Plain pill, no arrow circle. */
html.theme-soda .soda-btn--plain {
    padding: .82rem 1.9rem;
    gap: 0;
    font-size: .82rem;
    letter-spacing: .02em;
}

/* NEW — the reference has no disabled state, but cart/checkout need one
   (e.g. "continue" while the cart holds an unavailable line). */
html.theme-soda .soda-btn[disabled],
html.theme-soda .soda-btn[aria-disabled='true'] {
    opacity: .45;
    cursor: not-allowed;
    pointer-events: none;
}

/* NEW — full-width action, used inside commerce panels. */
html.theme-soda .soda-btn--block {
    width: 100%;
    justify-content: space-between;
}

/* ================================================================== panels */

html.theme-soda .soda-panel {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--r-plane);
}

html.theme-soda .soda-panel--glass {
    background: var(--glass-bg);
    border-color: var(--glass-border);
    -webkit-backdrop-filter: blur(var(--blur-md));
    backdrop-filter: blur(var(--blur-md));
}

html.theme-soda .soda-panel--pad {
    padding: clamp(1.3rem, 2.4vw, 2rem);
}

/* ============================================================ product cards */

html.theme-soda .soda-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--panel);
    /* Preview geometry: a .12 border and a 28px radius are what let an almost
       black card still read as an object on an almost black page. */
    border: 1px solid var(--glass-border);
    border-radius: var(--r-card);
    -webkit-backdrop-filter: blur(var(--blur-glass));
    backdrop-filter: blur(var(--blur-glass));
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

html.theme-soda .soda-card:hover,
html.theme-soda .soda-card:focus-within {
    /* Mood as an accent EDGE, never as a fill — and the card does not rise. */
    border-color: rgba(var(--accent-rgb), .5);
    background: var(--panel-hi);
}

html.theme-soda .soda-card__media {
    position: relative;
    aspect-ratio: 1 / 1;
    display: grid;
    place-items: center;
    padding: 1.2rem;
    background:
        radial-gradient(circle at 50% 58%, rgba(var(--halo-rgb), .16) 0%, transparent 62%),
        linear-gradient(180deg, rgba(255, 255, 255, .04), transparent 60%);
}

html.theme-soda .soda-card__media img {
    width: 84%;
    height: 84%;
    object-fit: contain;
    filter: drop-shadow(0 22px 34px rgba(0, 0, 0, .5));
}

html.theme-soda .soda-card__body {
    padding: 1.15rem 1.3rem 1.35rem;
    border-top: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    flex: 1;
}

html.theme-soda .soda-card__name {
    font-family: var(--font-display);
    font-size: 1.04rem;
    font-weight: 600;
    letter-spacing: -.02em;
    margin: 0;
}

html.theme-soda .soda-card__spec {
    font-size: .78rem;
    color: var(--ink-3);
    margin: .35rem 0 0;
}

html.theme-soda .soda-card__foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .8rem;
    margin-top: auto;
    padding-top: .9rem;
}

html.theme-soda .soda-card__link {
    font-family: var(--font-display);
    font-size: .8rem;
    font-weight: 600;
    color: var(--accent);
    opacity: .75;
    transition: opacity .3s ease;
}

html.theme-soda .soda-card:hover .soda-card__link {
    opacity: 1;
}

/* Stretched link: the whole card is clickable while only one real <a> exists
   in the accessibility tree. Interactive children must raise themselves above
   it (see .soda-card__form). */
html.theme-soda .soda-card__link--stretched::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
}

html.theme-soda .soda-card--linked {
    position: relative;
}

html.theme-soda .soda-card__form {
    position: relative;
    z-index: 2;
}

/* NEW — sold-out / unavailable card treatment. */
html.theme-soda .soda-card--muted .soda-card__media img {
    filter: drop-shadow(0 22px 34px rgba(0, 0, 0, .5)) grayscale(.7);
    opacity: .55;
}

/* =============================================================== category card */

html.theme-soda .soda-cat-card {
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 1.5rem 1.4rem 1.4rem;
    min-height: 210px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

html.theme-soda .soda-cat-card:hover,
html.theme-soda .soda-cat-card:focus-within {
    border-color: rgba(var(--accent-rgb), .55);
    background: var(--panel-hi);
}

/* Mood-driven glow. Decorative, sits behind content, never behind text at an
   alpha that could affect contrast. */
html.theme-soda .soda-cat-card__glow {
    position: absolute;
    inset: -40% 0 auto 0;
    height: 150%;
    pointer-events: none;
    background: radial-gradient(circle at 50% 22%, rgba(var(--accent-rgb), .16) 0%, transparent 58%);
    opacity: .55;
    transition: opacity var(--dur) ease;
}

html.theme-soda .soda-cat-card:hover .soda-cat-card__glow {
    opacity: 1;
}

html.theme-soda .soda-cat-card__icon {
    position: relative;
    width: 54px;
    height: 54px;
    color: var(--accent);
}

html.theme-soda .soda-cat-card__body {
    position: relative;
    margin-top: auto;
    /* The element is a <span>. Blockified as a flex item it becomes a block,
       but its children stay inline, so the name and the note shared one line
       box wherever the name was short enough to leave room. */
    display: flex;
    flex-direction: column;
    gap: .32rem;
}

html.theme-soda .soda-cat-card__name {
    font-family: var(--font-display);
    font-size: 1.16rem;
    font-weight: 600;
    letter-spacing: -.02em;
    margin: 0;
}

html.theme-soda .soda-cat-card__note {
    font-size: .78rem;
    line-height: 1.4;
    color: var(--ink-3);
    /* Spacing comes from the parent's gap; a margin here would stack with it. */
    margin: 0;
}

html.theme-soda .soda-cat-card__go {
    position: absolute;
    top: 1.5rem;
    right: 1.4rem;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: var(--r-control);
    background: var(--accent);
    color: var(--accent-ink);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
}

html.theme-soda .soda-cat-card:hover .soda-cat-card__go,
html.theme-soda .soda-cat-card:focus-within .soda-cat-card__go {
    opacity: 1;
    transform: none;
}

/* NEW — honest "upcoming category" state. No stock, so no fake link.
   The quiet reading is carried by the decoration, NOT by opacity on the card:
   opacity multiplies everything, so `opacity: .7` dragged --ink-3 (.55, 6.26:1)
   down to an effective .385 — about 3.4:1, below the AA floor that token exists
   to guarantee. */
html.theme-soda .soda-cat-card--upcoming .soda-cat-card__icon {
    /* Quiet, not invisible — and theme-aware rather than assuming a dark ground. */
    color: var(--ink-faint);
    opacity: .55;
}

html.theme-soda .soda-cat-card--upcoming .soda-cat-card__glow {
    opacity: .2;
}

html.theme-soda .soda-cat-card--upcoming .soda-cat-card__go {
    display: none;
}

/* ================================================================== badges */

html.theme-soda .soda-badge {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .38rem .72rem;
    border-radius: var(--r-mark);
    font-family: var(--font-display);
    font-size: .64rem;
    font-weight: 600;
    letter-spacing: .14em;
    text-transform: uppercase;
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}

/* NOVÉ — accent-on-ink inversion. */
html.theme-soda .soda-badge--new {
    background: var(--accent);
    color: var(--accent-ink);
}

/* POUŽITÉ — quiet glass. */
html.theme-soda .soda-badge--used {
    /* Was hardcoded white-on-white-ish: fine on the dark ground it was authored
       for, invisible (1.02:1) on ivory. Semantic tokens flip with the theme. */
    background: var(--surface-2);
    border: 1px solid var(--line-hi);
    color: var(--ink);
}

/* NEW — semantic availability badges mapped from the existing badge_class set.
   Colour is never the only signal: each carries its own text label. */
/* Semantic availability badges. `color-mix` derives the fill and the border
   from the state colour itself, so both themes get a tint of the right hue
   without a second set of literals — and the state colours are already
   theme-aware. Colour is never the only signal: each badge carries its label. */
html.theme-soda .soda-badge--in-stock {
    background: color-mix(in srgb, var(--success) 16%, transparent);
    color: var(--success);
    border: 1px solid color-mix(in srgb, var(--success) 34%, transparent);
}

html.theme-soda .soda-badge--on-order {
    background: color-mix(in srgb, var(--warning) 16%, transparent);
    color: var(--warning);
    border: 1px solid color-mix(in srgb, var(--warning) 34%, transparent);
}

html.theme-soda .soda-badge--sold-out {
    background: var(--surface-1);
    color: var(--ink-faint);
    border: 1px solid var(--line);
}

html.theme-soda .soda-badge--floating {
    position: absolute;
    top: .9rem;
    left: .9rem;
    z-index: 1;
}

/* ============================================================== filter pills

   The SEGMENTED CONTROL (.soda-seg / .soda-seg__btn) that used to live here is
   gone: no template renders it any more. The homepage's Vše / Nové / Použité /
   Skladem row is .soda-filter (home.css) — labels on a hairline — and the
   catalogue's category row is .soda-catalog__category-link. Both replaced a
   pill group with an accent-filled selected segment, which the direction rules
   out; the component itself had no remaining caller.
   -------------------------------------------------------------------------- */

/* Filter pills — links, so they work with JS off. *//* Filter pills — links, so they work with JS off. */
html.theme-soda .soda-pill {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem 1rem;
    border-radius: var(--r-mark);
    border: 1px solid var(--line);
    background: var(--panel);
    color: var(--ink-2);
    font-family: var(--font-display);
    font-size: .8rem;
    font-weight: 500;
    transition: background .3s ease, color .3s ease, border-color .3s ease;
}

html.theme-soda .soda-pill:hover {
    color: var(--ink);
    border-color: var(--line-hi);
}

html.theme-soda .soda-pill[aria-current='true'],
html.theme-soda .soda-pill.is-active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
}

html.theme-soda .soda-pill[aria-disabled='true'] {
    opacity: .4;
    pointer-events: none;
}

/* Horizontally scrollable pill row for mobile filters. */
html.theme-soda .soda-pill-row {
    display: flex;
    gap: .5rem;
    flex-wrap: wrap;
}

@media (max-width: 720px) {
    html.theme-soda .soda-pill-row {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        padding-bottom: .3rem;
        -webkit-overflow-scrolling: touch;
    }

    html.theme-soda .soda-pill-row::-webkit-scrollbar { display: none; }
    html.theme-soda .soda-pill-row > * { flex: none; }
}

/* ============================================================== breadcrumb */
/* NEW — the reference has no breadcrumb; catalog and detail need one. */

html.theme-soda .soda-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    font-size: .82rem;
    color: var(--ink-3);
    margin: 0 0 1.2rem;
    padding: 0;
    list-style: none;
}

html.theme-soda .soda-breadcrumb a {
    color: var(--ink-2);
}

html.theme-soda .soda-breadcrumb a:hover {
    color: var(--accent);
}

html.theme-soda .soda-breadcrumb li + li::before {
    content: '/';
    margin-right: .5rem;
    color: var(--ink-3);
    opacity: .6;
}

/* =================================================================== forms */
/* NEW — the reference prototype contains no forms at all. */

html.theme-soda .soda-field {
    display: flex;
    flex-direction: column;
    gap: .45rem;
    margin-bottom: 1.1rem;
}

html.theme-soda .soda-label {
    font-family: var(--font-display);
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--ink-2);
}

html.theme-soda .soda-input,
html.theme-soda .soda-select,
html.theme-soda .soda-textarea {
    width: 100%;
    padding: .8rem 1rem;
    border: 1px solid var(--line-hi);
    border-radius: var(--r-control);
    background: var(--field-bg);
    color: var(--ink);
    font-family: var(--font-main);
    font-size: .95rem;
    transition: border-color .3s ease, background .3s ease;
}

html.theme-soda .soda-input::placeholder,
html.theme-soda .soda-textarea::placeholder {
    color: var(--ink-3);
}

html.theme-soda .soda-input:hover,
html.theme-soda .soda-select:hover,
html.theme-soda .soda-textarea:hover {
    border-color: var(--line-hi);
}

/* Native select popups receive opaque, theme-owned foreground and background
   colours wherever the browser allows option styling. */
html.theme-soda .soda-select option,
html.theme-soda .soda-select optgroup {
    background-color: var(--select-option-bg);
    color: var(--select-option-ink);
}

html.theme-soda .soda-textarea {
    min-height: 7rem;
    resize: vertical;
}

/* Invalid state is driven by aria-invalid, so the visual cannot drift from
   what assistive tech announces. */
html.theme-soda .soda-input[aria-invalid='true'],
html.theme-soda .soda-select[aria-invalid='true'],
html.theme-soda .soda-textarea[aria-invalid='true'] {
    border-color: var(--error);
    background: rgba(252, 165, 165, .07);
}

html.theme-soda .soda-error {
    font-size: .82rem;
    color: var(--error);
}

html.theme-soda .soda-hint {
    font-size: .8rem;
    color: var(--ink-3);
}

/* Radio / checkbox rows */
html.theme-soda .soda-choice {
    display: flex;
    align-items: flex-start;
    gap: .7rem;
    padding: .9rem 1rem;
    border: 1px solid var(--line);
    border-radius: var(--r-control);
    background: var(--panel);
    cursor: pointer;
    transition: border-color .3s ease, background .3s ease;
}

html.theme-soda .soda-choice:hover {
    border-color: var(--line-hi);
    background: var(--panel-hi);
}

html.theme-soda .soda-choice input {
    margin-top: .18rem;
    accent-color: var(--accent);
}

/* Label body of a choice row: a title line plus optional explanatory text. */
html.theme-soda .soda-choice__text {
    display: block;
    min-width: 0;
}

html.theme-soda .soda-choice__title {
    display: block;
    font-family: var(--font-display);
    font-size: .92rem;
    font-weight: 600;
    color: var(--ink);
}

html.theme-soda .soda-choice__desc {
    display: block;
    margin-top: .2rem;
    font-size: .78rem;
    line-height: 1.45;
    color: var(--ink-2);
}

html.theme-soda .soda-qty {
    width: 5.5rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* =============================================================== accordion */

html.theme-soda .soda-acc {
    overflow: hidden;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--r-card);
    margin-bottom: .7rem;
    transition: border-color var(--dur) ease, background var(--dur) ease;
}

html.theme-soda .soda-acc.is-open {
    border-color: rgba(var(--accent-rgb), .38);
    background: var(--panel-hi);
}

html.theme-soda .soda-acc__q {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
    padding: 1.25rem 1.4rem;
    border: 0;
    background: transparent;
    color: var(--ink);
    font-family: var(--font-display);
    font-size: 1rem;
    font-weight: 500;
    letter-spacing: -.01em;
    text-align: left;
    cursor: pointer;
}

html.theme-soda .soda-acc__sign {
    position: relative;
    flex: none;
    width: 26px;
    height: 26px;
    border: 1px solid var(--glass-border);
    border-radius: var(--r-mark);
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

html.theme-soda .soda-acc__sign::before,
html.theme-soda .soda-acc__sign::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 10px;
    height: 1.5px;
    background: currentColor;
    transform: translate(-50%, -50%);
    transition: opacity var(--dur) ease;
}

html.theme-soda .soda-acc__sign::after {
    transform: translate(-50%, -50%) rotate(90deg);
}

html.theme-soda .soda-acc.is-open .soda-acc__sign {
    background: var(--accent);
    color: var(--accent-ink);
}

/* Plus becomes minus. */
html.theme-soda .soda-acc.is-open .soda-acc__sign::after {
    opacity: 0;
}

/* Height animation with no JS measurement: 0fr -> 1fr on a grid row. */
html.theme-soda .soda-acc__a {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows var(--dur) var(--ease);
}

html.theme-soda .soda-acc.is-open .soda-acc__a {
    grid-template-rows: 1fr;
}

html.theme-soda .soda-acc__a > div {
    overflow: hidden;
}

html.theme-soda .soda-acc__a p {
    margin: 0 1.4rem;
    font-size: .93rem;
    line-height: 1.65;
    max-width: 62ch;
    color: var(--ink-2);
}

html.theme-soda .soda-acc.is-open .soda-acc__a p {
    padding-bottom: 1.3rem;
}

/* With JS off every answer is simply open, so the content is always reachable. */
html.theme-soda:not(.js-soda) .soda-acc__a {
    grid-template-rows: 1fr;
}

html.theme-soda:not(.js-soda) .soda-acc__a p {
    padding-bottom: 1.3rem;
}

/* ============================================================ battery meter */
/* Existing thresholds preserved: green >= 90, yellow >= 85, orange below. */

html.theme-soda .soda-battery {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    font-size: .8rem;
    color: var(--ink-2);
    font-variant-numeric: tabular-nums;
}

html.theme-soda .soda-battery__bar {
    position: relative;
    width: 46px;
    height: 6px;
    border-radius: var(--r-pill);
    background: rgba(255, 255, 255, .12);
    overflow: hidden;
}

html.theme-soda .soda-battery__fill {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: var(--r-pill);
    background: var(--success);
}

html.theme-soda .soda-battery--mid .soda-battery__fill { background: var(--warning); }
html.theme-soda .soda-battery--low .soda-battery__fill { background: #fb923c; }

/* ============================================================= empty states */
/* NEW — the reference has none; catalog/cart/filters all need them. */

html.theme-soda .soda-empty {
    padding: clamp(2.2rem, 5vw, 3.6rem);
    text-align: center;
    background: var(--panel);
    border: 1px dashed var(--line-hi);
    border-radius: var(--r-plane);
    color: var(--ink-2);
}

html.theme-soda .soda-empty h2,
html.theme-soda .soda-empty h3 {
    margin: 0 0 .6rem;
    font-size: 1.15rem;
}

html.theme-soda .soda-empty p {
    margin: 0 auto 1.2rem;
    max-width: 46ch;
    color: var(--ink-3);
}

/* ================================================================= notices */

html.theme-soda .soda-notice {
    padding: .95rem 1.15rem;
    border-radius: var(--r-control);
    border: 1px solid var(--line);
    background: var(--panel);
    color: var(--ink-2);
    font-size: .9rem;
}

/*
 * A SEMANTIC STATE IS A RULE, NOT A TINTED BOX.
 *
 * These three carried a hard-coded dark-theme pastel as a fill — a tinted
 * callout, which the direction rules out, and which on a white plane resolved
 * to a faint wash that read as a smudge rather than as a warning. The state is
 * now a 2px rule down the left edge in the SEMANTIC colour, which is a token:
 * pastel on the dark ground, deep on the light plane, correct on both without
 * a second value.
 *
 * The colour is reinforcement, never the sole signal — every one of these
 * carries its own words, and the error summary additionally carries
 * role="alert" and in-page links to the controls that failed.
 */
html.theme-soda .soda-notice--warning,
html.theme-soda .soda-notice--error,
html.theme-soda .soda-notice--success {
    background: transparent;
    border-left-width: 2px;
}

html.theme-soda .soda-notice--warning {
    border-left-color: var(--warning);
}

html.theme-soda .soda-notice--error {
    border-left-color: var(--error);
}

html.theme-soda .soda-notice--success {
    border-left-color: var(--success);
}

/* ============================================================ generic grids */

html.theme-soda .soda-grid {
    display: grid;
    gap: var(--gap);
}

html.theme-soda .soda-grid--3 { grid-template-columns: repeat(3, 1fr); }
html.theme-soda .soda-grid--4 { grid-template-columns: repeat(4, 1fr); }

@media (max-width: 1180px) {
    html.theme-soda .soda-grid--4 { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 900px) {
    html.theme-soda .soda-grid--3,
    html.theme-soda .soda-grid--4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
    html.theme-soda .soda-grid--3,
    html.theme-soda .soda-grid--4 { grid-template-columns: 1fr; }
}

/* ============================================================ 404 page */
/* Owned by the integrator: the router renders the 404 outside any page
   controller, so it must not depend on a page-specific stylesheet. */

/* The 404 is a public page and carries the same direction as every other one:
   the dark Vesper ground with the category cards on a white plane. Its ink and
   chrome are scoped exactly as .soda-hero, .soda-catalog, .soda-detail,
   .soda-commerce and .soda-pg scope theirs. */
html.theme-soda .soda-notfound {
    background: var(--vesper-void);
    color: #fff;

    --ink: #ffffff;
    --ink-muted: rgba(255, 255, 255, .72);
    --ink-faint: rgba(255, 255, 255, .55);
    --ink-2: rgba(255, 255, 255, .72);
    --ink-3: rgba(255, 255, 255, .55);
    --line: rgba(255, 255, 255, .15);
    --line-hi: rgba(255, 255, 255, .3);
    --glass-border: rgba(255, 255, 255, .15);
    --panel: transparent;
    --panel-hi: rgba(255, 255, 255, .06);
    --hover-wash: rgba(255, 255, 255, .07);
    --focus: #ffffff;
    --btn-bg: rgba(0, 0, 0, .42);
    --btn-bg-hover: rgba(0, 0, 0, .68);
    --btn-border: rgba(255, 255, 255, .18);
    --accent: #ffffff;
    --accent-rgb: 255, 255, 255;
    --accent-ink: #000000;
}

html.theme-soda .soda-notfound .soda-sec-title,
html.theme-soda .soda-notfound .soda-eyebrow {
    color: #fff;
}

html.theme-soda .soda-notfound__head {
    max-width: 60ch;
    margin-bottom: clamp(2.5rem, 5vw, 4rem);
}

html.theme-soda .soda-notfound__actions {
    display: flex;
    flex-wrap: wrap;
    gap: .8rem;
    margin-top: 2rem;
}

html.theme-soda .soda-notfound__grid {
    margin-top: clamp(1.5rem, 3vw, 2.5rem);
    gap: 0;
}

/* Hairline-separated cells on the plane, like every other card grid on the
   site — not three boxes with a gutter. */
html.theme-soda .soda-notfound__grid .soda-cat-card {
    border: 0;
    border-right: 1px solid rgba(0, 0, 0, .15);
    border-radius: var(--r-none);
    background: transparent;
    padding: 1.667vw;
}

html.theme-soda .soda-notfound__grid .soda-cat-card:last-child {
    border-right: 0;
}

@media (max-width: 760px) {
    html.theme-soda .soda-notfound__grid .soda-cat-card {
        border-right: 0;
        border-bottom: 1px solid rgba(0, 0, 0, .15);
        padding: 1.25rem;
    }

    html.theme-soda .soda-notfound__grid .soda-cat-card:last-child {
        border-bottom: 0;
    }
}


/* ==================================================== THE WHITE VESPER PLANE

   docs/vesper/src/views/home/sections/financial-section.tsx, generalised.

   The homepage's white cards are hand-authored because each is a distinct
   composition. Every INNER page needs the same thing for a different reason:
   a working surface that reads as one plane on the dark Vesper ground, holding
   controls and data that were authored against the surface theme.

   This is that surface, and it is a TOKEN SCOPE first and a box second. The
   component layer consumes semantic names — --ink, --line, --panel, --accent —
   so re-pointing them here is what turns a whole page of existing components
   light-on-white without rewriting a single one of their rules. It is exactly
   the pattern tokens.css uses for .soda-hero and shell.css uses for the header
   and the footer, applied in the other direction.

   THE ACCENT IS INK HERE, deliberately. --accent is the product MOOD colour;
   on a working surface it appeared as an orange edge on every active tab,
   every selected filter row, every hovered card and every primary button —
   the old U Jabka system, reproduced by tokens rather than by rules. On the
   plane, state is carried by ink weight and a hairline. What survives is the
   SEMANTIC colour: --success / --warning / --error still say "skladem",
   "na objednávku" and "vyprodáno", because those carry information rather
   than brand, and they are pinned to their light-ground values so they stay
   readable on white under either surface theme.
   ========================================================================== */

/*
 * The supporting pages (/vykup, /jak-to-funguje, /faq, /kontakt, /o-nas and the
 * legal set) join by SELECTOR rather than by class, because their content
 * blocks are already wrapped in one container per section — every
 * .soda-pg-section IS a plane, and adding the class by hand to ten templates
 * would be ten chances to miss one. The masthead stays on the dark ground.
 */
/*
 * MEMBERSHIP IS ONE :is() GROUP, and it is the only place it is written.
 *
 * Three kinds of surface are the same plane: anything carrying .soda-plane
 * (catalogue, detail, cart, checkout, order pages), the homepage's white cards
 * (.soda-card-w), and each supporting page's section container. Every rule the
 * plane owns is scoped through the same
 * `:is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container)` group,
 * so adding a fourth surface is one edit here rather than one edit per rule —
 * and no rule can quietly serve two of the three and miss the third, which is
 * exactly how the FAQ accordion ended up ruled on the homepage and boxed on
 * /faq.
 *
 * The homepage's white cards join too. .soda-card-w was authored before this
 * component existed and declares the same opaque white surface with the same
 * hairline; adopting the token scope makes them literally the same plane as
 * the catalogue, the cart and the content pages, which is what lets ONE
 * accordion reskin (below) serve the homepage FAQ card, /faq and every content
 * page instead of three near-copies.
 */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) {
    /* The surface: opaque, square, one hairline, no shadow, no blur. */
    background: #fff;
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: var(--r-plane);
    color: #000;

    --ink: #14161a;
    --ink-muted: rgba(20, 22, 26, .72);
    --ink-faint: rgba(20, 22, 26, .62);
    --ink-soft: rgba(20, 22, 26, .55);
    --ink-2: rgba(20, 22, 26, .72);
    --ink-3: rgba(20, 22, 26, .62);

    --line: rgba(0, 0, 0, .15);
    --line-hi: rgba(0, 0, 0, .3);
    --line-glass: rgba(0, 0, 0, .12);
    --glass-border: rgba(0, 0, 0, .15);
    --line-nav: rgba(0, 0, 0, .15);

    /* Surfaces are transparent, not translucent: the plane IS the surface, and
       a second wash over it is the "card stack" the direction rules out. */
    --surface-1: transparent;
    --surface-2: rgba(0, 0, 0, .035);
    --surface-glass: transparent;
    --panel: transparent;
    --panel-hi: rgba(0, 0, 0, .035);
    --glass-bg: transparent;

    --field-bg: #fff;
    --select-option-bg: #ffffff;
    --select-option-ink: #14161a;

    --btn-bg: #000;
    --btn-bg-hover: #1c1f24;
    --btn-border: #000;
    --hover-wash: rgba(0, 0, 0, .045);
    --focus: #000;

    --accent: #000;
    --accent-rgb: 0, 0, 0;
    --accent-ink: #ffffff;
    --halo: #000;
    --halo-rgb: 0, 0, 0;

    /* Semantic colour survives; it carries information, not brand. Pinned to
       the light-ground values so the plane reads correctly under both themes. */
    --success: #0b5c3b;
    --success-ink: #ffffff;
    --warning: #6f4800;
    --warning-ink: #ffffff;
    --error: #96201a;
    --error-ink: #ffffff;
}

/*
 * NOTHING INSIDE AN OPAQUE PLANE MAY BLUR WHAT IS BEHIND IT, because there is
 * nothing behind it. The component layer applies backdrop-filter to cards,
 * badges, buttons and panels — every one of those was authored to float over
 * the dark ground, and over #fff each costs a full compositing pass to produce
 * no visible effect at all. Vesper's own card comments make exactly this
 * argument about its 30px blur.
 */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container),
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) * {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

/*
 * NO GLOW. The shared card paints a radial halo behind its media; on the dark
 * ground it is a product spotlight, on white it is a stain. Removed for every
 * card the plane holds.
 */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-card__media,
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-cat-card__glow {
    background-image: none;
}

/*
 * A PANEL ON A PLANE IS A BOX IN A BOX.
 *
 * .soda-panel was authored to float a bordered, tinted surface over the dark
 * ground; on the plane it draws a second outline around content that is
 * already on a surface — the "generic rounded commerce card stack" in its
 * squared-off form. The border and the fill come off everywhere at once. The
 * PADDING stays: it is real spacing that content depends on, not decoration.
 */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-panel {
    border: 0;
    border-radius: var(--r-none);
    background: transparent;
}

/* The primary control is ink-filled, because the plane has no accent to fill
   with. Its ghost sibling stays a ruled outline. */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn {
    color: #fff;
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn:hover {
    border-color: #1c1f24;
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn__arrow {
    background: #fff;
    color: #000;
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn--ghost {
    background: transparent;
    border-color: rgba(0, 0, 0, .25);
    color: #000;
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn--ghost:hover {
    background: rgba(0, 0, 0, .045);
    border-color: #000;
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn--ghost .soda-btn__arrow {
    background: #000;
    color: #fff;
}

/* A disabled control states its own unavailability; it must not read as ink. */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn[disabled],
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-btn[aria-disabled="true"] {
    background: transparent;
    border-color: rgba(0, 0, 0, .18);
    color: rgba(20, 22, 26, .45);
    cursor: default;
}


/* ============================================ THE ACCORDION, ON THE PLANE

   docs/vesper/src/views/home/sections/faq-section.tsx.

   Lifted out of home.css so that ONE definition serves the homepage FAQ card,
   the /faq page and every content page that carries an accordion. They were
   the same component wearing two different skins — ruled rows on the homepage,
   bordered boxes everywhere else — which is exactly the inconsistency this
   rollout exists to remove.

   The shared component (above) keeps its dark-ground treatment untouched for
   anything not on a plane.
   -------------------------------------------------------------------------- */
/* Open rows, not boxes: the shared component's fill, border, radius and
   bottom margin all come off, leaving one hairline per row. */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc,
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc.is-open {
    margin: 0;
    border: 0;
    border-bottom: 1px solid rgba(0, 0, 0, .15);
    border-radius: var(--r-none);
    background: transparent;
}

/* The whole row is the control — a question that only lights up when the
   cursor is exactly on its text reads as broken (the reference's note). */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__q {
    gap: 1.111vw;
    padding: 1.667vw .75vw;
    color: #000;
    font-size: var(--text-body);
    font-weight: 400;
    line-height: var(--leading-body);
    transition: background var(--dur) var(--ease);
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__q:hover {
    background: rgba(0, 0, 0, .04);
}

/* + -> x. Two 1px bars in a square box; the box rotates, so the bars stay
   crossed and simply change angle. */
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__sign,
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc.is-open .soda-acc__sign {
    width: 1.389vw;
    height: 1.389vw;
    min-width: 14px;
    min-height: 14px;
    border: 0;
    background: transparent;
    color: #000;
    transition: transform var(--dur) var(--ease);
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__sign::before,
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__sign::after,
html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc.is-open .soda-acc__sign::after {
    width: 100%;
    height: 1px;
    opacity: 1;
    background: #000;
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc.is-open .soda-acc__sign {
    transform: rotate(45deg);
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__a p {
    margin: 0 .75vw;
    max-width: none;
    font-size: var(--text-small);
    line-height: 1.45;
    color: rgba(0, 0, 0, .66);
}

html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc.is-open .soda-acc__a p,
html.theme-soda:not(.js-soda) :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__a p {
    padding-bottom: 1.667vw;
}

/* Below 1024 the row loosens up. The card's own responsive padding is not here
   — that belongs to the homepage card, and home.css owns it. */
@media (max-width: 1023.98px) {
    html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__q {
        gap: 1rem;
        padding: 1rem .5rem;
    }

    html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__a p {
        margin-inline: .5rem;
    }

    html.theme-soda :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc.is-open .soda-acc__a p,
    html.theme-soda:not(.js-soda) :is(.soda-plane, .soda-card-w, .soda-pg-section > .soda-container) .soda-acc__a p {
        padding-bottom: 1rem;
    }
}

/* ==================================================== interactive card edge

   The one interaction a product card gets. At rest it is the same neutral
   hairline every card already had. On hover or keyboard focus the edge darkens
   a little and a short highlight travels around it once every few seconds.

   The reference version of this is a thick neon frame. This is not that: the
   segment is a single low-opacity ink arc one pixel wide, on an edge that is
   already there. You should notice it while you are using the card, not from
   across the room.

   SCOPE IS DELIBERATELY NARROW, and is now the HOMEPAGE's featured cards only.
   The catalogue's product cards left this group when they took the real
   @maxim.bort.devel/info-card mechanic instead of this approximation of it: the
   arc there follows the POINTER rather than a clock, and the two cannot be one
   rule because one of them needs a per-card angle written by script. That rule
   lives in catalog.css, beside the cards it governs. Not buttons, not form
   controls, not nav items, not FAQ rows. The selector below IS the scope; there
   is no utility class to sprinkle around.

   HOW IT IS BUILT. A conic gradient rotated by a registered custom property,
   masked down to the one-pixel ring that sits exactly over the card's border.
   No library, no JS, no inline style, no extra element in the markup. Where
   @property is unsupported the angle simply never animates and the highlight
   sits still, which is a fine resting state rather than a broken one.

   MOTION IS NEVER PERMANENT. The animation is declared on :hover / :focus-within
   only, so nothing is running while the page sits idle - and under
   prefers-reduced-motion the travelling segment is removed entirely, leaving
   the border-colour change on its own to carry the interaction.
   -------------------------------------------------------------------------- */

/* THE ARC FOLLOWS THE POINTER, NOT A CLOCK. The 3.2s infinite rotation that
   used to run here was the rejected, generic version of the upstream idea.
   The real mechanic (21st @maxim.bort.devel/info-card — the file
   docs/component-prompts/product-border.md IS that component) measures the
   cursor against the card's own centre and draws a quarter-turn arc from that
   angle. card-pointer.js writes the angle (armed for these cards by ui.js and
   for the catalogue's by catalog.js); this rule only draws it. Same registered
   property, same arc geometry as catalog.css, so the two card families cannot
   drift into two border behaviours. */

@property --soda-card-angle {
    syntax: '<angle>';
    /* MUST INHERIT — the angle is written on the card, the arc is drawn by
       its ::before, and a non-inheriting registered property never reaches a
       pseudo-element. See the twin registration in catalog.css. */
    inherits: true;
    initial-value: 0deg;
}

html.theme-soda .soda-prod__item {
    transition: border-color var(--dur) var(--ease);
}

html.theme-soda .soda-prod__item:hover,
html.theme-soda .soda-prod__item:focus-within {
    border-color: rgba(0, 0, 0, .34);
}

html.theme-soda .soda-prod__item::before {
    content: '';
    position: absolute;
    /* One pixel outside the box, so the ring lands ON the border rather than
       beside it, and one step rounder to match that offset. */
    inset: -1px;
    z-index: 2;
    border-radius: calc(var(--r-card) + 1px);
    padding: 1px;
    /* The source's quarter turn, sitting ON the cursor: +90deg converts
       atan2's zero (3 o'clock) to the conic gradient's zero (12 o'clock),
       -45deg centres the span on the pointer. See catalog.css for the full
       note and tools/dev-arc-probe.mjs for the measurement. The soft ends
       are anti-aliasing on a one-pixel ring, not a design. */
    background: conic-gradient(from calc(var(--soda-card-angle) + 45deg),
        transparent 0deg,
        rgba(0, 0, 0, .12) 4deg,
        rgba(0, 0, 0, .8) 8deg 82deg,
        rgba(0, 0, 0, .12) 86deg,
        transparent 90deg 360deg);
    /* Keep the border band, drop the middle: the ring is the difference between
       the padding box and the content box. */
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
    pointer-events: none;
}

/* KEYBOARD FOCUS IS THE EQUAL STATE: with no pointer the arc rests at the
   source's own 0deg default, and the border-colour change carries the state. */
html.theme-soda .soda-prod__item:hover::before,
html.theme-soda .soda-prod__item:focus-within::before {
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    /* The travelling arc goes; the border-colour change stays. card-pointer.js
       does not track a pointer it would not animate. */
    html.theme-soda .soda-prod__item::before {
        display: none;
    }
}

