/* The EFT BackEnd fonts are a legacy Hebrew encoding: their Latin-1 slots
   draw Hebrew letters (é → צ, è → פ, à → ט, ç → ע, ü and þ whole Hebrew
   words), as do ‚ „ ‡ ˆ ˘ ˙ ˜ ı, the Armenian slots and some private-use
   points. unicode-range keeps the browser on these files ONLY for what they
   draw correctly — Basic Latin, Hebrew, the direction marks, – — ‘ ’ “ ” …
   and ₪ (checked against the files' cmap and outlines, see
   test/brand-font-range.test.js) — and every other character falls back, one
   character at a time, to the next family in the stack, which has the real
   Latin letters. Hebrew is drawn exactly as before. */
@font-face {
  font-family: 'EFT BackEnd Black';
  src: url('fonts/eft-backend-black.ttf') format('truetype');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0020-007E, U+0590-05FF, U+200C-200F, U+2013-2014, U+2018-2019, U+201C-201D, U+2026, U+20AA, U+FB1D-FB4F;
}
@font-face {
  font-family: 'EFT BackEnd Bold';
  src: url('fonts/eft-backend-bold.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0020-007E, U+0590-05FF, U+200C-200F, U+2013-2014, U+2018-2019, U+201C-201D, U+2026, U+20AA, U+FB1D-FB4F;
}
/* The brand display faces, as two properties so a non-Hebrew page can drop
   them whole: a French or English word half in EFT (its Latin) and half in
   the fallback (its accents) reads as two fonts. The shop's picker sets lang
   (app/translate-provider.js); a browser's own translator (Chrome's) adds
   translated-ltr / translated-rtl and may rewrite lang, so both are keyed.
   Arabic has no EFT glyphs at all and takes the fallback the same way. */
:root{--focus-ring:#0B5FFF; --focus-ring-halo:#FFFFFF; --focus-ring-width:3px;}
:root{--font-brand:'EFT BackEnd Black'; --font-brand-bold:'EFT BackEnd Bold';}
html[lang="en"], html[lang="fr"], html[lang="ar"], html.translated-ltr, html.translated-rtl{
  --font-brand:var(--font-display, 'Suez One'); --font-brand-bold:var(--font-body, 'Rubik');
}
  /* ---- Owner colour overrides (theme.ui) ----
     applyTheme() writes a --ui-* custom property on <html> for every key the
     owner filled in under "אפשרויות מתקדמות", and writes nothing for the ones
     they left empty. Every rule below reads them as
     `var(--ui-x, <the value this rule always had>)`, so an unset override is
     not a colour at all: the declaration falls back and the surface keeps its
     derived-from-brand look. Clearing one field therefore restores exactly that
     control, and never disturbs the rest of the theme.

     Two consequences worth keeping in mind when adding rules:
       - a component the accent block at the bottom of this file re-colours per
         category must read --ui-* THERE too, or the override would only apply
         to categories with no accent.
       - the high-contrast rules under [data-a11y-contrast="1"] declare their
         colours with !important, which outranks any of these fallbacks. That is
         deliberate: an owner's palette must not be able to break the
         accessible mode. */
  /* Three steps, derived from the shop's own --radius. Twelve unrelated values
     is what makes a theme read as generated: the eye picks up that nothing
     shares a shape. Panels are roundest, controls sit one step in, chips and
     tags one more. Pills stay fully round because that IS their shape. */
  :root{
    --r-panel:var(--radius, 16px);
    --r-control:calc(var(--radius, 16px) * 0.55);
    --r-chip:calc(var(--radius, 16px) * 0.35);
    /* The category accents are scoped: .category-section and the per-category
       [data-cat] rules app.js writes both set --accent-deep, and nothing set
       it above them. Anything reading it OUTSIDE a category therefore got an
       unresolved variable — which is how the order drawer's + button ended up
       with no fill at all, a white glyph on the light qty strip, while the
       same button on a product card was solid green. Seed it at the root so
       the fallback is a real colour everywhere; every scoped rule still
       overrides it, so nothing inside a category changes. */
    --accent-deep:var(--primary, #1F4D3A);
    --accent-mid:var(--primary-mid, var(--primary, #1F4D3A));
  }

  *{box-sizing:border-box; margin:0; padding:0; -webkit-tap-highlight-color:transparent;}
  /* [hidden] means hidden, everywhere. The UA sheet's [hidden]{display:none}
     is a bare attribute rule, so any class of ours that sets `display` on the
     same element outranks it and the element keeps drawing: the drawer's
     coupon rows (.drawer-total-row{display:flex}) showed «סכום לפני הנחה» and
     «הנחת קופון» with no coupon, and the footer had the same bug with an empty
     review pill. app.js and the modules toggle .hidden for exactly this
     meaning; nothing in the storefront shows an element that carries it. */
  [hidden]{display:none !important;}
  /* overflow-x:clip, not hidden. `hidden` computes overflow-y to `auto`, which
     makes html/body scroll containers — and a position:sticky child then sticks
     to that container instead of the viewport, so the category nav scrolled
     away. `clip` cuts the same horizontal overflow without the scrollport.
     `hidden` first so a browser without clip (Safari < 16) still gets the
     horizontal clamp; the @supports below upgrades everyone else. */
  /* The type scale. Every size in this file is in rem, so moving the root
     font size moves the whole storefront together rather than one heading at
     a time. --font-scale is a multiplier the owner sets (0.9 = a touch
     smaller, 1.15 = larger); left unset it resolves to 1 and the shop renders
     at exactly the 16px it always did.

     Declared on html rather than :root's block above because it has to land on
     the element the rem unit is measured from. */
  html{scroll-behavior:smooth; overflow-x:hidden;
       font-size:calc(100% * var(--font-scale, 1));}
  body{
    font-family:var(--font-body), sans-serif;
    background-color:var(--ui-page-bg, var(--stone));
    background-image:
      radial-gradient(ellipse 1100px 750px at 8% -8%, rgba(255,255,255,0.42), transparent 60%),
      radial-gradient(ellipse 900px 650px at 108% 12%, rgba(185,138,46,0.14), transparent 55%),
      radial-gradient(ellipse 700px 500px at 50% 100%, rgba(122,32,53,0.05), transparent 60%);
    background-repeat:no-repeat;
    color:var(--ink);
    -webkit-font-smoothing:antialiased;
    overflow-x:hidden;
    max-width:100vw;
  }
  /* Room for the fixed cart bar, only while it is up. --cart-h is the bar's
     measured height (app/cart-chrome.js), so the end of the page stops exactly
     at the bar's top edge instead of under it or a fixed 96px above it; the
     fallback is the bar's usual height for the frame before it is measured.
     Without a cart the page ends at the footer. */
  body.has-cart-bar{padding-bottom:var(--cart-h, 124px);}
  /* Guided theme-studio backgrounds. No attribute is the legacy background
     above, so shops that never choose one remain pixel-for-pixel unchanged. */
  html[data-theme-background] body{
    background-color:var(--ui-page-bg, var(--theme-page-bg));
    background-image:none;
    background-size:auto;
    background-position:0 0;
    background-repeat:repeat;
  }
  html[data-theme-background="fade"] body{
    background-image:linear-gradient(var(--theme-pattern-color),#ffffff00 85%);
  }
  html[data-theme-background="vignette"] body{
    background-image:radial-gradient(140% 90% at 50% 40%,#ffffff00 55%,var(--theme-pattern-color));
  }
  html[data-theme-background="duo"] body{
    background-image:linear-gradient(var(--theme-pattern-color) 0 150px,#ffffff00 150px);
  }
  html:is([data-theme-background="dots"],[data-theme-background="kraft"]) body{
    background-image:radial-gradient(circle at 1px 1px,var(--theme-pattern-color) 1px,transparent 1.2px);
    background-size:12px 12px;
  }
  html[data-theme-background="lines"] body{
    background-image:repeating-linear-gradient(45deg,var(--theme-pattern-color) 0 1px,transparent 1px 11px);
  }
  html[data-theme-background="grid"] body{
    background-image:linear-gradient(var(--theme-pattern-color) 1px,transparent 1px),
      linear-gradient(90deg,var(--theme-pattern-color) 1px,transparent 1px);
    background-size:22px 22px;
  }
  html[data-theme-background="linen"] body{
    background-image:repeating-linear-gradient(0deg,var(--theme-pattern-color) 0 1px,transparent 1px 5px),
      repeating-linear-gradient(90deg,var(--theme-pattern-color) 0 1px,transparent 1px 5px);
  }
  html:is([data-theme-background="hatch"],[data-theme-background="darkhatch"]) body{
    background-image:repeating-linear-gradient(45deg,var(--theme-pattern-color) 0 1px,transparent 1px 8px),
      repeating-linear-gradient(-45deg,var(--theme-pattern-color) 0 1px,transparent 1px 8px);
  }
  html:is([data-theme-background="herring"],[data-theme-background="darkherring"]) body{
    background-image:repeating-linear-gradient(45deg,var(--theme-pattern-color) 0 1px,transparent 1px 10px),
      repeating-linear-gradient(-45deg,var(--theme-pattern-color) 0 1px,transparent 1px 10px);
    background-size:20px 20px;background-position:0 0,10px 10px;
  }
  html[data-theme-background="arches"] body{
    background-image:radial-gradient(circle at 50% 100%,transparent 15px,var(--theme-pattern-color) 15px 16px,transparent 16px);
    background-size:34px 34px;
  }
  html[data-theme-background="topo"] body{
    background-image:radial-gradient(circle at 20% 30%,transparent 28px,var(--theme-pattern-color) 28px 29px,transparent 29px),
      radial-gradient(circle at 70% 75%,transparent 34px,var(--theme-pattern-color) 34px 35px,transparent 35px);
    background-size:90px 90px,110px 110px;
  }
  html[data-theme-background="terrazzo"] body{
    background-image:radial-gradient(circle at 12% 22%,var(--theme-pattern-color) 2.5px,transparent 3px),
      radial-gradient(circle at 68% 58%,var(--theme-pattern-alt) 1.8px,transparent 2.2px),
      radial-gradient(circle at 40% 85%,var(--theme-pattern-color) 1.4px,transparent 1.8px);
    background-size:64px 64px,48px 48px,72px 72px;
  }
  html[data-theme-background="marble"] body{
    background-image:linear-gradient(115deg,#ffffff00 45%,var(--theme-pattern-color) 50%,#ffffff00 55%),
      linear-gradient(70deg,#ffffff00 62%,var(--theme-pattern-color) 66%,#ffffff00 70%);
  }
  html[data-theme-background="darkfade"] body{
    background-image:linear-gradient(var(--theme-pattern-color),#00000000 55%);
  }
  html:is([data-theme-background="deep"],[data-theme-background="black"],
    [data-theme-background="ink"],[data-theme-background="espresso"],
    [data-theme-background="darkhatch"],[data-theme-background="darkherring"],
    [data-theme-background="darkfade"]) .category-section{
    border-color:rgba(255,255,255,.12);
  }
  html:is([data-theme-background="deep"],[data-theme-background="black"],
    [data-theme-background="ink"],[data-theme-background="espresso"],
    [data-theme-background="darkhatch"],[data-theme-background="darkherring"],
    [data-theme-background="darkfade"]) .category-head h2{
    color:#F3F5F3;
  }
  html:is([data-theme-background="deep"],[data-theme-background="black"],
    [data-theme-background="ink"],[data-theme-background="espresso"],
    [data-theme-background="darkhatch"],[data-theme-background="darkherring"],
    [data-theme-background="darkfade"]) .category-count{
    color:#F3F5F3;background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.16);
  }
  @supports (overflow-x: clip){
    html, body{overflow-x:clip;}
  }
  button, a{touch-action:manipulation;}

  /* ---------- ACCESSIBILITY: focus states ---------- */
  :where(a[href], button, input, select, textarea, summary, [role=button], [tabindex]:not([tabindex="-1"])):focus-visible{
    outline:var(--focus-ring-width,3px) solid var(--focus-ring,#0B5FFF); outline-offset:2px;
    box-shadow:0 0 0 calc(var(--focus-ring-width,3px) + 2px) var(--focus-ring-halo,#fff) !important;
  }
  .qty-btn:focus-visible, .nav-pill:focus-visible{outline-offset:1px;}
  h1,h2,h3,.display{font-family:var(--font-brand), var(--font-display), serif; font-weight:900;}
  a{color:inherit;} img,svg{display:block; max-width:100%;}
  ::selection{background:var(--gold-light); color:var(--ink);}
  .wrap{max-width:var(--container); margin:0 auto; padding:0 20px;}
  .btn{
    display:inline-flex; align-items:center; justify-content:center; gap:8px;
    font-family:'Rubik'; font-weight:700; font-size:0.90625rem;
    padding:12px 22px; border-radius:var(--r-control);
    text-decoration:none; cursor:pointer; border:none;
    white-space:nowrap; flex-shrink:0;
    transition:transform .18s ease, filter .2s ease, box-shadow .2s ease;
  }
  .btn:active{transform:scale(0.97);}
  .btn svg{width:18px; height:18px; flex-shrink:0;}
  .btn-primary{background:var(--ui-btn-primary-bg, linear-gradient(155deg, var(--primary-mid), var(--primary))); color:var(--ui-btn-primary-text, #fff); border:1px solid rgba(216,177,92,0.55); box-shadow:0 10px 24px -8px rgba(15,30,22,0.55);}
  .btn-primary:hover{transform:translateY(-2px); box-shadow:0 14px 28px -8px rgba(15,30,22,0.65);}
  .btn-outline{background:var(--ui-btn-outline-bg, transparent); border:1.5px solid var(--ui-btn-outline-border, rgba(251,246,236,0.5)); color:var(--ui-btn-outline-text, inherit);}
  /* Dark ink is only the no-fill green. A custom fill on the root without
     --ui-send-text must not keep that ink: that is dark-on-dark before
     contrast.js writes the readable colour. White is that pre-script ink. */
  html[style*="--ui-send-bg"]{--ui-send-ink:#fff}
  .btn-gold{background:var(--ui-send-bg, #25D366); color:var(--ui-send-text, var(--ui-send-ink, #10221A)); box-shadow:0 10px 24px -8px rgba(37,211,102,0.55); box-shadow:0 10px 24px -8px color-mix(in srgb, var(--ui-send-bg, #25D366) 55%, transparent);}
  .btn-gold:hover{transform:translateY(-2px); box-shadow:0 14px 28px -8px rgba(37,211,102,0.65); box-shadow:0 14px 28px -8px color-mix(in srgb, var(--ui-send-bg, #25D366) 55%, transparent);}

  /* ---------- HERO ---------- */
  .hero{
    /* --hero-title-size / --hero-desc-size are the headline and the description
       as the page draws them. #heroTitle and #heroDesc use them, and a moved
       layer (app/visual-canvas.js) does too: its own 3cqw is ~12px on a phone,
       so «גודל הכותרת» was multiplying a line that stayed small. Header styles
       and the phone rules below override the variable, same as they used to
       override font-size. */
    --hero-title-size:calc(clamp(1.625rem,5vw,2.5rem) * var(--hero-title-scale, 1));
    --hero-desc-size:calc(0.96875rem * var(--hero-desc-scale, 1));
    position:relative; overflow:hidden;
    /* Tall enough to feel like an entrance, short enough that the category row
       underneath is on screen without scrolling — a grocery's stock is the
       pitch, and a full-screen hero of empty background hid every item below
       the fold. At 100svh a laptop showed the hero and nothing else; 76svh
       leaves a full row of .nav-pill cards visible. min-height, so a hero with
       a long title or four info items still grows to fit its own content. */
    min-height:min(76svh, 560px);
    display:flex; flex-direction:column; justify-content:center;
    padding:48px 0 56px;
    color:var(--paper);
    text-align:center;
    background:radial-gradient(120% 140% at 50% -10%, #1d4634 0%, #123023 45%, #0c2118 100%);
  }
  /* Older engines that don't know svh fall back to the classic viewport unit. */
  @supports not (height:100svh){ .hero{min-height:min(76vh, 560px);} }
  /* Header style. The default above is the tall entrance every shop has had;
     these two are the alternatives the theme panel offers, applied as a data
     attribute on the hero itself so nothing but this block changes.

     "compact" is for a shop whose pitch is its stock rather than its photo:
     the hero shrinks to a banner and the category cards come up the screen.
     "tall" is the full-height version for a shop with one strong image. */
  .hero[data-header="compact"]{
    min-height:0;
    padding:26px 0 30px;
  }
  .hero[data-header="compact"] .hero-logo{width:calc(56px * var(--logo-scale, 1)); height:calc(56px * var(--logo-scale, 1)); margin-bottom:10px;}
  /* #home beats the phone rule and a boutique variable: the id is what used to
     make `.hero[data-header] #heroTitle` win over those font-size rules. */
  #home[data-header="compact"]{--hero-title-size:calc(clamp(1.25rem,3.4vw,1.75rem) * var(--hero-title-scale, 1));}
  .hero[data-header="tall"]{
    min-height:min(92svh, 720px);
  }
  /* Same fallback the default hero above gets: an engine without svh would
     otherwise read the whole declaration as invalid and leave a "tall" hero
     at the default height. */
  @supports not (height:100svh){ .hero[data-header="tall"]{min-height:min(92vh, 720px);} }
  /* "modern": the default height with a soft surface in the shop's own colour
     and a larger, balanced slogan. The surface is a layer of its own between
     the overlay and the content, because app.js paints the hero's background
     inline (brand gradient, or transparent under a photo) and would win over a
     rule here. srgb first; engines that mix in oklch get the smoother blend. */
  .hero[data-header="modern"]{
    --modern-glow:color-mix(in srgb, var(--primary) 50%, #ffffff);
    --modern-deep:color-mix(in srgb, var(--primary) 65%, #000000);
  }
  #home[data-header="modern"]{--hero-title-size:calc(clamp(1.875rem,6.4vw,3rem) * var(--hero-title-scale, 1));}
  @supports (color:color-mix(in oklch, red, blue)){
    .hero[data-header="modern"]{
      --modern-glow:color-mix(in oklch, var(--primary) 50%, #ffffff);
      --modern-deep:color-mix(in oklch, var(--primary) 65%, #000000);
    }
  }
  .hero[data-header="modern"]::before{
    content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
    background:
      radial-gradient(55% 60% at 88% 6%, color-mix(in srgb, var(--modern-glow) 34%, transparent), transparent 70%),
      radial-gradient(50% 55% at 8% 94%, color-mix(in srgb, var(--modern-glow) 22%, transparent), transparent 70%),
      radial-gradient(90% 70% at 50% 125%, color-mix(in srgb, var(--modern-deep) 55%, transparent), transparent 75%);
  }
  .hero[data-header="modern"] #heroTitle{line-height:1.18; text-wrap:balance;}

  .hero-bg{
    position:absolute; inset:0;
    background-size:cover; background-position:center;
    opacity:0.16; mix-blend-mode:soft-light;
  }
  /* The owner's photo for a phone (app/rich-content.js), over the computer one. */
  @media (max-width:640px){
    .hero-bg.has-phone-image{ background-image:var(--hero-phone-image) !important; }
  }
  .hero-overlay{
    position:absolute; inset:0;
    background:radial-gradient(120% 90% at 50% 0%, rgba(0,0,0,0) 0%, rgba(6,17,12,0.55) 100%);
  }
  .hero-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none;}
  .hero-video[hidden]{display:none;}
  .hero-video-toggle{
    position:absolute;inset-inline-end:1rem;inset-block-end:1rem;z-index:3;
    min-height:44px;min-width:44px;padding:.5rem .75rem;
    border:1px solid var(--ui-line,var(--line));border-radius:var(--r-control);
    color:var(--ui-ink,var(--ink));background:var(--ui-paper,var(--paper));
    font:inherit;font-size:.875rem;cursor:pointer;
  }
  .hero-video-toggle[hidden]{display:none;}
  .hero-content{position:relative; z-index:2; display:flex; flex-direction:column; align-items:center;}
  /* Always the top-left corner, physically: a shopper who switches to French or
     English finds the button where they just tapped it, instead of it jumping
     to the other side with the page direction. The menu and the note open
     from it toward the page, so they stay on screen either way. */
  .lang-picker{
    position:absolute; top:14px; left:14px; z-index:5;
  }
  /* The kill switch (app/translate-provider.js): no picker, even before app.js runs. */
  html[data-translate-mode="off"] .lang-picker{display:none;}
  /* While the shop's translation file loads (app/translate-provider.js). */
  .lang-toggle[aria-busy="true"]{cursor:progress; opacity:.65;
    box-shadow:0 0 0 3px rgba(255,255,255,.55), 0 4px 12px -2px rgba(0,0,0,0.4);}
  /* Shown when a translation file could not be loaded and the shop stays
     in Hebrew. */
  .lang-fallback-note{position:absolute; top:calc(100% + 8px); left:0; width:max-content;
    max-width:min(260px, calc(100vw - 28px)); margin:0; padding:8px 10px; padding-inline-end:30px; border-radius:10px;
    background:var(--paper); color:var(--ink); font-size:0.75rem; line-height:1.4; text-align:start;
    box-shadow:0 6px 18px -6px rgba(0,0,0,.35);}
  .lang-fallback-close{position:absolute; top:4px; inset-inline-end:4px; width:24px; height:24px; padding:0;
    border:0; border-radius:50%; background:none; color:var(--ink-soft); font:inherit; cursor:pointer;}
  .lang-toggle{
    width:42px; height:42px; border-radius:50%;
    padding:0; overflow:hidden;
    background:var(--paper); border:2px solid rgba(255,255,255,0.7);
    cursor:pointer;
    box-shadow:0 4px 12px -2px rgba(0,0,0,0.4);
    transition:transform .2s ease, box-shadow .2s ease;
    display:flex; align-items:center; justify-content:center;
  }
  .lang-toggle:hover{transform:translateY(-2px) scale(1.05); box-shadow:0 6px 16px -2px rgba(0,0,0,0.5);}
  .lang-toggle svg{width:100%; height:100%; display:block;}
  .lang-menu{
    position:absolute; top:calc(100% + 8px); left:0;
    width:max-content; min-width:170px; padding:6px;
    text-align:start;
    background:var(--paper); color:var(--ink); border:1px solid var(--line);
    border-radius:var(--r-panel); box-shadow:0 12px 30px rgba(0,0,0,0.28);
  }
  .lang-menu[hidden]{display:none;}
  .lang-option{
    width:100%; min-height:42px; padding:7px 10px;
    display:grid; grid-template-columns:28px 1fr 18px; align-items:center; gap:10px;
    border:0; border-radius:var(--r-control); background:transparent; color:inherit;
    font:inherit; font-weight:600; text-align:start; cursor:pointer;
  }
  .lang-option:hover, .lang-option:focus-visible{background:rgba(31,77,58,0.1);}
  .lang-option-badge{
    width:28px; height:22px; overflow:hidden; border-radius:var(--r-chip);
    border:1px solid rgba(0,0,0,0.12);
  }
  .lang-option-badge svg{width:100%; height:100%; display:block;}
  .lang-option-name{unicode-bidi:plaintext;}
  .lang-option-check{font-size:1rem; color:var(--primary); visibility:hidden;}
  .lang-option[aria-checked="true"] .lang-option-check{visibility:visible;}
  /* --logo-scale: the owner's «גודל הלוגו» (theme.logoScale, 0.7-1.6). */
  .hero-logo{
    width:calc(76px * var(--logo-scale, 1)); height:calc(76px * var(--logo-scale, 1)); object-fit:contain;
    /* The global img rule caps at 100% of a shrink-wrapped parent, which
       stops the explicit size from growing past the file's own pixels. */
    max-width:none;
    margin-bottom:14px;
    filter:drop-shadow(0 6px 14px rgba(0,0,0,0.45));
  }
  /* The slogan's size, kept on the id: it moved from h1 to h2 so the brand
     name above it can be the page's single h1, and a tag selector would have
     handed its type scale to the name and shrunk the slogan to a default h2. */
  /* --hero-title-scale / --hero-desc-scale: the owner's «גודל הכותרת» and
     «גודל משפט התיאור» (theme.heroTitleScale / heroDescScale, 0.7-1.6). They
     multiply --hero-title-size / --hero-desc-size above, which is what the
     headline, the description and a moved layer all read. overflow-wrap keeps
     a long word inside a phone hero at 160%.
     .wrap's 20px of side padding was wrapping a slogan that still fit the
     hero: the line was one pixel wider than the padded column, and
     text-wrap:balance then split it into two short lines with room on both
     sides. The title may use that padding. A slogan that really needs two
     lines still balances. */
  .hero #heroTitle{font-size:var(--hero-title-size); line-height:1.28; font-weight:800; overflow-wrap:break-word;
    width:max-content; max-width:calc(100% + 40px); margin-inline:-20px;}
  /* The brand name reads as an eyebrow over the slogan — it is the h1 for
     crawlers and for anyone whose logo does not load, not the loudest line. */
  .hero-name{
    font-size:clamp(1rem,2.6vw,1.25rem); line-height:1.3; font-weight:700;
    letter-spacing:.02em; margin-bottom:6px; color:rgba(251,246,236,0.92);
  }
  .hero-name:empty{display:none;}
  /* Off by default (business.showHeroName), because the logo directly above
     usually carries the same words. Clipped rather than `display:none`: this
     is the page's only h1 and it names the business, so it has to stay in the
     accessibility tree and in what a crawler reads. The rectangle is 1px and
     the element is pulled out of flow, so nothing above or below shifts. */
  .hero-name.is-hidden{
    position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
  }
  /* pre-wrap, so the blank lines and breaks the owner typed into the builder's
     textarea survive to the page instead of collapsing into one paragraph.
     Nothing here clamps or caps the height: the description is the owner's
     own words about the shop, and cutting it at two lines with no way to
     expand simply lost the rest. */
  .hero p{margin-top:14px; font-family:var(--font-brand-bold), var(--font-body), sans-serif; font-size:var(--hero-desc-size); line-height:1.6; overflow-wrap:break-word; color:rgba(251,246,236,0.9); max-width:44ch; white-space:pre-wrap;}
  .hero-actions{display:flex; flex-direction:column; gap:12px; margin-top:26px; max-width:400px; margin-left:auto; margin-right:auto;}
  /* The icon sits on the PHYSICAL left of the label on both hero CTAs, in
     either language. `flex-direction:row` is direction-relative, so under the
     shop's dir="rtl" it put the SVG on the right; forcing the row to lay out
     ltr pins the icon left.

     That ltr then has to be taken back off the label, or Hebrew mis-shapes:
     bidi resolves neutral characters against the paragraph direction, so a
     trailing "!" or a phone number inside the text jumps to the wrong end.
     `direction:unset` does NOT do this — direction is inherited, so unset
     resolves to the button's own ltr. Key it off the root instead, which
     app/translate-provider.js flips per language; en (ltr) then needs
     no special case. */
  .hero-actions .btn{width:100%; direction:ltr;}
  html[dir="rtl"] .hero-actions .btn > span{direction:rtl;}

  /* «שורות מידע» on the hero photo, between the description and the
     buttons: 2-4 equal items, each a light icon over its text, with hairline
     dividers. Hidden until a row has text. */
  .hero-info{
    margin-top:26px;
    display:flex; align-items:stretch; justify-content:center;
    max-width:640px;
    margin-left:auto; margin-right:auto;
  }
  .hero-info[hidden]{display:none;}
  .hero-info-item{
    flex:1;
    display:flex; flex-direction:column; align-items:center; gap:8px;
    padding:0 12px;
    border-inline-end:1px solid rgba(251,246,236,0.22);
  }
  .hero-info-item:last-child{border-inline-end:none;}
  .hero-info-item svg{width:24px; height:24px; color:var(--gold-light); flex-shrink:0;}
  .hero-info-item span{font-size:0.78125rem; font-weight:700; line-height:1.4; color:rgba(251,246,236,0.92); overflow-wrap:anywhere;}
  @media (max-width:480px){
    .hero-info-item{padding:0 8px;}
    .hero-info-item svg{width:20px; height:20px;}
    .hero-info-item span{font-size:0.6875rem;}
  }

  /* ---------- HERO ANNOUNCEMENTS ----------
     A contrast panel, not a whisper: this sits on the hero photo, so the text
     carries its own dark backing rather than trusting whatever the owner
     uploaded. One slide wide, scroll-snapped — see renderAnnouncements. */
  .hero-ann{
    margin:18px auto 0;
    max-width:640px;
    width:100%;
  }
  .hero-ann[hidden]{display:none;}
  .hero-ann-view{overflow:hidden; border-radius:var(--r-panel);}
  .hero-ann-track{
    display:flex;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    /* The strip is dragged, so hide the bar it would otherwise show. */
    scrollbar-width:none;
    -ms-overflow-style:none;
    -webkit-overflow-scrolling:touch;
  }
  .hero-ann-track::-webkit-scrollbar{display:none;}
  .hero-ann-slide{
    flex:0 0 100%;
    scroll-snap-align:start;
    /* min-width:0 lets a long word wrap instead of stretching the flex item and
       breaking the one-slide-per-view arithmetic currentIndex() depends on. */
    min-width:0;
    box-sizing:border-box;
    padding:13px 18px;
    background:rgba(12,10,8,0.46);
    border:1px solid rgba(251,246,236,0.2);
    border-radius:var(--r-panel);
    backdrop-filter:blur(3px);
    -webkit-backdrop-filter:blur(3px);
  }
  /* The hero styles `p` generally; these are inside it and want none of that. */
  .hero .hero-ann-slide p{
    margin:0;
    max-width:none;
    font-size:0.9375rem;
    line-height:1.55;
    font-weight:600;
    color:rgba(251,246,236,0.97);
    text-align:center;
  }
  .hero-ann-dots{
    display:flex; justify-content:center; gap:7px;
    margin-top:9px;
  }
  .hero-ann-dot{
    /* 22px of tappable box around a 7px dot: the mark stays subtle without
       becoming a target only a mouse can hit. */
    width:22px; height:22px; padding:0;
    border:none; background:none; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
    -webkit-appearance:none; appearance:none;
  }
  .hero-ann-dot::before{
    content:""; width:7px; height:7px; border-radius:50%;
    background:rgba(251,246,236,0.42);
    transition:background .2s ease, transform .2s ease;
  }
  .hero-ann-dot.is-on::before{
    background:var(--gold-light, rgba(251,246,236,0.95));
    transform:scale(1.25);
  }
  @media (max-width:900px){
    .hero-ann{margin-top:12px;}
    .hero-ann-slide{padding:11px 14px;}
    .hero .hero-ann-slide p{font-size:0.875rem; line-height:1.5;}
  }

  /* ---------- DEALS STRIP ---------- */
  /* The owner's flagged products, after category cards and product search, in a
     panel of their own: a soft rounded tray with a heading, a link out to the
     catalogue, edge fades and side arrows. The CARDS inside are the grid's
     cards — .product-grid.product-carousel filled with the same .card — so
     everything about a product card is still defined once, further down, with
     the category row. What lives here is only the tray around them.

     Every colour is a shop token, not the mock's greens: the panel is a tint of
     the shop's own brand over its paper, so a shop that moves its --primary
     moves the offers panel with it, exactly as the category panels move. */
  .deals-strip{
    position:relative; z-index:39;
    background:var(--ui-menu-bg, var(--stone));
    border-bottom:1px solid var(--line);
    padding:18px 0 16px;
  }
  .deals-strip[hidden]{display:none;}

  /* The panel ground, and the one colour the fades below have to match exactly
     or the row appears to sit on a slightly different surface than its own
     tray. Kept in a variable so the three rules cannot drift apart.

     Both --primary and --paper carry a literal fallback. Neither is declared in
     this file: applyTheme() writes them from the config, and a hand-written
     config that omits the palette would otherwise make the whole color-mix
     invalid — which does not fall back to anything, it simply drops the
     declaration and leaves a transparent tray. */
  .deals-strip{
    --deals-ground:var(--ui-deals-bg,
      color-mix(in srgb, var(--primary, #1F4D3A) 7%, var(--paper, #FBF7EF)));
  }
  .deals-panel{
    background:var(--deals-ground);
    border:1px solid color-mix(in srgb, var(--primary, #1F4D3A) 16%, transparent);
    border-radius:calc(var(--r-panel) * 1.35);
    padding:16px 14px 18px;
    box-shadow:0 18px 40px -28px rgba(11,66,48,.45);
  }
  /* The offers panel is page chrome, not catalogue content. On desktop it
     breaks out of .wrap's catalogue max-width and uses the whole viewport;
     cards keep the catalogue grid's own scale while an off-screen loop gap
     keeps repeated sets from appearing together in this wider window. */
  @media (min-width:900px){
    .deals-strip > .wrap{max-width:none; padding:0;}
    .deals-panel{
      border-inline:0;
      border-radius:0;
      padding:18px 0 20px;
    }
    .deals-view{width:100%;}
  }
  /* color-mix is recent enough that a shop on an older phone would otherwise
     get no panel ground at all — a transparent tray with cards floating in it.
     Declared after the rules above so it wins where the mix is not understood;
     a browser that does understand it never enters this block. */
  @supports not (background: color-mix(in srgb, red 5%, white)){
    .deals-strip{--deals-ground:var(--ui-deals-bg, var(--paper-dim, #F2EDE3));}
    .deals-panel{border-color:var(--line, #E0D8C8);}
  }

  .deals-head{
    display:flex; align-items:center; justify-content:space-between;
    gap:12px; padding:0 6px 2px;
  }
  .deals-title{min-width:0;}
  /* The display face and weight come from the global h1,h2,h3 rule, the same
     way .category-head h2 takes them — only the size and colour differ. */
  .deals-head h2{
    font-size:calc(1.375rem * var(--part-title-scale, 1)); line-height:1.1;
    color:var(--accent-deep); letter-spacing:-.01em;
    overflow-wrap:anywhere;
  }
  /* The mock's short gradient underline. Fades toward the inline end, so it
     mirrors with the page direction rather than pointing the same way in both:
     the same trick .category-rule uses, and the same reason. */
  .deals-rule{
    display:block; height:4px; width:76px; max-width:100%;
    margin-top:7px; border-radius:99px;
    background:linear-gradient(to left, var(--accent-deep), transparent);
  }
  [dir="ltr"] .deals-rule{background:linear-gradient(to right, var(--accent-deep), transparent);}

  .deals-all{
    flex-shrink:0;
    background:var(--ui-deals-pill-bg, var(--accent-deep));
    color:var(--ui-deals-pill-text, var(--paper));
    border-radius:99px; padding:8px 16px;
    font-size:0.875rem; font-weight:700; text-decoration:none;
    white-space:nowrap; transition:filter .2s ease, transform .2s ease;
  }
  .deals-all:hover{filter:brightness(1.12);}
  .deals-all:active{transform:scale(0.96);}

  .deals-view{position:relative; margin-top:14px;}
  /* The track is the category carousel, so it brings its own card width, snap
     and scrollbar. One thing differs here: a visible scrollbar under a panel
     that moves on its own reads as a second, contrary piece of motion — so
     this row hides it and the arrows say "there is more". */
  .deals-track{scrollbar-width:none !important; -ms-overflow-style:none;}
  .deals-track::-webkit-scrollbar{display:none; height:0; width:0;}
  .deals-strip .product-grid.product-carousel{
    /* A flex row of cards the width of a real catalogue card. */
    display:flex !important;
    padding:6px 8px 10px;
  }
  .deals-track > .deal-card{
    width:var(--deals-card-width, 210px);
    flex:0 0 var(--deals-card-width, 210px);
  }
  .deals-strip .product-carousel::-webkit-scrollbar,
  .deals-strip .product-carousel::-webkit-scrollbar-thumb,
  .deals-strip .product-carousel::-webkit-scrollbar-track{
    display:none; width:0; height:0; background:transparent;
  }

  /* The panel's ground bleeding over both ends of the row, so a card slides
     under it rather than being cut off at a hard edge. Physical left/right and
     a matching pair of gradients: the fade must sit on the same two screen
     edges in both directions, and only the gradient's direction flips. */
  .deals-view::before, .deals-view::after{
    content:""; position:absolute; top:0; bottom:0; width:40px;
    pointer-events:none; z-index:1;
  }
  /* The same --deals-ground the panel is painted with, so the fade dissolves
     into its own tray rather than into an approximation of it. */
  .deals-view::before{
    left:0;
    background:linear-gradient(to right, var(--deals-ground), transparent);
  }
  .deals-view::after{
    right:0;
    background:linear-gradient(to left, var(--deals-ground), transparent);
  }

  .deals-arrow{
    position:absolute; top:50%; transform:translateY(-50%); z-index:2;
    width:40px; height:40px; border-radius:99px;
    border:1px solid color-mix(in srgb, var(--primary, #1F4D3A) 18%, transparent);
    background:var(--paper); color:var(--accent-deep);
    display:flex; align-items:center; justify-content:center;
    cursor:pointer; padding:0;
    box-shadow:0 10px 22px -12px rgba(11,66,48,.7);
  }
  .deals-arrow svg{width:20px; height:20px;}
  .deals-arrow:active{transform:translateY(-50%) scale(0.92);}
  /* Inline, so the "previous" arrow is on the side the row comes from in both
     directions — the glyphs inside them are already direction-correct. */
  .deals-arrow.prev{inset-inline-start:-6px;}
  .deals-arrow.next{inset-inline-end:-6px;}
  /* No hover on a touch screen to reveal them, and a 40px circle over a 170px
     card covers too much of it. The row is swipeable there. */
  @media (max-width:640px){
    .deals-arrow{display:none;}
  }
  /* On a desktop the panel is the full viewport width (above), so the head
     takes the page gutter and the arrows sit inside the edge: at -6px the
     "next" arrow hung off-screen. After the base .deals-head / .deals-arrow
     rules on purpose: same specificity, so the later rule wins. */
  @media (min-width:900px){
    .deals-head{padding-inline:clamp(24px, 4vw, 64px);}
    .deals-arrow.prev{inset-inline-start:clamp(12px, 2vw, 32px);}
    .deals-arrow.next{inset-inline-end:clamp(12px, 2vw, 32px);}
  }

  /* The regular price, opposite the deal sticker on the photo. */
  /* Physically top-left in both directions, matching the mock's paired labels.
     The deal sticker stays physically top-right, so the two never swap sides
     when the shopper changes language. */
  .deal-was{
    position:absolute; z-index:2; top:8px; left:8px;
    display:flex; flex-direction:column; align-items:center; gap:2px;
    padding:5px 9px; border-radius:var(--r-chip);
    background:var(--paper, #FBF7EF);
    border:1px solid var(--line, #E0D8C8);
    line-height:1.2; pointer-events:none;
    max-width:calc(100% - 12px); overflow:hidden;
  }
  .deal-was-label{
    font-size:0.625rem; font-weight:500;
    color:var(--ink-soft); white-space:nowrap;
  }
  .deal-was-price, .deal-regular-price{
    font-size:0.75rem; font-weight:700; color:var(--ink);
    white-space:nowrap;
  }
  .deal-was-price{
    position:relative; display:inline-block;
    /* Draw the strike independently of the glyphs. Browser text-decoration on
       this small chip could visually swallow thin numerals; this keeps the
       regular price present and adds a durable, clearly separate line. */
    text-decoration:none !important;
  }
  .deal-was-price::after{
    content:""; position:absolute; z-index:1;
    left:-2px; right:-2px; top:50%; height:1.5px;
    background:currentColor; pointer-events:none;
  }

  /* Real offers get the same surface and badges in the catalogue grid and in
     the optional hot-deals strip. `deal` controls strip membership only. */
  .card.has-deal-offer{
    background:color-mix(in srgb, var(--primary, #1F4D3A) 5%, var(--paper, #FBF7EF));
    border-color:color-mix(in srgb, var(--primary, #1F4D3A) 16%, transparent);
    border-radius:calc(var(--r-panel) * 1.2);
  }
  .card.has-deal-offer .kashrut-tab{top:auto; bottom:0; border-radius:0 var(--r-chip) 0 var(--r-control);}
  /* One sticker for every kind, matching the captain's reference: a label
     over a price on the photo's top-right, tilted 7° clockwise (right side
     lower). It turns about its top-left corner: about the top-right one, as
     .price-tag does, the left corner would rise past the photo's clipped top
     edge and cut off the first line. The «מחיר רגיל» chip (.deal-was) holds
     the opposite corner for every kind. */
  /* Small enough to leave the product in view: at most 62% of the photo, and
     a label that wraps rather than widens (a translated label runs longer). */
  .card .deal-tag{padding:4px 9px 5px; max-width:62%; border-radius:var(--r-chip); color:#fff;
    transform-origin:0 0; transform:rotate(7deg); box-shadow:0 3px 8px rgba(20,25,20,0.26);}
  .card .deal-tag-sale{background:var(--danger, #B93632);}
  .card .deal-tag-bundle{background:var(--ui-qty-plus-bg, var(--accent-deep));}
  .card .deal-tag-tier{background:var(--ui-price-bg, var(--gold, #7A5C24));}
  .card .deal-tag-label{display:block; font-family:var(--font-body), sans-serif; font-size:0.6875rem; font-weight:600;
    line-height:1.15; white-space:pre-line;}
  .card .deal-tag-price{display:block; font-size:1.0625rem; font-weight:900; line-height:1.1; margin-top:1px;}
  .card .deal-tag-tier .deal-tag-label{font-size:0.625rem;}

  @media (min-width:900px){
    .deals-strip .product-grid.product-carousel{padding-inline:24px;}
  }

  @media (max-width:520px){
    .deals-strip{padding:14px 0 12px;}
    .deals-panel{padding:10px 8px 12px; border-radius:var(--r-panel);}
    .deals-head h2{font-size:calc(1.125rem * var(--part-title-scale, 1));}
    .deals-all{font-size:0.8125rem; padding:7px 13px;}
    .deals-view{margin-top:10px;}
    .deals-view::before, .deals-view::after{width:24px;}
    .deals-strip .product-grid.product-carousel{padding:4px 5px 7px;}
    /* The builder deliberately skips measured deal sizing. Keep its static
       mobile cards at the same compact fallback the live sizing code uses. */
    .deals-track > .deal-card{
      width:var(--deals-card-width, 170px);
      flex-basis:var(--deals-card-width, 170px);
    }
    .card .deal-tag{padding:4px 7px 5px;}
    .card .deal-tag-label, .card .deal-tag-tier .deal-tag-label{font-size:0.5625rem; line-height:1.05;}
    .card .deal-tag-tier{padding-block:3px 4px;}
    .card .deal-tag-price{font-size:0.9375rem;}
    /* A phone photo (~150px) cannot fit the sticker and the chip side by
       side, so the chip takes the free bottom-right corner; the bottom-left
       is the kashrut tab's and the ק״ג/יח׳ pill's. */
    .card.has-deal-offer .deal-was{top:auto; left:auto; bottom:4px; right:6px; padding:2px 6px;}
  }

  /* ---------- CATEGORY NAV ---------- */
  /* שורת כרטיסי הקטגוריה נשארת בזרימה הרגילה מתחת ל-hero. היא תצוגת עיון,
     לא סרגל כלים — נעיצה שלה (158px) בלעה חצי מסך בנייד. כשהיא נגללת החוצה,
     .cat-chips התחתון משתלט. */
  .nav-wrap{
    position:relative; z-index:40;
    background:var(--ui-menu-bg, var(--stone));
    border-bottom:1px solid var(--line);
    padding:16px 0 14px;
  }
  /* The owner's categories per row: theme.categoriesPerRow above 640px and
     theme.categoriesPerRowPhone at 640px and below (applyTheme writes both,
     1-10, through app/per-row.js; a shop without the phone value gets the
     computer one). That many cards fill the row; more than that never wrap
     to a second row or shrink — the row scrolls sideways instead, and a card
     is never narrower than 76px however many are asked for (owner,
     2026-10-11). A card stops growing at about 140px, so a low count on a
     desktop shows a row of normal cards instead of stretching posters; the
     max-width below reads whichever count is active. */
  html{--cat-cols:4;}
  html[data-categories-per-row="1"]{--cat-cols:1;}
  html[data-categories-per-row="2"]{--cat-cols:2;}
  html[data-categories-per-row="3"]{--cat-cols:3;}
  html[data-categories-per-row="5"]{--cat-cols:5;}
  html[data-categories-per-row="6"]{--cat-cols:6;}
  html[data-categories-per-row="7"]{--cat-cols:7;}
  html[data-categories-per-row="8"]{--cat-cols:8;}
  html[data-categories-per-row="9"]{--cat-cols:9;}
  html[data-categories-per-row="10"]{--cat-cols:10;}
  html[data-categories-per-row-phone="1"]{--cat-cols-phone:1;}
  html[data-categories-per-row-phone="2"]{--cat-cols-phone:2;}
  html[data-categories-per-row-phone="3"]{--cat-cols-phone:3;}
  html[data-categories-per-row-phone="4"]{--cat-cols-phone:4;}
  html[data-categories-per-row-phone="5"]{--cat-cols-phone:5;}
  html[data-categories-per-row-phone="6"]{--cat-cols-phone:6;}
  html[data-categories-per-row-phone="7"]{--cat-cols-phone:7;}
  html[data-categories-per-row-phone="8"]{--cat-cols-phone:8;}
  html[data-categories-per-row-phone="9"]{--cat-cols-phone:9;}
  html[data-categories-per-row-phone="10"]{--cat-cols-phone:10;}
  @media (max-width:640px){
    html[data-categories-per-row-phone]{--cat-cols:var(--cat-cols-phone);}
  }
  .nav-scroll{
    --cat-gap:12px; --cat-pad:20px;
    display:grid; grid-auto-flow:column;
    grid-auto-columns:max(76px, calc((100% - (var(--cat-cols) - 1) * var(--cat-gap)) / var(--cat-cols)));
    overflow-x:auto; overscroll-behavior-x:contain; scroll-snap-type:x proximity; scrollbar-width:none;
    gap:var(--cat-gap); padding:4px var(--cat-pad) 12px; margin:0 auto;
    max-width:min(var(--container), calc(var(--cat-cols) * 140px + (var(--cat-cols) - 1) * var(--cat-gap) + 2 * var(--cat-pad)));
  }
  /* On a wide screen the row is narrower than the strip, and a flex line that
     starts at the inline edge parks the pills against the right of an RTL page.
     Centre them from the width at which the product grid stops being a phone
     layout. Auto margins on the pills themselves, not justify-content:center:
     a centred line that overflows spills past scrollLeft:0 and hides its first
     card where no scroll can reach it, while an auto margin simply collapses to
     zero once the pills fill the strip — so a shop with more categories than
     fit still scrolls from the first one. (justify-content:safe center says the
     same thing in one line, but Safari only learned "safe" in 17.4.) */
  @media (min-width:640px){
    .cat-chips-scroll > :first-child{margin-inline-start:auto;}
    .cat-chips-scroll > :last-child{margin-inline-end:auto;}
  }
  .nav-scroll::-webkit-scrollbar{display:none;}
  .nav-scroll > .nav-pill{scroll-snap-align:start;}
  /* 118 x 158 was the fixed card; the proportion stays as the column narrows.
     The name and arrow size to the card's own width (cqi), so ten across a
     phone stay inside their cards. */
  .nav-pill{
    position:relative; min-width:0; min-height:0;
    aspect-ratio:118 / 158; container-type:inline-size;
    display:flex; flex-direction:column;
    background:var(--ui-cat-card-bg, var(--paper)) !important;
    border:2px solid var(--line) !important;
    -webkit-appearance:none; appearance:none;
    border-radius:var(--r-panel); cursor:pointer;
    box-shadow:0 4px 14px -6px rgba(31,77,58,0.18);
    transition:transform .2s cubic-bezier(.22,1,.36,1), box-shadow .25s ease, border-color .2s ease;
  }
  .nav-pill:hover{transform:translateY(-3px); box-shadow:0 10px 22px -6px rgba(31,77,58,0.3);}
  .nav-pill:focus:not(:focus-visible){ border-color:var(--line) !important; background:var(--ui-cat-card-bg, var(--paper)) !important;}
  .cat-card-photo{
    height:60%; flex:0 0 60%;
    background-color:var(--paper-dim);
    overflow:hidden; position:relative; border-radius:17px 17px 0 0;
  }
  .cat-card-photo img{
    width:100%; height:100%; object-fit:cover; display:block;
  }
  .cat-card-body{
    flex:1 1 auto; display:flex; align-items:center; justify-content:center;
    padding:8px 8px 0; background:var(--ui-cat-card-bg, var(--paper)) !important;
    border-radius:0 0 17px 17px;
  }
  .cat-card-name{font-family:'Rubik'; font-weight:700; font-size:0.78125rem; color:var(--ui-cat-name-color, var(--ink)); text-align:center; line-height:1.25;}
  .nav-pill .cat-card-body{min-height:0; overflow:hidden;}
  .nav-pill .cat-card-name{font-size:clamp(0.4375rem, 15cqi, 0.78125rem); overflow-wrap:anywhere;
    display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden;}
  .nav-pill .cat-card-arrow{width:clamp(14px, 27cqi, 32px); height:clamp(14px, 27cqi, 32px);}
  .nav-pill .cat-card-arrow svg{width:55%; height:55%;}
  /* Eight or more across a phone: no room for the arrow over the name. */
  @container (max-width:64px){
    .nav-pill .cat-card-arrow{display:none;}
    .nav-pill .cat-card-body{padding:3px 2px 0;}
  }
  /* The circle's centre sits on the photo's bottom edge, so it reads as half
     on the image and half on the label. That edge is the photo's height —
     60% here, 62% on the chip — not a number of its own. */
  .cat-card-arrow{
    position:absolute; top:60%; left:50%; transform:translate(-50%, -50%);
    width:32px; height:32px; border-radius:50%;
    background:var(--ui-cat-arrow-bg, var(--primary)); color:var(--ui-cat-arrow-icon, var(--paper));
    display:flex; align-items:center; justify-content:center;
    box-shadow:0 4px 12px -2px rgba(0,0,0,0.5), 0 0 0 3px var(--paper);
    z-index:10;
  }
  .cat-card-arrow svg{width:17px; height:17px;}
  [dir="ltr"] .cat-card-arrow svg{transform:scaleX(-1);}
  .nav-pill.active{
    background:var(--ui-cat-card-bg, var(--paper)) !important;
    border-color:var(--primary) !important;
    box-shadow:0 10px 24px -6px rgba(15,45,32,0.4);
    transform:translateY(-3px);
  }
  .nav-pill.active .cat-card-body{background:var(--ui-cat-card-bg, var(--paper)) !important;}
  .nav-pill.active .cat-card-arrow{background:var(--ui-cat-arrow-bg, var(--gold));}
  .nav-pill.soon{opacity:.62;}
  .nav-pill:active{transform:translateY(-1px) scale(.97); border-color:var(--primary) !important; background:var(--ui-cat-card-bg, var(--paper)) !important;}

  /* ---------- COMPACT CATEGORY CHIPS (floating) ---------- */
  /* מחליף את שורת הכרטיסים ברגע שהיא נגללת מהמסך. position:fixed ולא sticky:
     ל-html/body יש overflow-x, שהופך אותם ל-scrollport ומבטל sticky לגמרי.
     app.js מוסיף/מסיר את [hidden] ומודד את הגובה אל --chip-h. */
  .cat-chips{
    position:fixed; top:0; left:0; right:0; z-index:60;
    background:color-mix(in srgb, var(--ui-menu-bg, var(--stone)) 92%, transparent);
    -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
    border-bottom:1px solid var(--line);
    box-shadow:0 4px 16px -8px rgba(35,30,20,0.3);
    /* נכנס בהחלקה קלה מלמעלה במקום להבהב */
    transform:translateY(0); opacity:1;
    transition:transform .18s ease, opacity .18s ease;
  }
  .cat-chips[hidden]{display:none;}
  .cat-chips.is-entering{transform:translateY(-100%); opacity:0;}
  .cat-chips-scroll{
    display:flex; gap:8px; align-items:center;
    overflow-x:auto; scrollbar-width:none;
    padding:8px 16px; max-width:var(--container); margin:0 auto;
    /* גלילה אופקית חלקה בנייד בלי לתפוס את הגלילה האנכית של הדף */
    -webkit-overflow-scrolling:touch; overscroll-behavior-x:contain;
  }
  .cat-chips-scroll::-webkit-scrollbar{display:none;}
  /* The pinned bar is a switcher, not a second copy of the browse row: the
     104×138 photo cards it used to repeat made it 159px on a desktop and 143px
     on a phone, a third of a phone screen with the cart bar. A pill instead —
     a small round photo and the name, 44px tall (the touch minimum) — keeps
     the bar near 60px. The in-flow row (.nav-pill above) keeps its cards.
     Same markup as the card (catCardHTML); only the layout differs. */
  .cat-chip{
    flex:0 0 auto; position:relative;
    height:44px; max-width:220px;
    display:flex; flex-direction:row; align-items:center; gap:8px;
    padding-block:4px; padding-inline:4px 14px;
    background:var(--ui-cat-card-bg, var(--paper)); color:var(--ink);
    border:2px solid var(--line);
    border-radius:999px; overflow:hidden;
    cursor:pointer; text-align:start;
    -webkit-appearance:none; appearance:none;
    box-shadow:0 2px 8px -4px rgba(31,77,58,0.22);
    transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease;
  }
  .cat-chip .cat-card-photo{
    width:32px; height:32px; flex:0 0 32px;
    display:flex; align-items:center; justify-content:center;
    background-color:var(--paper-dim);
    overflow:hidden; position:relative; border-radius:50%;
  }
  .cat-chip .cat-card-photo img{width:100%; height:100%; object-fit:cover; display:block;}
  .cat-chip .cat-card-emoji{font-size:1.125rem; line-height:1;}
  .cat-chip .cat-card-body{
    flex:0 1 auto; min-width:0; display:block;
    padding:0; border-radius:0;
  }
  .cat-chip .cat-card-name{
    display:block; font-family:var(--font-body), sans-serif; font-weight:700; font-size:0.8125rem;
    color:var(--ui-cat-name-color, var(--ink)); line-height:1.2;
    /* שם ארוך נקטע בשורה אחת במקום להגביה את הסרגל */
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  /* No chevron on a pill: the whole pill is the button. */
  .cat-chip .cat-card-arrow{display:none;}
  /* מצב פעיל: מסגרת וצל, כמו .nav-pill.active, והשם בצבע המותג. */
  .cat-chip.active{
    border-color:var(--primary);
    box-shadow:0 6px 14px -6px rgba(15,45,32,0.38);
  }
  .cat-chip.active .cat-card-name{color:var(--ui-cat-name-color, var(--primary));}
  .cat-chip.soon{opacity:.62;}
  .cat-chip:active{transform:scale(.97);}
  .cat-chip:focus-visible{outline:3px solid var(--focus-ring,#0B5FFF); outline-offset:2px;}
  @media (max-width:480px){
    .cat-chips-scroll{padding:8px 12px;}
  }

  /* ---------- SHOP SECTIONS ---------- */
  main{max-width:var(--container); margin:0 auto; padding:0 20px;
    background:var(--ui-products-bg, transparent);}
  .category-section{
    /* Clear the floating chip bar when a chip scrolls a section into view. Only
       the chips overlap the page — the card row is in flow — so this tracks
       --chip-h, which app.js measures from the real bar. The fallback matches
       the compact pill bar (44px pill + padding), for the one frame before the
       first measurement lands. */
    scroll-margin-top:calc(var(--chip-h, 61px) + 12px);
    background:rgba(255,252,244,0.6);
    border:1px solid rgba(120,90,40,0.09);
    border-radius:var(--r-panel);
    padding:34px 22px 38px;
    margin:0 0 30px;
    box-shadow:0 1px 2px rgba(35,30,20,0.03), 0 16px 34px -20px rgba(35,30,20,0.14);
  }
  .category-section:first-child{margin-top:8px;}

  .category-head{display:flex; align-items:center; gap:12px; margin-bottom:6px;}
  .category-icon{
    width:44px; height:44px; flex-shrink:0; border-radius:var(--r-control);
    display:flex; align-items:center; justify-content:center; font-size:1.3125rem;
    background:var(--primary); color:var(--paper);
    box-shadow:0 6px 14px -6px rgba(31,77,58,0.5);
  }
  /* The category's own photo, when it has one, instead of its emoji. */
  .category-icon.has-photo{overflow:hidden; padding:0;}
  .category-icon.has-photo img{width:100%; height:100%; object-fit:cover; display:block;}
  .category-head h2{font-size:calc(1.5625rem * var(--part-title-scale, 1)); color:var(--primary);}
  /* One line, always: «8 מוצרים» broke in two beside a long name on a phone.
     flex:none keeps the heading, not the count, the thing that wraps. */
  .category-count{font-size:0.78125rem; color:var(--ink-soft); background:var(--paper-dim); border:1px solid var(--line); padding:3px 10px; border-radius:999px; margin-inline-start:auto; white-space:nowrap; flex:none;}
  .category-rule{
    height:3px; width:64px; margin:12px 0 24px;
    background:linear-gradient(90deg, var(--gold), transparent);
    border-radius:3px;
  }

  /* auto-fill kept packing more columns as the screen grew — six 168px cards
     across a laptop made the produce postage-stamp sized. Let each card have
     real width instead, and cap the row so a wide monitor adds air, not
     columns. */
  .product-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(148px, 1fr)); gap:13px;}
  @media (min-width:640px){
    .product-grid{grid-template-columns:repeat(auto-fill, minmax(210px, 1fr)); gap:18px;}
  }
  /* Every fixed-count track below is minmax(0, 1fr) rather than a bare 1fr: a
     1fr track's automatic minimum is min-content, so one unbreakable product
     name would widen its column past the viewport and shove the neighbouring
     one off a page that clips rather than scrolls. The 0 floor makes the
     track's width the grid's to give, not the content's to demand. The card's
     own min-width:0 is the same guard one level down. (The two auto-fill rules
     above keep their px minimum — that floor is the column width the layout is
     built on, and auto-fill is already free to drop to one column.) */
  /* The owner's products per row, chosen apart for a computer and a phone
     (applyTheme writes both through app/per-row.js): --prod-cols is
     theme.productsPerRow (1-6), --prod-cols-phone theme.productsPerRowPhone
     (1-4; a shop without it gets min(computer, 2), its old phone look).
     - phone, 640px and below: exactly --prod-cols-phone, through the
       !important rules at max-width:640px / 480px further down;
     - tablet, 641-1099px: the computer number, with a card floor of 210px or
       a third of the row, whichever is smaller — so a small tablet, an iPad
       mini or a landscape phone keeps the 3 per row it always had (the 210px
       floor alone dropped 641-~750px to 2). Wider than that the computer
       number takes over: from a window of about 960-980px (measured: 950 → 3,
       980 → 4), before the 1100px desktop breakpoint, a shop on the default
       4 (or 4-6) shows 4 per row where it used to show 3. That is the one
       intended change to an existing shop's look (18px is the gap here);
     - desktop, 1100px and up: exactly --prod-cols.
     No attribute is the shop as it always was: 4, 3 and 2. None of these
     reaches a carousel: its grid-template-columns:none !important outranks
     the plain rules, and the phone rules exclude the carousel class. */
  html{--prod-cols:4; --prod-cols-phone:2;}
  html[data-products-per-row="1"]{--prod-cols:1;}
  html[data-products-per-row="2"]{--prod-cols:2;}
  html[data-products-per-row="3"]{--prod-cols:3;}
  html[data-products-per-row="4"]{--prod-cols:4;}
  html[data-products-per-row="5"]{--prod-cols:5;}
  html[data-products-per-row="6"]{--prod-cols:6;}
  html[data-products-per-row-phone="1"]{--prod-cols-phone:1;}
  html[data-products-per-row-phone="2"]{--prod-cols-phone:2;}
  html[data-products-per-row-phone="3"]{--prod-cols-phone:3;}
  html[data-products-per-row-phone="4"]{--prod-cols-phone:4;}
  @media (min-width:640px){
    html[data-products-per-row] .product-grid{grid-template-columns:repeat(auto-fill, minmax(max(min(210px, calc((100% - 36px) / 3)), calc((100% - (var(--prod-cols) - 1) * 18px) / var(--prod-cols))), 1fr));}
  }
  /* The attribute selector too, or the tablet rule above (more specific)
     would keep a desktop at three. */
  @media (min-width:1100px){
    .product-grid,
    html[data-products-per-row] .product-grid{grid-template-columns:repeat(var(--prod-cols), minmax(0, 1fr)); gap:22px;}
  }

  /* Swipeable row for a featured category. Declared after the responsive grid
     overrides — and matching their !important — so it wins at every width. */
  .product-grid.product-carousel{
    display:grid !important;
    grid-auto-flow:column;
    /* A fixed card width, not a percentage of the row: with a percentage a
       handful of products always fitted the frame, so the strip never scrolled
       and read as a plain row that had ignored the setting. */
    grid-auto-columns:170px;
    justify-content:start;
    grid-template-columns:none !important;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    scroll-padding-inline:2px;
    -webkit-overflow-scrolling:touch;
    padding-bottom:10px;
    scrollbar-width:thin;
  }
  .product-grid.product-carousel > .card{scroll-snap-align:start;}
  @media (min-width:640px){
    .product-grid.product-carousel{grid-auto-columns:210px;}
  }
  /* A thin, unobtrusive scrollbar reads as "this scrolls" on desktop. */
  .product-carousel::-webkit-scrollbar{height:7px;}
  .product-carousel::-webkit-scrollbar-thumb{
    background:rgba(35,30,20,0.22); border-radius:99px;
  }
  .product-carousel::-webkit-scrollbar-track{background:transparent;}

  .card{
    background:var(--ui-card-bg, var(--paper)); border:1px solid rgba(107,124,58,0.16); border-radius:var(--r-panel);
    padding:12px 12px 14px; display:flex; flex-direction:column; position:relative;
    /* A size container, so a narrow card (3 or 4 to a phone) can rearrange its
       stepper: see the @container shopcard block near the end of the file. */
    container:shopcard / inline-size;
    /* A grid item's automatic minimum is its content, so without this the card
       hands the long-name problem straight back to the track above. */
    min-width:0;
    box-shadow:0 1px 2px rgba(35,30,20,0.05), 0 10px 22px -12px rgba(35,30,20,0.18);
    transition:box-shadow .2s ease, border-color .2s ease, transform .2s ease;
  }
  .card:hover{transform:translateY(-2px); box-shadow:0 2px 4px rgba(35,30,20,0.06), 0 16px 28px -14px rgba(35,30,20,0.24);}
  .card.in-cart{border-color:rgba(47,107,79,0.4); box-shadow:0 2px 6px rgba(31,77,58,0.08), 0 14px 28px -14px rgba(31,77,58,0.32);}
  /* Shop photos arrive from phones and suppliers: some on white, some cut out,
     some still in packaging. A single flat ground behind every one — plus a
     hairline to close the shape — makes a mixed catalogue read as one set. */
  /* position:relative is the containing block for the two things pinned to the
     photo: the kashrut ribbon down the top-left corner and the price sticker
     on the top-right one. */
  /* The well is white unless the owner coloured it: «רקע התמונה בכרטיס»
     first, else «רקע כרטיס מוצר» when that one is set (--ui-card-bg exists
     only then), else white. */
  .card-img{
    aspect-ratio:4/3; border-radius:var(--r-control);
    background:var(--ui-card-photo-bg, var(--ui-card-bg, #fff));
    box-shadow:inset 0 0 0 1px rgba(35,30,20,0.06);
    display:flex; align-items:center; justify-content:center;
    overflow:hidden; margin-bottom:10px; position:relative;
  }
  /* The hechsher tab, reading down the top-left corner of the photo.
     Physically left on an RTL page too: `left`, not an inset-inline logical
     property, which the page's dir would flip to the right. It holds the
     corner opposite the price sticker, which is pinned physically top-right,
     so on a normal card the two never meet.

     writing-mode:vertical-rl runs the lettering top-to-bottom. The rl is the
     block direction — how successive lines stack — not the reading order:
     a single line of Hebrew still reads top→bottom, which is the point, and a
     one-line tab is all the max-height allows anyway. text-orientation:mixed
     keeps Hebrew glyphs upright and rotates only Latin runs, so a "BADATZ"
     typed in English still reads down the corner.

     max-height caps a long string instead of letting it run the full height —
     the text is the owner's and can be anything. */
  .kashrut-tab{
    position:absolute; top:0; left:0; z-index:1;
    writing-mode:vertical-rl; text-orientation:mixed;
    max-height:calc(100% - 12px); overflow:hidden;
    padding:7px 3px; box-sizing:border-box;
    background:#6E7F8D; color:#FFF;
    font-size:0.625rem; font-weight:600; line-height:1.2; letter-spacing:.02em;
    /* Mirrored with the corner: square against the photo's top-left, rounded
       on the two edges that face into the picture. */
    border-radius:var(--r-control) 0 var(--r-chip) 0;
    pointer-events:none;
  }
  /* Contain, not cover: cropping a whole vegetable to fill a square loses the
     thing being sold. Padding keeps every subject off the edges. */
  .card-img img{padding:6px;}
  .card-img img{width:100%; height:100%; object-fit:contain; display:block;}
  .card-img .img-fallback{width:100%; height:100%; display:flex; align-items:center; justify-content:center;}
  /* The price as a sticker stuck on the top-right corner of the photo, tilted
     a few degrees the way a hand-stuck label sits. Under the picture it was one
     more line of centred text and stopped reading as a price at a glance.

     Physically top-right — `right`, not an inset-inline logical property,
     which the page's dir would flip to the left on an RTL shop. The corner is
     "ימין עליון" in a Hebrew shop and the same corner in an LTR one, so both
     directions get the identical sticker instead of a mirrored layout.

     The tilt still reads in the corner, but it is rotated about the right edge
     rather than the pill's middle: with transform-origin:center a rotated pill
     lifts its right corner off the photo edge and dips its left one, and the
     inset had to grow to hide it. Anchoring the origin to the corner it is
     pinned to keeps the gap even.

     max-width leaves the opposite corner to the kashrut ribbon: a long price
     wraps inside the pill instead of running across into the tab. The reserve
     is the tab's own width — one 10px line at line-height 1.2 plus its 3px
     side padding, ~18px — plus this pill's 8px inset and a gap of about the
     same, so the two stay clearly apart rather than just short of touching. */
  .price-tag{
    position:absolute; z-index:2;
    top:8px; right:8px;
    transform:rotate(-3deg);
    transform-origin:100% 50%;
    width:max-content; max-width:calc(100% - 34px);
    padding:4px 10px;
    background:var(--ui-price-bg, var(--gold)); color:var(--ui-price-text, #2A1F08);
    font-family:var(--font-brand), 'Suez One'; font-size:0.78125rem;
    border-radius:var(--r-chip);
    box-shadow:0 2px 6px rgba(35,30,20,0.22);
    text-align:center; line-height:1.3;
    pointer-events:none;
  }
  /* Price block. One structure on every card (grid, deals strip, product
     details), drawn by priceTagHtml (app.js) and offer-chrome.js:

       .price-tag                  the block; .deal-tag + .deal-tag-<kind> on an offer
         .deal-tag-label           offer only: «מחיר מבצע», «2 ב-…» (boutique: moved
                                   onto the photo as .boutique-sale-badge)
         .deal-tag-price           offer only: wraps the new price below
           .price-main             amount + currency, ONE unbreakable unit
             .price-amount         "22.90"
             .price-currency       "₪" (before or after, locale.currencyPosition)
           .price-unit             "/ק״ג", " ליח׳" — may wrap to its own line
         .deal-was                 the regular price («מחיר רגיל»): a chip on the
                                   photo (classic), a struck line in the price row
                                   (boutique)
           .deal-was-label
           .deal-was-price         struck (sale) / .deal-regular-price (other kinds)

     A template or a future «עיצוב מחיר ומבצע» preset restyles these hooks; it
     never splits .price-main or clips .deal-was-price. Prices use tabular
     figures so a row of cards lines its numbers up. */
  .price-main, .deal-was-price, .deal-regular-price{white-space:nowrap;}
  .price-tag, .deal-was-price, .deal-regular-price, .selected-mode-price{font-variant-numeric:tabular-nums;}
  .price-currency{}
  .price-amount{}
  .price-unit{}
  /* A product name is free text and unbounded, and a long enough one with no
     space in it is a single unbreakable word. Left alone it becomes the card's
     min-content width, the grid track grows to fit it, and the two-column phone
     layout is pushed off a 360px screen — with html/body carrying overflow-x,
     the displaced column is clipped rather than reachable by scrolling.
     break-word moves an overlong word to its own line; anywhere is what also
     lets it break mid-word when even that line cannot hold it, which is the
     case that matters here. min-width:0 stops the name from setting a floor
     under its flex parent, for the same reason the grid tracks use minmax(0,…).
     Purely layout: the text is still escaped where it is written, and the whole
     name is still shown, wrapped rather than truncated. */
  .card-name{
    font-size:0.875rem; font-weight:600; line-height:1.3; margin-bottom:4px;
    text-align:center; overflow-wrap:anywhere; word-break:break-word; min-width:0;
  }
  /* «ארץ מוצא» (app/origins.js): a small flag and the country under the name,
     centred like it. Only when the owner set one; the flag is decorative (the
     name says it), with a hairline so a white stripe keeps its edge. */
  .card-origin{display:flex; align-items:center; justify-content:center; gap:5px; min-width:0;
    margin:0 8px 6px; font-size:0.75rem; line-height:1.3; color:var(--ink-soft);}
  .card-origin-flag{flex:none; width:1.25em; height:auto; aspect-ratio:4/3; padding:0;
    border-radius:2px; box-shadow:0 0 0 1px rgba(0,0,0,.12); object-fit:cover;}
  .card-origin span{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .variant-picker{display:flex;flex-direction:column;gap:4px;margin:0 10px 8px;font-size:.7rem;
    color:var(--ink-soft);text-align:start;}
  .variant-picker select{width:100%;min-height:34px;border:1px solid var(--line);border-radius:var(--radius-sm);
    background:var(--surface);color:var(--ink);padding:5px 8px;font:inherit;}
  .variant-need{margin:0;color:var(--sale,#C2410C);font-weight:600;}
  .drawer-row .variant-line{display:block;color:var(--ink-soft);font-weight:600;margin-top:2px;}
  /* «אפשרויות הכנה» (app/prep-card.js): the options as icons over a word, one
     picked, then an optional one-line note. Always one row: the options share
     the card's width, and when they would come under 50px each the row scrolls
     sideways instead of squeezing them or wrapping (owner, 2026-10-11). */
  .prep{margin:0 0 8px; min-width:0;}
  .prep-pick{display:flex; gap:3px; overflow-x:auto; overscroll-behavior-x:contain; scrollbar-width:none;
    padding:2px 1px 4px; margin-bottom:6px;}
  .prep-pick::-webkit-scrollbar{display:none;}
  .prep-opt{flex:1 0 50px; min-width:50px; display:flex; flex-direction:column; align-items:center; gap:2px; text-align:center;
    padding:5px 2px 4px; border:1px solid transparent; border-radius:var(--r-chip, 10px); background:none;
    color:var(--ink); font:inherit; font-size:0.6875rem; font-weight:600; line-height:1.15; cursor:pointer;}
  .prep-opt svg{width:24px; height:24px;}
  .prep-opt.on{border-color:var(--primary, #1F4D3A); color:var(--primary, #1F4D3A);
    background:color-mix(in srgb, var(--primary, #1F4D3A) 8%, transparent);}
  .prep-opt:focus-visible{outline:2px solid var(--focus-ring,#0B5FFF); outline-offset:1px;}
  .prep-note{display:flex; align-items:center; gap:6px; border:1px solid var(--line, #E0D8C8); border-radius:var(--r-chip, 10px);
    background:var(--paper, #fff); padding:0 8px; color:var(--ink-soft);}
  .prep-note input{flex:1; min-width:0; border:0; background:none; font:inherit; font-size:0.6875rem; color:var(--ink); padding:7px 0;
    text-overflow:ellipsis;}
  .prep-note:focus-within{outline:3px solid var(--focus-ring,#0B5FFF);outline-offset:2px;box-shadow:0 0 0 5px var(--focus-ring-halo,#fff);}
  .prep-note svg{width:16px; height:16px; flex:none;}
  .card.is-oos .prep{opacity:.55; pointer-events:none;}
  .tier-hint{
    font-size:0.6875rem; color:var(--accent-deep); background:#E4F1F4; border:1px dashed #B9DDE6;
    border-radius:var(--r-chip); padding:4px 6px; margin-bottom:8px; line-height:1.35;
  }
  .tier-hint.active{background:#0E5A73; color:#fff; border-color:#0E5A73; font-weight:600;}
  /* Cap the control at a comfortable width: stretched across a 257px card the
     buttons drifted to opposite corners and stopped reading as one stepper. */
  .qty-row{
    margin-top:auto; display:flex; align-items:center; justify-content:space-between;
    background:var(--paper-dim); border-radius:var(--r-control); padding:5px;
    max-width:150px;
  }
  /* The cap leaves free space in the card, and a flex item with no inline
     margin takes the start of it — so the stepper hugged the inline edge while
     the price and the name above it were centred, and on an RTL shop that read
     as a 40px lean to one side. Auto inline margins split the leftover evenly.
     Scoped to the card: .drawer-row is a horizontal flex row, where auto inline
     margins would eat its free space and shove the stepper off the row. */
  /* The card stepper is wider than the shared cap allows now that its buttons
     are bigger: two 42px squares plus the typable number no longer fit in
     150px, and the row would either overflow or squeeze the buttons. */
  .card .qty-row{margin-inline:auto; max-width:172px;}
  /* flex-shrink:0 so the row's free space comes out of the number in the
     middle, never out of the buttons — a shrunk square stops being the 44px
     target the padded tap area exists to give it. */
  .qty-btn{
    width:34px; height:34px; flex-shrink:0;
    border:none; border-radius:var(--r-chip);
    background:var(--ui-qty-plus-bg, var(--primary)); color:var(--ui-qty-plus-text, var(--paper));
    font-size:1.125rem; font-weight:700; display:flex; align-items:center; justify-content:center; cursor:pointer;
    transition:transform .12s ease;
  }
  .qty-btn:active{transform:scale(0.9);}
  /* Longhand again: `background:#fff` would drop the content-box clip and
     paint the outlined square across the whole tap area.
     The outline is an inset box-shadow rather than a border, because a border
     grows the element: with box-sizing:border-box it eats into the padding, so
     the white fill and the ring around it drift apart by the border width.
     An inset shadow is painted, not laid out, so the square keeps its size. */
  .qty-btn.minus{
    background-color:var(--ui-qty-minus-bg, #fff); color:var(--ui-qty-minus-text, var(--primary));
    border:none;
    box-shadow:inset 0 0 0 1px var(--ui-qty-minus-border, var(--line));
    background-clip:content-box;
  }
  .qty-btn.minus:disabled{opacity:.35; cursor:not-allowed;}
  /* The middle slot is the flexible one: it takes what the buttons leave and
     gives space back before they ever shrink, while min-width keeps the field
     wide enough to type a two-digit quantity into. */
  .qty-value{
    flex:1 1 auto; min-width:34px;
    font-weight:800; font-size:0.9375rem; text-align:center;
    font-variant-numeric:tabular-nums; line-height:1.2;
  }
  /* The number is typable, so it has to keep looking like the plain value it
     replaced: no field chrome until it is focused. */
  .qty-input{
    width:100%; min-width:34px; padding:0; border:none; background:none;
    font:inherit; font-weight:800; color:inherit; text-align:center;
    font-variant-numeric:tabular-nums; line-height:1.2; border-radius:var(--r-chip);
    -moz-appearance:textfield;
  }
  .qty-input::-webkit-outer-spin-button, .qty-input::-webkit-inner-spin-button{
    -webkit-appearance:none; margin:0;
  }
  .qty-input:focus{outline:2px solid var(--focus-ring,#0B5FFF); outline-offset:1px; background:#fff;}
  .qty-unit{font-size:0.625rem; font-weight:700; color:var(--primary); display:block; margin-top:1px; letter-spacing:.01em; opacity:.75;}
  /* A sack priced by weight, or a product with both list prices, can be bought
     either way. The choice gets its own row above the stepper: squeezed into
     the caption slot under the number it ran under the + on a two-up phone
     card. The caption keeps showing the one live unit. */
  .unit-choice{display:flex; justify-content:center; margin-bottom:6px;}
  .card .unit-choice{margin-top:auto;}
  .card .unit-choice + .qty-row{margin-top:0;}
  .unit-toggle{
    display:inline-flex; max-width:100%; border-radius:var(--r-chip);
    background:rgba(0,0,0,.05); padding:2px; gap:2px; line-height:1;
  }
  .unit-opt{
    position:relative; border:none; background:none; cursor:pointer; padding:5px 10px;
    font:inherit; font-size:0.6875rem; font-weight:700; color:var(--ink-soft);
    border-radius:var(--r-chip); letter-spacing:.01em; white-space:nowrap;
    min-width:0; overflow:hidden; text-overflow:ellipsis;
    transition:background .12s ease, color .12s ease;
  }
  .unit-opt.on{background:var(--primary); color:var(--paper);}
  /* Grow the tap target vertically without overlapping the stepper below. */
  .unit-opt::before{content:""; position:absolute; inset:-6px 0 -2px;}
  .unit-opt:focus-visible{outline:2px solid var(--focus-ring,#0B5FFF); outline-offset:1px;}
  /* On the product card the choice is a small pill laid over the photo's
     bottom-left corner — physically left, like the kashrut tab, so it stays
     opposite the price sticker in both directions. The row it used to take
     under the name is gone, which is what makes the card shorter. Segments
     are 32px squares at least: the whole tap target, no ::before reach. */
  .card-img .unit-choice-img{
    position:absolute; left:8px; bottom:8px; z-index:2; margin:0; display:block;
  }
  .unit-choice-img .unit-toggle{
    background:#fff; padding:3px; gap:2px; border-radius:999px;
    box-shadow:0 2px 8px rgba(35,30,20,0.2);
  }
  .unit-choice-img .unit-opt{
    display:inline-flex; align-items:center; justify-content:center;
    min-width:32px; min-height:32px; padding:0 10px; border-radius:999px;
    font-size:0.8125rem; font-weight:800; color:var(--ink);
  }
  .unit-choice-img .unit-opt.on{background:var(--primary); color:#fff;}
  .unit-choice-img .unit-opt::before{content:none;}
  /* The kashrut tab runs down the same left edge (and sits at the bottom on a
     deal card): start the pill past it rather than on top of it. */
  .card-img .kashrut-tab ~ .unit-choice-img{left:26px;}
  /* A sold-out card's overlay owns the photo, and its stepper is dead, so a
     mode to pick has nothing to act on. */
  .card.is-oos .unit-choice-img{display:none;}
  /* Phone-width cards: a photo there is ~140–175px wide, and the 38px pill
     above covered well over half of it. Drawn at 24px with 11px type, it sits
     in the corner and leaves the product visible; each segment still takes a
     32px-tall tap through an invisible ::before that reaches 6px above and
     below the pill (it stops inside the photo, which clips hit-testing). */
  @media (max-width:560px){
    .card-img .unit-choice-img{left:6px; bottom:6px;}
    .card-img .kashrut-tab ~ .unit-choice-img{left:24px;}
    .unit-choice-img .unit-toggle{padding:2px; gap:1px; box-shadow:0 1px 5px rgba(35,30,20,0.2);}
    .unit-choice-img .unit-opt{min-width:0; min-height:20px; height:20px; padding:0 6px;
      font-size:0.6875rem; line-height:1; overflow:visible;}
    .unit-choice-img .unit-opt::before{content:""; position:absolute; inset:-6px -1px;}
  }

  .coming-soon{
    grid-column:1/-1; text-align:center; padding:44px 20px;
    background:var(--paper); border:1.5px dashed var(--line); border-radius:16px;
    color:var(--ink-soft); font-size:0.90625rem; line-height:1.7;
  }

  /* ---------- OUT OF STOCK ---------- */
  /* A product the shop has run out of. The card keeps its place and its shape —
     the shopper is told, rather than left to wonder where it went — and every
     part of it is pulled back at once so the row reads as unavailable at a
     glance rather than as a card with one odd detail.

     Every fill here comes from the shop's own tokens: --primary for the stamp
     and the + strip, --paper for what sits on them, --ink-soft for the muted
     text. The mock is green because the shop it was drawn for is green; a shop
     themed maroon or navy has to get the same card in its own colours rather
     than a green one bolted onto it. */

  /* The photo greys out and drops back, so colour on the card belongs to the
     stamp and the button over it. Scoped to the img: the white well behind it
     stays put, and greying the whole card would take the stamp with it. */
  .card.is-oos .card-img img{
    filter:grayscale(0.85) contrast(0.95);
    opacity:0.55;
  }
  /* An out-of-stock card must not answer a hover the way a buyable one does:
     the lift reads as "this is live, press it". */
  .card.is-oos:hover{transform:none;}
  /* The price is still the shop's price, and worth showing — a shopper deciding
     whether to wait for it needs to know what it costs — but it is not on offer
     right now, so it loses the gold sticker's pull and takes a strike through
     it. Struck AND muted: a strike alone is a small mark on a bright pill, and
     on the mock's grey it is the pairing that says "not this price today". */
  .card.is-oos .price-tag{
    background:var(--paper-dim); color:var(--ink-soft);
    text-decoration:line-through;
    box-shadow:0 1px 3px rgba(35,30,20,0.14);
  }
  /* The name follows the photo back rather than staying full-strength ink over
     a greyed picture, which is the detail that otherwise keeps the card looking
     half-live. */
  .card.is-oos .card-name{color:var(--ink-soft);}

  /* The two controls, stacked over the middle of the photo. Inset rather than
     `inset:0` so the well's rounded corners still show around them, and the
     kashrut tab keeps its own corner: the overlay starts below the top edge the
     tab and the price sticker share, so neither is covered by it. */
  .oos-overlay{
    position:absolute; z-index:3;
    /* Below the top strip, not over it. The price sticker is pinned 8px from
       the top and the kashrut tab starts at 0, so an overlay that began at the
       photo's top edge sat across both — on a two-up phone card the stamp cut
       the hechsher in half. Starting past them leaves each its own band, and
       the two pills still centre in what is left. */
    inset:38px 10px 8px;
    display:flex; flex-direction:column; align-items:stretch; justify-content:center;
    gap:8px; padding:0 12px;
    /* The stamp is a label, not a target; only the button below it takes taps,
       and it turns pointer events back on for itself. Without this the overlay
       would swallow presses meant for the card underneath. */
    pointer-events:none;
  }
  /* The stamp. A solid bar in the shop's primary, which is what makes it read
     as the shop saying so rather than as a browser artefact over the picture. */
  .oos-stamp{
    background:var(--primary); color:var(--paper);
    font-family:var(--font-brand), var(--font-display), serif;
    font-size:0.9375rem; line-height:1.2; font-weight:800; letter-spacing:.02em;
    min-height:34px; padding:6px 14px;
    display:flex; align-items:center; justify-content:center; text-align:center;
    border-radius:999px;
    box-shadow:0 4px 14px rgba(15,30,22,0.35);
    /* The stamp sits over a photo of unknown darkness, so it cannot rely on the
       picture for contrast — the fill is its own. Free text is not involved
       here, but a translated label can still be long: wrap rather than spill. */
    overflow-wrap:anywhere;
  }
  /* "Tell me when it's back". A real button: it is the one thing on this card a
     shopper can still do. Light body against the dark stamp above it, so the
     two do not read as one slab, with the + strip carrying the accent. */
  .oos-notify{
    pointer-events:auto;
    display:flex; align-items:center; justify-content:center; gap:0;
    min-height:34px; padding:0; overflow:hidden;
    background:var(--paper); color:var(--ink);
    border:1px solid var(--line); border-radius:999px;
    font-family:inherit; font-size:0.8125rem; font-weight:800;
    cursor:pointer; text-align:center;
    box-shadow:0 3px 12px rgba(15,30,22,0.25);
    transition:background .14s ease, border-color .14s ease, box-shadow .14s ease, transform .12s ease;
  }
  .oos-notify:hover{
    background:color-mix(in srgb, var(--primary) 8%, var(--paper));
    border-color:var(--primary);
  }
  .oos-notify:active{transform:translateY(1px);}
  /* The label takes the free space and the + strip keeps its width, so a long
     translation shrinks the text rather than squeezing the strip out of square.
     min-width:0 lets it actually shrink inside the flex row. */
  .oos-notify-label{
    flex:1 1 auto; min-width:0; padding:5px 4px 5px 10px;
    line-height:1.2; overflow-wrap:anywhere;
  }
  /* The green + strip down the side of the pill, from the mock. `flex:none` and
     `align-self:stretch` give it the button's full height as a solid block
     rather than a floating glyph. It is decoration on the button, not a second
     control: the whole pill is the target. */
  .oos-notify-plus{
    flex:none; align-self:stretch; width:42px;
    display:flex; align-items:center; justify-content:center;
    background:var(--primary); color:var(--paper);
  }
  .oos-notify-plus svg{width:15px; height:15px;}
  /* Asked-for. The pill tints to say the tap registered and the label changes
     underneath; the + strip stays as it is, so the button keeps its shape and
     the state reads as a fill rather than as a different control. */
  .oos-notify.on{
    background:color-mix(in srgb, var(--primary) 12%, var(--paper));
    border-color:var(--primary);
  }

  /* The stepper stays visible — the card would look broken without it — but it
     is plainly dead. The controls carry a real `disabled`, so this is only the
     look; nothing here is what stops a press. */
  .card.is-oos .qty-row{opacity:0.5;}
  .card.is-oos .qty-btn{
    background-color:var(--paper-dim); color:var(--ink-soft);
    box-shadow:inset 0 0 0 1px var(--line);
    cursor:not-allowed;
  }
  /* The + is filled from --primary in the base rule, and background-color alone
     would leave a gradient behind on a themed shop. */
  .card.is-oos .qty-btn.plus{background-image:none;}
  .card.is-oos .qty-btn:active{transform:none;}
  .card.is-oos .qty-input{color:var(--ink-soft); cursor:not-allowed;}

  /* High contrast mode overrides the shop's palette everywhere else on the
     page, and these two carry text on a fill of their own, so they have to be
     restated here or the stamp keeps a themed green the mode was turned on to
     get rid of. Same !important-on-a-forced-palette pattern as the rules
     around .price-tag and .qty-btn above. */
  html[data-a11y-contrast="1"] .oos-stamp{
    background:#111111 !important; color:#ffffff !important;
  }
  html[data-a11y-contrast="1"] .oos-notify{
    background:#ffffff !important; color:#111111 !important;
    border-color:#111111 !important;
  }
  html[data-a11y-contrast="1"] .oos-notify-plus{
    background:#111111 !important; color:#ffffff !important;
  }
  /* The greyed photo is a deliberate signal rather than decoration, so it is
     kept — but at full strength it fights a mode whose whole point is legibility
     of what remains. Ease it back rather than dropping it. */
  html[data-a11y-contrast="1"] .card.is-oos .card-img img{
    opacity:0.75; filter:grayscale(1);
  }

  /* ---------- FLOATING CART BAR ---------- */
  /* ---------- floating actions ---------- */
  /* Above the cart bar's safe area so a full cart never covers them. Always the
     bottom-right corner, physically, like the language button's top-left: a
     shopper who switches to French or English finds the accessibility button
     where it was (WhatsApp keeps the bottom-left, see .float-wa). */
  .float-actions{
    position:fixed; right:14px; z-index:75;
    bottom:calc(16px + env(safe-area-inset-bottom));
    display:flex; flex-direction:column; gap:10px;
    transition:bottom .18s ease;
  }
  /* Lift clear of the cart bar the moment it appears, so a full basket never
     covers the WhatsApp or accessibility button. --cart-h is the bar's
     measured height (safe area included), so the gap above it is the same
     16px on every device instead of a guess at ~124px. */
  body.has-cart-bar .float-actions{bottom:calc(var(--cart-h, 124px) + 4px);}
  body.has-cart-bar .a11y-panel{bottom:calc(var(--cart-h, 124px) + 68px);}
  .float-btn{
    width:52px; height:52px; border-radius:999px; border:none; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
    box-shadow:0 6px 18px -4px rgba(35,30,20,0.35);
    transition:transform .15s ease;
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  }
  .float-btn:active{transform:scale(0.93);}
  .float-btn:focus-visible{outline:3px solid var(--focus-ring,#0B5FFF); outline-offset:3px;}
  .float-a11y{background:var(--paper); color:var(--primary);
    box-shadow:0 6px 18px -4px rgba(35,30,20,0.35), inset 0 0 0 2px var(--primary);}

  .a11y-panel{
    position:fixed; right:14px; z-index:76;
    bottom:calc(80px + env(safe-area-inset-bottom));
    width:min(320px, calc(100vw - 28px)); max-height:60vh; overflow:auto;
    background:var(--paper); color:var(--ink);
    border:1px solid var(--line); border-radius:var(--r-panel);
    box-shadow:0 14px 40px -12px rgba(35,30,20,0.45);
    display:none; padding:14px 16px 16px;
  }
  .a11y-panel.open{display:block;}
  .a11y-head{display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;}
  .a11y-head h3{font-size:0.9375rem;}
  .a11y-close{background:none; border:none; font-size:1.125rem; line-height:1;
    cursor:pointer; color:var(--ink-soft); padding:6px; margin:-6px;}
  .a11y-body{display:flex; flex-direction:column; gap:8px;}
  .a11y-opt{
    display:flex; align-items:center; gap:9px; width:100%; text-align:start;
    background:#fff; border:1px solid var(--line); border-radius:var(--r-control);
    padding:10px 12px; font:inherit; font-size:0.84375rem; color:var(--ink); cursor:pointer;
  }
  .a11y-opt[aria-pressed="true"]{background:var(--primary); color:var(--paper);
    border-color:var(--primary); font-weight:600;}
  .a11y-opt[disabled]{opacity:.45; cursor:default;}
  /* The step the text buttons are at, so a viewer can see where they are and
     why a further press stops doing anything. */
  .a11y-step{margin-inline-start:auto; font-weight:700; font-variant-numeric:tabular-nums;
    background:var(--paper-dim); border:1px solid var(--line); border-radius:999px;
    min-width:2.2em; text-align:center; padding:1px 6px; font-size:0.75rem;}
  .a11y-opt[aria-pressed="true"] .a11y-step{background:transparent; border-color:currentColor;}
  .a11y-step-now{font-size:0.75rem; font-weight:600; color:var(--ink); text-align:center;
    margin-top:2px;}
  .a11y-note{font-size:0.71875rem; color:var(--ink-soft); line-height:1.6; margin-top:4px;}
  .a11y-note a{color:var(--primary);}

  /* Accessibility modes. Kept as data attributes on <html> so a viewer's choice
     survives a re-render and can be stored. */

  /* ---- High contrast ----
     A real theme, not a filter. filter on #appRoot or its children creates a
     containing block, and .cat-chips / .cart-bar (position:fixed) then pin to
     the filtered ancestor instead of the viewport, so the floating bars drift
     away on scroll. Nothing here filters anything.

     The palette itself is inline custom properties that applyTheme() writes on
     <html> from the shop's config, so a stylesheet rule cannot outrank it —
     applyA11y() swaps those same inline properties for the high-contrast set.
     What is left for CSS is everything hardcoded in this file: the cream tints,
     the soft borders, the translucent text on the dark surfaces. */
  html[data-a11y-contrast="1"]{
    --hc-ink:#111111; --hc-paper:#ffffff; --hc-line:#111111;
  }
  html[data-a11y-contrast="1"] body{
    background-image:none; background-color:var(--hc-paper); color:var(--hc-ink);
  }
  /* Cream-on-cream gone: every light surface becomes plain white on near-black,
     and every hairline becomes a border you can actually see. */
  html[data-a11y-contrast="1"] .card,
  html[data-a11y-contrast="1"] .card-img,
  html[data-a11y-contrast="1"] .nav-pill,
  html[data-a11y-contrast="1"] .cat-chip,
  html[data-a11y-contrast="1"] .cat-card-body,
  html[data-a11y-contrast="1"] .a11y-panel,
  html[data-a11y-contrast="1"] .a11y-opt,
  html[data-a11y-contrast="1"] .drawer,
  html[data-a11y-contrast="1"] .field input,
  html[data-a11y-contrast="1"] .field textarea,
  html[data-a11y-contrast="1"] .category-count,
  html[data-a11y-contrast="1"] .drawer-list-count{
    background:var(--hc-paper) !important; color:var(--hc-ink) !important;
    border-color:var(--hc-line) !important;
  }
  html[data-a11y-contrast="1"] .card,
  html[data-a11y-contrast="1"] .nav-pill,
  html[data-a11y-contrast="1"] .cat-chip,
  html[data-a11y-contrast="1"] .a11y-opt,
  html[data-a11y-contrast="1"] .field input,
  html[data-a11y-contrast="1"] .field textarea{border-width:2px !important; border-style:solid !important;}
  /* Secondary text stops being a soft grey. */
  html[data-a11y-contrast="1"] .category-count,
  html[data-a11y-contrast="1"] .drawer-row .name small,
  html[data-a11y-contrast="1"] .drawer-total-row .label,
  html[data-a11y-contrast="1"] .estimate-note,
  html[data-a11y-contrast="1"] .saved-note,
  html[data-a11y-contrast="1"] .field label,
  html[data-a11y-contrast="1"] .a11y-note,
  html[data-a11y-contrast="1"] .a11y-step-now,
  html[data-a11y-contrast="1"] .coming-soon{color:var(--hc-ink) !important;}
  /* The dark surfaces (hero, cart bar, footer) keep their weight but lose the
     translucent type — white on near-black instead of 55%-opacity cream. */
  html[data-a11y-contrast="1"] .hero,
  html[data-a11y-contrast="1"] .cart-inner,
  html[data-a11y-contrast="1"] footer{background-image:none !important; background-color:#111111 !important;}
  /* With a hero photo, applyBranding makes .hero transparent and paints the
     photo at full opacity with the brand wash over it — so blanking .hero's own
     background is no longer enough to reach near-black here. Both of those
     layers are inline styles, hence the !important. High contrast is a promise
     of white on near-black; a photograph cannot keep it at any wash strength,
     so the picture stands down for as long as the mode is on. */
  html[data-a11y-contrast="1"] .hero-bg,
  html[data-a11y-contrast="1"] .hero-overlay{display:none !important;}
  /* The element itself as well as its descendants. The `*` alone was enough
     while these surfaces took their colour from --paper, which high contrast
     already replaced; theme.ui can now set a colour directly on .cart-inner,
     and that declaration would otherwise be the one left standing on the bar's
     own text. */
  html[data-a11y-contrast="1"] .hero,
  html[data-a11y-contrast="1"] .hero *,
  html[data-a11y-contrast="1"] .cart-inner,
  html[data-a11y-contrast="1"] .cart-inner *,
  html[data-a11y-contrast="1"] footer,
  html[data-a11y-contrast="1"] footer *{color:#ffffff !important;}
  html[data-a11y-contrast="1"] .hero .btn-gold,
  html[data-a11y-contrast="1"] .cart-cta,
  html[data-a11y-contrast="1"] .whatsapp-btn{background:#0a7a34 !important; color:#ffffff !important;
    border:2px solid #ffffff !important;}
  /* Pressed a11y options and the active category keep a filled look, so they
     invert rather than washing out. */
  html[data-a11y-contrast="1"] .a11y-opt[aria-pressed="true"]{background:#111111 !important;
    color:#ffffff !important; border-color:#111111 !important;}
  html[data-a11y-contrast="1"] .a11y-opt[aria-pressed="true"] *{color:#ffffff !important;}
  /* The active category keeps a white card body — .cat-card-body carries the
     background as !important in the base rules and cannot be inverted here, so
     filling the chip and whitening the label would put white on white. Mark the
     selection with a thick border instead, and leave the label dark. */
  html[data-a11y-contrast="1"] .nav-pill.active,
  html[data-a11y-contrast="1"] .cat-chip.active{border-color:#111111 !important;
    border-width:4px !important; border-style:solid !important;}
  html[data-a11y-contrast="1"] .nav-pill.active .cat-card-name,
  html[data-a11y-contrast="1"] .cat-chip.active .cat-card-name,
  html[data-a11y-contrast="1"] .cat-chip .cat-card-name{color:#111111 !important;}
  html[data-a11y-contrast="1"] .price-tag{background:#111111 !important; color:#ffffff !important;
    border:1px solid #ffffff !important;}
  /* Per-category accents come from [data-cat] custom properties that applyTheme()
     writes into its own <style>, so overriding --primary alone never reaches
     them — the category heading, the qty buttons and the unit label kept the
     shop's mid-green. Flatten the accent itself. */
  html[data-a11y-contrast="1"] .category-section,
  html[data-a11y-contrast="1"] .deals-strip{
    --accent-deep:#111111 !important; --accent-mid:#111111 !important;
    --accent-tint:#ffffff !important; --accent-panel:transparent !important; --accent-image:none !important;
  }
  /* The strip's own ground, which is --stone rather than a card surface. */
  html[data-a11y-contrast="1"] .deals-strip,
  html[data-a11y-contrast="1"] .deals-panel{
    background:var(--hc-paper) !important; border-color:var(--hc-line) !important;
  }
  /* The panel's own chrome. The fades are a tint of a ground that no longer
     exists in this mode, and a gradient over text is exactly the low-contrast
     wash the mode is here to remove — so they go entirely rather than being
     recoloured. The gradient rule under the heading goes the same way, flat. */
  html[data-a11y-contrast="1"] .deals-view::before,
  html[data-a11y-contrast="1"] .deals-view::after{display:none !important;}
  html[data-a11y-contrast="1"] .deals-rule{background:#111111 !important;}
  html[data-a11y-contrast="1"] .deals-all{
    background:#111111 !important; color:#ffffff !important;
    border:2px solid #111111 !important;
  }
  html[data-a11y-contrast="1"] .deals-arrow{
    background:#ffffff !important; color:#111111 !important;
    border:2px solid #111111 !important; box-shadow:none !important;
  }
  html[data-a11y-contrast="1"] .deal-was{
    background:#ffffff !important; border:2px solid #111111 !important;
  }
  html[data-a11y-contrast="1"] .deal-was-label,
  html[data-a11y-contrast="1"] .deal-regular-price{color:#111111 !important;}
  /* Longhands, and an inset shadow instead of a border: the `background`
     shorthand would reset the content-box clip these buttons paint with and
     flood the whole padded tap area, and a border would ring that same tap
     area instead of the square the shopper sees. */
  html[data-a11y-contrast="1"] .qty-btn{
    background-color:#111111 !important; color:#ffffff !important;
    background-clip:content-box !important;
    border:none !important;
    box-shadow:inset 0 0 0 2px #111111 !important;
  }
  html[data-a11y-contrast="1"] .qty-btn.minus{
    background-color:#ffffff !important; color:#111111 !important;
    box-shadow:inset 0 0 0 2px #111111 !important;
  }
  html[data-a11y-contrast="1"] .qty-unit{color:#111111 !important; opacity:1 !important;}
  html[data-a11y-contrast="1"] .unit-opt{color:#111111 !important;}
  html[data-a11y-contrast="1"] .unit-toggle{background:#ffffff !important; border:1px solid #111111 !important;}
  html[data-a11y-contrast="1"] .unit-opt.on{background:#111111 !important; color:#ffffff !important;}
  html[data-a11y-contrast="1"] .category-head h2,
  html[data-a11y-contrast="1"] .card-name,
  html[data-a11y-contrast="1"] .qty-value{color:#111111 !important;}
  html[data-a11y-contrast="1"] .qty-input{color:#111111 !important; background:#ffffff !important;}
  html[data-a11y-contrast="1"] .category-icon{background:#111111 !important; color:#ffffff !important;}
  /* The chip bar sits on its own surface; keep its labels dark on white rather
     than inheriting the white the dark surfaces use. */
  html[data-a11y-contrast="1"] .cat-chips{background:#ffffff !important;
    border-bottom:2px solid #111111 !important;}
  /* The surfaces theme.ui can now colour directly. Until these tokens existed
     each of them took its colour from a palette variable high contrast had
     already replaced, so blanking the palette was enough; an owner's hex is
     set on the property itself and would otherwise be the declaration left
     standing. High contrast outranks the owner's palette, so name them. */
  html[data-a11y-contrast="1"] .nav-wrap{background:#ffffff !important;
    border-bottom:2px solid #111111 !important;}
  html[data-a11y-contrast="1"] main{background:transparent !important;}
  /* Every category label, not only the active one two rules above: an inactive
     pill's name reads --ui-cat-name-color before it reads --ink. */
  html[data-a11y-contrast="1"] .cat-card-name{color:#111111 !important;}
  /* The chevron chip is the one filled circle on the card. Keep it filled —
     white on near-black, ringed by the card so it stays legible where it
     straddles the photo's edge. */
  html[data-a11y-contrast="1"] .cat-card-arrow{background:#111111 !important;
    color:#ffffff !important; box-shadow:0 0 0 3px #ffffff !important;}

  /* ---- Emphasise links ----
     Underline real links, and the tappable controls people read as links: the
     hero's call/WhatsApp actions, the floating WhatsApp button, the footer and
     the panel's phone number. Ordinary buttons stay buttons. */
  html[data-a11y-links="1"] a[href],
  html[data-a11y-links="1"] .hero-actions .btn,
  html[data-a11y-links="1"] .float-wa,
  html[data-a11y-links="1"] .whatsapp-btn,
  html[data-a11y-links="1"] .cart-cta,
  html[data-a11y-links="1"] footer a,
  html[data-a11y-links="1"] .a11y-note a{
    text-decoration:underline !important; text-underline-offset:3px;
    text-decoration-thickness:2px;
  }

  /* ---- Stop animations ----
     Durations alone leave the page moving: hover still lifts cards and buttons
     by a transform, the chip bar still slides in from -100%, and the document
     still scrolls smoothly to a category. Kill all four. */
  html[data-a11y-motion="1"]{scroll-behavior:auto !important;}
  html[data-a11y-motion="1"] *{
    animation-duration:0.001ms !important; animation-iteration-count:1 !important;
    animation-delay:0s !important;
    transition-duration:0.001ms !important; transition-delay:0s !important;
    scroll-behavior:auto !important;
  }
  html[data-a11y-motion="1"] *:hover,
  html[data-a11y-motion="1"] *:active,
  html[data-a11y-motion="1"] *:focus{transform:none !important;}
  html[data-a11y-motion="1"] .cat-chips.is-entering{transform:none !important; opacity:1 !important;}

  @media (prefers-reduced-motion: reduce){
    html{scroll-behavior:auto;}
    *{animation-duration:0.001ms !important; transition-duration:0.001ms !important;}
    *:hover, *:active{transform:none !important;}
    .cat-chips.is-entering{transform:none !important; opacity:1 !important;}
  }

  /* ---- Motion level (theme.motionLevel → html[data-motion]) ----
     "subtle" is the default and today's movement plus quiet scroll reveals;
     "lively" moves a little further and longer; "off" drops the reveals and
     slides. Every animation below sits behind no-preference and behind
     :not([data-a11y-motion="1"]), so the customer's own setting always wins,
     and no rule ever leaves content invisible: a reveal only runs while its
     card is entering the viewport, and does nothing at all where scroll-
     driven animations are not supported. Transform and opacity only. */
  html[data-motion="subtle"]{--reveal-shift:14px; --reveal-scale:1; --kb-scale:1.05; --kb-dur:28s;}
  html[data-motion="lively"]{--reveal-shift:28px; --reveal-scale:.96; --kb-scale:1.08; --kb-dur:18s;}
  html[data-motion="off"] .drawer,
  html[data-motion="off"] .cart-inner,
  html[data-motion="off"] .overlay{transition:none;}
  @media (prefers-reduced-motion: no-preference){
    html[data-motion="lively"]:not([data-a11y-motion="1"]) .drawer{transition-duration:.48s;}
    html[data-motion="lively"]:not([data-a11y-motion="1"]) .cart-inner{transition-duration:.48s, .4s;}

    /* Slow zoom on the «מודרני» hero photo. Scale only, never opacity: the
       image is the largest paint on the page and has to be there at once. */
    html[data-motion="subtle"]:not([data-a11y-motion="1"]) .hero[data-header="modern"] .hero-bg,
    html[data-motion="lively"]:not([data-a11y-motion="1"]) .hero[data-header="modern"] .hero-bg{
      animation:hero-ken-burns var(--kb-dur) ease-in-out infinite alternate;
    }

    @supports (animation-timeline: view()){
      /* translate/scale rather than transform, so the hover lift on .card and
         .nav-pill still composes; backwards fill only, so a card that has
         finished entering carries no animation (and no stacking context). The
         carousels scroll sideways and are left alone. */
      html[data-motion="subtle"]:not([data-a11y-motion="1"]) .product-grid:not(.product-carousel) > .card,
      html[data-motion="lively"]:not([data-a11y-motion="1"]) .product-grid:not(.product-carousel) > .card,
      html[data-motion="subtle"]:not([data-a11y-motion="1"]) .nav-scroll > .nav-pill,
      html[data-motion="lively"]:not([data-a11y-motion="1"]) .nav-scroll > .nav-pill{
        animation:reveal-in linear backwards;
        animation-timeline:view();
        animation-range:entry 0% entry 90%;
      }
    }
  }
  @keyframes reveal-in{
    from{opacity:0; translate:0 var(--reveal-shift, 14px); scale:var(--reveal-scale, 1);}
  }
  @keyframes hero-ken-burns{
    from{transform:scale(1);}
    to{transform:scale(var(--kb-scale, 1.05));}
  }

  .cart-bar{position:fixed; left:0; right:0; bottom:0; z-index:70; display:flex; justify-content:center; padding:12px 16px calc(12px + env(safe-area-inset-bottom)); pointer-events:none;}
  .cart-inner{
    pointer-events:auto; width:100%; max-width:560px;
    background:var(--ui-cart-bg, var(--primary)); color:var(--ui-cart-text, var(--paper));
    border-radius:var(--r-panel); box-shadow:0 16px 40px -10px rgba(0,0,0,0.4); display:flex; flex-direction:column;
    padding:0; overflow:hidden; transform:translateY(120%); opacity:0;
    transition:transform .35s cubic-bezier(.22,1,.36,1), opacity .3s ease;
  }
  .cart-inner.visible{transform:translateY(0); opacity:1;}

  .cart-preview{
    width:100%; display:flex; align-items:center; justify-content:space-between; gap:10px;
    background:rgba(251,246,236,0.06); border:none; border-bottom:1px solid rgba(251,246,236,0.12);
    color:rgba(251,246,236,0.88); padding:8px 16px; font-family:'Rubik'; font-size:0.78125rem; font-weight:600;
    cursor:pointer; text-align:start; transition:background .2s ease;
  }
  .cart-preview:hover{background:rgba(251,246,236,0.12);}
  .cart-preview-text{overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1; text-align:start;}
  .cart-preview-chevron{width:14px; height:14px; flex-shrink:0; opacity:.8;}

  .cart-main-row{display:flex; align-items:center; gap:14px; padding:12px 14px;}
  /* --gold-ink is picked by WCAG ratio against the owner's gold (applyTheme,
     app/cart-chrome.js); the fixed dark ink was 2.74:1 on a raspberry gold. */
  .cart-count{background:var(--gold); color:var(--gold-ink, #2A1F08); font-weight:700; font-size:0.875rem; border-radius:999px; min-width:30px; height:30px; display:flex; align-items:center; justify-content:center; flex-shrink:0;}
  .cart-summary{flex:1; min-width:0; cursor:pointer;}
  .cart-summary .label{font-size:0.75rem; color:rgba(251,246,236,0.7);}
  .cart-summary .total{font-family:var(--font-brand), 'Suez One'; font-size:1.1875rem;}
  .cart-cta{
    background:var(--ui-send-bg, #25D366); color:var(--ui-send-text, var(--ui-send-ink, #10221A));
    border:none; border-radius:var(--r-control); font-family:'Rubik'; font-weight:800; font-size:0.90625rem;
    padding:12px 18px; cursor:pointer; white-space:nowrap; display:flex; align-items:center; gap:7px;
    box-shadow:0 8px 18px -6px rgba(37,211,102,0.55); box-shadow:0 8px 18px -6px color-mix(in srgb, var(--ui-send-bg, #25D366) 55%, transparent);
    transition:transform .15s ease, box-shadow .2s ease, background-color .2s ease;
  }
  .cart-cta:hover{box-shadow:0 10px 22px -6px rgba(37,211,102,0.65); box-shadow:0 10px 22px -6px color-mix(in srgb, var(--ui-send-bg, #25D366) 55%, transparent); transform:translateY(-1px);}
  .cart-cta:active{transform:scale(0.96);}
  .cart-cta svg{width:17px; height:17px; flex-shrink:0;}

  /* ---------- DRAWER ---------- */
  .overlay{position:fixed; inset:0; z-index:80; background:rgba(28,43,34,0.5); opacity:0; pointer-events:none; transition:opacity .25s ease;}
  .overlay.visible{opacity:1; pointer-events:auto;}
  .drawer{
    position:fixed; left:0; right:0; bottom:0; z-index:81; background:var(--paper);
    border-radius:var(--r-panel) 20px 0 0; max-height:88vh; display:flex; flex-direction:column;
    transform:translateY(100%); transition:transform .35s cubic-bezier(.22,1,.36,1);
    box-shadow:0 -10px 30px rgba(0,0,0,.25);
  }
  .drawer.visible{transform:translateY(0);}
  /* A bottom sheet on a phone; on a desktop the full-width sheet stretched a
     two-line basket and its form across 1440px. Same sheet, a readable column,
     centred, rounded at both top corners. */
  @media (min-width:900px){
    .drawer{width:min(640px, calc(100vw - 48px)); margin-inline:auto;
      border-radius:var(--r-panel) var(--r-panel) 0 0;}
  }
  .drawer-handle{width:40px; height:4px; border-radius:2px; background:var(--line); margin:12px auto 4px;}
  .drawer-head{display:flex; align-items:center; justify-content:space-between; padding:8px 20px 14px; border-bottom:1px solid var(--line);}
  .drawer-head h3{font-size:1.25rem; color:var(--primary);}
  .drawer-close{background:none; border:none; font-size:1.375rem; color:var(--ink-soft); cursor:pointer; line-height:1; padding:4px;}
  .drawer-scroll{overflow-y:auto; flex:1; -webkit-overflow-scrolling:touch;}
  .drawer-list-label{
    display:flex; align-items:center; justify-content:space-between;
    padding:12px 20px 4px; font-size:0.78125rem; font-weight:700; color:var(--primary);
  }
  .drawer-list-count{font-weight:500; color:var(--ink-soft); background:var(--paper-dim); padding:2px 9px; border-radius:999px; font-size:0.71875rem;}
  /* The count and the reset control share the end of the label row, so the
     reset never lands next to the drawer's close button — a mis-tap there
     would empty the basket instead of closing the panel. */
  .drawer-list-tools{display:flex; align-items:center; gap:8px;}
  /* Deliberately quiet: this is a destructive action sitting above the list,
     not something to aim for. Underlined text rather than a button face, in the
     muted ink the count already uses. The negative margin keeps the 44px tap
     area from pushing the row taller than the count beside it. */
  .clear-cart-btn{
    background:none; border:none; cursor:pointer; font:inherit;
    font-weight:600; font-size:0.71875rem; color:var(--ink-soft);
    text-decoration:underline; text-underline-offset:2px;
    padding:11px 6px; margin:-11px -6px;
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  }
  .clear-cart-btn:hover{color:var(--primary);}
  .drawer-list{padding:0 20px;}
  .drawer-empty{padding:36px 0; text-align:center; color:var(--ink-soft); font-size:0.875rem;}
  .drawer-row{display:flex; align-items:center; gap:10px; padding:11px 0; border-bottom:1px dashed var(--line);}
  .drawer-row:last-child{border-bottom:none;}
  .drawer-row .name{flex:1; font-weight:600; font-size:0.875rem;}
  .drawer-row .name small{display:block; font-weight:400; font-size:0.6875rem; color:var(--ink-soft); margin-top:2px;}
  .drawer-row .line-total{font-weight:700; font-size:0.84375rem; color:var(--primary); white-space:nowrap;}
  .drawer-row .qty-row{width:118px; padding:4px;}
  /* In the drawer the choice sits under the product name, start-aligned with
     it, and a size smaller than on the card. */
  .drawer-row .unit-choice{justify-content:flex-start; margin:5px 0 0;}
  .drawer-row .unit-opt{padding:4px 8px; font-size:0.625rem;}
  /* The drawer stepper is deliberately small and sits in a list, not under a
     thumb aiming at a grid — it wants no padded tap area, so drop the padding
     the shared rule adds and let the whole 25px square be painted. */
  .drawer-row .qty-btn{width:25px; height:25px; font-size:0.875rem; padding:0; margin:0;}
  /* The + sits on .qty-row's own light strip, so a fill that fails to resolve
     leaves a white glyph on near-white — which is exactly what happened while
     --accent-deep was category-scoped (see :root). The fill is stated here on
     the drawer's own rule rather than left to the shared fallback chain, and
     the glyph is bumped to 800 so the small square reads as firmly as the
     bigger one on a card. The owner's token still wins when they set it. */
  .drawer-row .qty-btn.plus{
    background-color:var(--ui-qty-plus-bg, var(--primary));
    color:var(--ui-qty-plus-text, var(--paper));
    font-weight:800;
    box-shadow:none;
  }
  /* − keeps its outlined square and its ring: this is about bringing + up to
     it, never about quieting the control next to it. */
  .drawer-row .qty-btn.minus{
    box-shadow:inset 0 0 0 1px var(--ui-qty-minus-border, var(--line));
  }

  /* One tap empties a line, instead of holding − down to zero. Secondary to
     the stepper by design — no fill, muted ink, and a lighter glyph — so the
     row still reads as +/- first. It sits at the inline end, after the line
     total, away from the stepper: on RTL and LTR alike the two destructive-ish
     controls (− and ✕) then never sit adjacent. The painted glyph stays small
     while padding plus a matching negative margin grow the tap area to 44px,
     the same trick .qty-btn uses. */
  .drawer-row .row-remove{
    flex:none; background:none; border:none; cursor:pointer;
    color:var(--ink-soft); font-size:0.875rem; line-height:1;
    width:44px; height:44px; padding:0;
    /* Logical, so the overhang lands past the row's inline end on both an RTL
       and an LTR shop rather than always on the physical right. */
    margin-block:-9px; margin-inline:0 -12px;
    display:flex; align-items:center; justify-content:center;
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  }
  .drawer-row .row-remove:hover{color:var(--danger);}

  /* Quantity buttons are tapped dozens of times per order, and a 34px control
     is below the 44px both Apple and Google recommend. Pad the button out to
     44px and pull the padding back with a negative margin, so the tap area
     really grows while the painted button stays the same size. */
  /* box-sizing is border-box globally, so padding alone shrinks the content
     instead of growing the element: set the outer size explicitly and let
     background-clip keep the painted square at its original 34px. */
  /* Outlined until the product is in the cart: a solid green square on every
     card competed with the produce photo it sits under, which is the thing
     being sold. Filling on demand also shows what is already in the basket. */
  /* The tap area is 44px but the painted button stays 34px; box-shadow follows
     the padding box, so draw the visible square with a border on an inner
     element instead — background-clip alone hid the glyph. */
  .qty-btn{
    width:44px; height:44px; padding:5px; margin:-5px;
    background-clip:content-box;
    background-color:var(--primary);
  }
  /* On a card the painted square is the whole control a shopper aims at, and
     at 34px it read as a footnote under the photo. Grow what is painted, not
     the tap area: 46px outer less 2px of padding leaves
     background-clip:content-box painting a 42px square, still with the padded
     margin trick around it. The drawer keeps its own smaller buttons — that
     row is a list of lines, not a grid of targets. */
  .card .qty-btn{
    width:46px; height:46px; padding:2px; margin:-2px;
    font-size:1.25rem;
  }
  .drawer-close{padding:10px; margin:-6px;}
  /* A finger that lands between two buttons should not select text or wait
     300ms for a double-tap that is never coming. */
  .qty-btn, .drawer-close, .lang-toggle, .nav-pill{
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  }

  .customer-form{padding:14px 20px 4px; border-bottom:1px solid var(--line);}
  .customer-form h4{font-size:0.90625rem; color:var(--primary); margin-bottom:10px;}
  .field{margin-bottom:10px;}
  .field label{display:block; font-size:0.75rem; color:var(--ink-soft); margin-bottom:4px; font-weight:600;}
  .field input, .field textarea{
    width:100%; border:1px solid var(--line); border-radius:10px; padding:12px 13px;
    /* Never below 16px: iOS Safari zooms the page when a focused field's text
       is smaller, and the "smaller text" step would otherwise take it to 14px.
       max() keeps the larger steps working. */
    font-family:'Rubik'; font-size:max(1rem, 16px); background:#fff; color:var(--ink); resize:vertical;
  }
  .field input:focus, .field textarea:focus{outline:2px solid var(--focus-ring,#0B5FFF); outline-offset:1px;}
  .field.error input, .field.error textarea{border-color:var(--danger);}
  .field-error-msg{font-size:0.6875rem; color:var(--danger); margin-top:3px; display:none;}
  .field.error .field-error-msg{display:block;}
  /* Delivery or pickup (app/fulfilment.js): two choices as one row of pills,
     then the point and the date as the form's own fields. Only in a shop with
     a pickup point; without one the form is exactly as before. */
  .ff-choice{margin-bottom:12px;}
  .ff-methods{display:flex; flex-wrap:wrap; gap:8px; border:0; padding:0; margin:0 0 12px;}
  .ff-methods legend{width:100%; font-size:0.75rem; color:var(--ink-soft); font-weight:600; margin-bottom:6px; padding:0;}
  .ff-method{position:relative; flex:1 1 0; min-width:0; cursor:pointer;}
  .ff-method input{position:absolute; inset:0; opacity:0; margin:0; cursor:pointer;}
  .ff-method span{display:flex; align-items:center; justify-content:center; min-height:44px; padding:0 12px;
    border:1px solid var(--line); border-radius:var(--r-control, 10px); background:var(--paper); color:var(--ink);
    font-size:0.875rem; font-weight:600; text-align:center;}
  .ff-method input:checked + span{border-color:var(--primary); background:var(--primary); color:var(--paper);}
  .ff-method input:focus-visible + span{outline:2px solid var(--focus-ring,#0B5FFF); outline-offset:2px;}
  .field select{width:100%; min-height:44px; padding:8px 10px; border:1px solid var(--line); border-radius:var(--r-control, 10px);
    background:var(--paper); color:var(--ink); font:inherit; font-size:max(1rem, 16px);}   /* 16px: no iOS zoom, as the inputs */
  .field select:focus{outline:2px solid var(--focus-ring,#0B5FFF); outline-offset:1px;}
  .field select:disabled{color:var(--ink-soft);}
  .field.error select{border-color:var(--danger);}

  .drawer-foot{padding:12px 20px calc(16px + env(safe-area-inset-bottom)); border-top:1px solid var(--line); background:var(--paper);}
  .coupon-box{margin-bottom:12px;padding:10px;border:1px solid var(--line);border-radius:var(--r-control);background:var(--paper-dim);}
  .coupon-box>label{display:block;font-size:.75rem;font-weight:700;color:var(--primary);margin-bottom:5px;}
  .coupon-entry{display:flex;gap:7px;}
  .coupon-entry input{min-width:0;flex:1;border:1px solid var(--line);border-radius:var(--r-control);padding:9px 10px;font:inherit;font-size:max(1rem,16px);text-transform:uppercase;background:#fff;color:var(--ink);}
  .coupon-entry input:focus{outline:2px solid var(--focus-ring,#0B5FFF);outline-offset:1px;}
  .coupon-entry button{border:0;border-radius:var(--r-control);padding:8px 14px;background:var(--primary);color:#fff;font:inherit;font-weight:700;cursor:pointer;}
  .coupon-status{min-height:1.2em;margin-top:5px;font-size:.71875rem;color:var(--danger);}
  .coupon-status.ok{color:var(--primary);font-weight:700;}
  .coupon-remove{border:0;background:none;color:var(--ink-soft);font:inherit;font-size:.71875rem;text-decoration:underline;cursor:pointer;padding:2px 0;}
  .drawer-total-row{display:flex; justify-content:space-between; align-items:baseline; margin-bottom:6px;}
  .drawer-total-row .label{font-size:0.84375rem; color:var(--ink-soft);}
  .drawer-total-row .value{font-family:var(--font-brand), 'Suez One'; font-size:1.375rem; color:var(--primary);}
  .coupon-total-row .value{font-family:'Rubik';font-size:.84375rem;font-weight:700;}
  .coupon-total-row.discount .value{color:var(--primary);}
  .estimate-note{font-size:0.65625rem; color:var(--ink-soft); line-height:1.5; margin-bottom:10px;}
  .whatsapp-btn{
    width:100%; background:var(--ui-send-bg, #25D366); color:var(--ui-send-text, var(--ui-send-ink, #10221A));
    border:none; font-family:'Rubik'; font-weight:800; font-size:1.0625rem;
    padding:16px; border-radius:var(--r-panel); display:flex; align-items:center; justify-content:center; gap:10px; cursor:pointer;
    box-shadow:0 10px 24px -6px rgba(37,211,102,0.55), 0 2px 6px rgba(0,0,0,0.08); box-shadow:0 10px 24px -6px color-mix(in srgb, var(--ui-send-bg, #25D366) 55%, transparent), 0 2px 6px rgba(0,0,0,0.08);
    transition:transform .15s ease, box-shadow .2s ease, background-color .2s ease;
  }
  .whatsapp-btn:hover{box-shadow:0 14px 30px -6px rgba(37,211,102,0.65), 0 3px 8px rgba(0,0,0,0.1); box-shadow:0 14px 30px -6px color-mix(in srgb, var(--ui-send-bg, #25D366) 55%, transparent), 0 3px 8px rgba(0,0,0,0.1); transform:translateY(-2px);}
  .whatsapp-btn:active{transform:scale(0.97) translateY(0);}
  .whatsapp-btn svg{width:22px; height:22px;}
  /* Washed toward white so a custom fill does not look identical to the live
     button. The label is a fixed dark ink: white owner text on this lightened
     fill fails 4.5:1. */
  .whatsapp-btn:disabled{
    background:color-mix(in srgb, var(--ui-send-bg, #25D366) 45%, #fff);
    color:#10221A; cursor:not-allowed; box-shadow:none; transform:none;
  }
  /* Without color-mix the mix is dropped and the fill would stay solid. A white
     veil over the same fill is the fade; opacity would wash the label out too.
     The fallback fill is the real green — a pale mint veil disappears on the
     page. The label is the same fixed dark ink. */
  @supports not (background: color-mix(in srgb, red 5%, white)){
    .whatsapp-btn:disabled{background:linear-gradient(rgba(255,255,255,.55), rgba(255,255,255,.55)), var(--ui-send-bg, #25D366);color:#10221A;}
  }
  .payment-btn{width:100%;border:0;border-radius:var(--r-panel);padding:15px 18px;margin-bottom:9px;
    background:var(--primary);color:#fff;font-family:'Rubik';font-weight:800;font-size:1.0625rem;
    cursor:pointer;box-shadow:0 8px 22px -8px rgba(0,0,0,.4);transition:.15s;}
  .payment-btn:hover{filter:brightness(1.08);transform:translateY(-1px);}
  .payment-btn:disabled{background:#d3f6df;color:#10221A;cursor:wait;box-shadow:none;transform:none;filter:none;}
  .send-error{text-align:center; font-size:0.8125rem; font-weight:600; color:var(--danger); margin-top:8px; line-height:1.4;}
  .saved-note{text-align:center; font-size:0.6875rem; color:var(--ink-soft); margin-top:8px; opacity:0; transition:opacity .3s ease;}
  .saved-note.show{opacity:1;}

  /* ---------- FOOTER ---------- */
  /* The bottom padding clears the two fixed circles pinned to the bottom
     corners — the floating WhatsApp button on the left and the accessibility
     one on the right, each 54px tall and sitting 22px off the bottom. Without
     it the centred legal links end up underneath them on a short page, where
     the footer is the last thing on screen. */
  footer{background:var(--primary); color:rgba(251,246,236,0.8); padding:34px 0 92px; margin-top:56px;}
  /* Three zones on a wide screen: the logo at the inline start (physical right
     on this RTL shop), the icon row in the middle, the contact details at the
     inline end. `1fr auto 1fr` keeps the middle column optically centred in the
     footer whatever the two outer zones weigh, so a long address does not drag
     the icon row off centre. align-items:center rather than start because the
     three zones are different heights and a shared baseline reads as a row. */
  .footer-zones{
    display:grid; grid-template-columns:1fr auto 1fr;
    gap:22px 40px; align-items:center;
  }
  /* app.js hides every optional footer element by toggling [hidden]; the
     base [hidden] rule at the top of this file keeps them hidden whatever
     display rule they carry (an empty review pill once showed under the
     WhatsApp chip). */

  .footer-zone{display:flex; flex-direction:column; gap:8px; min-width:0;}
  .footer-zone-brand{align-items:flex-start;}
  .footer-zone-social{align-items:center;}
  /* The contact column is read as a block of details, so it hugs the inline
     end and sets its text the same way. */
  .footer-zone-contact{align-items:flex-end; text-align:end;}
  .footer-logo{width:54px; height:54px; object-fit:contain;}
  .footer-name{font-family:var(--font-brand), 'Suez One'; font-size:calc(1.0625rem * var(--part-title-scale, 1)); color:#fff;}
  .footer-info{font-size:0.8125rem; line-height:2; white-space:pre-wrap;}
  .footer-info a{text-decoration:underline;}
  .footer-contact-link{
    font-size:0.8125rem; color:rgba(251,246,236,0.8);
    text-decoration:underline; text-underline-offset:3px;
  }
  .footer-contact-link:hover{color:#fff;}

  /* «נקודות איסוף» (app/pickup-points.js): a full row under the three zones,
     each point a small block of its own — the name, the address, the days and
     hours, a note, and two real links (Waze, Google Maps) the browser opens on
     the tap. The footer's own colours, so an owner's palette carries over. */
  .footer-zone-pickup{grid-column:1/-1; align-items:stretch; text-align:start;
    padding-top:18px; border-top:1px solid rgba(251,246,236,0.18);}
  .footer-pickup-title{font-size:0.9375rem; font-weight:700; color:#fff; margin:0;}
  .footer-pickup-list{list-style:none; margin:0; padding:0; display:grid; gap:12px 28px;
    grid-template-columns:repeat(auto-fill, minmax(min(100%, 15rem), 1fr));}
  .footer-pickup-point{display:flex; flex-direction:column; gap:3px; min-width:0;
    font-size:0.8125rem; line-height:1.55;}
  .footer-pickup-name{color:#fff; font-weight:700;}
  .footer-pickup-address,.footer-pickup-note{overflow-wrap:anywhere;}
  .footer-pickup-hours{display:flex; flex-wrap:wrap; gap:0 6px;}
  .footer-pickup-day bdi{font-variant-numeric:tabular-nums;}
  .footer-pickup-links{display:flex; flex-wrap:wrap; gap:8px; margin-top:4px;}
  .footer-pickup-link{display:inline-flex; align-items:center; min-height:32px; padding:0 12px;
    border:1px solid rgba(251,246,236,0.45); border-radius:var(--r-chip, 999px);
    color:rgba(251,246,236,0.92); text-decoration:none; font-weight:600;}
  .footer-pickup-link:hover{color:#fff; border-color:#fff;}

  /* The social row sits inside the centre zone now, so it no longer sets its
     own top margin — the grid's gap spaces it. Drawn only for what the config
     actually holds: app.js hides the row entirely when no network is set. */
  .footer-social{display:flex; flex-wrap:wrap; justify-content:center; gap:12px;}
  /* A plain circular chip rather than each network's own brand colour: five
     saturated circles fight the footer, and one neutral set reads as a row.
     currentColor carries the glyph, so the high-contrast rule that repaints
     `footer *` white takes the icons with it. */
  .social-chip{
    width:42px; height:42px; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    background:rgba(251,246,236,0.12);
    border:1px solid rgba(251,246,236,0.25);
    color:rgba(251,246,236,0.9);
    text-decoration:none;
    transition:transform .15s ease, background-color .2s ease, border-color .2s ease;
  }
  .social-chip:hover{background:rgba(251,246,236,0.22); border-color:rgba(251,246,236,0.5); transform:translateY(-2px);}
  .social-chip:active{transform:translateY(0) scale(0.96);}
  .social-chip svg{width:20px; height:20px;}
  /* The chips are the smallest tap targets in the footer; 42px keeps them at
     the size the rest of the shop uses for a circular control. */

  /* The Google review link. Same neutral treatment as the chips so the centre
     zone reads as one group, but a pill with a word in it — it asks for an
     action rather than pointing at a page. 42px tall matches the chip row. */
  .footer-review{
    display:inline-flex; align-items:center; gap:8px;
    min-height:42px; padding:0 16px; border-radius:999px;
    background:rgba(251,246,236,0.12);
    border:1px solid rgba(251,246,236,0.25);
    color:rgba(251,246,236,0.9);
    font-size:0.8125rem; text-decoration:none;
    transition:transform .15s ease, background-color .2s ease, border-color .2s ease;
  }
  .footer-review:hover{background:rgba(251,246,236,0.22); border-color:rgba(251,246,236,0.5); transform:translateY(-2px);}
  .footer-review:active{transform:translateY(0) scale(0.98);}
  .footer-review svg{width:18px; height:18px; flex:none;}

  .footer-legal{
    display:flex; flex-wrap:wrap; justify-content:center; align-items:center;
    gap:10px; margin-top:16px; font-size:0.8125rem;
  }
  .footer-legal a{color:rgba(251,246,236,0.8); text-decoration:underline; text-underline-offset:3px;}
  .footer-legal a:hover{color:#fff;}
  .footer-legal .sep{color:rgba(251,246,236,0.35);}
  footer .copy{text-align:center; font-size:0.75rem; margin-top:26px; padding-top:18px; border-top:1px solid rgba(251,246,236,0.15); color:rgba(251,246,236,0.55);}
  /* The name is translate=no; a browser translator (Chrome's) drops the spaces around such a span, so keep them here. */
  footer .copy .copy-name{margin-inline:0.3em;}

  /* On a phone the three zones become one centred column in reading order —
     logo, then the icons and the Google link, then the contact details — with
     the legal row and the copyright keeping their place underneath. The
     contact column drops its inline-end alignment here: centred under a
     centred icon row, an end-aligned block would look like a mistake. */
  @media (max-width:768px){
    .footer-zones{grid-template-columns:1fr; justify-items:center; gap:26px;}
    .footer-zone, .footer-zone-brand, .footer-zone-contact{align-items:center; text-align:center;}
    /* The pickup points stay a left-to-right list of blocks (start-aligned
       text reads better than a centred address); a finger-size link. */
    .footer-zone-pickup{width:100%; align-items:stretch; text-align:start;}
    .footer-pickup-link{min-height:44px; padding:0 16px;}
  }

  /* ---------- FLOATING WHATSAPP ---------- */
  /* Taken out of the .float-actions column and pinned to the bottom-left corner
     on its own: .float-actions sits bottom-RIGHT with the accessibility button,
     and the two would stack on the same control. left keeps WhatsApp on the
     left in every language, clear of the a11y button the standard puts on the
     right. */
  .float-wa{
    position:fixed; bottom:22px; left:16px; z-index:75;
    width:54px; height:54px; border-radius:50%;
    background:#25D366; color:#10221A;
    display:flex; align-items:center; justify-content:center;
    box-shadow:0 12px 26px -8px rgba(37,211,102,0.6);
    text-decoration:none;
    transition:transform .2s ease, bottom .25s ease;
  }
  .float-wa:hover{transform:scale(1.06);}
  .float-wa svg{width:27px; height:27px;}
  body.has-cart-bar .float-wa{bottom:calc(var(--cart-h, 124px) + 4px);}
  @media (max-width:480px){
    .float-wa{
      bottom:calc(16px + env(safe-area-inset-bottom));
      width:48px; height:48px;
    }
    .float-wa svg{width:24px; height:24px;}
    /* The circles are 48px and sit 16px up here, so the footer needs a little
       less room than on a wide screen — but still more than its own padding. */
    footer{padding-bottom:calc(80px + env(safe-area-inset-bottom));}
  }

  @media (prefers-reduced-motion: reduce){ *{transition:none !important; scroll-behavior:auto !important;} }
  /* extra-safe breakpoint: guarantees 2 columns even inside a narrower host container (e.g. a page builder) */
  @media (max-width:640px){
    .product-grid:not(.product-carousel){grid-template-columns:repeat(var(--prod-cols-phone), minmax(0, 1fr)) !important;}
    .product-grid{gap:8px !important;}
    .category-section{padding-left:10px !important; padding-right:10px !important;}
    main{padding-left:10px !important; padding-right:10px !important;}
  }
  @media (max-width:600px){
    .hero{
      /* A floor keeps it an entrance; the cap stops it becoming an empty page.
         Between them the category strip peeks in and invites the scroll.
         At 72svh a 390px phone opened on the hero and nothing else — the
         category cards began exactly at the fold. 64svh hands back enough that
         a sliver of the strip is on screen at rest, which is what tells a
         shopper there is a shop below. Still min-height: a long title or four
         info items grows the hero rather than clipping it. */
      min-height:min(64svh, 470px); min-height:min(64vh, 470px);
      padding:26px 0 30px;
      display:flex; align-items:center; justify-content:center;
      /* Default header only. compact/modern set --hero-title-size on #home,
         which outranks this .hero rule, so a modern phone keeps its headline. */
      --hero-title-size:calc(1.25rem * var(--hero-title-scale, 1));
      --hero-desc-size:calc(0.75rem * var(--hero-desc-scale, 1));
    }
    .hero-content{width:100%;}
    /* Same 76px base as the desktop logo. 44px made «גודל הלוגו» at its
       160% maximum only 70px, which still read as the small mark. */
    .hero-logo{width:calc(76px * var(--logo-scale, 1)); height:calc(76px * var(--logo-scale, 1)); margin-bottom:8px;}
    .hero #heroTitle{line-height:1.2;}
    .hero-name{font-size:0.8125rem; margin-bottom:4px;}
    /* The -webkit-line-clamp that used to sit here cut the description at two
       lines on phones — where most shoppers are — with no "more" to tap. The
       hero is min-height, not fixed height, so the full text grows it. */
    .hero p{
      margin-top:8px; line-height:1.45; max-width:34ch;
    }
    .hero-info{margin-top:14px;}
    .hero-actions{margin-top:14px; gap:8px;}
    .hero-actions .btn{padding:11px 16px; font-size:0.875rem;}
  }
  @media (max-width:480px){
    .card-name{font-size:0.8125rem;}
    .hero-actions{flex-direction:column; align-items:stretch;}
    .hero-actions .btn{width:100%;}
    /* Keep the padded tap area on phones, where it matters most: 44px outer,
       38px painted. Cards override this again below with their bigger square. */
    .qty-btn{width:44px; height:44px; font-size:1.1875rem; padding:3px; margin:-3px;}
    /* A two-up card is about 164px of content on a 390px phone, so the stepper
       takes the width it has rather than the roomier desktop cap. The buttons
       hold their painted size and the number in the middle absorbs the
       difference. */
    .card .qty-row{max-width:100%; padding:4px;}
    .card .qty-btn{width:46px; height:46px; padding:2px; margin:-2px; font-size:1.25rem;}
    .drawer-row .qty-row{width:128px;}
    .drawer-row .qty-btn{width:30px; height:30px; font-size:1rem; padding:0; margin:0;}
    .category-section{padding:22px 10px 26px; border-radius:var(--r-panel);}
    main{padding:0 10px;}
    .product-grid:not(.product-carousel){grid-template-columns:repeat(var(--prod-cols-phone), minmax(0, 1fr)) !important;}
    .product-grid{gap:7px !important;}
    .card{padding:8px 8px 10px; border-radius:16px;}
    /* Two cards to a 390px phone leaves the photo about 148px wide, so the
       overlay gives its side padding back to the two pills and they shed the
       type size and height they can afford to. The + strip narrows but stays a
       strip — dropping it would lose the one bit of the mock that says which
       half of the button is the action. */
    /* Two cards to a 390px phone leaves a photo well about 148x111, and two
       full-size pills stacked in it hid the product almost completely — the
       card then said "out of stock" without saying out of stock OF WHAT. The
       pills sit at the bottom of the well instead of centred in it, and shed
       the height and type size they can afford, so the top half of the picture
       stays visible above them. */
    .oos-overlay{inset:30px 5px 5px; padding:0 2px; gap:4px; justify-content:flex-end;}
    .oos-stamp{font-size:0.6875rem; min-height:24px; padding:4px 6px; border-radius:999px;}
    .oos-notify{font-size:0.625rem; min-height:24px;}
    .oos-notify-label{padding:3px 2px 3px 5px;}
    .oos-notify-plus{width:26px;}
    .oos-notify-plus svg{width:11px; height:11px;}
    .card-img{border-radius:var(--r-control);}
    .nav-pill{border-radius:16px;}
    .cat-card-photo{border-radius:var(--r-control) 13px 0 0;}
    .cat-card-body{border-radius:0 0 13px 13px; padding:6px 6px 0;}
    .cat-card-name{font-size:0.65625rem;}
    .cat-card-arrow{width:26px; height:26px;}
    .cat-card-arrow svg{width:14px; height:14px;}
    .nav-scroll{--cat-gap:8px; --cat-pad:14px;}
    .cart-cta{padding:13px 16px;}
    .whatsapp-btn{padding:15px;}
    .drawer{max-height:92vh; max-height:92dvh;}
    .price-tag{font-size:0.625rem; padding:3px 8px;}
  }
  @media (max-width:340px){
    .product-grid{gap:6px !important;}
    .qty-value{font-size:0.8125rem; min-width:28px;}
  }
  /* Narrow cards: 3 or 4 to a phone leave a card 55-100px of content, where
     two 46px buttons and a number cannot share one line. Under 110px the
     stepper wraps: the typable number on its own line, «−» and «+» below it
     sharing the width (reaching into the card's side padding), each a 40px
     tall target with at least a 28px painted square. The sticker, the unit
     pill and the name scale with the card (cqi). A card with 110px of content
     or more — every desktop and tablet card, 1-2 to a phone (about 118px on
     a 320px phone) — never matches. The
     drawer's stepper is not in a card and is untouched. */
  @container shopcard (max-width:109.98px){
    .card .qty-row{flex-wrap:wrap; justify-content:center; gap:3px; max-width:none;
      margin-inline:-6px; padding:3px 0 0; background:none;}
    .card .qty-value{flex:1 0 100%; order:-1; min-width:0; padding:2px 0;
      background:var(--paper-dim); border-radius:var(--r-chip);}
    .card .qty-input{min-width:0;}
    .card .qty-btn{flex:1 1 0; width:auto; min-width:0; height:40px; margin:0; padding:6px 2px;
      font-size:1rem;}
    .card .price-tag{top:4px; right:4px; max-width:calc(100% - 8px); padding:2px 6px;
      font-size:clamp(0.5rem, 11cqi, 0.625rem);}
    .card-name{font-size:clamp(0.625rem, 12cqi, 0.8125rem);}
    .card-img .unit-choice-img, .card-img .kashrut-tab ~ .unit-choice-img{left:3px; bottom:3px;}
    .unit-choice-img .unit-opt{min-width:0; min-height:18px; height:18px; padding:0 3px;
      font-size:clamp(0.5rem, 10cqi, 0.6875rem);}
    .card .deal-was{top:4px; left:4px; padding:2px 4px; max-width:calc(100% - 8px);}
    .card .deal-was-label{font-size:clamp(0.4375rem, 9cqi, 0.625rem);}
    .card .deal-was-price{font-size:clamp(0.5rem, 11cqi, 0.75rem);}
    .card .deal-tag{padding:3px 6px 4px;}
    .card .deal-tag-label{font-size:clamp(0.4375rem, 9cqi, 0.6875rem);}
    .card .deal-tag-price{font-size:clamp(0.75rem, 14cqi, 1.0625rem);}
  }
  .drawer{overscroll-behavior:contain;}
  @supports (padding: max(0px)){
    .cart-bar{padding-left:max(16px, env(safe-area-inset-left)); padding-right:max(16px, env(safe-area-inset-right));}
  }

/* ---- accent-driven (set per-category via [data-cat] custom properties) ---- */
.category-section{--accent-deep:var(--primary);--accent-mid:var(--primary-mid);
  --accent-tint:var(--paper-dim);--accent-panel:transparent;}
.category-section{background:var(--accent-panel);}
/* «עיצוב הקטגוריה» → תמונה: the photo under a wash of the page colour. */
.category-section{background-image:var(--accent-image, none); background-size:cover; background-position:center;}
.category-icon{background:var(--accent-deep);}
.category-head h2{color:var(--accent-deep);}
.category-rule{background:linear-gradient(90deg,var(--accent-mid),transparent);}
.card-img{background:var(--ui-card-photo-bg, var(--ui-card-bg, #fff));}
.price-tag{background:var(--ui-price-bg, var(--accent-mid));color:var(--ui-price-text, #fff);}
/* background-clip travels with the `background` shorthand: setting the accent
   colour here silently reset it to border-box and painted the whole padded tap
   area, which is what the padding/negative-margin pair exists to prevent. Use
   the longhand so the button keeps the painted square its own rule sized. */
.qty-btn{background-color:var(--ui-qty-plus-bg, var(--accent-deep)); background-clip:content-box;}
.qty-btn.minus{background-color:var(--ui-qty-minus-bg, #fff); color:var(--ui-qty-minus-text, var(--accent-deep));}
.card.in-cart{border-color:color-mix(in srgb,var(--accent-mid) 40%,transparent);}
.qty-unit{color:var(--accent-deep);}
.tier-hint.active{color:var(--accent-deep);background:var(--accent-tint);}

/* ---- boot states ---- */
#bootLoader{display:flex;align-items:center;justify-content:center;min-height:70vh;
  flex-direction:column;gap:14px;color:var(--ink-soft);font-family:var(--font-body),sans-serif;}
#bootLoader .spinner{width:34px;height:34px;border:3px solid var(--line);
  border-top-color:var(--primary);border-radius:50%;animation:spin .8s linear infinite;}
@keyframes spin{to{transform:rotate(360deg);}}
#bootError{display:none;max-width:520px;margin:16vh auto;padding:28px;text-align:center;
  font-family:var(--font-body),sans-serif;color:var(--ink);}
#bootError .boot-error-msg{font-size:1.0625rem;font-weight:700;margin-bottom:8px;}
#bootError .boot-error-detail{font-size:0.8125rem;color:var(--ink-soft);
  font-family:ui-monospace,monospace;word-break:break-word;}
.drawer-empty{padding:26px;text-align:center;color:var(--ink-soft);font-size:0.875rem;}
.cat-card-emoji{font-size:1.625rem;line-height:1;display:flex;align-items:center;
  justify-content:center;width:100%;height:100%;}

/* ---- product search ----
   The mock's proportions — a white rounded bar, a glass on the leading edge, a
   solid action button on the trailing one — drawn entirely in the shop's own
   tokens. The mock's greens were one shop's palette; --primary and --paper are
   every shop's, so a blue or a burgundy shop gets its own bar rather than a
   green stripe. Radii come off --r-panel/--r-control like every other surface.

   The bar sits in the page's flow between the category cards and the grid; it
   is not fixed and not sticky (html/body carry overflow-x, which reduces sticky
   to a no-op — see AGENTS.md). */
.prod-search{max-width:var(--container); margin:0 auto 18px; padding:0 20px;
  position:relative; font-family:var(--font-body), sans-serif;}
.prod-search-bar{display:flex; align-items:center; gap:12px;
  background:var(--ui-search-bg, var(--paper)); border:1px solid var(--line);
  border-radius:var(--r-panel); padding:10px 14px;
  box-shadow:0 10px 26px -14px rgba(0,0,0,0.30);}
.prod-search-bar:focus-within{outline:3px solid var(--focus-ring,#0B5FFF);outline-offset:2px;box-shadow:0 0 0 5px var(--focus-ring-halo,#fff);}
.prod-search-glass{width:21px; height:21px; flex:none; color:var(--ink-soft);}
/* The input carries no chrome of its own — the bar around it is the control. */
#prodSearchInput{flex:1; min-width:0; border:none; background:transparent;
  font:inherit; font-size:1.0625rem; color:var(--ink); padding:8px 0;
  -webkit-appearance:none; appearance:none;}
#prodSearchInput::placeholder{color:var(--ink-soft); opacity:1;}
/* Safari draws its own clear affordance on type=search; ours is the button. */
#prodSearchInput::-webkit-search-cancel-button,
#prodSearchInput::-webkit-search-decoration{-webkit-appearance:none; appearance:none;}
.prod-search-clear{flex:none; width:34px; height:34px; border:none; border-radius:999px;
  background:transparent; color:var(--ink-soft); cursor:pointer;
  display:flex; align-items:center; justify-content:center; padding:0;}
.prod-search-clear svg{width:16px; height:16px;}
.prod-search-clear:hover{background:var(--paper-dim); color:var(--ink);}
.prod-search-go{flex:none; display:flex; align-items:center; gap:8px; border:none;
  border-radius:var(--r-control); cursor:pointer; font:inherit; font-size:0.9375rem;
  font-weight:700; padding:10px 18px; white-space:nowrap;
  background:var(--ui-search-btn-bg, var(--primary));
  color:var(--ui-search-btn-text, var(--paper));}
.prod-search-go svg{width:18px; height:18px; flex:none;}
.prod-search-go:hover{filter:brightness(1.08);}
.prod-search-go:active{transform:translateY(1px);}

/* Optional fixed mode: the same search node rides below the category cards,
   inside their existing fixed bar. No sticky positioning is introduced. */
.cat-chips .prod-search.is-floating{
  max-width:var(--container); margin:0 auto; padding:0 16px 10px;
}
.cat-chips .prod-search.is-floating .prod-search-bar{
  border-radius:var(--r-control); padding-block:7px;
  box-shadow:0 6px 18px -12px rgba(0,0,0,.35);
}
.cat-chips .prod-search.is-floating .prod-search-list{left:16px; right:16px;}
/* The hand-off lifts #prodSearch out of flow. Scroll anchoring then yanks
   scrollY back by the bar's height and the two states swap every frame
   (app/float-search.js). The inline style is what covers the first move;
   this rule keeps the opt-out on the element the attribute names. */
html[data-float-search="1"]{overflow-anchor:none;}

/* The dropdown overlays the grid rather than pushing it down: the catalogue
   behind it is never filtered, and a list that shoved the page around would
   move the very card the shopper is about to be scrolled to. */
.prod-search-list{position:absolute; z-index:60; left:20px; right:20px; top:calc(100% + 6px);
  background:var(--paper); border:1px solid var(--line); border-radius:var(--r-panel);
  padding:6px; box-shadow:0 18px 40px -16px rgba(0,0,0,0.38); overflow:hidden;}
.prod-search-row{display:flex; align-items:center; gap:12px; padding:10px 12px;
  border-radius:var(--r-control); cursor:pointer; width:100%; border:none;
  background:transparent; font:inherit; text-align:start; color:var(--ink);}
.prod-search-row:hover,.prod-search-row:focus-visible{background:var(--paper-dim);}
/* Two letters off the product name, not a photo: the row has to stay one line
   tall on a phone, and a thumbnail per suggestion is a lot of layout for a list
   that is dismissed in a second. */
.prod-search-mark{flex:none; width:38px; height:38px; border-radius:var(--r-control);
  background:var(--paper-dim); color:var(--primary); display:flex; align-items:center;
  justify-content:center; font-size:0.75rem; font-weight:700;}
.prod-search-text{flex:1; min-width:0; display:flex; flex-direction:column; gap:1px;}
.prod-search-name{font-size:0.9375rem; font-weight:500; color:var(--ink);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.prod-search-cat{font-size:0.8125rem; color:var(--ink-soft);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.prod-search-price{flex:none; font-size:0.875rem; font-weight:700; color:var(--primary);}
.prod-search-empty{padding:14px 12px; font-size:0.875rem; color:var(--ink-soft);}

/* The card the shopper picked, marked for a moment so the eye lands on it
   after the scroll — the grid still holds every other product. */
.card.search-hit{box-shadow:0 0 0 3px var(--primary); transition:box-shadow .25s ease;}

@media (max-width:560px){
  .prod-search{padding:0 10px; margin-bottom:14px;}
  .prod-search-list{left:10px; right:10px;}
  .prod-search-bar{gap:8px; padding:8px 10px;}
  #prodSearchInput{font-size:1rem;}
  /* The label goes, the glass stays: a full-width Hebrew word beside an input
     leaves the input too narrow to read what you typed. */
  .prod-search-go{padding:10px 12px;}
  .prod-search-go span{display:none;}
  .cat-chips .prod-search.is-floating{padding:0 12px 8px; margin-bottom:0;}
  .cat-chips .prod-search.is-floating .prod-search-list{left:12px; right:12px;}
}

html[data-a11y-contrast="1"] .prod-search-bar,
html[data-a11y-contrast="1"] .prod-search-list,
html[data-a11y-contrast="1"] .prod-search-mark{
  background:var(--hc-paper) !important; color:var(--hc-ink) !important;
  border-color:var(--hc-line) !important;
}
html[data-a11y-contrast="1"] .prod-search-name,
html[data-a11y-contrast="1"] .prod-search-cat,
html[data-a11y-contrast="1"] .prod-search-price,
html[data-a11y-contrast="1"] #prodSearchInput{color:var(--hc-ink) !important;}

.checkout-privacy{font-size:.8125rem;line-height:1.65;color:#526158;margin:0 0 14px;}
.checkout-privacy a{color:inherit;text-decoration:underline;}

/* Owner-selected display face; regular text and prices retain their fonts. */
h1,h2,h3,.display,.footer-name{font-family:var(--font-heading, var(--font-brand)), var(--font-display, 'Suez One'), serif;}

/* Scale promotions with the photo well, including narrow carousel cards on
   desktop. Keep the regular price in one line so the two corner labels leave
   the centre of a phone photo visible. */
@container shopcard (max-width:210px){
  .card .deal-tag{padding:2px 5px 3px; max-width:58%;}
  .card .deal-tag-label,.card .deal-tag-tier .deal-tag-label{
    font-size:clamp(0.4375rem, 5.5cqi, 0.625rem); line-height:1.1;
  }
  .card .deal-tag-price{font-size:clamp(0.625rem, 8cqi, 0.9375rem); line-height:1.05;}
  .card.has-deal-offer .deal-was{
    top:auto; left:auto; bottom:3px; right:5px;
    flex-direction:row; flex-wrap:wrap; justify-content:center; gap:2px 4px;
    padding:2px 4px; max-width:calc(100% - 10px);
  }
  /* The ק״ג/יח׳ pill holds the same bottom edge from the left, and a one-line
     chip ran over it on phones: beside the pill the chip stacks its label
     over the price and keeps to the photo's right part. */
  .card.has-deal-offer .card-img:has(.unit-choice-img) .deal-was{
    flex-direction:column; gap:0; max-width:45%;
  }
  .card .deal-was-label{font-size:clamp(0.4375rem, 5.5cqi, 0.625rem); line-height:1.1;}
  .card .deal-was-price,.card .deal-regular-price{
    font-size:clamp(0.5rem, 6.5cqi, 0.75rem); line-height:1.1;
  }
}
/* Narrower still, the two do not fit side by side (a translated pill is
   wider), and the photo's right side is the deal sticker's: the chip sits on
   the pill at its left edge, label over price, at most half the photo wide. */
@container shopcard (max-width:160px){
  .card.has-deal-offer .card-img:has(.unit-choice-img) .deal-was{
    right:auto; left:6px; bottom:32px; max-width:52%;
  }
  .card.has-deal-offer .card-img:has(.unit-choice-img) .kashrut-tab ~ .deal-was{left:24px;}
  .card.has-deal-offer .card-img:has(.unit-choice-img) .deal-was-label{white-space:normal; text-align:center;}
}
/* Three cards to a phone row: the struck price alone says it. */
@container shopcard (max-width:130px){
  .card.has-deal-offer .card-img:has(.unit-choice-img) .deal-was-label{display:none;}
}
/* Three to a 320px row the photo holds the sticker and the pill and nothing
   else: the sale sticker alone tells the offer. */
@container shopcard (max-width:109.98px){
  .card.has-deal-offer .card-img:has(.unit-choice-img) .deal-was{display:none;}
}

/* The chosen kg/unit price is separate from the stable offer artwork. */
.selected-mode-price{
  margin:0 0 8px; text-align:center; font-family:var(--font-body), sans-serif;
  font-size:0.875rem; font-weight:700; line-height:1.3; color:var(--ink);
  overflow-wrap:anywhere;
}
.selected-mode-price .price-unit{font-size:0.75rem;font-weight:500;}

/* «השהיית החנות» (app/shop-paused.js): the shop says it is closed for orders,
   and nothing that orders is shown — the steppers, the kg/unit and
   preparation choices, the cart bar and its drawer. */
.paused-banner{display:flex; flex-direction:column; align-items:center; gap:2px; padding:12px 16px; text-align:center;
  background:#FFF4E0; color:#5A3A00; border-bottom:1px solid #F0D9A8; font-size:0.9375rem; line-height:1.45;}
.paused-banner strong{font-size:1rem;}
html.is-paused .qty-row, html.is-paused .unit-choice, html.is-paused .prep,
html.is-paused #cartBar, html.is-paused #drawer{display:none !important;}

/* Optional rich content: empty legacy shops do not create these sections. */
.editorial{max-width:1120px;margin:36px auto;padding:0 18px;display:grid;gap:28px;}
.editorial-block{background:var(--card-bg,#fff);border:1px solid color-mix(in srgb,var(--primary) 12%,transparent);border-radius:var(--radius);padding:clamp(20px,4vw,44px);overflow:hidden;}
.editorial-block h2{margin:0 0 14px;font-size:clamp(1.5rem,3vw,2.3rem);}
.editorial-story,.editorial-imageText{display:grid;grid-template-columns:1fr 1fr;align-items:center;gap:32px;}
.editorial-story:not(:has(>img)),.editorial-imageText:not(:has(>img)){grid-template-columns:1fr;}
.editorial-block>img{width:100%;max-height:420px;object-fit:cover;border-radius:calc(var(--radius)*.7);}
.rich-text{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.8;}
.editorial-collection{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin:22px 0;}
.editorial-collection-item{background:transparent;border:0;padding:8px;cursor:pointer;color:inherit;font:inherit;display:grid;gap:12px;}
.editorial-collection-item img{width:100%;aspect-ratio:1;object-fit:contain;background:#fff;border-radius:var(--radius);}
.editorial-link{display:inline-block;color:var(--primary);font-weight:700;}
.editorial-question{border-top:1px solid color-mix(in srgb,var(--primary) 15%,transparent);padding:16px 0;}
.editorial-question summary{cursor:pointer;font-weight:600;}
/* A card with details opens them anywhere (app/rich-content.js); the round
   icon over its photo says so on hover — always shown where there is no hover. */
.card:has(>.product-details-open){cursor:pointer;}
.card:has(>.product-details-open) :is(button,select,input,label){cursor:pointer;}
.product-details-open{position:absolute;z-index:3;top:10px;inset-inline-end:10px;display:grid;place-items:center;width:34px;height:34px;padding:0;
  border:0;border-radius:50%;background:color-mix(in srgb,var(--card-bg,#fff) 92%,transparent);color:var(--primary);
  box-shadow:0 2px 8px rgba(20,20,20,.18);cursor:pointer;opacity:0;transform:scale(.85);transition:opacity .18s ease,transform .18s ease;}
/* Beside the kashrut tab, the vertical strip on the photo's left edge. */
.card:not(.has-deal-offer):has(.kashrut-tab)>.product-details-open{inset-inline-end:36px;}
.product-details-open svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.card:hover>.product-details-open,.product-details-open:focus-visible{opacity:1;transform:none;}
@media(hover:none){.product-details-open{opacity:.95;transform:none;}}
@media(prefers-reduced-motion:reduce){.product-details-open{transition:none;}}
.rich-product-dialog{width:min(960px,calc(100vw - 28px));max-height:calc(100svh - 28px);border:0;border-radius:var(--r-panel);padding:clamp(20px,4vw,40px);color:var(--text,#252525);background:var(--card-bg,#fff);box-sizing:border-box;overflow:auto;}
/* `*{margin:0}` above wipes the UA's margin:auto that centres a modal
   <dialog>, which left it stuck to the top and the inline start. Put it back;
   the boutique phone bottom sheet (boutique.css) still sets its own margin. */
.rich-product-dialog{margin:auto;}
.rich-product-dialog::backdrop{background:rgba(20,12,18,.65);}
.rich-close{float:left;border:0;border-radius:50%;width:42px;height:42px;font-size:28px;cursor:pointer;background:#f5f1f3;}
.rich-product-title{clear:both;margin:10px 0 24px;}
.rich-product-layout{display:grid;grid-template-columns:1fr 1fr;gap:32px;align-items:start;}
.rich-product-photo{width:100%;aspect-ratio:1;object-fit:contain;background:#fff;border-radius:var(--r-control);}
.rich-thumbnails{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;}
.rich-thumbnails button{width:60px;height:60px;padding:3px;background:#fff;border:1px solid #ddd;border-radius:var(--r-chip);cursor:pointer;}
.rich-thumbnails button[aria-pressed=true]{border:2px solid var(--primary);}
.rich-thumbnails img{width:100%;height:100%;object-fit:contain;}
.rich-product-purchase{margin-top:20px;padding:16px;}
.rich-product-purchase .card-img{height:auto;min-height:0;aspect-ratio:auto;overflow:visible;background:var(--ui-card-photo-bg, var(--ui-card-bg, #fff));}
.rich-product-purchase .card-img>img,.rich-product-purchase .img-fallback{display:none;}
.rich-product-purchase .price-tag{position:static;width:fit-content;margin-bottom:14px;}
@media(max-width:640px){.editorial-story,.editorial-imageText,.rich-product-layout{grid-template-columns:1fr;}.editorial-collection{grid-template-columns:repeat(2,minmax(0,1fr));}.editorial{padding:0 12px;margin:24px auto;gap:18px;}.rich-product-dialog{padding:20px;}.rich-product-title{font-size:1.6rem;}}
.card-img img{object-fit:var(--product-fit-desktop,contain);object-position:var(--product-position-desktop,50% 50%);}
@media(max-width:640px){.card-img img{object-fit:var(--product-fit-phone,contain);object-position:var(--product-position-phone,50% 50%);}}
.hero-primary-action{display:inline-flex;align-items:center;justify-content:center;min-height:46px;padding:12px 26px;margin-top:20px;border-radius:var(--r-control);background:#FFF8EF;color:var(--primary);font-weight:700;text-decoration:none;box-shadow:0 6px 24px rgba(0,0,0,.1);}
.hero-primary-action:hover{background:#fff;}
.hero-primary-action:focus-visible,.product-details-open:focus-visible,.editorial-collection-item:focus-visible{outline:3px solid var(--focus-ring,#0B5FFF);outline-offset:4px;}

/* Rich products use the same purchase controls in a quieter, readable panel. */
.rich-product-dialog{color:var(--ink);background:var(--paper,#FBF7EF);}
.rich-product-category{clear:both;margin:8px 0;color:var(--primary);font-size:.85rem;letter-spacing:.04em;}
.rich-product-title{font-family:var(--font-heading,var(--font-body,'Rubik')),sans-serif;font-size:clamp(1.65rem,3vw,2.4rem);line-height:1.25;margin:0 0 28px;}
.rich-product-information h3{font-family:var(--font-body,'Rubik'),sans-serif;font-size:.95rem;margin:24px 0 8px;}
.rich-product-information section:first-child h3{margin-top:0;}
.rich-product-information .rich-text{font-size:.95rem;color:var(--ink-soft,var(--ink));}
.rich-product-purchase{border:0;box-shadow:none;background:transparent;padding:24px 0 0;border-top:1px solid var(--line);border-radius:0;}
.rich-product-purchase .card-img{background:var(--ui-card-photo-bg, var(--ui-card-bg, #fff));border-radius:var(--r-control);padding:14px 16px;margin-bottom:18px;}
.rich-product-purchase .price-tag{display:block;background:transparent;color:var(--ink);transform:none;box-shadow:none;border:0;font-size:1.6rem;padding:0;margin:0;text-align:start;font-family:var(--font-body,'Rubik'),sans-serif;}
.rich-product-purchase .qty-row{display:grid;grid-template-columns:44px 64px minmax(120px,1fr);gap:10px;align-items:center;max-width:none;}
.rich-product-purchase .qty-value{width:auto;}
.rich-product-purchase .qty-btn.rich-add-to-cart{width:auto;min-width:120px;height:48px;padding:0 18px;font-size:.95rem;background:var(--ui-btn-primary-bg,var(--primary));color:var(--ui-btn-primary-text,#fff);border-radius:var(--r-control);box-shadow:none;}
.rich-product-purchase .qty-btn.rich-add-to-cart:disabled{opacity:.45;}
.rich-product-purchase select[hidden]{display:none;}
.rich-variant-choices{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0 18px;}
.rich-variant-choice{font:inherit;font-size:.85rem;border:1px solid var(--line);border-radius:var(--r-control);padding:10px 12px;background:var(--paper);color:var(--ink);cursor:pointer;}
.rich-variant-choice[aria-pressed="true"]{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 8%,var(--paper));box-shadow:inset 0 0 0 1px var(--primary);}
.rich-variant-choice:focus-visible{outline:3px solid var(--focus-ring,#0B5FFF);outline-offset:3px;}
.rich-product-photo{border:1px solid var(--line);}
.editorial-block h2{font-family:var(--font-heading,var(--font-body,'Rubik')),sans-serif;line-height:1.3;}
.editorial-story:not(:has(>img)){max-width:860px;margin:auto;text-align:center;background:transparent;border:0;}
.editorial-copy>.rich-text{max-width:65ch;color:var(--ink-soft,var(--ink));}
.editorial-story .rich-text{margin-inline:auto;}
.editorial-collection-item{border-radius:var(--r-control);transition:background .18s ease,transform .18s ease;}
.editorial-collection-item:hover{background:color-mix(in srgb,var(--primary) 5%,transparent);transform:translateY(-3px);}
.editorial-collection-item span{font-weight:600;line-height:1.4;}
.editorial-question summary{list-style:none;display:flex;justify-content:space-between;align-items:center;gap:16px;min-height:28px;}
.editorial-question summary::-webkit-details-marker{display:none;}
.editorial-question summary::after{content:'+';font-size:1.5rem;font-weight:400;color:var(--primary);}
.editorial-question[open] summary::after{content:'−';}
.editorial-question p{max-width:70ch;color:var(--ink-soft,var(--ink));}
@media(prefers-reduced-motion:reduce){.editorial-collection-item{transition:none;}.editorial-collection-item:hover{transform:none;}}
html[data-motion="off"] .editorial-collection-item{transition:none;}
html[data-motion="off"] .editorial-collection-item:hover{transform:none;}
/* Per-section values remain independent at the two responsive sizes. Missing
   properties use the existing layout rather than creating a new default. */
.editorial{max-width:none;}
.editorial .editorial-block{width:100%;max-width:var(--block-width-desktop,var(--block-default-width,1084px));margin-inline:auto;
  text-align:var(--block-align-desktop,var(--block-default-align,start));background:var(--block-background-desktop,var(--block-default-background,var(--card-bg,#fff)));
  border:var(--block-border-desktop,var(--block-default-border,1px solid color-mix(in srgb,var(--primary) 12%,transparent)));
  border-radius:var(--block-radius-desktop,var(--radius));padding:var(--block-padding-desktop,clamp(20px,4vw,44px));}
.editorial-story:not(:has(>img)){--block-default-width:860px;--block-default-align:center;--block-default-background:transparent;--block-default-border:0 solid transparent;}
.editorial-story,.editorial-imageText{grid-template-columns:var(--block-tracks-desktop,repeat(2,minmax(0,1fr)));gap:var(--block-gap-desktop,32px);}
.editorial .editorial-copy{min-width:0;order:var(--block-text-order-desktop,1);}
.editorial .editorial-block>img{order:var(--block-image-order-desktop,0);aspect-ratio:var(--block-ratio-desktop,auto);max-height:var(--block-image-max-desktop,420px);object-fit:var(--block-fit-desktop,cover);object-position:var(--block-position-desktop,50% 50%);}
.editorial .editorial-block h2{font-size:calc(clamp(1.5rem,3vw,2.3rem) * var(--block-title-scale-desktop,1));overflow-wrap:anywhere;}
.editorial .rich-text,.editorial-question summary{font-size:calc(1em * var(--block-text-scale-desktop,1));}
.editorial .editorial-copy>.rich-text{margin-inline-start:var(--block-copy-start-desktop,var(--block-default-copy,0));margin-inline-end:var(--block-copy-end-desktop,var(--block-default-copy,0));}
.editorial-story{--block-default-copy:auto;}
.editorial .editorial-collection{grid-template-columns:repeat(var(--block-columns-desktop,4),minmax(0,1fr));gap:var(--block-gap-desktop,16px);}
@media(max-width:640px){
  .editorial .editorial-block{max-width:var(--block-width-phone,100%);text-align:var(--block-align-phone,var(--block-default-align,start));
    background:var(--block-background-phone,var(--block-default-background,var(--card-bg,#fff)));border:var(--block-border-phone,var(--block-default-border,1px solid color-mix(in srgb,var(--primary) 12%,transparent)));
    border-radius:var(--block-radius-phone,var(--radius));padding:var(--block-padding-phone,24px 18px);}
  .editorial-story,.editorial-imageText{grid-template-columns:var(--block-tracks-phone,minmax(0,1fr));gap:var(--block-gap-phone,24px);}
  .editorial .editorial-copy{order:var(--block-text-order-phone,1);}
  .editorial .editorial-block>img{order:var(--block-image-order-phone,0);aspect-ratio:var(--block-ratio-phone,auto);max-height:var(--block-image-max-phone,420px);object-fit:var(--block-fit-phone,cover);object-position:var(--block-position-phone,50% 50%);}
  .editorial .editorial-block h2{font-size:calc(clamp(1.5rem,3vw,2.3rem) * var(--block-title-scale-phone,1));}
  .editorial .rich-text,.editorial-question summary{font-size:calc(1em * var(--block-text-scale-phone,1));}
  .editorial .editorial-copy>.rich-text{margin-inline-start:var(--block-copy-start-phone,var(--block-default-copy,0));margin-inline-end:var(--block-copy-end-phone,var(--block-default-copy,0));}
  .editorial .editorial-collection{grid-template-columns:repeat(var(--block-columns-phone,2),minmax(0,1fr));gap:var(--block-gap-phone,16px);}
}
@media(max-width:640px){.rich-product-photo{aspect-ratio:4/3;}.rich-product-layout{gap:24px;}.rich-product-purchase .qty-row{grid-template-columns:40px 52px minmax(100px,1fr);gap:8px;}.rich-product-purchase .qty-btn.rich-add-to-cart{min-width:100px;padding:0 10px;}.editorial-block{padding:24px 18px;}}
/* ---- the later section kinds (app/rich-content.js extra()) ---- */
.editorial-testimonials,.editorial-gallery,.editorial-logos{--block-default-align:center;}
.editorial-reviews{display:grid;grid-template-columns:repeat(var(--block-columns-desktop,3),minmax(0,1fr));gap:var(--block-gap-desktop,20px);margin-top:22px;text-align:start;}
.editorial-review{margin:0;display:flex;flex-direction:column;gap:14px;padding:22px;border-radius:calc(var(--radius)*.8);
  background:color-mix(in srgb,var(--primary) 5%,var(--card-bg,#fff));border:1px solid color-mix(in srgb,var(--primary) 10%,transparent);}
.editorial-review blockquote{margin:0;position:relative;padding-top:22px;}
.editorial-review blockquote::before{content:"\201D";position:absolute;top:-10px;inset-inline-start:-2px;font-size:3.2rem;line-height:1;color:var(--primary);opacity:.35;font-family:Georgia,serif;}
.editorial-review blockquote p{margin:0;line-height:1.65;}
.editorial-review figcaption{display:flex;flex-direction:column;gap:2px;margin-top:auto;}
.editorial-review figcaption span{color:var(--ink-soft,var(--ink));font-size:.9em;opacity:.8;}
.editorial-banner{display:grid;gap:var(--block-gap-desktop,24px);grid-template-columns:var(--block-tracks-desktop,minmax(0,1fr));--block-default-align:center;
  --block-default-background:color-mix(in srgb,var(--primary) 9%,var(--paper,#FBF7EF));--block-default-border:0 solid transparent;}
.editorial-banner:has(>img){grid-template-columns:var(--block-tracks-desktop,repeat(2,minmax(0,1fr)));align-items:center;}
.editorial-cta{display:inline-flex;align-items:center;justify-content:center;min-height:48px;margin-top:18px;padding:12px 26px;border-radius:999px;
  background:var(--primary);color:var(--on-primary,#fff);font-weight:700;text-decoration:none;transition:transform .18s ease,box-shadow .18s ease;}
.editorial-cta:hover{transform:translateY(-2px);box-shadow:0 8px 20px color-mix(in srgb,var(--primary) 30%,transparent);}
.editorial-cta:focus-visible{outline:3px solid var(--focus-ring,#0B5FFF);outline-offset:4px;}
.editorial-photos{display:grid;grid-template-columns:repeat(var(--block-columns-desktop,3),minmax(0,1fr));gap:var(--block-gap-desktop,12px);margin-top:20px;}
.editorial-photos img{width:100%;aspect-ratio:var(--block-ratio-desktop,1);object-fit:var(--block-fit-desktop,cover);border-radius:calc(var(--radius)*.7);}
.editorial-logo-row{display:grid;grid-template-columns:repeat(var(--block-columns-desktop,4),minmax(0,1fr));gap:var(--block-gap-desktop,20px);margin-top:20px;align-items:center;}
.editorial-logo-row img{width:100%;max-height:90px;object-fit:contain;filter:grayscale(.15);}
.editorial-contact-box{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:var(--block-gap-desktop,24px);margin-top:18px;align-items:start;text-align:start;}
.editorial-contact-list{list-style:none;margin:0;padding:0;display:grid;gap:14px;}
.editorial-contact-list li{display:grid;gap:2px;}
.editorial-contact-label{font-size:.85em;font-weight:600;color:var(--primary);}
.editorial-contact-list a{color:inherit;font-weight:600;}
.editorial-contact-box .rich-text{white-space:pre-line;}
.editorial-nav{display:flex;flex-wrap:wrap;gap:8px;}
.editorial-nav-link{display:inline-flex;align-items:center;min-height:40px;padding:8px 14px;border-radius:999px;border:1px solid color-mix(in srgb,var(--primary) 25%,transparent);
  color:var(--primary)!important;text-decoration:none;font-size:.92em;}
.editorial-map{width:100%;min-height:260px;aspect-ratio:4/3;border:0;border-radius:calc(var(--radius)*.7);background:#eee;}
.editorial-video-frame{position:relative;width:100%;aspect-ratio:16/9;margin-top:18px;border-radius:calc(var(--radius)*.7);overflow:hidden;background:#000;}
.editorial-video-frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;}
@media(max-width:640px){
  .editorial-reviews{grid-template-columns:repeat(var(--block-columns-phone,1),minmax(0,1fr));gap:var(--block-gap-phone,14px);}
  .editorial-banner,.editorial-banner:has(>img){grid-template-columns:var(--block-tracks-phone,minmax(0,1fr));gap:var(--block-gap-phone,18px);}
  .editorial-photos{grid-template-columns:repeat(var(--block-columns-phone,2),minmax(0,1fr));gap:var(--block-gap-phone,8px);}
  .editorial-photos img{aspect-ratio:var(--block-ratio-phone,1);object-fit:var(--block-fit-phone,cover);}
  .editorial-logo-row{grid-template-columns:repeat(var(--block-columns-phone,2),minmax(0,1fr));gap:var(--block-gap-phone,14px);}
  .editorial-map{min-height:220px;}
}
@media(prefers-reduced-motion:reduce){.editorial-cta{transition:none;}.editorial-cta:hover{transform:none;}}
html[data-motion="off"] .editorial-cta{transition:none;}

/* How a typed quantity was read («= 0.75 ק״ג · 6.00 ₪»), over the box while
   the shopper types (app/qty.js). Fixed, centred just above the box; hidden on blur. */
.qty-hint{position:fixed;z-index:95;transform:translateX(-50%);padding:4px 10px;border-radius:999px;
  background:var(--ink,#1c2b22);color:#fff;font-size:.8125rem;font-weight:600;line-height:1.4;white-space:nowrap;
  pointer-events:none;direction:rtl;box-shadow:0 4px 12px rgba(0,0,0,.18);}
.qty-hint[hidden]{display:none;}

/* The bar is the control. The input's own ring plus the wrapper ring read as two. */
#prodSearchInput:focus,
#prodSearchInput:focus-visible,
.prep-note input:focus,
.prep-note input:focus-visible{outline:none;box-shadow:none !important;}

/* Prefer keyboard focus on composite fields; focus-within supports older engines. */
@supports selector(:has(input:focus-visible)){
  :is(.prep-note,.prod-search-bar):focus-within:not(:has(input:focus-visible)){outline:none;box-shadow:none;}
  :is(.prep-note,.prod-search-bar):has(input:focus-visible){outline:3px solid var(--focus-ring,#0B5FFF);outline-offset:2px;box-shadow:0 0 0 5px var(--focus-ring-halo,#fff);}
}

/* Optional photo cards in the floating category switcher. Unset keeps the
   current compact pills; the in-flow category row is independent. */
html[data-category-nav="cards"] .cat-chip{
  width:6.5rem;height:8.625rem;max-width:none;flex-direction:column;gap:0;
  padding:0;border-radius:var(--r-panel);text-align:center;
}
html[data-category-nav="cards"] .cat-chip .cat-card-photo{
  width:100%;height:62%;flex:0 0 62%;border-radius:0;
}
html[data-category-nav="cards"] .cat-chip .cat-card-body{
  flex:1 1 auto;width:100%;display:flex;align-items:center;justify-content:center;
  padding:0.375rem;box-sizing:border-box;
}
html[data-category-nav="cards"] .cat-chip .cat-card-name{
  white-space:normal;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden;
}
html[data-category-nav="cards"] .cat-chip .cat-card-emoji{font-size:2.125rem;}
