
/* palmir: app-style browsing, using the existing identity. */
/* This block used to open by hiding the loader outright, which killed the intro.
   The markup, the animations and hideLoader() were all still in place, so nothing
   looked broken — the curtain simply never drew. That rule had the same specificity
   as the loader's own rule at the top of the file but came later in the document,
   so it won. Removed, and INTRO_MIN put back to 1900ms below. */
header{background:rgba(251,244,232,.96);backdrop-filter:blur(12px)}
.hero{min-height:0;padding-top:126px;padding-bottom:30px}
.hero .kicker{margin-bottom:16px}.hero h1{font-size:clamp(2.6rem,5vw,4.6rem);line-height:1.12}
.hero h1 .h-line{display:inline;overflow:visible}.hero h1 .h-line>span{display:inline;transform:none}
.hero h1 .h-line+.h-line::before{content:' '}
.hero-foot{margin-top:20px;gap:20px}.hero-lead{max-width:620px;font-size:1rem;line-height:1.6}
.hero .scrollcue,.mq{display:none}
#shop{padding-top:20px;padding-bottom:35px;overflow:visible}
#shop>.sec-label{margin-bottom:10px}#shop>.sec-label .num{display:none}
#shop>.sec-label h2{font-size:clamp(1.6rem,3vw,2.5rem)}
#shop>.var-intro{font-size:1rem;max-width:680px;margin:0 0 16px;line-height:1.5}
/* The rail scrolls with a finger and with a wheel, and it does it quietly: a
   scrollbar drawn across a row of chips is desktop furniture on a phone, and the
   screenshot the owner sent had one sitting under them. Hidden, not disabled —
   the rail still scrolls, still takes the keyboard, and still snaps.
   overscroll-behavior-x stops a sideways flick from turning into a back gesture,
   which is the one thing that makes a horizontal row feel broken. */
#shopCats{position:sticky;top:78px;z-index:45;display:flex;flex-wrap:nowrap;gap:10px;overflow-x:auto;padding:12px 0;margin:0 0 25px;background:var(--bg);scroll-padding-inline:10px;
  scroll-snap-type:x proximity;overscroll-behavior-x:contain;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;-ms-overflow-style:none}
#shopCats::-webkit-scrollbar{display:none}
#shopCats:empty{display:none}
#shopCats .shop-cat{display:inline-flex;flex:none;align-items:center;gap:10px;min-height:48px;padding:10px 20px;border:1px solid #725c37;border-radius:999px;font:inherit;font-size:.9rem;color:var(--cream);text-decoration:none;white-space:nowrap;background:transparent;scroll-snap-align:start}
#shopCats small{font-size:.75rem;opacity:.8}
#lineHead{display:none}
#shop .var-wrap{display:block}#varStage{display:none!important}
#varList{display:block;overflow:visible;max-height:none;border:0;padding:0}
.catalog-section{padding:0;scroll-margin-top:160px;margin-bottom:40px;min-width:0}
.catalog-heading{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:18px}
.catalog-heading h3{font-family:var(--disp);font-size:1.7rem;font-weight:400;color:var(--cream);line-height:1.3}
.catalog-heading p{font-size:.85rem;color:var(--ink-muted);margin-top:5px}
/* The claims strip. Three things only this line can say, set in mono so they
   read as facts rather than as marketing. Restored after v5 dropped the
   #lineHead block — the data never went away, only the rendering did.
   .82rem on a 16px root is 13.1px, above the 11px floor the sweep enforces. */
.catalog-claims{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 0;
  margin-top:6px;padding:0;list-style:none}
.catalog-claims .claim{font-family:var(--mono);font-size:.72rem;line-height:1.5;
  letter-spacing:.01em;text-transform:none;padding:0;border:0;
  color:var(--gold-ink);white-space:nowrap}
/* a separator between them, never before the first or after the last */
.catalog-claims .claim + .claim::before{content:"·";margin:0 7px;opacity:.55}
.ritual-claims{margin:10px 0 0}
html[dir=rtl] .catalog-claims .claim{font-family:var(--ar-ui,var(--ar));letter-spacing:0}
@media(max-width:560px){.catalog-claims .claim{font-size:.7rem}.catalog-claims .claim + .claim::before{margin:0 6px}}
.catalog-tools{display:flex;align-items:center;gap:8px;flex-shrink:0}
.catalog-tools button{border:1px solid #725c37;border-radius:999px;min-width:44px;min-height:44px;padding:8px 12px;font:inherit;font-size:.85rem;color:var(--cream);background:transparent}
.catalog-tools button:disabled{opacity:.35;cursor:default}
/* Same treatment for the product rows: no scrollbar drawn, momentum kept, and a
   sideways flick contained so it never becomes a back navigation.

   NO SCROLL SNAPPING. The rail used to carry scroll-snap-type:x proximity with
   scroll-snap-align:start on every card, and the owner's report was that a swipe
   moved the row past two varieties and stopped rather than carrying on to the
   last one. That is what proximity snapping does to a flick on iOS Safari: it
   catches the momentum at the next card boundary instead of letting it run.

   Be straight about the evidence. This cannot be reproduced in the Chromium the
   instruments drive — flung there, the rail reaches its end in the same number
   of gestures with snapping on or off, and a single hard fling reaches it either
   way (fling3, four runs). So the removal rests on his report and on the one
   property in this rule that can arrest momentum at a card boundary, not on a
   local repro. What the measurements do settle is the cost: there is none. The
   rail behaves identically here without it.

   Snapping buys tidy alignment. It is not worth a shop whose owner says he
   cannot swipe to the end of his own range. */
.catalog-rail{display:flex;gap:16px;overflow-x:auto;overscroll-behavior-x:contain;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;scroll-padding-inline:2px;padding:2px 2px 15px;scrollbar-width:none;-ms-overflow-style:none}
.catalog-rail::-webkit-scrollbar{display:none}
.catalog-card{flex:0 0 260px;min-width:0;background:var(--white);border:0;border-radius:18px;overflow:hidden;display:flex;flex-direction:column}
.catalog-detail{display:block;width:100%;text-align:start;background:transparent;color:inherit;padding:0;border:0;font:inherit;cursor:pointer}
.catalog-photo{display:block;position:relative;aspect-ratio:1/1;background:#f4efe6;overflow:hidden}
.catalog-photo .date-photo{display:block;width:100%;height:100%;object-fit:cover;filter:none!important;mix-blend-mode:normal;transform:none}
.catalog-photo>svg{width:100%;height:100%}
.catalog-photo .top-tag{position:absolute;top:12px;inset-inline-start:12px;font-size:.75rem}
.catalog-photo .photo-pending{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;text-align:center;padding:0 16px;color:#8E7C64;font-size:.78rem;line-height:1.4}
/* Tasting Flight thumbnail: the box cut into its four real varieties, not
   one illustration repeated three times. A 1px gold seam is the only
   division -- the four photographs already carry the colour contrast. */
.flq{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;width:100%;height:100%;gap:1px;background:var(--gold,#cd8b3c);}
.flq-cell{position:relative;overflow:hidden;background:#f4efe6;display:block;}
.flq-cell>svg{width:100%;height:100%;display:block;}
.flq-img{width:100%;height:100%;object-fit:cover;object-position:center;display:block;}
/* Density pass. The name sat in 16px of padding under a 1.25rem/1.4 line, and
   the note below it reserved 2.6em whether it needed two lines or one — which is
   the empty band the owner saw above and below "Sukkari · caramel, soft". The
   note now takes the room it actually uses; the card is a flex column with the
   foot on margin-top:auto, so cards in a row still line up. */
.catalog-name{display:block;padding:11px 14px 2px;font-family:var(--disp);font-size:1.08rem;line-height:1.3}
.catalog-note{display:block;padding:0 14px;color:var(--ink-muted);font-size:.8rem;line-height:1.45}
.catalog-foot{padding:8px 14px 12px;margin-top:auto;display:flex;align-items:flex-end;justify-content:space-between;gap:8px}
.catalog-price{font-size:.95rem;line-height:1.35;color:var(--gold-ink)}.catalog-price small{display:block;font-size:.72rem;line-height:1.3;color:var(--ink-muted);margin-top:1px}
.catalog-buy{min-height:44px;border-radius:999px;padding:9px 13px;background:var(--gold);color:var(--on-fill);font:inherit;font-size:.8rem;border:1px solid var(--gold)}
.catalog-buy .cb-i{display:none}          /* the word is enough where there is room */
.catalog-card.is-out .catalog-buy{background:transparent;border-color:#725c37;color:var(--ink-muted)}
.catalog-rail.is-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));overflow:visible}
.catalog-rail.is-grid .catalog-card{min-width:0}
.catalog-rail:focus-visible,.catalog-tools button:focus-visible,.catalog-buy:focus-visible,.catalog-detail:focus-visible,#shopCats a:focus-visible,#shopCats button:focus-visible{outline:3px solid var(--gold);outline-offset:4px}
#nextLines{display:flex;flex-direction:column;gap:16px;margin-top:24px}
/* display:flex outranks the browser's own [hidden]{display:none}, so without this
   the coming-soon lines stayed on screen while the attribute said they were gone.
   Exactly the bug .top-tag had: an element that is hidden in the DOM and drawn on
   the page. Any rule that sets display on something that can be hidden needs its
   own [hidden] escape. */
#nextLines[hidden]{display:none}
/* ── the tasting-box reel ──────────────────────────────────────────────────
   Laid out left-to-right in both languages on purpose: a filmstrip's travel is
   not a reading direction, and one animation that behaves the same everywhere is
   worth more than a mirrored one that has to be maintained twice. The captions
   isolate themselves, so an Arabic name still sets right-to-left inside its own
   box. width:max-content is what lets translateX(-50%) mean "exactly one pass". */
.reel{overflow:hidden;background:#f4efe6;border-radius:18px 18px 0 0}
.reel-track{display:flex;direction:ltr;width:max-content;animation:reel-run 32s linear infinite}
.reel-dup{display:flex}
.reel:hover .reel-track,.reel:focus-within .reel-track{animation-play-state:paused}
@keyframes reel-run{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.reel-shot{margin:0;flex:0 0 auto;width:150px;position:relative}
.reel-shot .date-photo,.reel-shot>svg{display:block;width:150px;height:150px;object-fit:cover}
.reel-shot figcaption{position:absolute;inset-inline-start:8px;bottom:8px;unicode-bidi:isolate;
  padding:3px 8px;border-radius:999px;background:rgba(251,244,232,.72);color:var(--cream);
  font-size:.68rem;line-height:1.5}
.reel-list{list-style:none;margin:14px 0 0;padding:0;display:grid;gap:7px}
.reel-list li{display:flex;justify-content:space-between;gap:12px;font-size:.85rem;
  padding-bottom:7px;border-bottom:1px solid var(--line)}
.reel-list li span{color:var(--ink-muted);font-size:.78rem;text-align:end}
.reel-note{margin:12px 0 0;color:var(--ink-muted);font-size:.8rem;line-height:1.5}
@media(min-width:701px){.reel-shot,.reel-shot .date-photo,.reel-shot>svg{width:190px}
  .reel-shot .date-photo,.reel-shot>svg{height:190px}}
/* Motion nobody can stop is motion nobody wants: under reduced motion the reel
   holds still and becomes a strip the customer pushes themselves. */
@media(prefers-reduced-motion:reduce){
  .reel{overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none}
  .reel::-webkit-scrollbar{display:none}
  .reel-track{animation:none}
  .reel-dup{display:none}
}
#nextLines .next-line{scroll-margin-top:165px;border-radius:18px;padding:24px;background:var(--white)}
#nextLines .nl-note{font-size:1rem}#nextLines .nl-claims{display:none}
.catalog-coming-caption{font-size:.85rem;color:var(--ink-muted);margin-top:8px}
html[dir=rtl] .catalog-heading h3,html[dir=rtl] .catalog-name{font-family:var(--ar);letter-spacing:0}
html[dir=rtl] .hero h1.fitted{white-space:normal}
@media(max-width:700px){
 .hero{padding-top:100px;padding-bottom:18px}.hero .kicker{margin-bottom:12px}.hero h1{font-size:2.35rem!important;line-height:1.25}
 .hero-foot{margin-top:12px}.hero-lead{font-size:.95rem;line-height:1.5}.hero #heroCta{display:none}
 .hero .kicker .eyebrow{font-size:.75rem}#shop{padding-top:12px}
 #shop>.sec-label h2{font-size:1.5rem}#shop>.var-intro{margin-bottom:12px}
 #shopCats{top:66px;margin-bottom:18px;padding-block:10px}#shopCats .shop-cat{padding:9px 15px;font-size:.85rem;min-height:44px}
 .catalog-section{margin-bottom:28px;scroll-margin-top:145px}
 .catalog-heading{gap:8px;align-items:flex-start}.catalog-heading h3{font-size:1.35rem}
 .catalog-heading p{font-size:.8rem}.catalog-tools{gap:4px}.catalog-tools button{font-size:.75rem;padding:8px}
 .catalog-tools .rail-arrow{display:none}
 /* ---- the shelf on a phone: a grid, not a rail ----

    Three varieties on screen used to mean 2.4 cards across a swiping rail, and
    at 393 that is a 137px card carrying a 135px photograph. Measured against the
    rest of the page it was plainly the odd one out — the tasting flight beside it
    is 353 wide and a new arrival is 320 — and it is the main shelf.

    Two columns is what mobile shops do, near universally, and it is the shape a
    shopper already knows. It is also the one change that gets both things the
    owner has asked for at different times: the card goes 137 → 170 and the
    photograph 135 → 168 (a 55% larger picture), and because a grid runs downward
    you see FOUR varieties in a screen instead of two and a sliver, rather than
    fewer. The rail was making those two wishes fight each other; the grid does
    not. Jenkins' grid study found the same thing from the other end — enlarging
    the elements inside a grid cut scanning time by more than adding a column did.

    The grid already existed behind the "View all" toggle. All that changes here
    is which one a phone gets first, and the toggle goes with it: there is nothing
    left to expand when the shelf already shows everything. The rail, its arrows
    and the toggle all stay on desktop, where a row of eight reads fine. */
 /* Full bleed, off the real gutter rather than a guess at it. The rail steps out
    by exactly --gutter and pads straight back by --gutter + 2px — the 2 being the
    rail's own padding — which lands the first card where it already sat and makes
    the rail's content column identical to the grid's. That identity is the whole
    mechanism: it is what lets one width formula produce the same card in both.

    Hardcoding -20px here was wrong. 20px is the gutter only below 561px; it is
    12px from 561 to 620 and about 31–35px above that, so a fixed -20 overhung the
    viewport by 8px a side in the middle band. Nothing was visibly broken because
    html and body carry overflow-x:clip, which is exactly why it would have gone
    unnoticed — the peek this rule exists to create was being clipped off screen. */
 .catalog-rail{padding-bottom:11px;
   margin-inline:calc(var(--gutter,20px) * -1);
   padding-inline:calc(var(--gutter,20px) + 2px);
   scroll-padding-inline:calc(var(--gutter,20px) + 2px);
   /* The grid's gap, named because the card width below is derived from it and
      the two must never drift. The row sets its own, smaller gap underneath. */
   --grid-gap:12px;
   /* And this is where the peek is paid for. Two cards of the grid's width plus
      the grid's gap are the content column exactly, leaving nothing for a third —
      so the row closes its own gap instead, and the 4px per gap it saves become
      the sliver at the screen edge. 8px still reads as two separate cards; the
      cards themselves do not shrink by a pixel. */
   gap:8px}
 /* ONE CARD SIZE, ROW AND GRID.

    The grid's column — half the content column minus the gap — is the size the
    owner approved, so the row now states the same arithmetic instead of its own.
    It used to subtract 40px rather than 12 to buy a 28px sliver of the third card,
    and that sliver cost 14px off every card in the row: 155px against the grid's
    168 on a 15. Switching between the two shrank every product, which is what he
    saw and did not want.

    Paying for the peek out of the card is the wrong pocket. The row pays for it
    out of the page gutter instead: .catalog-rail bleeds 20px past the content
    column on both sides and pads it straight back, so the first card still starts
    exactly where the heading does while the row runs to the screen edge and the
    third card is cut by it rather than hidden behind the gutter. */
 .catalog-card{flex:0 0 calc((100% - var(--grid-gap))/2);min-width:0;max-width:none;border-radius:16px}
 .catalog-name{padding:11px 12px 4px;font-size:1.06rem;line-height:1.26;
   display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
 html[dir=rtl] .catalog-name{line-height:1.5;padding-bottom:4px}
 html[dir=rtl] .catalog-note{line-height:1.55;padding-bottom:2px}
 /* Two lines, and the room for two lines whether or not the second is used —
    "Caramel · extra soft" wraps where "Caramel · soft" does not, and without a
    floor the card beside it sat 18px shorter and the row looked broken. */
 .catalog-note{padding:0 12px;font-size:.8rem;line-height:1.42;min-height:2.84em;
   display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
 /* ── THE PRODUCT-CARD STANDARD ──────────────────────────────────────────
    Every product anywhere in the shop — the shelf, New arrivals, and whatever
    Pantry and Fuel bring — uses these sizes. Name .85rem, taste .7rem, price
    .85rem with the weight beside it at .66rem, and the buy control on the SAME
    ROW as the price. Stacking the button under the price cost 44px of height on
    every card in every section; at three cards to a screen the word does not fit
    beside the price, so the button becomes the mark it always was — a plus, with
    the word kept for screen readers and for wider screens. */
 .catalog-foot{padding:10px 12px 12px;flex-direction:row;align-items:center;
   justify-content:space-between;gap:6px}
 .catalog-price{font-size:1rem;line-height:1.25;display:flex;align-items:baseline;
   gap:10px;flex-wrap:wrap;min-width:0}
 .catalog-price small{display:inline;font-size:.72rem;margin:0;opacity:.78}
 .catalog-foot .catalog-buy{--hit-expanded:1;position:relative;flex:none;
   width:40px;height:40px;min-height:40px;padding:0;display:grid;place-items:center}
 .catalog-foot .catalog-buy::before{content:"";position:absolute;top:50%;left:50%;
   transform:translate(-50%,-50%);width:44px;height:44px;border-radius:50%}
 .catalog-foot .catalog-buy .cb-t{position:absolute;width:1px;height:1px;overflow:hidden;
   clip:rect(0 0 0 0);white-space:nowrap}          /* still read aloud, just not drawn */
 .catalog-foot .catalog-buy .cb-i{display:block;width:18px;height:18px}
 /* THE FOOT IS ONE ROW ON EVERY CARD, SOLD OUT OR NOT — and that is what closed
    the gap the owner saw between a taste line and its price.

    A sold-out card used to spell out "Sold out · Details" across a row of its own
    under the price, because those words cannot fit beside a price at 169px. That
    made its foot 44px taller than every other foot in the row — and cards in a
    flex row stretch to the tallest, so a buyable card got 44px of dead space
    dropped between its description and its price row. Eight products, one of them
    sold out, and every other card in the shelf paid for it.

    The words were never carrying their weight anyway: the photograph already wears
    a "Sold out" badge, and the button is outlined instead of gold. So the control
    keeps the shape it has everywhere else — a round 40px button beside the price —
    and says the one thing worth saying, which is what happens if you press it: a
    bell, "Tell me", the same promise as "Tell me when it lands" on the card
    itself. Screen readers still get the whole sentence from aria-label. */
 .product-state{inset-inline-end:5px;bottom:5px;padding:3px 6px;font-size:.58rem}
 /* The grid stays inside the page gutter — only the row bleeds to the edge, so
    every part of the full-bleed rule is undone here, scroll padding included. Its
    columns are what set the card size for the whole shop: half the column, minus
    the gap, which is why it takes --grid-gap rather than restating 12px. */
 .catalog-rail.is-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
   gap:var(--grid-gap);overflow:visible;padding-bottom:0;
   margin-inline:0;padding-inline:2px;scroll-padding-inline:2px}
 .catalog-rail.is-grid .catalog-card{flex:none}
}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}.catalog-rail,#shopCats{scroll-behavior:auto}.hero *{animation:none!important;transition:none!important;transform:none!important}}

