/* ==========================================================================
   Kaviár Dům — the shop's stylesheet
   --------------------------------------------------------------------------
   The first half is the design kit, copied from docs/design/kaviardum/kd.css
   rather than rewritten: tokens, masthead, hero, doors, the lit case, the
   ordinary shelf, footer and product page. Its comments are the reasoning and
   are part of the deliverable. The second half moves the rest of the shop —
   sheet, forms, cart, checkout, account, legal — onto the same tokens.
   ========================================================================== */

/* ==========================================================================
   Kaviár Dům — design kit (prototype CSS)
   --------------------------------------------------------------------------
   One idea runs through the whole shop: a dark room with lit display cases.
   The page is the room (abyss), every product photograph or video sits in a
   lit case (vitrine), and the one warm thing in the room is the roe grain.

   The "island of the ordinary" (fish & crayfish) switches the room off and
   turns the daylight on: paper ground, plain sans, price tags. Same shop,
   different shelf, and the reader feels the step down without being told.
   ========================================================================== */

:root {
    /* the room */
    --abyss:     #0b1216;   /* page ground; black with the green of sturgeon skin */
    --depth:     #121b21;   /* a raised surface in the room */
    --hair:      rgba(235, 231, 221, .14);  /* rules on dark */
    --hair-soft: rgba(235, 231, 221, .07);

    /* the cases */
    --vitrine:   #ebe7de;   /* lit ground behind a product; warm, not white */
    --vitrine-2: #e2ddd2;   /* its shadowed lower edge */

    /* type on dark: mother-of-pearl, never pure white */
    --pearl:     #ece8df;
    --nacre:     #a8a59c;   /* secondary text, 5.5:1 on abyss */
    --nacre-dim: #6f6f69;   /* tertiary, still 3.6:1 */

    /* the one warm accent */
    --ikura:     #c00505;   /* fill only: the grain, the buy button */
    --ikura-lit: #ff5a45;   /* the same grain with light through it; for text on dark */

    /* the ordinary shelf (island) */
    --shelf:     #f2eee6;   /* paper */
    --shelf-2:   #e6e0d3;   /* kraft edge */
    --shelf-ink: #1c1a16;
    --shelf-mute:#6d685f;
    --shelf-card:  #fff;    /* a tile, a chip */
    --shelf-on-ink:#fff;    /* type on an ink chip or button */

    --ok:        #7cc7a3;

    --font-display: "Cormorant Garamond", "Iowan Old Style", Georgia, serif;
    --font-body:    "Manrope", -apple-system, "Segoe UI", Roboto, sans-serif;

    /* scale: 1.25 on phone, opens up on desktop */
    --t-0: 1rem;
    --t-1: 1.125rem;
    --t-2: 1.375rem;
    --t-3: clamp(1.75rem, 1.4rem + 1.4vw, 2.25rem);
    --t-4: clamp(2.5rem, 1.8rem + 3vw, 4rem);
    --t-5: clamp(3rem, 1.6rem + 6vw, 6.5rem);

    --gutter: clamp(1rem, 4vw, 2rem);
    --max: 1360px;
    --masthead-h: 60px;
    --r: 3px;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
    margin: 0;
    background: var(--abyss);
    color: var(--pearl);
    font-family: var(--font-body);
    font-size: var(--t-0);
    line-height: 1.5;
    font-variant-numeric: tabular-nums;
    -webkit-font-smoothing: antialiased;
}
img, video { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
:focus-visible { outline: 2px solid var(--ikura-lit); outline-offset: 3px; }
h1, h2, h3 { font-family: var(--font-display); font-weight: 400; line-height: 1.02; margin: 0; letter-spacing: -.005em; }
.wrap { max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }

/* --- masthead ------------------------------------------------------------- */
.mast {
    position: sticky; top: 0; z-index: 40;
    background: color-mix(in srgb, var(--abyss) 82%, transparent);
    backdrop-filter: blur(14px) saturate(1.2);
    border-bottom: 1px solid var(--hair-soft);
}
.mast__row { display: flex; align-items: center; gap: 1.25rem; min-height: var(--masthead-h); }
.mast__logo { flex: none; } /* the wordmark never gives up width; the bar's breakpoints do */
.mast__logo img { height: 22px; width: auto; }
.mast__nav { display: none; }
.mast__right { margin-left: auto; display: flex; align-items: center; gap: .25rem; }
.mast__link { font-size: .9375rem; color: var(--nacre); padding: .5rem .6rem; }
.mast__link:hover { color: var(--pearl); }
/* The number is a round trip to a customer, so the call is in the bar at almost
   every width — but spelled out only where the row has room for it. Below that
   it is a handset like the two apps beside it; the number itself is on the first
   screen anyway ("or call …"). Wrapped onto two lines it was the ugliest thing
   in the bar, so it never wraps: it is the whole number or the icon. Only on the
   smallest phones does the handset leave the bar altogether (see below). */
.mast__tel {
    width: 40px; height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
    font-weight: 600; color: var(--pearl); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.mast__tel svg { width: 19px; height: 19px; flex: none; }
.mast__tel-num { display: none; }
/* With the handset in it the phone bar is five icons and the wordmark; on a
   390px phone that is ~25px too many at the roomy sizes, and on a 360px
   Android more. The icons give up their spare width, not their height. */
@media (max-width: 419px) {
    .mast__row { gap: .75rem; }
    .mast__right { gap: 0; }
    .mast__chat, .mast__tel { width: 36px; }
}
/* Under 380px the wordmark and five icons are wider than the row (~326px
   against 288 at 320). The handset is the one that goes: it is the only icon
   whose target is repeated elsewhere — the number in the sheet on every page,
   plus "or call +420 …" on the home page's first screen — while the apps and the
   bag exist nowhere else. */
@media (max-width: 379px) {
    .mast__logo img { height: 19px; }
    .mast__row { gap: .5rem; }
    .mast__tel { display: none; }
}
/* Two widths have room for the whole number: the burger range from a small
   tablet up (the branches are in the sheet, the bar is only icons), and the
   wide bar once the branches and the number both fit. Between 1100 and 1260
   the branches take that room and the number stays a handset. */
/* 1420, not 1260, since the fourth branch (crab meat) and the music controls:
   measured, at 1300 with music on the spelled-out number pushed the language
   switch off the window (15.10.2026). */
@media (min-width: 640px) and (max-width: 1099px), (min-width: 1420px) {
    .mast__tel { width: auto; padding: .5rem .8rem; }
    .mast__tel svg { width: 15px; height: 15px; }
    .mast__tel-num { display: inline; }
}
/* The two apps stay in the bar at every width, but not in their own colours.
   The palette here is the room, the pearl type and one red grain; a brand green
   and a brand blue are the only foreign colours on the page and they sit right
   beside the wordmark, out-shouting it. Monochrome like the bag and the burger:
   both apps are recognised by their shape, so the colour was paying nothing. */
.mast__chat { width: 40px; height: 44px; flex: none; display: inline-flex; align-items: center; justify-content: center; color: var(--pearl); opacity: .92; }
.mast__chat svg { width: 21px; height: 21px; }
.mast__chat:hover { opacity: 1; }
.iconbtn {
    position: relative; width: 44px; height: 44px;
    display: inline-flex; align-items: center; justify-content: center; color: var(--pearl);
}
.iconbtn svg { width: 22px; height: 22px; }
/* Hidden until Alpine takes over, or the empty basket's badge and the closed
   pop-ups flash for a moment on every load. */
[x-cloak] { display: none !important; }
.iconbtn__n {
    position: absolute; top: 5px; right: 4px; min-width: 17px; height: 17px; padding-inline: 4px;
    border-radius: 999px; background: var(--ikura); color: #fff; font-size: .6875rem; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
}
/* The bar and the gap are variables because the open state has to know them:
   the outer bars travel one bar + two gaps to meet in the middle, and a cross
   drawn from a hard-coded distance silently breaks the day someone makes the
   burger thicker. The state comes from aria-expanded, which Alpine already
   binds for the screen reader — no second source of truth in a class. */
.burger { flex-direction: column; --bar: 1.5px; --gap: 3px; }
.burger span {
    display: block; width: 20px; height: var(--bar); background: var(--pearl);
    margin: var(--gap) auto;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1), opacity .12s linear;
}
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(calc(var(--bar) + 2 * var(--gap))) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; transform: scaleX(.2); }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(calc(-1 * (var(--bar) + 2 * var(--gap)))) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .burger span, .mast__nav { transition: none; } }

/* With the sheet open the page behind it must not scroll. Keyed off the same
   aria-expanded as the cross above, so there is one state, not two.
   overflow:hidden on the root and NOT position:fixed on the body: the
   masthead is position:sticky, and a fixed body takes away the scroller it
   sticks to — the bar would scroll off and leave a gap above the sheet.
   Hiding the root's overflow also keeps the scroll position, so closing the
   menu does not jump the page to the top. */
html:has(.burger[aria-expanded="true"]) { overflow: hidden; }

@media (min-width: 900px) {
    :root { --masthead-h: 72px; }
    .mast__logo img { height: 26px; }
}

/* The bar has its own breakpoint, later than the page's 900px. Four branch
   names, the apps, the account word, the bag and the language capsule need
   ~1070px in Russian (the longest) on one line; between 900 and that they
   wrapped into two-line stacks on a landscape tablet. Below it the bar keeps
   its phone shape — burger and sheet — however wide the page layout already is.
   The sheet's x-data in the layout listens to the same width. */
@media (min-width: 1100px) {
    .mast__nav { display: flex; gap: 1.6rem; margin-left: 2rem; transition: opacity .3s ease, visibility .3s; }
    /* Once the category rail stands beside the bar, the bar is naming the
       same branches twice. It steps back — visibility, not display, so the
       row keeps its width and nothing to the right of it moves. */
    .mast__nav.is-hushed { opacity: 0; visibility: hidden; }
    /* A page that opens with the rail already in place starts hushed;
       only a change of state on the way down is worth a fade. */
    .mast__nav.is-abrupt { transition: none; }
    .mast__nav a { font-size: .9375rem; color: var(--nacre); padding: .5rem 0; border-bottom: 1px solid transparent; white-space: nowrap; }
    .mast__nav a:hover, .mast__nav a[aria-current] { color: var(--pearl); border-bottom-color: var(--pearl); }
    .mast__nav a.is-shelf { color: var(--nacre); font-weight: 500; }
    .burger { display: none; }
}
/* The narrow end of the wide bar: four branches plus nine controls are ~50px
   wider than 1100. The gaps give it back, not the controls. */
@media (min-width: 1100px) and (max-width: 1279px) {
    .mast__nav { gap: 1.1rem; margin-left: 1.25rem; }
    .mast__right { gap: 0; }
    .mast__row { gap: .75rem; }
}
@media (min-width: 1100px) {
    /* And with it the scroll lock: the sheet is a phone shape, and a desktop
       window must never lose its scrollbar because of a stale attribute on a
       button nobody can see. */
    html:has(.burger[aria-expanded="true"]) { overflow: visible; }
}

/* --- hero ------------------------------------------------------------------
   One picture, the darkest thing we sell, filling the first screen. The film
   goes here when it is shot; until then the photograph does the job. The
   headline sits at the bottom, where the eye lands after the picture.

   The height is a floor, never a fixed size. It used to be fixed, with the
   text pinned to the bottom and the overflow clipped: a headline that broke
   onto four lines instead of three (Russian and Vietnamese, on a landscape
   tablet) grew upward past the top edge and lost its first line. Now the text
   is in the flow at the bottom of a flex box, and a longer headline in any
   language makes the picture taller instead of cutting itself off.
   ----------------------------------------------------------------------- */
.hero {
    position: relative; overflow: hidden;
    display: flex; flex-direction: column; justify-content: flex-end;
    min-height: max(520px, min(82vh, 760px));
    min-height: max(520px, min(82svh, 760px));
    background: #000;
}
.hero__media { position: absolute; inset: 0; }
.hero__media img, .hero__media video { width: 100%; height: 100%; object-fit: cover; object-position: 60% 50%; }
/* The films lie over the photograph in their own stacking context: a layer
   raised to fade over the other must not climb over the headline too. */
.hero__media { isolation: isolate; }
.hero__media picture { display: block; width: 100%; height: 100%; }
.hero__film { position: absolute; inset: 0; opacity: 0; transition: opacity 1.2s ease; }
.hero__film.is-cut { transition: none; }
/* Shown only when the phone refused to start the films by itself (iOS Low
   Power Mode): a tap on the hero starts them. */
.hero__play {
    display: none; position: absolute; top: 1rem; right: calc(var(--gutter) + 2px); z-index: 3;
    width: 44px; height: 44px; border-radius: 50%;
    background: rgba(11,18,22,.45); border: 1px solid rgba(236,232,223,.6);
}
.hero__play::before {
    content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-35%, -50%);
    border-style: solid; border-width: 8px 0 8px 13px; border-color: transparent transparent transparent var(--pearl);
}
.hero--tap .hero__play { display: block; }
.hero__veil {
    position: absolute; inset: 0;
    background:
        linear-gradient(to top, rgba(11,18,22,.92) 0%, rgba(11,18,22,.45) 38%, rgba(11,18,22,0) 70%),
        linear-gradient(to right, rgba(11,18,22,.35), transparent 60%);
}
.hero__text {
    position: relative; width: 100%;
    padding-top: 2.5rem; padding-bottom: clamp(1.75rem, 5vw, 4rem);
}
/* On a short landscape screen the width-driven size alone made a headline
   taller than the screen it opens; the height caps it too. */
.hero h1 { font-size: min(var(--t-5), 11vh); font-size: min(var(--t-5), 11svh); font-weight: 300; max-width: 12ch; text-wrap: balance; }
.hero h1 em { font-style: italic; font-weight: 300; }
.hero__sub { font-size: var(--t-1); color: var(--nacre); max-width: 44ch; margin: 1rem 0 1.5rem; }
.hero__cta { display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; align-items: center; }
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
    min-height: 48px; padding: .7rem 1.4rem; border-radius: var(--r);
    font-size: .9375rem; font-weight: 600; letter-spacing: .01em;
    transition: background .15s, color .15s, border-color .15s;
}
.btn--pearl { background: var(--pearl); color: var(--abyss); }
.btn--pearl:hover { background: #fff; }
.btn--line { border: 1px solid var(--hair); color: var(--pearl); }
.btn--line:hover { border-color: var(--pearl); }
.btn--ikura { background: var(--ikura); color: #fff; }
.btn--ikura:hover { background: #a10404; }
.btn--block { width: 100%; }
.btn[disabled] { opacity: .4; cursor: not-allowed; }
.hero__tel { color: var(--nacre); font-weight: 500; }
.hero__tel b { color: var(--pearl); font-weight: 600; }

/* On a phone the text covers half the square film, over its lightest parts
   (the bread, the hand): a deeper veil that reaches higher, a lighter
   subtitle and a soft shadow under the words. */
@media (max-width: 899px) {
    .hero__veil {
        background: linear-gradient(to top, rgba(11,18,22,.95) 0%, rgba(11,18,22,.82) 40%, rgba(11,18,22,.5) 62%, rgba(11,18,22,0) 85%);
    }
    .hero__sub { color: #d6d2c8; text-shadow: 0 1px 12px rgba(11,18,22,.9), 0 0 2px rgba(11,18,22,.8); }
    .hero h1 { text-shadow: 0 2px 18px rgba(11,18,22,.7); }
}

@media (min-width: 900px) {
    .hero {
        min-height: max(480px, min(calc(100vh - var(--masthead-h)), 860px));
        min-height: max(480px, min(calc(100svh - var(--masthead-h)), 860px));
    }
    .hero__media img, .hero__media video { object-position: 50% 45%; }
}

/* --- catalogue: the categories, always in reach ----------------------------
   Phone: one scrolling row of chips under the doors, sticky under the bar so
   the tree is one thumb away anywhere in the catalogue. Desktop: a quiet rail
   on the left of the whole catalogue, the same tree the shop has today.
   ----------------------------------------------------------------------- */
.catalogue { display: grid; gap: 0; grid-template-columns: minmax(0, 1fr); }
.catalogue > * { min-width: 0; }
/* Whole pixels on the vertical, deliberately, and not rem: .6rem is 9.6px and
   .45rem is 7.2px, which put the pill's top at 70.594 and its bottom at
   107.969 — both a fraction off the device grid, the bottom by .91 of a
   physical pixel at 3x. The browser then antialiases that edge, and on a
   999px radius a softened edge reads as a chamfer: the corners look cut off
   while the top ones stay round. Only the block axis is pinned; the gutter
   stays as it is. */
.catnav { position: sticky; top: var(--masthead-h); z-index: 20; background: color-mix(in srgb, var(--abyss) 92%, transparent); backdrop-filter: blur(10px); margin-inline: calc(var(--gutter) * -1); padding: 9px var(--gutter); border-bottom: 1px solid var(--hair-soft); }
/* overflow-x: auto forces overflow-y to auto as well, so this row is a scroll
   container in both directions and clips at its own edge. Its height comes out
   fractional (37.38px), the client box rounds down to 37, and the third of a
   pixel that falls off is exactly where the pill's bottom border sits — a
   hairline shave on a phone, magnified by pinch-zoom. The 1px of slack moves
   the rounding boundary off the border; the negative margin gives it back, so
   nothing in the layout moves. */
/* The row bleeds to the screen edge and carries the gutter as its own padding,
   with a fade the width of that gutter at both ends. The strip opens scrolled
   to the category you are in, so whatever chip sits at the edge is cut mid-word
   ("timent 64") — under a fade a cut reads as "there is more", not as a bug.
   At rest the first chip starts where the fade has already reached full ink,
   so nothing is dimmed until it actually slides under the edge. */
.catnav ul {
    list-style: none; margin: -1px calc(var(--gutter) * -1); padding: 1px var(--gutter);
    display: flex; gap: .4rem; overflow-x: auto; scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent);
    mask-image: linear-gradient(to right, transparent, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent);
}
.catnav ul::-webkit-scrollbar { display: none; }
.catnav li { flex: none; }
.catnav a { display: inline-flex; align-items: center; gap: .35rem; padding: 7px .8rem; border-radius: 999px; border: 1px solid var(--hair); font-size: .875rem; color: var(--pearl); white-space: nowrap; }
.catnav a small { color: var(--nacre-dim); font-size: .75rem; }
.catnav a[aria-current] { background: var(--pearl); color: var(--abyss); border-color: var(--pearl); }
.catnav a[aria-current] small { color: var(--nacre-dim); }
.catnav .is-root a { font-weight: 600; }
.catnav .is-shelf a { border-style: dashed; color: var(--nacre); }
@media (min-width: 900px) {
    .catalogue { grid-template-columns: 220px minmax(0, 1fr); gap: 3rem; align-items: start; }
    /* The air above the rail is its own margin, not the sticky offset.
       A sticky element whose natural top is ABOVE its threshold gets pushed
       down to it — but only while its container has room to give, so on a
       category holding one product the rail sprang 23 px up the page and the
       tree appeared to jump between pages. With the margin doing the spacing,
       the natural position already equals the threshold and nothing moves. */
    .catnav { position: sticky; top: calc(var(--masthead-h) + 1.5rem); margin: 1.5rem 0 0; padding: 0; background: none; backdrop-filter: none; border: 0; }
    .catnav ul { flex-direction: column; gap: 0; margin: 0; padding: 0; overflow: visible; -webkit-mask-image: none; mask-image: none; }
    .catnav a { display: flex; justify-content: space-between; padding: .38rem 0; border: 0; border-radius: 0; font-size: .9375rem; color: var(--nacre); }
    .catnav a:hover { color: var(--pearl); }
    /* The rail marks where you are the way the sort links do: the name
       underlined in the grain. Only the name — a rule running on to the count
       would read as a row of its own. */
    .catnav a[aria-current] { background: none; color: var(--pearl); }
    /* text-decoration, not a border: a border needs padding to sit off the
       baseline, and that padding is part of the box — it pushed the current
       row and everything under it two pixels down the rail. An underline is
       drawn, not laid out, so the rhythm of the list never moves. */
    .catnav a[aria-current] > span {
        text-decoration: underline;
        text-decoration-color: var(--ikura);
        text-decoration-thickness: 1px;
        text-underline-offset: 5px;
    }
    .catnav .is-root { margin-top: .9rem; } .catnav .is-root:first-child { margin-top: 0; }
    .catnav .is-root a { color: var(--pearl); }
    .catnav .is-sub a { padding-left: .9rem; }
    .catnav .is-root.is-shelf { margin-top: 1.6rem; padding-top: 1rem; border-top: 1px solid var(--hair-soft); }
    .catnav .is-shelf a { border: 0; }
    .catnav .is-shelf.is-root a { color: var(--pearl); }
    /* The same 1.5rem the rail now carries, so the tree and the first heading
       beside it start on one line. */
    .catalogue .sect:first-of-type { padding-top: 1.5rem; }
}

/* --- the three facts ----------------------------------------------------
   The wording is the owner's and does not change; only the setting does.
   ----------------------------------------------------------------------- */
.facts { display: grid; gap: 1.25rem; padding-block: clamp(1.5rem, 4vw, 2.5rem); border-top: 1px solid var(--hair-soft); border-bottom: 1px solid var(--hair-soft); }
.fact h3 { font-size: 1.375rem; font-weight: 500; margin-bottom: .3rem; }
.fact h3::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--ikura); margin-right: .55rem; vertical-align: .25em; }
.fact p { margin: 0; color: var(--nacre); font-size: .9375rem; max-width: 40ch; }
@media (min-width: 700px) { .facts { grid-template-columns: repeat(3, 1fr); gap: 2rem; } .fact + .fact { padding-left: 2rem; border-left: 1px solid var(--hair-soft); } }

/* --- section titles ------------------------------------------------------- */
.sect { padding-block: clamp(2.5rem, 6vw, 5rem); }
.sect__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: clamp(1.25rem, 3vw, 2rem); }
.sect__head h2 { font-size: var(--t-4); font-weight: 300; }
.sect__head h2 small { font-family: var(--font-body); font-size: .8125rem; color: var(--nacre-dim); margin-left: .6rem; vertical-align: .5em; }
.sect__more { color: var(--nacre); font-size: .9375rem; border-bottom: 1px solid var(--hair); padding-bottom: 2px; white-space: nowrap; }
.sect__more:hover { color: var(--pearl); border-color: var(--pearl); }

/* --- the three doors -----------------------------------------------------
   Kaviár and the sea delicacies are two lit cases; the third door is
   already the ordinary shelf, so it already looks like paper.
   ----------------------------------------------------------------------- */
.doors { display: grid; gap: .75rem; grid-template-columns: 1fr 1fr; }
.door {
    position: relative; overflow: hidden; border-radius: var(--r);
    aspect-ratio: 4 / 5; background: var(--depth);
}
.door img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s ease; }
.door:hover img { transform: scale(1.04); }
.door__veil { position: absolute; inset: 0; background: linear-gradient(to top, rgba(11,18,22,.85), rgba(11,18,22,0) 55%); }
.door__text { position: absolute; left: 1rem; right: 1rem; bottom: 1rem; }
.door__text h3 { font-size: var(--t-3); font-weight: 400; }
.door__text p { margin: .25rem 0 0; color: var(--nacre); font-size: .875rem; }
.door--shelf { grid-column: 1 / -1; aspect-ratio: auto; min-height: 150px; background: var(--shelf); color: var(--shelf-ink); display: grid; grid-template-columns: 1fr auto; align-items: center; padding: 1.25rem; gap: 1rem; }
.door--shelf h3 { font-family: var(--font-body); font-weight: 700; font-size: 1.25rem; letter-spacing: -.01em; }
.door--shelf p { margin: .3rem 0 0; color: var(--shelf-mute); font-size: .9375rem; max-width: 40ch; }
/* With a photograph the tile becomes picture-over-paper: the shot fills the
   top, the words keep their paper strip underneath. Without one it stays the
   original paper tile with three cut-outs. */
.door--shot { padding: 0; grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) auto; gap: 0; align-items: stretch; }
/* On a phone the tile has no fixed proportion, so the photograph sets the
   height: 4:3 keeps it a picture over a shelf rather than a poster taking
   most of the screen. On desktop the tile is 3:4 and the shot fills it. */
.door--shot .door__shot { width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
@media (min-width: 900px) { .door--shot .door__shot { height: 100%; aspect-ratio: auto; } }
.door--shot .door__paper { padding: .9rem 1.1rem 1.1rem; background: var(--shelf); }
/* The plate under the shelf door's photograph: the room's raised surface,
   not paper. Next to the two lit doors a paper plate shouted (Myron, Bogdan
   15.10); a neutral grey (#2a2e31) then read as plastic, so it takes the
   room's own green-black, one step up from the floor. */
.door--shot { background: var(--depth); color: var(--pearl); }
.door--shot .door__paper { background: var(--depth); }
.door--shot h3 { color: var(--pearl); }
.door--shot p { color: var(--nacre); }
.door--shelf .door__pics { display: flex; }
.door--shelf .door__pics img { width: 64px; height: 64px; object-fit: contain; background: #fff; border-radius: 50%; border: 2px solid var(--shelf); margin-left: -18px; mix-blend-mode: multiply; }
.door--shelf .door__pics img:first-child { margin-left: 0; }
@media (min-width: 900px) {
    .doors { grid-template-columns: 1fr 1fr 1fr; gap: 1rem; }
    .door { aspect-ratio: 3 / 4; }
    .door--shelf { grid-column: auto; aspect-ratio: 3 / 4; grid-template-columns: 1fr; align-content: end; padding: 1.5rem; }
    .door--shelf .door__pics { order: -1; align-self: start; }
    .door--shot { padding: 0; align-content: stretch; }
}

.doors--one .door:not(.door--shelf) { grid-column: 1 / -1; aspect-ratio: 16 / 10; }
@media (min-width: 900px) {
    .doors--one { grid-template-columns: 1.3fr 1fr; }
    .doors--one .door:not(.door--shelf) { grid-column: auto; aspect-ratio: 4 / 3; }
}

/* A door's film lies over its photograph and shows only while playing
   (.is-on, from the layout script). Mouse: plays on hover, holds its frame on
   leave. Touch: three films at once is too much, so nothing starts by itself —
   the round switch in the corner plays one, and the rest of the tile is still
   the link. The photo does not zoom under a film: the zoom would show through
   the dissolve. */
.door--film:hover img { transform: none; }
.door__film {
    position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
    opacity: 0; transition: opacity .8s ease; pointer-events: none;
}
.door__film.is-on { opacity: 1; }
.door__frame { position: relative; display: block; overflow: hidden; min-height: 0; }
.door__frame .door__shot { display: block; }
@media (min-width: 900px) { .door--shot .door__frame .door__shot { height: 100%; } }
.door__play {
    display: none; position: absolute; top: .75rem; right: .75rem; z-index: 2;
    width: 44px; height: 44px; border-radius: 50%;
    background: rgba(11,18,22,.55); border: 1px solid rgba(236,232,223,.55);
}
.door__play::before {
    content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-35%, -50%);
    border-style: solid; border-width: 8px 0 8px 13px; border-color: transparent transparent transparent var(--pearl);
}
.door.is-playing .door__play::before {
    transform: translate(-50%, -50%); width: 4px; height: 15px; box-sizing: content-box;
    border: 0; border-left: 4px solid var(--pearl); border-right: 4px solid var(--pearl);
}
@media (hover: none) and (prefers-reduced-motion: no-preference) { .door__play { display: block; } }

/* --- product card (the lit case) ------------------------------------------
   The photo is the poster of the film. On hover (pointer) or when the card
   is mid-screen (touch), the photo sinks: it scales up a touch and the film
   fades in over it, starting from that same frame. No hard cut, no play
   icon over the food. The vitrine is warm and light so a white-background
   cut-out melts into it with mix-blend-mode: multiply.
   ----------------------------------------------------------------------- */
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem .75rem; }
@media (min-width: 640px)  { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2rem 1rem; } }
@media (min-width: 1100px) { .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 2.5rem 1.25rem; } }

.card { display: flex; flex-direction: column; min-width: 0; }
.case {
    position: relative; overflow: hidden; border-radius: var(--r);
    aspect-ratio: 1 / 1;                /* was 4 / 5; square since 2026-10-04 */
    background: linear-gradient(to bottom, var(--vitrine), var(--vitrine-2));
}
.case__photo {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: contain; padding: 10%;
    mix-blend-mode: multiply;           /* white cut-out backgrounds vanish */
    transition: transform 1.2s cubic-bezier(.2,.7,.2,1), opacity .9s ease .1s;
}
.case__film {
    position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
    opacity: 0; transition: opacity .9s ease .15s;
}
/* prototype stand-in: until the operator's footage exists the "film" is the
   same photo, panning slowly. Delete .case__film--fake when real files exist. */
.case__film--fake { object-fit: contain; padding: 10%; mix-blend-mode: multiply; transform-origin: 50% 60%; }
/* Only a card that HAS a film dissolves out of its photograph. Fading the
   photo on every card left an empty lit panel under the pointer for every
   product not yet filmed — which is all of them today. A card without a film
   answers with the scale alone, which still reads as a response. */
.card:hover .case__photo { transform: scale(1.04); }
/* The film takes over only once it really plays (is-playing, from the video's
   own 'playing' event). Until then the photo stays and breathes (is-loading):
   on a slow connection a waiting picture, not a black square (Bogdan, 15.10). */
.card--film.is-playing .case__film  { opacity: 1; }
/* A real film starts on the very frame that stands in as the photo, so the
   photo must not zoom away from it, and once played the film stays: leaving
   the card only pauses it where it is. */
.card--film .case__photo--scene { transition: opacity .4s ease; }
.card--film:hover .case__photo--scene, .card--film.is-playing .case__photo--scene { transform: none; }
/* The film's first frame IS the photo, so the photo never leaves: the film
   fades in on top of it and the picture simply comes alive. The breathing
   starts only after half a second of waiting — a fast connection never shows it. */
.case__film, .tile__film, .stage .pdp__film { transition: opacity .6s ease; }
.is-loading .case__photo, .is-loading .tile__photo, .stage.is-loading .pdp__poster { animation: kd-breathe 1.2s ease-in-out .5s infinite; }
@keyframes kd-breathe { 50% { opacity: .5; } }
.card--film:hover .case__film--fake, .card--film.is-live .case__film--fake { animation: sink 6s ease-in-out infinite alternate; }
@keyframes sink { from { transform: scale(1.06) translateY(0); } to { transform: scale(1.14) translateY(-3%); } }
@media (prefers-reduced-motion: reduce) {
    .case__photo, .case__film { transition: none; animation: none !important; }
    .card:hover .case__photo, .card.is-live .case__photo { transform: none; opacity: 1; }
    .card:hover .case__film,  .card.is-live .case__film  { opacity: 0; }
}
/* A product with no photograph yet: the case is still a case, and an empty
   lit panel reads as "not shot yet" rather than as a broken image. */
.case__none, .tile__none {
    position: absolute; inset: 0; display: grid; place-items: center;
    color: var(--vitrine-2); font-size: 2rem;
}
.tile__none { position: static; aspect-ratio: 1; color: var(--shelf-2); }

.case__out {
    /* Temporarily (owner, 2026-10-04) no veil over the whole photo — just a
       small label in the corner. The veil was: inset: 0; display: grid;
       place-items: center; background: rgba(11,18,22,.55). */
    position: absolute; left: .5rem; top: .5rem;
    padding: .25rem .6rem; border-radius: 999px;
    background: rgba(11,18,22,.78); color: var(--pearl); font-size: .75rem; letter-spacing: .04em;
}
.case__add {
    position: absolute; right: .5rem; bottom: .5rem; width: 40px; height: 40px; border-radius: 50%;
    background: var(--abyss); color: var(--pearl); display: grid; place-items: center;
    box-shadow: 0 6px 18px -8px rgba(0,0,0,.6);
    transition: background .15s, transform .15s;
}
.case__add:hover { background: var(--ikura); color: #fff; transform: scale(1.06); }
.case__add svg { width: 18px; height: 18px; }
/* The pick: "+" alone until something is in the basket, then it opens to
   the left into − · count · +. The "+" never moves, so tapping it again lands
   where the first tap did; the way back from a stray tap sits next to it.
   The count rides on a grain of roe — the dot over the ů in the wordmark:
   ikura on the dark card, black roe on the paper island (no warm colour
   there). */
.pick {
    position: absolute; right: .5rem; bottom: .5rem;
    display: flex; align-items: center; gap: 2px;
    border-radius: 999px;
    transition: background-color .18s ease, box-shadow .18s ease;
}
.pick .case__add { position: static; box-shadow: none; }
.pick.is-open { background: var(--abyss); box-shadow: 0 6px 18px -8px rgba(0,0,0,.6); }
.pick__less {
    width: 40px; height: 40px; border-radius: 50%;
    display: grid; place-items: center;
    color: var(--pearl); background: transparent;
    transition: background-color .15s;
}
.pick__less svg { width: 18px; height: 18px; }
.pick__less:hover { background: rgba(236,232,223,.1); }
.pick__grain {
    min-width: 28px; height: 28px; padding-inline: 7px;
    border-radius: 999px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .8125rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: #fff; text-shadow: 0 1px 1px rgba(60,0,0,.45);
    /* Light comes through a grain of ikura from the upper left and pools at
       the lower edge; the rim is the darker membrane. */
    background: radial-gradient(circle at 33% 28%, #ff9a84 0 8%, #e8301f 28%, var(--ikura) 58%, #7a0303 100%);
    box-shadow: inset 0 -2px 4px rgba(40,0,0,.35);
    user-select: none;
}
.pick__grain.is-up   { animation: grain-plump .34s cubic-bezier(.3, 1.6, .5, 1); }
/* The stock ran short: a line above the pick, and a "no" shake of the "+"
   when nothing at all could go in. */
.pick__note {
    position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 2;
    width: max-content; max-width: 12rem; padding: .3rem .6rem; border-radius: 8px;
    background: var(--abyss); color: var(--pearl); font-size: .75rem; font-weight: 600; line-height: 1.3;
    box-shadow: 0 6px 18px -8px rgba(0,0,0,.6); pointer-events: none;
}
.tile .pick__note { background: var(--shelf-ink); color: var(--shelf-on-ink); max-width: 9.5rem; }
.pick.is-no .case__add, .pick.is-no .tile__add { animation: pick-no .36s ease; }
@keyframes pick-no { 20%, 60% { transform: translateX(-3px); } 40%, 80% { transform: translateX(3px); } }
.pick__grain.is-down { animation: grain-give .3s ease; }
@keyframes grain-plump { 40% { transform: scale(1.24); } }
@keyframes grain-give  { 40% { transform: scale(.76); } }
@media (prefers-reduced-motion: reduce) {
    .pick, .pick__less { transition: none; }
    .pick__grain.is-up, .pick__grain.is-down, .pick.is-no .case__add, .pick.is-no .tile__add { animation: none; }
}

/* Paper island: ink instead of ikura. The pick has a line of its own under
   the price (a 150 px tile cannot hold the price and the open pick side by
   side): grains on the left, the pick on the right, opening leftwards from a
   "+" that stays put. While it is open the step's price beside the grains
   steps aside — the price line above already says what the basket pays. */
.tile__foot { display: flex; align-items: center; gap: .3rem; min-height: 34px; margin-top: .35rem; min-width: 0; }
.tile__foot .ladder { margin-top: 0; flex-wrap: nowrap; min-width: 0; overflow: hidden; }
.tile .pick { position: relative; right: auto; bottom: auto; margin-left: auto; }
.tile__foot:has(.pick.is-open) .ladder__price { display: none; }
.tile__foot:not(:has(.pick, .ladder)) { display: none; }
.tile .pick.is-open { background: var(--shelf-card); box-shadow: 0 0 0 1px var(--shelf-2), 0 4px 12px -6px rgba(0,0,0,.35); }
.tile .pick__less { width: 28px; height: 34px; color: var(--shelf-ink); }
.tile .pick__less svg { width: 16px; height: 16px; }
.tile .pick__less:hover { background: var(--shelf-2); }
.tile .pick__grain {
    min-width: 24px; height: 24px;
    text-shadow: 0 1px 1px rgba(0,0,0,.6);
    background: radial-gradient(circle at 33% 28%, #8d8a83 0 7%, #34322d 30%, #161512 65%, #000 100%);
    box-shadow: inset 0 -2px 3px rgba(0,0,0,.5);
}
.card__body { padding: .75rem .1rem 0; display: flex; flex-direction: column; gap: .15rem; }
.card__name { font-family: var(--font-display); font-size: 1.25rem; line-height: 1.15; font-weight: 500; }
.card__name a:hover { color: #fff; }
.card__meta { font-size: .8125rem; color: var(--nacre-dim); }
.card__price { display: flex; align-items: baseline; gap: .5rem; margin-top: .3rem; font-weight: 600; font-size: 1.0625rem; }
.card__price small { font-weight: 400; font-size: .75rem; color: var(--nacre-dim); }
.card--out .card__price { color: var(--nacre); }
@media (min-width: 900px) { .card__name { font-size: 1.375rem; } }

/* An "Akce" price: the old figure struck through, never a badge on the food. */
.price__was { color: var(--nacre-dim); text-decoration: line-through; font-weight: 400; font-size: .875rem; }
/* The add button answers, briefly, so the eye knows the tap landed. On the
   dark card that answer is the buy colour; on the paper shelf it is a nudge,
   because the island has no warm colour in it at all. */
.card.is-added .case__add { background: var(--ikura); color: #fff; }
.tile__add { transition: transform .15s ease; }
.tile.is-added .tile__add { transform: scale(1.14); }
/* Sold out takes its own line under the price rather than squeezing in beside
   it: in a 158 px tile the two of them side by side wrap each other. The photo
   goes quiet at the same time, so the state is visible before the words are
   read. */
.tile__out {
    display: block; margin-top: .35rem; padding-top: .35rem;
    border-top: 1px solid var(--shelf-2);
    font-size: .75rem; font-weight: 600; color: var(--shelf-mute);
}
.tile--out img { opacity: .45; filter: grayscale(1); }
.tile--out .tag { color: var(--shelf-mute); }
.tile a { display: block; }

/* --- the promise line ---------------------------------------------------- */
.promise { border-top: 1px solid var(--hair-soft); border-bottom: 1px solid var(--hair-soft); padding-block: clamp(1.5rem, 4vw, 2.5rem); }
.promise p { font-family: var(--font-display); font-size: clamp(1.375rem, 1rem + 1.6vw, 2rem); line-height: 1.3; font-weight: 300; max-width: 46ch; margin: 0; text-wrap: pretty; }
.promise p b { font-weight: 500; color: #fff; }
.promise ul { list-style: none; margin: 1.25rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem 2rem; color: var(--nacre); font-size: .9375rem; }
.promise li::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--ikura); margin-right: .6rem; vertical-align: .1em; }

/* --- the island: fish & crayfish ------------------------------------------
   The room goes light. Sans-serif everywhere, price on a sticker, photos on
   plain white tiles, denser grid. Nothing here is styled to impress; that
   is the point, and it makes the dark shop above look darker.
   ----------------------------------------------------------------------- */
.shelf { background: var(--shelf); color: var(--shelf-ink); padding-block: clamp(2rem, 5vw, 4rem); }
/* The light is let in, not switched on (owner, 17.10: "luxury — smooth but
   intense"). The island exists in two themes, the dark room's and the
   paper's, and --lit (0 dark … 1 paper) inverts every colour of it in
   place: ground, kraft edges, ink, tiles, chips. The layout script runs
   --lit up frame by frame (kdLit): on the home page once the band is in
   view, on the island as a page on arrival (.is-dawn holds it dark until
   the script takes over), also on a reload — not when moving within the
   shelf. Not a CSS transition or animation of a registered property:
   Brave/Chrome repainted the grounds but left the type in its old colour
   (a blank ink chip), while a style written each frame repaints all. */
.shelf {
    --lit: 1;
    --shelf:        color-mix(in oklab, #f2eee6 calc(var(--lit) * 100%), #0b1216);
    --shelf-2:      color-mix(in oklab, #e6e0d3 calc(var(--lit) * 100%), #26333b);
    --shelf-card:   color-mix(in oklab, #fff calc(var(--lit) * 100%), #121b21);
    /* Type must never cross its ground at the same pace: halfway, ink and
       paper would be the same grey and an ink chip a blank pill. So the
       grounds glide over the whole run, the ink turns over in a blink
       (a twentieth of it) as they pass the middle, and type on an ink
       chip flips outright at the midpoint. */
    --lit-ink:  clamp(0, calc((var(--lit) - .5) * 20 + .5), 1);
    --lit-flip: clamp(0, calc((var(--lit) - .5) * 1000 + .5), 1);
    --shelf-ink:    color-mix(in oklab, #1c1a16 calc(var(--lit-ink) * 100%), #ece8df);
    --shelf-mute:   color-mix(in oklab, #6d685f calc(var(--lit-ink) * 100%), #a8a59c);
    --shelf-on-ink: color-mix(in oklab, #fff calc(var(--lit-flip) * 100%), #0b1216);
}
@media (prefers-reduced-motion: no-preference) { .shelf--page.is-dawn { --lit: 0; } }
@media (prefers-reduced-motion: reduce) { .shelf { --lit: 1 !important; } }
.shelf--page { padding-top: 0; }
.shelf__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem; flex-wrap: wrap; }
.shelf__head h2 { font-family: var(--font-body); font-weight: 800; font-size: clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem); letter-spacing: -.02em; }
.shelf__head p { margin: .2rem 0 0; color: var(--shelf-mute); font-size: .9375rem; }
/* The same edges as the category strip above: the row bleeds to the screen
   edge, carries the gutter as its own padding and fades out over it, so a
   chip cut at either end reads as "there is more" instead of being sliced
   off by a hard line. At rest nothing is dimmed. */
.shelf__tabs {
    display: flex; gap: .4rem; overflow-x: auto; scrollbar-width: none;
    margin-inline: calc(var(--gutter) * -1); padding: 0 var(--gutter) .25rem;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent);
    mask-image: linear-gradient(to right, transparent, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent);
}
.shelf__tabs::-webkit-scrollbar { display: none; }
.shelf__tabs a { flex: none; padding: .45rem .9rem; border-radius: 999px; border: 1px solid var(--shelf-2); font-size: .875rem; font-weight: 600; color: var(--shelf-ink); background: var(--shelf-card); }
.shelf__tabs a[aria-current] { background: var(--shelf-ink); color: var(--shelf-on-ink); border-color: var(--shelf-ink); }
/* As many columns as keep a tile at least 170 px wide: under that the line
   "grains, step price, +" no longer fits (1 000 Kč was running under the
   "+" at six columns on a 1280 screen). Two on a phone, four or five on a
   desktop; empty tracks stay, so a short category keeps the same tile size. */
.shelf__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(170px, calc(50% - .375rem)), 1fr)); gap: .75rem; margin-top: 1rem; }
@media (min-width: 1100px) { .shelf__grid { gap: 1rem; } }
.tile { background: var(--shelf-card); color: var(--shelf-ink); border-radius: 6px; padding: .6rem .6rem .7rem; display: flex; flex-direction: column; gap: .3rem; border: 1px solid var(--shelf-2); }
/* height:auto matters: the <img> carries width/height attributes, and with a
   used height coming from the attribute the aspect-ratio below is ignored —
   the tile then stands 300 px tall with the photo adrift in the middle of it. */
.tile img { width: 100%; height: auto; aspect-ratio: 1; object-fit: contain; }
.tile__name { font-size: .875rem; font-weight: 600; line-height: 1.25; }
/* A shelf tile with a shoot film: the dark square of the film inside the
   paper tile; the photo is its first frame, so the change is invisible. */
.tile__media { position: relative; aspect-ratio: 1; border-radius: 4px; overflow: hidden; background: #0b1216; cursor: pointer; }
.tile__media img, .tile__media video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; aspect-ratio: auto; }
.tile__film { opacity: 0; }
.tile--film.is-playing .tile__film { opacity: 1; }
.tile__meta { font-size: .75rem; color: var(--shelf-mute); }
.tile__row { display: flex; align-items: center; justify-content: space-between; margin-top: auto; padding-top: .3rem; }
/* The sticker is one line, always: a price broken across two lines stops
   looking like a price and starts looking like a mistake. */
.tag { font-weight: 800; font-size: 1rem; letter-spacing: -.01em; white-space: nowrap; font-variant-numeric: tabular-nums; }
.tag small { font-weight: 600; font-size: .6875rem; color: var(--shelf-mute); }
.tag .price__was { color: var(--shelf-mute); font-weight: 500; font-size: .8125rem; margin-right: .2rem; }
/* The sticker is yellow paper in both themes, so its type stays dark ink. */
.tag:not(.tile--out .tag) { color: #1c1a16; }
.tag:not(.tile--out .tag) small { color: #6d685f; }
.tile__add { width: 34px; height: 34px; border-radius: 50%; background: var(--shelf-ink); color: var(--shelf-on-ink); display: grid; place-items: center; }
.tile__add svg { width: 16px; height: 16px; }
.shelf__foot { margin-top: 1.25rem; font-size: .9375rem; color: var(--shelf-mute); }
.shelf__foot a { font-weight: 700; color: var(--shelf-ink); border-bottom: 2px solid var(--shelf-ink); }

/* The fish branch has a page of its own, and on it the whole page is the
   shelf: the rail, the sort links and the pager have to come into the
   daylight with it, or the island arrives with the dark room's furniture
   still standing in it. */
/* The chip row is sticky, so on the shelf it needs the paper under it — with
   the dark room's translucent ground removed and nothing put back, the tiles
   scrolled straight through the chips. */
.shelf .catnav { background: color-mix(in srgb, var(--shelf) 94%, transparent); border-bottom-color: var(--shelf-2); }
.shelf .catnav a { color: var(--shelf-ink); border-color: var(--shelf-2); background: var(--shelf-card); }
.shelf .catnav a small { color: var(--shelf-mute); }
.shelf .catnav a[aria-current] { background: var(--shelf-ink); color: var(--shelf-on-ink); border-color: var(--shelf-ink); }
.shelf .catnav a[aria-current] small { color: var(--shelf-2); }
.shelf__title { font-family: var(--font-body); font-weight: 800; font-size: clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem); letter-spacing: -.02em; }
.shelf__lede { margin: .2rem 0 0; color: var(--shelf-mute); font-size: .9375rem; max-width: 60ch; }
.cat__lede { margin: .35rem 0 0; color: var(--nacre); font-size: .9375rem; max-width: 60ch; }
.shelf .sortbar__link { color: var(--shelf-mute); }
.shelf .sortbar__link:hover { color: var(--shelf-ink); border-bottom-color: var(--shelf-2); }
.shelf .sortbar__on { color: var(--shelf-ink); }
.shelf .sortbar__mark { color: var(--shelf-mute); }
.shelf .pager__page, .shelf .pager__step, .shelf .pager__gap { border-color: var(--shelf-2); color: var(--shelf-mute); background: var(--shelf-card); }
.shelf .pager a:hover { border-color: var(--shelf-ink); color: var(--shelf-ink); }
.shelf .pager [aria-current="page"] { background: var(--shelf-ink); border-color: var(--shelf-ink); color: var(--shelf-on-ink); }
.shelf .empty { color: var(--shelf-mute); }
@media (min-width: 900px) {
    .shelf .catnav { background: none; backdrop-filter: none; }
    .shelf .catnav a { background: none; border: 0; color: var(--shelf-mute); }
    .shelf .catnav a:hover { color: var(--shelf-ink); }
    .shelf .catnav a[aria-current] { background: none; color: var(--shelf-ink); }
    /* Ink on the island: no warm colour lives on the paper. */
    .shelf .catnav a[aria-current] > span { text-decoration-color: var(--shelf-ink); }
    /* The chip's counter is knocked out on a dark pill; in the rail there is
       no pill behind it, so it has to come back to the paper's grey. */
    .shelf .catnav a[aria-current] small { color: var(--shelf-mute); }
    .shelf .catnav .is-root a { color: var(--shelf-ink); }
    .shelf .catnav .is-root.is-shelf { border-top-color: var(--shelf-2); }
}

/* --- footer -------------------------------------------------------------- */
.foot { border-top: 1px solid var(--hair-soft); padding-block: clamp(2rem, 5vw, 4rem) 1.5rem; color: var(--nacre); font-size: .9375rem; }
.foot__cols { display: grid; gap: 2rem; }
.foot__logo img { height: 22px; width: auto; margin-bottom: .75rem; }
.foot h3 { font-family: var(--font-body); font-size: .8125rem; font-weight: 600; color: var(--pearl); margin-bottom: .6rem; }
.foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.foot a:hover { color: var(--pearl); }
.foot__legal { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--hair-soft); font-size: .8125rem; color: var(--nacre-dim); }
@media (min-width: 900px) { .foot__cols { grid-template-columns: 1.4fr 1fr 1fr; } }
/* The way to wholesale: Caviar House's own mark, in pearl like ours. */
.foot__trade {
    display: grid; gap: 1rem; justify-items: start; align-items: center;
    margin-top: 2.5rem;
}
.foot__trade img { height: 36px; width: auto; }
.foot__trade-text { display: grid; gap: .25rem; max-width: 52ch; }
.foot__trade-text b { color: var(--pearl); font-weight: 600; }
.foot__trade:hover .foot__trade-cta { border-color: var(--pearl); }
@media (min-width: 900px) {
    .foot__trade { grid-template-columns: auto 1fr auto; gap: 2rem; }
    .foot__trade img { height: 42px; }
}

/* --- product page ---------------------------------------------------------- */
.pdp { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; padding-block: 0 clamp(2rem, 5vw, 4rem); }
/* minmax(0, …): the gallery's row of thumbs scrolls inside its column instead
   of stretching the column, and with it the page, wider than the phone. */
.pdp > * { min-width: 0; }
.pdp__media { margin-inline: calc(var(--gutter) * -1); }        /* full bleed on phone */
.stage {
    position: relative; overflow: hidden; aspect-ratio: 1 / 1;   /* square like the cards since 2026-10-04; was 4 / 5 */
    background: linear-gradient(to bottom, var(--vitrine), var(--vitrine-2));
}
.stage .case__photo { padding: 8%; }
/* A photo shot on a dark backdrop (AppServiceProvider::isScene) is not a
   cut-out: padding and multiply would leave it a dark square on the paper.
   It fills the case edge to edge instead; cover trims the square shot's
   sides to the 4:5 case. */
.case__photo--scene,
.stage .case__photo--scene { padding: 0; object-fit: cover; mix-blend-mode: normal; }
/* The light vitrine behind it still shows as a pale hairline where the
   rounded corners and sub-pixel scaling antialias the photo's edge — give a
   scene case a ground the colour of those dark backdrops instead. */
.case:has(> .case__photo--scene),
.stage:has(> .case__photo--scene) { background: #0f1014; }
.stage__play {
    position: absolute; left: 50%; bottom: 1rem; transform: translateX(-50%);
    display: inline-flex; align-items: center; gap: .5rem; padding: .55rem 1rem .55rem .7rem;
    border-radius: 999px; background: rgba(11,18,22,.82); color: var(--pearl); font-size: .875rem; font-weight: 600;
    backdrop-filter: blur(8px);
}
.stage__play svg { width: 18px; height: 18px; }
.stage.is-live .case__photo { transform: scale(1.08); opacity: 0; }
.stage.is-live .case__film { opacity: 1; }
.stage.is-live .case__film--fake { animation: sink 8s ease-in-out infinite alternate; }
.stage.is-live .stage__play { opacity: 0; pointer-events: none; }
.thumbs { display: flex; gap: .5rem; padding: .75rem var(--gutter) 0; position: relative; z-index: 2; overflow-x: auto; scrollbar-width: none; }
.thumbs::-webkit-scrollbar { display: none; }
.thumbs button { flex: none; }
.thumbs button { width: 56px; height: 56px; border-radius: var(--r); overflow: hidden; background: var(--vitrine); border: 1px solid transparent; }
.thumbs button[aria-current] { border-color: var(--pearl); }
.thumbs img { width: 100%; height: 100%; object-fit: contain; mix-blend-mode: multiply; padding: 6px; }
.thumbs .thumbs__film { position: relative; }
.stage .pdp__film { opacity: 0; }
.stage.is-playing .pdp__film { opacity: 1; }
.stage:has(> .pdp__film) { background: #0f1014; }
/* Shoot frames are dark, edge to edge: no multiply, no padding in the thumbs. */
.thumbs img[src*="-DSCF"], .thumbs img[src*="-IMG8"], .thumbs img[src*="-IMG9"], .thumbs__film img { mix-blend-mode: normal; padding: 0; object-fit: cover; }
/* Shoot frames are dark: hold the same dark ground as the case while the thumb loads, not the lit vitrine. */
.thumbs button:has(img[src*="-DSCF"], img[src*="-IMG8"], img[src*="-IMG9"]), .thumbs .thumbs__film { background: #0f1014; }
.thumbs .thumbs__film::after { content: "▶"; position: absolute; inset: 0; display: grid; place-items: center; font-size: .75rem; color: var(--pearl); background: rgba(11,18,22,.5); }

.pdp__crumbs { font-size: .8125rem; color: var(--nacre-dim); display: flex; flex-wrap: wrap; gap: .5rem; padding-block: .75rem; }
.pdp__crumbs a:hover { color: var(--pearl); }
.pdp__brand { font-size: .8125rem; color: var(--nacre); margin: 0 0 .5rem; }
.pdp h1 { font-size: var(--t-4); font-weight: 400; text-wrap: balance; }
.pdp__lede { font-family: var(--font-display); font-size: 1.25rem; line-height: 1.35; color: var(--nacre); margin: 1rem 0 0; max-width: 46ch; }
.pdp__price { display: flex; align-items: baseline; gap: .75rem; margin-top: 1.5rem; }
.pdp__now { font-size: 1.75rem; font-weight: 600; }
.pdp__kg { font-size: .875rem; color: var(--nacre-dim); }
.pdp__stock { font-size: .875rem; color: var(--ok); margin-top: .35rem; }
.pdp__stock--out { color: var(--nacre); }
.buy { display: grid; grid-template-columns: auto 1fr; gap: .75rem; margin-top: 1.25rem; }
/* iOS Safari reads two quick taps in one place as "zoom in", and the controls
   below are exactly where a finger taps quickly. touch-action: manipulation
   turns off that one gesture and leaves scrolling and pinch alone.

   It goes on whole controls, not on their buttons: there is a gap between the
   − , the number and the +, and a tap two pixels off a button is the same
   gesture to Safari. That is also why the whole of the fixed chrome is listed
   — the burger, the bag, the language capsule, the rows inside both sheets,
   the sort trigger. Opening and closing the menu twice in a row is the same
   two taps in the same place as pressing "+" twice.

   Deliberately NOT on the page as a whole: there a double tap must still
   magnify the product photographs. */
.qty, .cartline__qty, .tile__add, .case__add, .pick__less,
.iconbtn, .mast__chat, .langs__now, .langs__pop a,
.sheet__link, .sheet__langs a, .chat,
.sorter__btn, .sortbar__link, .sortbar__on { touch-action: manipulation; }
.qty { display: inline-flex; align-items: center; border: 1px solid var(--hair); border-radius: var(--r); height: 48px; }
.qty button { width: 44px; height: 100%; font-size: 1.25rem; color: var(--nacre); }
.qty button:hover { color: var(--pearl); }
.qty button.is-capped { opacity: .35; cursor: not-allowed; }
.qty button.is-no { animation: pick-no .36s ease; }
@media (prefers-reduced-motion: reduce) { .qty button.is-no { animation: none; } }
.qty output { min-width: 3.5ch; text-align: center; font-weight: 600; }
.qty output small { font-weight: 400; color: var(--nacre-dim); font-size: .75rem; margin-left: 2px; }
.pdp__facts { list-style: none; margin: 1.5rem 0 0; padding: 1.25rem 0 0; border-top: 1px solid var(--hair-soft); display: grid; gap: .6rem; color: var(--nacre); font-size: .9375rem; }
.pdp__facts li::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--ikura); margin-right: .6rem; vertical-align: .1em; }
.pdp__prose { color: var(--nacre); margin-top: 1.5rem; max-width: 60ch; }
.pdp__prose h3 { font-size: 1.375rem; color: var(--pearl); margin-bottom: .4rem; }
.spec { display: grid; grid-template-columns: auto 1fr; gap: .4rem 1.25rem; margin: 1.25rem 0 0; font-size: .9375rem; }
.spec dt { color: var(--nacre-dim); } .spec dd { margin: 0; color: var(--pearl); }

/* sticky buy bar on phone: price and the button never scroll away */
.buybar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    padding: .6rem var(--gutter) calc(.6rem + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--abyss) 88%, transparent); backdrop-filter: blur(14px);
    border-top: 1px solid var(--hair-soft);
}
/* The row, not the bar, is the flex line: the bar holds one of two shapes and
   each brings its own row. */
.buybar__row { display: flex; align-items: center; gap: 1rem; }
.buybar__price { font-weight: 600; font-size: 1.125rem; line-height: 1.1; white-space: nowrap; }
.buybar__price small { display: block; font-weight: 400; font-size: .75rem; color: var(--nacre-dim); }
.buybar .btn { flex: 1; }
@media (min-width: 900px) {
    .pdp { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 3rem; align-items: start; padding-top: 0; }
    /* Capped: a 4:5 case across half of a wide screen is a metre of jar, and
       the column beside it then ends halfway up the page. */
    .pdp__media { margin-inline: 0; position: sticky; top: calc(var(--masthead-h) + 1rem); max-width: 560px; }
    .stage { border-radius: var(--r); }
    .thumbs { padding-inline: .75rem; }   /* off the edge of the case, like the top (owner, 17.10) */
    .pdp__body { padding-top: 1rem; }
    .buybar { display: none; }
}
.pairs { padding-block: 1rem clamp(2.5rem, 6vw, 4rem); }   /* air above the footer rule (owner, 17.10) */
.pairs h2 { font-size: var(--t-3); font-weight: 300; margin-bottom: 1rem; }
.pairs .grid { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 900px) { .pairs .grid { grid-template-columns: repeat(4, 1fr); } }

.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ==========================================================================
   The rest of the shop, on the same tokens
   --------------------------------------------------------------------------
   Everything above is the design kit (docs/design/kaviardum/kd.css) and is
   copied from it rather than rewritten. What follows is the machinery the kit
   does not draw — the menu sheet, forms, cart, checkout, account, legal pages
   — moved onto the same room: dark ground, pearl text, hairline rules, one
   ikura button per screen. No screen keeps the old ice/marine skin.
   ========================================================================== */

/* The footer sits at the bottom of a short page instead of halfway up it. */
.page { display: flex; flex-direction: column; min-height: 100vh; }
.page > main { flex: 1 0 auto; }

/* --- the phone sheet ------------------------------------------------------
   The bar's second shape. On --depth, not on paper: a white panel dropping
   out of a dark bar is the single loudest thing a dark page can do.
   ----------------------------------------------------------------------- */
.sheet {
    position: fixed; left: 0; right: 0; top: var(--masthead-h); bottom: 0;
    z-index: 39;
    background: var(--depth);
    border-top: 1px solid var(--hair-soft);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-block: .5rem 2rem;
}
.sheet__link {
    display: flex; align-items: center; gap: .6rem;
    min-height: 52px;
    border-bottom: 1px solid var(--hair-soft);
    font-size: 1.0625rem;
    color: var(--pearl);
}
.sheet__link--tel { font-weight: 600; font-variant-numeric: tabular-nums; }
.sheet__link svg { width: 17px; height: 17px; color: var(--nacre); }
.sheet__chats { display: flex; gap: .5rem; padding-block: 1rem; }
.sheet__langs { display: flex; gap: .25rem; padding-top: .5rem; }
.sheet__langs a {
    min-width: 44px; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--hair); border-radius: var(--r);
    font-size: .875rem; color: var(--nacre); text-transform: uppercase;
}
.sheet__langs a[aria-current="true"] { background: var(--pearl); border-color: var(--pearl); color: var(--abyss); }

/* The two apps, written out where there is room for the word. */
.chat {
    display: inline-flex; align-items: center; gap: .45rem;
    min-height: 44px; padding: .5rem .9rem;
    border: 1px solid var(--hair); border-radius: var(--r);
    font-size: .875rem; color: var(--pearl);
}
.chat svg { width: 16px; height: 16px; color: var(--nacre); }
.chat:hover { border-color: var(--pearl); }

/* Language in the wide bar: a capsule showing the one in use, with the other
   four behind it. Five codes spelled out in a row spent the width of a whole
   nav item on a control almost nobody touches twice. */
.langs { display: none; position: relative; margin-left: .4rem; }
@media (min-width: 1100px) { .langs { display: block; } }
.langs__now {
    display: inline-flex; align-items: center; gap: .35rem;
    height: 30px; padding: 0 .45rem 0 .6rem;
    border: 1px solid var(--hair); border-radius: 999px;
    font-size: .8125rem; letter-spacing: .06em; text-transform: uppercase;
    color: var(--nacre);
}
.langs__now:hover { color: var(--pearl); border-color: var(--nacre-dim); }
.langs__now svg { width: 9px; height: 9px; opacity: .7; }
.langs__pop {
    position: absolute; right: 0; top: calc(100% + .45rem); z-index: 50;
    display: grid; gap: .1rem; min-width: 72px; padding: .25rem;
    background: var(--depth); border: 1px solid var(--hair); border-radius: var(--r);
    box-shadow: 0 20px 44px -26px #000;
}
.langs__pop a {
    padding: .45rem .6rem; border-radius: 2px;
    font-size: .8125rem; letter-spacing: .06em; text-transform: uppercase;
    color: var(--nacre);
}
.langs__pop a:hover { background: rgba(235, 231, 221, .06); color: var(--pearl); }

/* --- buttons the kit does not name ---------------------------------------
   The kit has --pearl / --line / --ikura. The blades speak in --primary /
   --ghost / --danger, so those are the same three buttons under the names
   the markup already uses, plus the one red that takes something away.
   ----------------------------------------------------------------------- */
.btn--primary { background: var(--ikura); color: #fff; }
.btn--primary:hover { background: #a10404; }
.btn--ghost { border: 1px solid var(--hair); color: var(--pearl); }
.btn--ghost:hover { border-color: var(--pearl); }
/* Ikura is the colour of the button that buys, so the button that removes
   must not wear it. */
.btn--danger { background: none; border: 1px solid rgba(255, 90, 69, .5); color: var(--ikura-lit); }
.btn--danger:hover { border-color: var(--ikura-lit); background: rgba(255, 90, 69, .08); }
.btn--sm { min-height: 38px; padding: .4rem .9rem; font-size: .875rem; }

/* The account link is a word, and the phone bar has no room for one: it moves
   into the sheet with the nav rather than shrinking below a thumb target. */
.mast__link { display: none; }
@media (min-width: 1100px) { .mast__link { display: inline-flex; align-items: center; white-space: nowrap; } }
.mast__link svg { display: none; width: 20px; height: 20px; }
/* 1100–1319: four branches, the loupe and the rest leave no room for the
   word — "Мой аккаунт" alone squeezed the wordmark from 161 to 141 px before
   the loupe was there. The figure stands in for it, as the handset does for
   the number. The word's threshold is later than the number's (1260): with
   the number spelled out, Russian "Мой аккаунт" needs ~1310 px without
   leaning into the gutter (measured 2026-09-30 with the live branch names;
   re-measure when they change). */
@media (min-width: 1100px) and (max-width: 1319px) {
    .mast__link { width: 40px; height: 44px; justify-content: center; padding: 0; color: var(--pearl); }
    .mast__link svg { display: block; }
    .mast__link-word { display: none; }
}

.linkish {
    font-size: .8125rem; color: var(--nacre-dim);
    text-decoration: underline; text-underline-offset: 3px;
}
.linkish:hover { color: var(--pearl); }

/* --- page furniture ------------------------------------------------------- */
.pagehead { padding-block: clamp(1.75rem, 4vw, 3rem) 1.25rem; }
.pagehead h1 { font-size: var(--t-4); font-weight: 300; }
.pagetitle { font-size: var(--t-4); font-weight: 300; margin: 0 0 1.25rem; }
.muted { color: var(--nacre); }
.eyebrow { font-size: .8125rem; color: var(--nacre); margin: 0 0 .5rem; }
.lede { font-family: var(--font-display); font-size: 1.25rem; line-height: 1.35; color: var(--nacre); }
.empty { color: var(--nacre); grid-column: 1 / -1; padding-block: 2rem; }
.crumbs { font-size: .8125rem; color: var(--nacre-dim); display: flex; gap: .5rem; padding-block: .75rem; }
.crumbs a:hover { color: var(--pearl); }

.notice {
    border: 1px solid var(--hair); border-left: 2px solid var(--ikura);
    border-radius: var(--r);
    background: var(--depth);
    padding: .7rem 1rem; margin: 0 0 .75rem;
    font-size: .9375rem; color: var(--pearl);
}
.hint { font-size: .8125rem; color: var(--nacre-dim); margin: .25rem 0 .75rem; }

.stock { font-size: .875rem; color: var(--ok); }
.stock--low { color: #d8b45f; }
.stock--out { color: var(--nacre); }

/* --- sort links -----------------------------------------------------------
   Four links, not a dropdown: a <select> that submits itself fired once per
   arrow key and came back from the reload disagreeing with its own results.
   ----------------------------------------------------------------------- */
.sortbar { display: flex; align-items: baseline; flex-wrap: wrap; gap: .75rem; font-size: .875rem; }
.sortbar__mark { color: var(--nacre-dim); flex: none; transform: translateY(2px); }
.sortbar__link { color: var(--nacre); border-bottom: 1px solid transparent; padding-bottom: 1px; }
.sortbar__link:hover, .sortbar__link:focus-visible { color: var(--pearl); border-bottom-color: var(--hair); }
/* The current order is stated, not offered — so it is not a link. */
.sortbar__on { color: var(--pearl); font-weight: 600; border-bottom: 1px solid var(--ikura); padding-bottom: 1px; }

/* Wide screens: the row of links, exactly as before. The trigger, the veil
   and the sheet's own title exist only for the phone. */
.sorter { position: relative; }
.sorter__btn, .sorter__veil, .sortbar__title { display: none; }
/* The row never gives way to a long category description beside it: the
   text wraps, the four links stay on one line. */
@media (min-width: 900px) {
    .sorter { flex: none; }
    .sortbar { flex-wrap: nowrap; white-space: nowrap; }
}

@media (max-width: 899px) {
    /* A phone gets a button that names the order in use; the four options
       arrive as a sheet from the bottom, where the thumb is. */
    .sorter__btn {
        display: inline-flex; align-items: center; gap: .45rem;
        min-height: 40px; padding: .4rem .9rem;
        border: 1px solid var(--hair); border-radius: 999px;
        font-size: .875rem; color: var(--pearl);
    }
    .sorter__veil { display: block; position: fixed; inset: 0; z-index: 60; background: rgba(4, 8, 10, .6); }
    .sortbar {
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 61;
        display: block;
        padding: 1rem var(--gutter) calc(1rem + env(safe-area-inset-bottom));
        background: var(--depth);
        border-top: 1px solid var(--hair);
        border-radius: 14px 14px 0 0;
        transform: translateY(101%);
        transition: transform .22s cubic-bezier(.2, .7, .2, 1);
        visibility: hidden;
    }
    .sortbar.is-open { transform: none; visibility: visible; }
    .sortbar .sortbar__mark { display: none; }
    .sortbar__title {
        display: block; margin-bottom: .5rem;
        font-size: .75rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
        color: var(--nacre-dim);
    }
    .sortbar__link, .sortbar__on {
        display: block; padding: .85rem 0; border-bottom: 1px solid var(--hair-soft);
        font-size: 1.0625rem; border-radius: 0;
    }

    /* The sheet is the dark panel whatever the page behind it is, so on the
       paper shelf it must keep the dark-page inks. Without this the shelf's
       own overrides win on specificity and paint the current order in
       --shelf-ink, #1c1a16 on a #121b21 panel: a contrast of 1.06:1, which is
       no text at all. The trigger has the mirror problem — it sits ON the
       paper, so there it takes the paper's ink instead of pearl. */
    .shelf .sortbar__link { color: var(--nacre); }
    /* Hover too, and not for the mouse: a tap leaves :hover stuck on a touch
       screen, so without this the option the customer just pressed would go
       black-on-black on the way out. */
    .shelf .sortbar__link:hover { color: var(--pearl); border-bottom-color: var(--hair-soft); }
    .shelf .sortbar__on   { color: var(--pearl); }
    .shelf .sorter__btn   { color: var(--shelf-ink); border-color: var(--shelf-2); }
    /* The order in use is marked by the grain, not by a red rule running the
       width of the sheet. */
    .sortbar__on { border-bottom-color: var(--hair-soft); }
    .sortbar__on::before {
        content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
        background: var(--ikura); margin-right: .6rem; vertical-align: .15em;
    }
    .sortbar > :last-child { border-bottom: 0; }
}

/* --- pagination ----------------------------------------------------------- */
.pager { display: flex; align-items: center; gap: .3rem; }
/* Catalogue feed (layout script): the pager is the no-JS way and the
   fallback; while the feed works it is hidden and three grains breathe where
   it was, only while a page is on its way. */
.feed__pager { margin-top: 2.5rem; }
.feed__more { height: 3rem; margin-top: 1.5rem; display: flex; justify-content: center; align-items: center; gap: .5rem; }
.feed__more.is-loading::before, .feed__more.is-loading::after {
    content: ""; width: 7px; height: 7px; border-radius: 50%;
    background: var(--ikura); animation: feed-grain 1s ease-in-out infinite;
}
.feed__more.is-loading::after { animation-delay: .33s; }
/* The paper island takes no warm colour: black roe there, as on its pick. */
.shelf .feed__more.is-loading::before, .shelf .feed__more.is-loading::after { background: var(--shelf-ink); }
@keyframes feed-grain { 50% { opacity: .25; transform: scale(.7); } }
.feed__back { display: inline-block; margin-bottom: 1.25rem; font-size: .875rem; color: var(--nacre-dim); }
.feed__back:hover { color: var(--pearl); }
.pager__page, .pager__step, .pager__gap {
    min-width: 40px; height: 40px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--hair); border-radius: var(--r);
    font-size: .9375rem; color: var(--nacre);
}
.pager__gap { border-color: transparent; }
.pager a:hover { border-color: var(--pearl); color: var(--pearl); }
.pager [aria-current="page"] { background: var(--pearl); border-color: var(--pearl); color: var(--abyss); font-weight: 600; }
.pager [aria-disabled="true"] { opacity: .35; }

/* --- forms ----------------------------------------------------------------
   Inputs are a lit-but-not-white surface on the dark ground: --depth with a
   hairline, and never below 16 px, which is the size at which iOS Safari
   stops zooming the page on focus.
   ----------------------------------------------------------------------- */
.fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; }
.field { display: flex; flex-direction: column; gap: .3rem; }
.field--wide { grid-column: 1 / -1; }
.field > span { font-size: .8125rem; color: var(--nacre); }
.field > span em { font-style: normal; color: var(--nacre-dim); }
.field input, .field textarea, .field select, .codeform input {
    font: inherit;
    font-size: 1rem;
    padding: .7rem .8rem;
    border: 1px solid var(--hair);
    border-radius: var(--r);
    background: var(--depth);
    color: var(--pearl);
    width: 100%;
}
.field input:focus, .field textarea:focus, .field select:focus, .codeform input:focus {
    border-color: var(--nacre); outline: none;
}
.field select { appearance: none; }
.field select option { background: var(--depth); color: var(--pearl); }
::placeholder { color: var(--nacre-dim); opacity: 1; }

.choices { display: grid; gap: .5rem; }
.choice {
    display: flex; align-items: flex-start; gap: .75rem;
    padding: .85rem 1rem;
    border: 1px solid var(--hair); border-radius: var(--r);
    background: var(--depth);
    cursor: pointer;
    transition: border-color .15s ease;
}
.choice:hover { border-color: var(--nacre-dim); }
.choice--on { border-color: var(--pearl); }
.choice input { margin-top: .25rem; accent-color: var(--ikura); flex: 0 0 auto; }
.choice__body { display: flex; flex-direction: column; gap: .1rem; flex: 1; min-width: 0; }
.choice__name { font-weight: 600; font-size: .9375rem; }
.choice__note { font-size: .8125rem; color: var(--nacre-dim); }
.choice__eta { font-size: .8125rem; color: var(--ok); }
.choice__price { font-weight: 600; white-space: nowrap; font-size: .9375rem; }
.choice__free { color: var(--ok); font-weight: 600; font-size: .8125rem; }

.terms { display: flex; gap: .6rem; align-items: flex-start; margin-top: 1rem; font-size: .875rem; color: var(--nacre); }
.terms input { margin-top: .2rem; accent-color: var(--ikura); }
.terms a { color: var(--pearl); text-decoration: underline; text-underline-offset: 3px; }

/* --- panels: the checkout's numbered steps, the account's boxes ------------ */
.panel { border: 1px solid var(--hair); border-radius: var(--r); padding: 1.25rem; background: var(--depth); }
.panel__title {
    font-family: var(--font-body);
    font-size: .8125rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
    color: var(--nacre-dim);
    display: flex; align-items: center; gap: .6rem;
    margin: 0 0 1rem;
}
/* Numbered because checkout genuinely is a sequence — the number says how
   much is left, not merely that a divider exists. */
.panel__n {
    width: 22px; height: 22px; border-radius: 999px;
    background: var(--pearl); color: var(--abyss);
    font-size: .6875rem; letter-spacing: 0;
    display: inline-flex; align-items: center; justify-content: center;
}
.panel--offer { border-color: rgba(255, 90, 69, .45); }

/* The small heading over a box of rows — the summary, the points, an order's
   items. Same voice as .panel__title, but it can stand on its own. */
.boxtitle {
    font-family: var(--font-body);
    font-size: .8125rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
    color: var(--nacre-dim);
    margin: 0 0 .75rem;
}

/* --- cart ------------------------------------------------------------------
   The line's photograph is the shop's case at 88 px, so a jar looks the same
   in the cart as it did in the window.
   ----------------------------------------------------------------------- */
.cartpage { padding-block: 2rem 5rem; }
.cartgrid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: clamp(1.5rem, 1rem + 3vw, 3rem);
    align-items: start;
}
.cartline {
    --cartline-tin: 88px;
    position: relative;
    display: grid;
    grid-template-columns: 88px minmax(0, 1fr) auto auto;
    gap: 1rem;
    align-items: center;
    padding: 1rem 0;
    border-bottom: 1px solid var(--hair-soft);
}
.cartline__tin {
    width: 88px; height: 88px; flex: none;
    position: relative; overflow: hidden; border-radius: var(--r);
    background: linear-gradient(to bottom, var(--vitrine), var(--vitrine-2));
    display: grid; place-items: center;
    color: var(--nacre-dim);
}
.cartline__tin img { width: 100%; height: 100%; object-fit: contain; padding: 8%; mix-blend-mode: multiply; }
/* A shoot photo fills the tin like on the shelf (.case__photo--scene); the lit
   vitrine behind it was for white cut-outs and showed as a pale frame. */
.cartline__tin--scene { background: #0f1014; }
.cartline__tin--scene img { object-fit: cover; padding: 0; mix-blend-mode: normal; }
.cartline__name { font-family: var(--font-display); font-size: 1.25rem; font-weight: 500; }
.cartline__name:hover { color: #fff; }
.cartline__meta { margin: .2rem 0 0; font-size: .8125rem; color: var(--nacre-dim); }
.cartline__promo { margin: .2rem 0 0; font-size: .8125rem; color: var(--ok); }
.cartline__promo-sum { margin-left: .45em; white-space: nowrap; font-weight: 600; }
.cartline__qty {
    display: inline-flex; align-items: stretch;
    border: 1px solid var(--hair); border-radius: var(--r);
    overflow: hidden;
}
.cartline__qty button { width: 38px; color: var(--nacre); font-size: 1.1rem; }
.cartline__qty button:hover { color: var(--pearl); }
.cartline__qty button:disabled { color: var(--nacre-dim); opacity: .4; cursor: not-allowed; }
/* The stock is all in this line: said plainly, in the place a tier hint would be. */
.cartline__stock { margin: .2rem 0 0; font-size: .8125rem; color: var(--nacre); }
.cartline__qty .qty__value { min-width: 62px; padding: .5rem .2rem; font-size: .9375rem; text-align: center; }
.cartline__sum { text-align: right; display: grid; gap: .2rem; justify-items: end; }

/* --- remove guard ----------------------------------------------------------
   "Odebrat" asks first, and it asks over the line it is about to take away,
   so the answer is given where the eye already is. The photo stays in view
   and goes grey: the customer sees exactly which jar is at stake.
   ----------------------------------------------------------------------- */
.cartline--confirming .cartline__tin { filter: grayscale(1); transition: filter .15s ease; }
.cartline__guard {
    position: absolute;
    inset: .35rem 0 .35rem calc(var(--cartline-tin) + 1rem);
    z-index: 2;
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: .5rem .9rem .5rem 1.1rem;
    background: var(--depth);
    border: 1px solid var(--hair);
    border-left: 2px solid var(--ikura);
    border-radius: var(--r);
    box-shadow: 0 18px 40px -24px #000;
    opacity: 0;
    visibility: hidden;      /* also keeps the two answers out of the tab order */
    pointer-events: none;
    transition: opacity .15s ease, visibility 0s linear .15s;
}
.cartline--confirming .cartline__guard {
    opacity: 1; visibility: visible; pointer-events: auto;
    transition: opacity .15s ease, visibility 0s;
}
.cartline__guard-q { margin: 0; font-family: var(--font-display); font-size: 1.25rem; color: var(--pearl); }
.cartline__guard-text { min-width: 0; }
.cartline__guard-name {
    margin: .15rem 0 0; font-size: .8125rem; color: var(--nacre-dim);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cartline__guard-actions { display: flex; align-items: center; gap: .5rem; flex-shrink: 0; }

/* --- the summary box ------------------------------------------------------ */
.summary {
    position: sticky; top: calc(var(--masthead-h) + 1.5rem);
    border: 1px solid var(--hair); border-radius: var(--r);
    background: var(--depth);
    padding: 1.25rem;
}
.summary__rows { margin: 0 0 .5rem; display: grid; gap: .5rem; }
.summary__rows > div { display: flex; justify-content: space-between; gap: 1rem; font-size: .9375rem; }
.summary__rows dt { color: var(--nacre); }
.summary__rows dd { margin: 0; font-weight: 600; }
.summary__minus { color: var(--ok); }
.summary__total {
    padding-top: .75rem; margin-top: .25rem;
    border-top: 1px solid var(--hair-soft);
    font-size: 1.375rem !important;
}
.summary__total dt { font-weight: 600; color: var(--pearl) !important; }
.summary__total dd { font-size: 1.375rem; }
.summary__note { font-size: .8125rem; color: var(--nacre-dim); margin: 0 0 1rem; }
.summary__items {
    list-style: none; margin: 0 0 1rem; padding: 0 0 1rem;
    border-bottom: 1px solid var(--hair-soft);
    display: grid; gap: .5rem; font-size: .875rem;
}
.summary__items li { display: flex; justify-content: space-between; gap: .75rem; }
.summary__items em { font-style: normal; color: var(--nacre-dim); }

.codeform { margin-bottom: 1rem; }
.codeform__row { display: flex; gap: .4rem; margin-top: .35rem; }
.codeform input { flex: 1; min-width: 0; font-size: 1rem; }
.points { border-top: 1px solid var(--hair-soft); padding-top: 1rem; }
.points__line { font-size: .875rem; color: var(--nacre); margin: 0 0 .6rem; }

/* --- checkout and confirmation -------------------------------------------- */
.checkout, .thanks { padding-block: 2rem 5rem; }
.checkout__grid, .thanks__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 340px;
    gap: clamp(1.5rem, 1rem + 3vw, 3rem);
    align-items: start;
}
.checkout__main { display: grid; gap: 1rem; }
.checkout__submit-total { display: none; }
.thanks__title { font-size: var(--t-4); font-weight: 300; margin: .3rem 0 .6rem; }

.paybox { display: flex; gap: 1.25rem; align-items: flex-start; flex-wrap: wrap; }
/* The QR is read by a bank app's camera, so it keeps its white quiet zone
   whatever the room around it is doing. */
.paybox__qr { background: #fff; border-radius: var(--r); padding: 8px; }
.paybox__rows { margin: 0; display: grid; gap: .5rem; flex: 1; min-width: 180px; }
.paybox__rows > div { display: flex; justify-content: space-between; gap: 1rem; font-size: .9375rem; }
.paybox__rows dt { color: var(--nacre-dim); }
.paybox__rows dd { margin: 0; font-weight: 600; }

/* --- accounts ------------------------------------------------------------- */
.authpage { padding-block: 3rem 5rem; display: flex; justify-content: center; }
.authcard { width: 100%; max-width: 420px; }
.authcard__title { font-size: var(--t-3); font-weight: 300; margin: 0 0 1.25rem; }
.authcard .field + .field { margin-top: .75rem; }
.authcard__foot {
    margin-top: 1.25rem; padding-top: 1rem;
    border-top: 1px solid var(--hair-soft);
    display: flex; flex-direction: column; gap: .4rem;
    font-size: .875rem; color: var(--nacre-dim);
}
.authcard__foot a { color: var(--pearl); text-decoration: underline; text-underline-offset: 3px; }

/* Signing in without a password: provider buttons, then the e-mail link. */
.nopass { display: grid; gap: .6rem; }
.nopass__provider { gap: .6rem; }
.nopass__magic { display: grid; gap: .6rem; margin-top: .4rem; }
.nopass__hint { margin: 0; font-size: .8125rem; color: var(--nacre-dim); }
.authsplit {
    display: flex; align-items: center; gap: .75rem; margin: 1.25rem 0;
    font-size: .8125rem; color: var(--nacre-dim);
}
.authsplit::before, .authsplit::after { content: ""; flex: 1; border-top: 1px solid var(--hair-soft); }

.accountpage { padding-block: 2rem 5rem; }
.accountgrid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: clamp(1.5rem, 1rem + 3vw, 3rem);
    align-items: start;
}
.orderrow {
    display: grid; grid-template-columns: auto 1fr auto auto; gap: 1rem;
    align-items: baseline;
    padding: .9rem 0;
    border-bottom: 1px solid var(--hair-soft);
    font-size: .9375rem;
}
.orderrow:hover { color: #fff; }
.orderrow__no { font-weight: 600; }
.orderrow__date, .orderrow__status { color: var(--nacre-dim); font-size: .8125rem; }
.orderrow__sum { font-weight: 600; white-space: nowrap; }
.pointsbig { font-family: var(--font-display); font-size: 2.75rem; line-height: 1; margin: 0 0 .2rem; font-weight: 300; }
.pointslist { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; font-size: .8125rem; }
.pointslist li { display: flex; justify-content: space-between; gap: .75rem; }
.pointslist em { font-style: normal; color: var(--nacre-dim); display: block; font-size: .75rem; }
.pointslist--plus { color: var(--ok); font-weight: 600; }
.whatsinside { margin-top: 2rem; border-top: 1px solid var(--hair-soft); padding-top: 1.25rem; }
.whatsinside ul { margin: 0; padding-left: 1.1rem; color: var(--nacre); display: grid; gap: .3rem; }

/* --- legal pages -----------------------------------------------------------
   The one place on the site read line by line rather than scanned, so the
   measure is narrow and the leading loose.
   ----------------------------------------------------------------------- */
.legal {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 15rem;
    gap: clamp(2rem, 1rem + 4vw, 4.5rem);
    align-items: start;
    padding-block: 2.5rem 5rem;
}
.legal__title { font-family: var(--font-display); font-size: var(--t-4); font-weight: 300; line-height: 1.08; margin: 0; max-width: 20ch; }
.legal__doc { min-width: 0; max-width: 68ch; }
.legal__aside { position: sticky; top: calc(var(--masthead-h) + 1.5rem); }
.legal__nav { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.legal__nav a { color: var(--nacre); font-size: .9375rem; border-bottom: 1px solid transparent; }
.legal__nav a:hover, .legal__nav a:focus-visible { color: var(--pearl); border-bottom-color: var(--hair); }
/* The page you are on stays in the list — lit, so the list is the same seven
   lines everywhere and only the emphasis moves. */
.legal__nav-on { color: var(--pearl); font-weight: 600; font-size: .9375rem; border-bottom: 1px solid var(--ikura); }

/* The body comes out of the editor as bare tags, so everything it can emit is
   styled here rather than by a class the admin would have to remember. */
.prose { max-width: 68ch; color: var(--nacre); line-height: 1.7; }
.prose > :first-child { margin-top: 0; }
.prose h2 { font-family: var(--font-display); font-size: 1.75rem; font-weight: 400; color: var(--pearl); margin: 2.25rem 0 .6rem; }
.prose h3 { font-size: 1.125rem; font-weight: 600; color: var(--pearl); margin: 1.5rem 0 .4rem; }
.prose p, .prose ul, .prose ol { margin: 0 0 .9rem; }
.prose ul, .prose ol { padding-left: 1.25rem; }
.prose li { margin-bottom: .35rem; }
.prose li::marker { color: var(--ikura-lit); }
.prose a { color: var(--pearl); text-decoration: underline; text-underline-offset: 3px; }
.prose strong { color: var(--pearl); }
.prose hr { border: 0; height: 1px; background: var(--hair-soft); margin: 2rem 0; }
/* The withdrawal form and the company block are what a customer copies out of
   the page, so they get a surface of their own. */
.prose blockquote {
    margin: 1.25rem 0; padding: 1.1rem 1.25rem;
    background: var(--depth);
    border: 1px solid var(--hair); border-left: 2px solid var(--ikura);
    color: var(--pearl);
}
.prose blockquote p:last-child { margin-bottom: 0; }
.prose table { width: 100%; border-collapse: collapse; margin: 0 0 1.25rem; font-size: .9375rem; }
.prose th, .prose td { text-align: left; padding: .55rem .75rem; border-bottom: 1px solid var(--hair-soft); vertical-align: top; }
.prose th { color: var(--pearl); font-weight: 600; }

/* ==========================================================================
   Narrow screens
   --------------------------------------------------------------------------
   Most of this shop is read on a phone, so this is not a fallback. Two rules
   drive it: the summary goes above the form and the button pins to the bottom
   of the screen, and nothing a thumb needs is ever smaller than 44 px.
   ========================================================================== */

@media (max-width: 860px) {
    .checkout__grid, .thanks__grid { grid-template-columns: minmax(0, 1fr); }

    /* The summary belongs above the form — the customer wants to see what they
       are paying before filling anything in. The button must not come with it
       (nobody submits a form they have not filled), so it pins to the bottom
       of the screen and takes the total along. */
    .checkout__grid .summary, .thanks__grid .summary { order: -1; position: static; }
    .checkout__submit {
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
        margin: 0 !important;
        padding: 1.05rem 1.25rem calc(1.05rem + env(safe-area-inset-bottom));
        border-radius: 0;
        justify-content: space-between;
        font-size: 1rem;
    }
    .checkout__submit-total { display: inline; }
    /* Room for the bar, so it never sits on top of the last field. */
    .checkout { padding-bottom: 7rem; }
}

@media (max-width: 860px) {
    .legal { grid-template-columns: minmax(0, 1fr); }
    .legal__doc { max-width: none; }
    .legal__aside { position: static; border-top: 1px solid var(--hair-soft); padding-top: 1.5rem; }
}

@media (max-width: 800px) {
    .accountgrid { grid-template-columns: minmax(0, 1fr); }
    .orderrow { grid-template-columns: minmax(0, 1fr) auto; row-gap: .2rem; }
    .orderrow__date, .orderrow__status { grid-column: 1; }
    .orderrow__sum { grid-row: 1 / 3; grid-column: 2; align-self: center; }
}

@media (max-width: 760px) {
    .cartgrid { grid-template-columns: minmax(0, 1fr); }
    .summary { position: static; }
    .cartline { --cartline-tin: 64px; grid-template-columns: 64px minmax(0, 1fr) auto; row-gap: .6rem; }
    .cartline__tin { width: 64px; height: 64px; }
    .cartline__qty { grid-column: 2; }
    .cartline__sum { grid-column: 3; }
}

@media (max-width: 560px) {
    .fields { grid-template-columns: minmax(0, 1fr); }
    .panel { padding: 1rem; }
    .paybox { justify-content: center; }
    .summary { padding: 1rem; }

    /* One tap target per row, full width, in reach of a thumb. */
    .buy { grid-template-columns: minmax(0, 1fr); gap: .6rem; }
    .qty { width: 100%; justify-content: space-between; }
    .qty output { flex: 1; }
    .cartline__qty button { width: 44px; height: 44px; }

    .cartline { grid-template-columns: 56px minmax(0, 1fr); row-gap: .5rem; column-gap: .8rem; }
    .cartline__tin { width: 56px; height: 56px; }
    .cartline__qty, .cartline__sum { grid-column: 2; justify-self: start; }
    .cartline__sum { text-align: left; justify-items: start; }

    /* The line is tall and narrow here, so the guard stacks and the two
       answers become full-width thumb targets. */
    .cartline__guard {
        inset: .35rem 0;
        flex-direction: column; align-items: stretch; justify-content: center;
        gap: .75rem; padding: .9rem; text-align: center;
    }
    .cartline__guard-actions { flex-direction: column; align-items: stretch; }
    .cartline__guard-actions form { display: contents; }
    .cartline__guard-actions .btn { width: 100%; }
}

/* A three-column price table does not fit a phone, and the page must not be
   the thing that scrolls sideways — the table is. */
@media (max-width: 640px) {
    .prose table { display: block; overflow-x: auto; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}

/* --- search: the finder ------------------------------------------------------
   One field in two places (shop.partials.finder). Phone: a round chip with a
   loupe at the head of the sticky category strip; a tap turns the whole strip
   into the field and the chips step aside until it is closed. Desktop: the
   field at the top of the rail. Sheet: the first row of the burger sheet.
   Written last on purpose: .catnav a and .shelf .catnav a would otherwise
   dress the links in the hint list as category chips.
   ----------------------------------------------------------------------- */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

.finder { position: relative; }
.finder__toggle {
    width: 38px; height: 38px; flex: none; border-radius: 999px; border: 1px solid var(--hair);
    display: inline-flex; align-items: center; justify-content: center; color: var(--pearl);
}
.finder__toggle svg, .finder__field > svg { width: 17px; height: 17px; flex: none; }
.finder__field {
    display: flex; align-items: center; gap: .5rem; height: 38px; padding: 0 .3rem 0 .85rem;
    border-radius: 999px; border: 1px solid var(--hair); background: rgba(235, 231, 221, .04); color: var(--nacre);
}
.finder__field:focus-within { border-color: var(--nacre); }
/* 16px, not the chips' 14: iOS zooms the page into any field set smaller. */
.finder__field input {
    flex: 1; min-width: 0; height: 100%; border: 0; outline: 0; background: none; padding: 0;
    font: inherit; font-size: 16px; color: var(--pearl); -webkit-appearance: none; appearance: none;
}
.finder__field input::placeholder { color: var(--nacre-dim); }
.finder__field input::-webkit-search-cancel-button { display: none; }
.finder__clear { width: 30px; height: 30px; flex: none; display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; color: var(--nacre); }
.finder__clear svg { width: 14px; height: 14px; }

.finder__hints {
    z-index: 30; background: var(--depth); color: var(--pearl);
    border: 1px solid var(--hair); border-radius: 8px; padding: .35rem;
    box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
    overflow-y: auto; overscroll-behavior: contain;
}
.finder__cats { display: flex; flex-wrap: wrap; gap: .35rem; padding: .35rem .35rem .5rem; border-bottom: 1px solid var(--hair-soft); margin-bottom: .25rem; }
.finder .finder__hints a.finder__cat {
    display: inline-flex; align-items: center; gap: .3rem; padding: 5px .7rem; border-radius: 999px;
    border: 1px solid var(--hair); background: none; font-size: .8125rem; font-weight: 600; color: var(--pearl);
}
.finder .finder__hints a.finder__cat svg { width: 11px; height: 11px; color: var(--nacre); }
.finder .finder__hints a.finder__hit {
    display: grid; grid-template-columns: 48px minmax(0, 1fr) auto; align-items: center; gap: .75rem;
    padding: .4rem; border: 0; border-radius: 6px; background: none; color: var(--pearl); font-size: .875rem;
    white-space: normal; /* the strip's chips are nowrap; a name that ends in (M) or (XL) must be read whole */
}
.finder .finder__hints a.finder__hit:hover, .finder .finder__hints a.finder__hit:focus-visible { background: rgba(235, 231, 221, .06); }
.finder__thumb { width: 48px; height: 48px; border-radius: 4px; background: var(--vitrine); overflow: hidden; }
.finder__thumb img { width: 100%; height: 100%; object-fit: contain; }
.finder__text { display: flex; flex-direction: column; min-width: 0; }
.finder__name { line-height: 1.25; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.finder .finder__text small { color: var(--nacre); font-size: .75rem; }
.finder__price { font-weight: 600; white-space: nowrap; font-size: .875rem; }
.finder__none { margin: 0; padding: .8rem .6rem; color: var(--nacre); font-size: .875rem; }
.finder .finder__hints a.finder__all {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    margin-top: .25rem; padding: .7rem .6rem; border: 0; border-top: 1px solid var(--hair-soft); border-radius: 0;
    background: none; font-size: .875rem; font-weight: 600; color: var(--pearl);
}
.finder .finder__hints a.finder__all svg { width: 14px; height: 14px; }

/* Phone: the chip at the head of the strip, the strip becoming the field. */
@media (max-width: 899px) {
    .catnav { display: flex; align-items: center; }
    .catnav .finder--strip { position: static; flex: none; }
    .finder--strip .finder__field { display: none; }
    .finder--strip.is-open { flex: 1; min-width: 0; }
    .finder--strip.is-open .finder__toggle { display: none; }
    .finder--strip.is-open .finder__field { display: flex; }
    .catnav:has(.finder--strip.is-open) ul { display: none; }
    /* The chips start right after the loupe and slide under a short fade
       beside it; the right end keeps its gutter-wide fade. */
    .catnav ul {
        flex: 1; min-width: 0; margin-left: 0; padding-left: 10px;
        -webkit-mask-image: linear-gradient(to right, transparent, #000 10px, #000 calc(100% - var(--gutter)), transparent);
        mask-image: linear-gradient(to right, transparent, #000 10px, #000 calc(100% - var(--gutter)), transparent);
    }
    /* Hung from the sticky strip, edge to edge, down to the keyboard. */
    .finder--strip .finder__hints {
        position: absolute; left: 0; right: 0; top: 100%;
        max-height: calc(100svh - var(--masthead-h) - 60px);
        border-radius: 0; border-inline: 0; padding: .35rem var(--gutter) .5rem;
    }
}
/* Desktop: the field heads the rail, the hints open wider than the rail. */
@media (min-width: 900px) {
    .finder__toggle { display: none; }
    .finder--strip { margin-bottom: 1rem; }
    .finder--strip .finder__hints { position: absolute; left: 0; top: calc(100% + 6px); width: min(440px, 60vw); max-height: 70vh; }
}
/* The wide bar: a loupe beside the bag, and a panel hung under the bar,
   right-aligned to the page edge. Only from 1100 px, with the bar itself. */
.mast__find { display: none; width: 40px; }
.mast__find svg { width: 20px; height: 20px; }
.mast__search { position: absolute; left: 0; right: 0; top: 100%; pointer-events: none; }
.mast__search .wrap { display: flex; justify-content: flex-end; }
.finder--bar {
    pointer-events: auto; width: min(480px, 100%); margin-top: 8px; padding: .6rem;
    background: var(--depth); border: 1px solid var(--hair); border-radius: 10px;
    box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
}
.finder--bar .finder__field { height: 44px; }
.finder--bar .finder__hints { margin-top: .5rem; border: 0; box-shadow: none; padding: 0; max-height: 70vh; }
@media (min-width: 1100px) { .mast__find { display: inline-flex; } }

/* The sheet: always open, the hints in the flow under the field. */
.finder--sheet { margin-block: .75rem .25rem; }
.finder--sheet .finder__field { height: 46px; }
.finder--sheet .finder__hints { margin-top: .5rem; }

/* On the shelf pages the strip is paper; the field goes with it. */
.shelf .finder__toggle, .shelf .finder__field { border-color: var(--shelf-2); background: var(--shelf-card); color: var(--shelf-ink); }
.shelf .finder__field input { color: var(--shelf-ink); }
.shelf .finder__field input::placeholder { color: var(--shelf-mute); }
.shelf .finder__clear { color: var(--shelf-mute); }

/* Results page: branches named like the search, as doors above the grid. */
.found-cats { display: flex; flex-wrap: wrap; gap: .4rem; margin: 0 0 1.25rem; }
.found-cats a {
    display: inline-flex; align-items: center; gap: .35rem; padding: 7px .9rem; border-radius: 999px;
    border: 1px solid var(--hair); font-size: .875rem; font-weight: 600; color: var(--pearl);
}
.found-cats a:hover { border-color: var(--nacre); }

/* --- music ------------------------------------------------------------------
   The player in the bar (public/js/music.js). Off: one note. On: a pause and a
   "next" beside it. The state is html.kd-music-on, so the hero's invitation and
   the bar switch together on every page. */
/* In the bar only where the bar has room (no burger); below that the player
   is hidden but still plays, and the sheet carries its switch. */
.music { display: none; align-items: center; }
@media (min-width: 1100px) { .music { display: flex; } }
.music__next { display: none; }
/* Once the music has played, "next" stays even when paused: the bar must not jump (Bogdan, 15.10). */
.kd-music-on .music__next, .kd-music-started .music__next { display: inline-flex; }
.music__pause, .kd-music-on .music__play { display: none; }
.music__resume, .kd-music-started .music__invite { display: none; }
.kd-music-started:not(.kd-music-on) .music__resume { display: revert; }
.kd-music-on .music__pause { display: revert; }
.kd-music-on .music__toggle { color: var(--pearl); }
.music audio { display: none; }

/* Loading (html.kd-music-loading, from music.js): a thin ring turns round the
   switch while the track's first seconds download; in the sheet the note
   breathes instead. */
.hero__sound-dot, .music__toggle { position: relative; }
.kd-music-loading .hero__sound-dot::after, .kd-music-loading .music__toggle::after {
    content: ''; position: absolute; inset: -1px; border-radius: 50%;
    border: 1.5px solid transparent; border-top-color: var(--pearl);
    animation: kd-spin .8s linear infinite;
}
.kd-music-loading .music__toggle::after { inset: 6px; }
.kd-music-loading .hero__sound-dot::after { inset: -5px; border-width: 2px; }
.kd-music-loading .sheet__music-main svg { animation: kd-breathe 1s ease-in-out infinite alternate; }
@keyframes kd-spin { to { transform: rotate(1turn); } }
@keyframes kd-breathe { from { opacity: .35; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .kd-music-loading .hero__sound-dot::after, .kd-music-loading .music__toggle::after { animation-duration: 2.4s; }
}

/* The burger sheet's entry: one row dressed as one of its links — the row
   is the play/pause switch, "next" a round button at its right end. */
.sheet__music { display: flex; align-items: center; gap: .75rem; border-bottom: 1px solid var(--hair-soft); }
.sheet__music-main { flex: 1; min-width: 0; background: none; border: 0; font: inherit; font-size: 1.0625rem; text-align: left; cursor: pointer; }
.sheet__music-main svg.music__resume, .sheet__music-main svg.music__pause { width: 15px; height: 15px; margin-inline: 1px; }
.kd-music-on .sheet__music-main svg { color: var(--pearl); }
.sheet__music-next {
    flex: none; width: 44px; height: 44px; align-items: center; justify-content: center;
    border: 1px solid var(--hair); border-radius: 50%; background: none; color: var(--pearl); cursor: pointer;
    touch-action: manipulation;
}
.sheet__music-next svg { width: 18px; height: 18px; }
.sheet__music-next:active { background: var(--hair-soft); }
.sheet__music-main:focus-visible, .sheet__music-next:focus-visible { outline: 2px solid var(--pearl); outline-offset: 2px; }

/* The hero's sound switch, bottom right, level with the headline's buttons.
   A round mark always; the word beside it only where there is room. Playing,
   the note turns into three bars moving. */
.hero__sound {
    position: absolute; z-index: 2; right: max(var(--gutter), calc((100% - var(--max)) / 2 + var(--gutter))); bottom: clamp(1.75rem, 5vw, 4rem);
    display: inline-flex; align-items: center; gap: .65rem;
    padding: 0; background: none; border: 0; color: var(--pearl); font: inherit; font-weight: 500; cursor: pointer;
}
.hero__sound-dot {
    width: 48px; height: 48px; border-radius: 50%; flex: none;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(11,18,22,.45); border: 1px solid rgba(236,232,223,.5);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
    transition: border-color .2s, background .2s;
}
.hero__sound:hover .hero__sound-dot { border-color: var(--pearl); background: rgba(11,18,22,.6); }
.hero__sound-dot svg { width: 20px; height: 20px; }
.hero__sound-word { display: none; order: -1; color: var(--nacre); }
.hero__eq { display: none; align-items: flex-end; gap: 3px; height: 16px; }
.hero__eq i { width: 3px; height: 100%; background: var(--pearl); border-radius: 1px; transform-origin: bottom; animation: eq 1.1s ease-in-out infinite; }
.hero__eq i:nth-child(2) { animation-delay: -.35s; }
.hero__eq i:nth-child(3) { animation-delay: -.7s; }
@keyframes eq { 0%, 100% { transform: scaleY(.3); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) { .hero__eq i { animation: none; transform: scaleY(.6); } }
.kd-music-on .hero__eq { display: inline-flex; }
@media (min-width: 900px) {
    .hero__sound-word.music__play { display: inline; }
    .kd-music-on .hero__sound-word.music__play { display: none; }
}
/* Phone: the headline's buttons fill the bottom row, so the switch goes up
   into the picture's top right corner. The low-power play mark, when it shows,
   hangs under it on the same axis (44 px under 48, hence the 2 px) — the switch
   is always there and must not move when the mark appears. */
@media (max-width: 899px) {
    .hero__sound { top: 1rem; bottom: auto; right: var(--gutter); }
    .hero__play { top: calc(1rem + 48px + .75rem); }
}

/* Turbo's own bar along the top while the next page loads. */
.turbo-progress-bar { height: 2px; background: var(--pearl); }

/* ==========================================================================
   Quantity prices (15.10.2026)
   --------------------------------------------------------------------------
   A product can cost less per piece from N pieces up. Three pieces of it:
   the storefront switch (Za kus | Více kusů) in the category rail, which
   flips every such card to its first step; the price swap itself, driven by
   <html data-qty="multi">; and the ladder under the price — one roe grain per
   piece up to the deepest step, filling red as the basket fills, the steps'
   prices beneath. The grain is the room's one warm thing, so the only red on
   a card is what the customer has already put in the basket.
   ========================================================================== */

/* The switch: a slider, two halves of one pill and one thumb that slides
   under the chosen half. It follows a drag, by finger or by mouse (see
   qtyswitch in layouts/shop); the halves themselves stay unfilled. The
   halves keep their own widths, so the script measures them (--x0, --w0,
   --w1, --dx) and the thumb stretches as it travels; until it has, the
   fallback is two equal halves. */
.qtyswitch {
    display: inline-flex; padding: 3px; border: 1px solid var(--hair); border-radius: 999px; margin-bottom: 9px;
    position: relative; isolation: isolate; touch-action: pan-y; user-select: none; -webkit-user-select: none;
    --t: 0;
}
[data-qty="multi"] .qtyswitch { --t: 1; }
.qtyswitch::before {
    content: ""; position: absolute; z-index: -1; top: 3px; bottom: 3px;
    left: var(--x0, 3px); width: calc(var(--w0, 50% - 3px) + (var(--w1, 50% - 3px) - var(--w0, 50% - 3px)) * var(--t));
    border-radius: 999px; background: var(--pearl);
    transition: transform .28s cubic-bezier(.3, .7, .2, 1), width .28s cubic-bezier(.3, .7, .2, 1);
    transform: translateX(calc(var(--t) * var(--dx, 100%)));
}
/* Under the finger or the mouse the thumb moves with it, no easing; and
   nothing eases before the script has placed it (is-ready). */
.qtyswitch.is-drag::before, .qtyswitch:not(.is-ready)::before { transition: none; }
.qtyswitch.is-drag, .qtyswitch.is-drag button { cursor: grabbing; }
.qtyswitch button { flex: none; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; min-height: 32px; padding: 0 .85rem; border: 0; border-radius: 999px; background: none; color: var(--nacre); font: inherit; font-size: .8125rem; cursor: pointer; white-space: nowrap; transition: color .2s ease; }
.qtyswitch button[aria-checked="true"] { color: var(--abyss); font-weight: 600; }
.qtyswitch button:focus-visible { outline: 2px solid var(--pearl); outline-offset: 2px; }
.qtyswitch__grains { display: inline-flex; gap: 2px; }
.qtyswitch__grains i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: .55; }
.qtyswitch button[aria-checked="true"] .qtyswitch__grains i { background: var(--ikura); opacity: 1; }
@media (min-width: 900px) {
    .qtyswitch { margin: 0 0 1rem; }
}
/* Phone: in the sticky strip's one row the pill pushed the loupe and the
   chips off (Bogdan, 15.10: "нахуя это в одну строку"). The strip wraps: the
   switch is a row of its own, full width, two equal halves; the loupe and the
   chips are the row under it. */
@media (max-width: 899px) {
    .catnav:has(.qtyswitch) { flex-wrap: wrap; row-gap: 8px; }
    .catnav .qtyswitch { display: grid; grid-template-columns: 1fr 1fr; flex: 0 0 100%; margin: 0; }
    .qtyswitch button { min-height: 34px; }
    /* An open search field has no width of its own (basis 0) and would stay
       on the switch's row at zero width; it takes the whole second row. */
    .catnav:has(.qtyswitch) .finder--strip.is-open { flex-basis: 100%; }
}


/* The price swap. Only cards that HAVE a step change; the rest keep q-one. */
.q-multi { display: none; }
[data-qty="multi"] .has-tier .q-one,
[data-qty="multi"] .tile__row:has(.q-multi) .q-one { display: none; }
[data-qty="multi"] .q-multi { display: contents; }
[data-qty="multi"] .tag.q-multi { display: inline-block; }
.card__price .q-one { display: contents; }
/* "Více kusů" is the offer for an empty basket. Once the product is in it
   (the pick is open), the card says what the basket really pays, in either
   mode: two jars after a "−" from three are the single price again, and the
   grains name the step still to reach. */
[data-qty="multi"] .card:has(.pick.is-open) .has-tier .q-one { display: contents; }
[data-qty="multi"] .card:has(.pick.is-open) .q-multi { display: none; }
[data-qty="multi"] .card:has(.pick.is-open) .ladder__price { visibility: visible; }
[data-qty="multi"] .tile:has(.pick.is-open) .tile__row .q-one { display: inline-block; }
[data-qty="multi"] .tile:has(.pick.is-open) .tag.q-multi { display: none; }

/* The ladder. */
/* min-height = one line of the price: when the last step is reached the
   price beside the grains goes, and the grains must not rise with it. */
.ladder { --grain: 8px; margin-top: .6rem; min-height: 1.5em; display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .7rem; font-size: .8125rem; color: var(--nacre); }
/* The card's one price beside the grains: the plain text colour, always. */
.ladder__price { color: var(--pearl); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ladder__price small { color: var(--nacre); font-weight: 400; font-size: .75rem; margin-right: .25rem; }
.ladder--shelf .ladder__price { color: var(--shelf-ink); }
.ladder--shelf .ladder__price small { color: var(--shelf-mute); }
.ladder--pick .ladder__steps { flex-basis: 100%; }
.ladder__grains { display: flex; gap: 4px; align-items: center; }
.ladder__grains i { width: var(--grain); height: var(--grain); border-radius: 50%; border: 1px solid var(--nacre-dim); transition: background-color .25s ease, border-color .25s ease; }
/* A grain where a step begins sits a hair larger: the threshold you can see. */
.ladder__grains i.is-step { --grain: 10px; border-color: var(--nacre); }
.ladder__grains i.is-in { background: var(--ikura); border-color: var(--ikura); }
.ladder__steps { display: flex; flex-wrap: wrap; gap: .1rem .9rem; }
.ladder__step { display: inline-flex; align-items: baseline; gap: .3rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ladder__step small { color: var(--nacre-dim); font-size: .75rem; }
/* The step the basket is on. With nothing in the basket that is "1 ks". */
.ladder__step.is-on { color: var(--pearl); font-weight: 600; }
.ladder__step.is-on small { color: var(--nacre); font-weight: 400; }
@media (prefers-reduced-motion: reduce) { .ladder__grains i, .qtyswitch::before { transition: none; } }

/* On the paper shelf the same ladder in shelf inks; the grain stays red. */
.ladder--shelf { color: var(--shelf-mute); margin-top: .45rem; }
.ladder--shelf .ladder__grains i { border-color: var(--shelf-mute); }
.ladder--shelf .ladder__grains i.is-step { border-color: var(--shelf-ink); }
.ladder--shelf .ladder__grains i.is-in { background: var(--ikura); border-color: var(--ikura); }
.ladder--shelf .ladder__step small { color: var(--shelf-mute); }
.ladder--shelf .ladder__step.is-on { color: var(--shelf-ink); }
.ladder--shelf .ladder__step.is-on small { color: var(--shelf-mute); }

/* Product page: bigger ladder, each step a target that sets the stepper. */
.ladder--pick { --grain: 10px; margin-top: 1rem; font-size: .9375rem; }
.ladder--pick .ladder__grains i.is-step { --grain: 13px; }
.ladder--pick .ladder__steps { gap: .25rem 1.4rem; }
.ladder--pick .ladder__step { cursor: pointer; padding: .2rem 0; border-bottom: 1px solid transparent; }
.ladder--pick .ladder__step small { font-size: .8125rem; }
.ladder--pick .ladder__step:hover { color: var(--pearl); }
.ladder--pick .ladder__step.is-on { border-bottom-color: var(--ikura-lit); }
.ladder--pick .ladder__step:focus-visible { outline: 2px solid var(--pearl); outline-offset: 3px; }

/* Basket: the next quantity step, one press of this line's + away. */
.cartline__next { margin: .2rem 0 0; font-size: .8125rem; color: var(--nacre); display: flex; align-items: baseline; gap: .4rem; }
.cartline__next::before { content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%; border: 1px solid var(--ikura-lit); transform: translateY(-1px); }
/* A two-up phone card has no room for the replaced price as well; the
   ladder right under it still says what one piece costs. */
@media (max-width: 480px) {
    .card__price .q-multi .price__was { display: none; }
    /* Struck single price + step price fill a phone card; the per-kg figure
       would wrap under them and push the grains down. */
    .card__price .q-reached small { display: none; }
}

/* On the paper shelf the switch wears the shelf's chip inks (white pill,
   kraft edge; the chosen half inked like the current category chip). The
   dark room's pearl-on-paper read as nothing chosen at all (Bogdan, 15.10). */
.shelf .qtyswitch { border-color: var(--shelf-2); background: var(--shelf-card); }
.shelf .qtyswitch button { color: var(--shelf-mute); }
.shelf .qtyswitch::before { background: var(--shelf-ink); }
.shelf .qtyswitch button[aria-checked="true"] { color: var(--shelf-on-ink); }
.shelf .qtyswitch button:focus-visible { outline-color: var(--shelf-ink); }

/* Switched shelf: the headline already is the step price, so the same figure
   beside the grains goes; the grains stay to count the basket. */
[data-qty="multi"] .card .ladder__price,
[data-qty="multi"] .tile .ladder__price { visibility: hidden; } /* keeps its room: the card must not change height on a switch */

/* The home page's paper shelf has its own row of tabs and the category strip
   has scrolled away by then, so it carries its own switch (Bogdan, 15.10) in
   a sticky strip of its own, in shelf inks: on a phone the switch is a
   full-width row above the tabs; on a wide screen tabs left, switch right.
   Same component, kept in step with the strip's (kd-qty). */
.shelf__bar {
    position: sticky; top: var(--masthead-h); z-index: 15;
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 1rem;
    margin-inline: calc(var(--gutter) * -1); padding: 9px var(--gutter);
    background: color-mix(in srgb, var(--shelf) 94%, transparent); backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--shelf-2);
}
.shelf__bar .shelf__tabs { flex: 1 1 0; min-width: 0; padding-bottom: 0; }
.shelf__switch .qtyswitch { margin: 0; }
@media (min-width: 900px) { .shelf__switch { order: 2; } }
@media (max-width: 899px) {
    .shelf__switch { flex: 0 0 100%; }
    .shelf__switch .qtyswitch { display: grid; grid-template-columns: 1fr 1fr; }
    .shelf__bar .shelf__tabs { flex-basis: 100%; }
}

/* Product page: the step as one press, under the buy button. Outlined, so the
   red button stays the one obvious action; the three grains say "more". */
.buy__tier { grid-column: 1 / -1; display: inline-flex; align-items: center; justify-content: center; gap: .5rem; }
.buy__tier .qtyswitch__grains i { background: var(--ikura); opacity: 1; }
.buybar__tier { flex: none; padding-inline: .9rem; }

/* iOS zooms on a quick double tap — on the gallery thumbs, a stepper, a
   switch — and the page is left magnified and shifted sideways (Bogdan,
   15.10; patched element by element before, it kept coming back with every
   new control). manipulation turns off double-tap zoom for the whole page;
   pinch zoom stays. */
html { touch-action: manipulation; }

/* Gift sets. The offer in the case's top right corner (the out-of-stock label
   owns the left): "10 + 1 zdarma" on the roe-grain red — the one warm thing
   in the room, so the eye lands on it first. Numbers big, the word small. */
.case__gift {
    position: absolute; right: .5rem; top: .5rem; z-index: 2;
    display: inline-flex; align-items: baseline; gap: .3rem;
    padding: .3rem .7rem; border-radius: 999px;
    background: var(--ikura); color: #fff;
    font-weight: 700; font-size: .9375rem; letter-spacing: -.01em; font-variant-numeric: tabular-nums;
    box-shadow: 0 6px 18px -8px rgba(0,0,0,.6);
}
.case__gift small { font-weight: 500; font-size: .6875rem; letter-spacing: 0; opacity: .9; }
/* Product page of a gift set: the offer line, the badge set into the text. */
.pdp__set { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem; margin: .9rem 0 0; color: var(--nacre); font-size: .9375rem; }
.pdp__set .case__gift { position: static; box-shadow: none; }
.pdp__set b { color: var(--pearl); font-weight: 600; }
.whatsinside__gift { color: var(--pearl); }
.whatsinside__gift small { color: var(--ikura-lit); font-size: .8125rem; margin-left: .25rem; }

/* Account page: own details form under the order list. */
.profileform { margin-top: 2.5rem; }
.profileform__sub { margin-top: 1.5rem; }
.profileform__save { margin-top: 1.25rem; }
