/* ═══════════════════════════════════════════════════════════════════════════
   PRINTWAY SHOP
   One sheet, one ink. The page is paper; the only saturated colour on it is the
   configured brand ink (--ink-b and its derived variants, injected inline by
   _ShopLayout, contrast-checked there against both sheets). The logo gold
   (--ink-a) is reserved for the logo itself — the arrow and nothing else — so
   the accent a deployment configures is the accent the visitor actually sees.

   Discipline:
     · single-class selectors; each page area owns its own spacing
     · logical properties only — two of the shop's three languages read RTL
     · every colour is a token, defined for BOTH sheets in the same block
     · one elevation, one radius family, one 4px spacing scale
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── faces ───────────────────────────────────────────────────────────────── */
@font-face{
  font-family:'Anton'; font-style:normal; font-weight:400; font-display:swap;
  src:url('fonts/anton-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2212,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Archivo'; font-style:normal; font-weight:100 900; font-display:swap;
  src:url('fonts/archivo-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2212,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Vazirmatn'; font-style:normal; font-weight:100 900; font-display:swap;
  src:url('fonts/vazirmatn-arabic.woff2') format('woff2');
  unicode-range:U+0600-06FF,U+0750-077F,U+0870-088E,U+08A0-08FF,U+200C-200E,U+2010-2011,U+FB50-FDFF,U+FE70-FEFC;
}
@font-face{
  font-family:'Spline Sans Mono'; font-style:normal; font-weight:300 700; font-display:swap;
  src:url('fonts/splinesansmono-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2212,U+FEFF,U+FFFD;
}

/* ── tokens ──────────────────────────────────────────────────────────────── */
:root{
  color-scheme:light;

  /* stock */
  --paper:#FBFAF7;            /* the sheet                      */
  --paper-sunk:#F4F2EC;       /* wells the sheet sits inside    */
  /* A sheet laid on a sheet is never COOLER than the one beneath it. Pure white
     has zero warm bias and turned every card into an app card floating over
     paper; #FEFDFA lifts without going cold. Literal #FFFFFF survives only where
     it is doing a job — the plinths under pictures shot on white. */
  --surface:#FEFDFA;          /* cards and panels               */
  --surface-2:#F3F1EB;        /* image wells, quiet plates      */
  --surface-3:#EAE7DF;        /* pressed / hovered plates       */

  /* Ink, in three tiers — and BOTH of the quieter two are still text, so both are
     set at an alpha that measurably clears 4.5:1 once composited over the LIGHTEST
     backdrop they ever sit on (--surface, not --paper). The obvious ramp — .64/.46 —
     looks right and puts every micro label, count and caption on the page at 3.0:1.
     --line is decoration (dividers, card edges); --line-2 is the edge of an actual
     CONTROL, so it carries the 3:1 non-text bar instead. */
  --ink:#191713;
  --ink-rgb:25,23,19;
  --ink-2:rgba(25,23,19,.74);   /* secondary text      ~7.7:1 */
  --ink-3:rgba(25,23,19,.62);   /* labels & metadata   ~4.9:1 */
  --line:rgba(25,23,19,.10);
  --line-2:rgba(25,23,19,.47);  /* control edge        ~3.1:1 */
  --on-ink:#FBFAF7;           /* text printed on a solid ink fill */

  /* ── GOLD = PRINTWAY. INK-B = STATE. ────────────────────────────────────────
     Gold is the brand speaking as itself and never carries meaning: the arrow,
     the basket, the active language, the how-to-order numerals. It is a FIXED
     constant — no deployment can reconfigure it — so the portability argument
     that governs --ink-b has never applied to it. #1C1206 on it measures 9.3:1
     light / 12.1:1 dark. What gold must NOT do is bound a control: the plate
     itself is 1.62:1 against paper, so a gold-edged button fails 1.4.11. Hence
     the primary button stays ink, and every selected/focus state stays --ink-b. */
  --ink-a:#F2A900;
  --on-gold:#1C1206;          /* text printed on the gold ink */

  /* --ink-b / --ink-b-text arrive inline from
     _ShopLayout, derived from ShopSettings.ThemeColor and contrast-checked. */
  --tint:rgba(var(--ink-b-rgb),.10);
  --tint-2:rgba(var(--ink-b-rgb),.18);

  /* WhatsApp green is a third-party brand colour and cannot be adjusted — so the
     LABEL moves instead. White on it measures 3.09:1, which fails AA for a button
     label; their own dark green clears it at 6.7:1 and keeps the button instantly
     recognisable, which is the whole reason for using their colour at all. */
  --wa:#25D366; --on-wa:#06381B;

  /* one elevation family */
  --shadow-1:0 1px 2px rgba(25,23,19,.05);
  --shadow-2:0 4px 10px -2px rgba(25,23,19,.07), 0 12px 30px -8px rgba(25,23,19,.10);
  --shadow-3:0 18px 50px -12px rgba(25,23,19,.24);

  /* One radius family. A family is right — a single 6px on a 44px chip and a
     264px card genuinely reads as two curvatures — but 14/20 was phone-UI soft
     on a brand whose whole vocabulary is sheets, plates and tickets. */
  --r-xs:6px; --r-sm:10px; --r:10px; --r-lg:14px; --r-pill:999px;

  /* one spacing scale (4px) */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px;
  --s7:32px; --s8:40px; --s9:48px; --s10:64px; --s11:80px; --s12:112px;

  /* the page's own side margin, named because the horizontal strips have to cancel it
     to run off the edge of the screen and then put it back as scroll padding */
  --gutter:clamp(18px,4.5vw,44px);

  /* type */
  --font-display:'Anton','Vazirmatn','Arial Narrow',Impact,sans-serif;
  --font-body:'Archivo','Vazirmatn',system-ui,'Segoe UI',Tahoma,sans-serif;
  /* Vazirmatn rides along so localized words inside mono labels still shape as
     Arabic script instead of dropping to Courier. Codes stay Latin/Spline. */
  --font-mono:'Spline Sans Mono','Vazirmatn',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  --fs-display:clamp(2.5rem,6.2vw,4.4rem);
  --fs-h1:clamp(1.6rem,3.2vw,2.4rem);
  --fs-h2:clamp(1.1rem,1.7vw,1.35rem);
  --fs-h3:1.02rem;
  --fs-sm:.875rem;
  --fs-xs:.8rem;
  --fs-micro:.72rem;

  --ease:cubic-bezier(.2,.7,.3,1);
}

/* Dark is a REMEMBERED CHOICE, never the device default: the brand is printed
   on paper, so a visitor whose phone sits in dark mode still lands on the white
   sheet. The masthead toggle writes data-theme on <html> (applied in <head> by
   _ShopLayout, so there is no flash) and shop.js persists it. The attribute
   selector also outranks the layout's inline :root, which is what lets the
   derived dark ink actually take effect. */
html[data-theme="dark"]{
  color-scheme:dark;
  --paper:#100F0D;
  --paper-sunk:#0A0908;
  --surface:#191714;
  --surface-2:#211E1A;
  --surface-3:#2A2621;

  --ink:#EFEDE7;
  --ink-rgb:239,237,231;
  --ink-2:rgba(239,237,231,.78);
  --ink-3:rgba(239,237,231,.58);
  --line:rgba(239,237,231,.12);
  --line-2:rgba(239,237,231,.42);
  --on-ink:#100F0D;

  --ink-a:#FFC845;
  /* --on-gold is NOT re-declared here: #1C1206 measures 12.1:1 on the dark gold,
     better than it does on the light one. A second value would be churn. */

  --ink-b:var(--ink-b-dark-mark);
  --ink-b-rgb:var(--ink-b-dark-mark-rgb);
  --ink-b-text:var(--ink-b-dark);

  --tint:rgba(var(--ink-b-rgb),.16);
  --tint-2:rgba(var(--ink-b-rgb),.26);

  --shadow-1:0 1px 2px rgba(0,0,0,.5);
  --shadow-2:0 4px 10px -2px rgba(0,0,0,.5), 0 12px 30px -8px rgba(0,0,0,.6);
  --shadow-3:0 18px 50px -12px rgba(0,0,0,.75);
}

/* ── base ────────────────────────────────────────────────────────────────── */
*{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
body{
  margin:0; background:var(--paper); color:var(--ink);
  font-family:var(--font-body); font-size:16px; line-height:1.6;
  -webkit-font-smoothing:antialiased; font-synthesis:none;
}
a{ color:inherit; text-decoration:none; }
img{ max-width:100%; display:block; }
button{ font:inherit; color:inherit; }
h1,h2,h3{ margin:0; font-weight:650; line-height:1.25; }
p{ margin:0; }
ul{ margin:0; padding:0; list-style:none; }

:focus-visible{ outline:2px solid var(--ink-b); outline-offset:2px; border-radius:var(--r-xs); }

.wrap{ width:100%; max-width:1300px; margin-inline:auto; padding-inline:var(--gutter); }

/* Off-screen until focused: the first Tab on any page jumps straight to the
   goods instead of walking the masthead and the whole department rail. */
.skip{
  position:fixed; z-index:80; inset-block-start:var(--s3); inset-inline-start:var(--s3);
  padding:10px 18px; border-radius:var(--r-sm); font-size:var(--fs-sm); font-weight:650;
  background:var(--ink); color:var(--on-ink); box-shadow:var(--shadow-3);
  transform:translateY(-200%); transition:transform .18s var(--ease);
}
.skip:focus{ transform:none; }
/* An open overlay inerts .mast/main/.foot/.buybar — but the skip link is a SIBLING of
   all of them, so Tab could still reach it and jump focus out of the modal. */
html[data-pw-lock] .skip{ display:none; }

/* Visually hidden but still announced — never display:none on a control's name. */
.vh{
  position:absolute; inline-size:1px; block-size:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

/* ── type ────────────────────────────────────────────────────────────────── */
/* Anton is a single 400 weight, so font-synthesis:none keeps it from faux-bolding
   while variable Vazirmatn genuinely renders its heavier grades for Arabic script. */
.display{
  font-family:var(--font-display); font-weight:400; text-transform:uppercase;
  line-height:1.02; letter-spacing:.005em; overflow-wrap:break-word;
}
html[dir="rtl"] .display{ font-weight:700; line-height:1.28; letter-spacing:0; }

.mono{ font-family:var(--font-mono); font-variant-numeric:tabular-nums; }

.eyebrow{
  font-family:var(--font-mono); font-size:var(--fs-micro); font-weight:500;
  letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3);
}

/* Arabic-script NAMES on any page (marked .ar by the views, dir="auto" for bidi):
   Latin display type is set solid and would clip ascenders and dots. */
/* Weight belongs to the NAME's script, not the page's. Anton's @font-face excludes
   Arabic, so an Arabic-script name falls through to Vazirmatn — which is variable, and
   at 400 prints limp exactly where the tile IS the artwork. ShopText.ScriptClass()
   marks these per name, which is why the rule lives here and not on [dir="rtl"]. */
.face-name.ar, .door-name.ar, .pane-title.ar, .slide-h.ar{ font-weight:700; line-height:1.3; letter-spacing:0; }

/* Tracking is a Latin luxury: letter-spacing breaks Arabic-script joining. */
html[dir="rtl"] .eyebrow,
html[dir="rtl"] .face-cat,
html[dir="rtl"] .stamp,
html[dir="rtl"] .crumb{ letter-spacing:0; }

/* the brand's rising arrow — the logo mark, and the only gold on the page */
.brandarrow{ color:var(--ink-a); inline-size:1.05em; block-size:1.05em; flex:0 0 auto; }
html[dir="rtl"] .brandarrow{ transform:scaleX(-1); }

/* faint dot texture — empty states only; the rest of the page stays clean */
.ht-k{
  background-image:radial-gradient(circle, rgba(var(--ink-rgb),.17) 1px, transparent 1.4px);
  background-size:9px 9px;
}
.ht-b{ background-image:none; }
.ht-duo{
  background-image:
    radial-gradient(circle, rgba(var(--ink-b-rgb),.34) 1px, transparent 1.4px),
    radial-gradient(circle, rgba(var(--ink-rgb),.15) 1.2px, transparent 1.6px);
  background-size:9px 9px, 13px 13px;
  background-position:0 0, 5px 7px;
}

/* ── buttons ─────────────────────────────────────────────────────────────── */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s2);
  cursor:pointer; border:1px solid var(--line-2); background:var(--surface);
  color:var(--ink); border-radius:var(--r-sm); padding:12px 22px; min-block-size:44px;
  font-weight:600; font-size:var(--fs-sm); text-align:center; letter-spacing:.01em;
  transition:border-color .16s var(--ease), background-color .16s var(--ease),
             color .16s var(--ease), box-shadow .16s var(--ease);
}
.btn:hover{ border-color:var(--ink-3); background:var(--surface-2); }
.btn:disabled{ opacity:.5; cursor:not-allowed; }

/* The primary action is INK, not the accent: a solid near-black plate clears AA
   on both sheets whatever hex a deployment configures, and the accent then stays
   free to mean "selected" everywhere else without the two fighting. */
/* The primary action stays INK, and this is the one gold role not restored: gold's
   LABEL is excellent (9.3:1) but the gold plate is 1.62:1 against paper and its
   border is the same gold, so a gold-filled button has no discernible boundary —
   1.4.11. No resting shadow: a 1px 5%-black smudge is not depth, and the hover
   step to --shadow-2 reads more clearly from a flat rest state. */
.btn-press{
  background:var(--ink); border-color:var(--ink); color:var(--on-ink);
  font-weight:700;
}
.btn-press:hover{ background:var(--ink); border-color:var(--ink); box-shadow:var(--shadow-2); }
.btn-press:disabled{
  background:var(--surface-2); border-color:var(--line); color:var(--ink-3);
  box-shadow:none; opacity:1;
}
.btn-wa{ background:var(--wa); border-color:var(--wa); color:var(--on-wa); font-weight:700; }
.btn-wa:hover{ background:var(--wa); border-color:var(--wa); box-shadow:var(--shadow-2); }
.btn-wa:disabled{ box-shadow:none; }
.btn-wa svg{ inline-size:18px; block-size:18px; flex:0 0 auto; }

.icon-btn{
  display:inline-grid; place-items:center; cursor:pointer; padding:0;
  inline-size:40px; block-size:40px; flex:0 0 auto;
  background:transparent; border:1px solid transparent; border-radius:var(--r-pill);
  color:var(--ink-2); transition:color .16s var(--ease), background-color .16s var(--ease);
}
.icon-btn:hover{ color:var(--ink); background:var(--surface-2); }
.icon-btn svg{ inline-size:18px; block-size:18px; }

/* ── chips (the variant picker) ──────────────────────────────────────────── */
.chip{
  display:inline-flex; align-items:center; gap:var(--s2); cursor:pointer;
  border:1px solid var(--line-2); background:var(--surface); color:var(--ink);
  border-radius:var(--r-pill); padding:9px 16px; min-block-size:44px;
  font-size:var(--fs-sm); font-weight:500;
  transition:border-color .14s var(--ease), background-color .14s var(--ease),
             color .14s var(--ease), box-shadow .14s var(--ease);
}
.chip:hover{ border-color:var(--ink-3); }
.chip[aria-pressed="true"]{
  background:var(--tint); border-color:var(--ink-b); color:var(--ink-b-text);
  font-weight:650; box-shadow:inset 0 0 0 1px var(--ink-b);
}
/* Not with the CURRENT pick — still clickable: it slides the other axis to a pair
   that exists, so it keeps the pointer cursor and the struck-through look carries
   the warning on its own (paint() also writes an aria-label spelling it out). */
.chip[data-off="1"]{ color:var(--ink-3); border-style:dashed; text-decoration:line-through; }
.chip[data-off="1"] .dot{ opacity:.4; }
.chip .dot{
  inline-size:14px; block-size:14px; border-radius:50%; flex:0 0 auto;
  border:1px solid rgba(var(--ink-rgb),.28); box-shadow:inset 0 0 0 1px rgba(255,255,255,.35);
}

/* ── masthead ────────────────────────────────────────────────────────────── */
.mast{
  position:sticky; inset-block-start:0; z-index:40;
  background:var(--paper);
  background:color-mix(in srgb, var(--paper) 88%, transparent);
  -webkit-backdrop-filter:saturate(150%) blur(12px);
  backdrop-filter:saturate(150%) blur(12px);
  border-block-end:1px solid var(--line);
}
@supports not (backdrop-filter: blur(1px)){ .mast{ background:var(--paper); } }
.mast-in{ display:flex; align-items:center; gap:var(--s3); min-block-size:64px; padding-block:var(--s2); }
.wordmark{
  display:inline-flex; align-items:center; gap:var(--s2); min-inline-size:0;
  font-family:var(--font-display); font-weight:400; text-transform:uppercase;
  font-size:1.32rem; letter-spacing:.02em; line-height:1;
}
html[dir="rtl"] .wordmark{ font-weight:700; letter-spacing:0; }
.wordmark > span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ── deployment brand mark (ShopSettings.LogoPartial) ──────────────────────
   Sized by HEIGHT, never width: the mark is ~3.6:1, and a width-driven rule
   makes it tall enough to grow the 64px masthead on a phone. block-size with
   inline-size:auto keeps the cap height constant while the width follows.

   It is NOT mirrored in RTL — unlike .brandarrow, which is a directional
   glyph. This is a wordmark: the letters read left-to-right in every
   language, so flipping it would print the brand backwards. */
.wordmark-art{ gap:0; }
.wordmark-logo{
  display:block; block-size:30px; inline-size:auto; max-inline-size:min(48vw, 240px);
  flex:0 1 auto;
}
@media (min-width:560px){ .wordmark-logo{ block-size:34px; } }

/* A mark supplied as two picture files rather than as inline markup carries one
   per sheet, and only one may be on screen at a time. This is display, not
   opacity or visibility: the hidden face must not occupy a box beside the one
   that shows, or the masthead pays for the width twice.

   Both selectors are single-class (0,1,0) and the base .wordmark-logo above
   already sets display:block, so .logo-dark's `none` has to come AFTER it to
   win on order; the [data-theme] rules are (0,2,0) and outrank both. */
.logo-dark{ display:none; }
html[data-theme="dark"] .logo-light{ display:none; }
html[data-theme="dark"] .logo-dark{ display:block; }
.mast-end{ margin-inline-start:auto; display:flex; align-items:center; gap:var(--s1); }
.mast-rule{ inline-size:1px; block-size:22px; background:var(--line-2); margin-inline:var(--s2); }

.langs{ display:flex; gap:2px; }
.langs a{
  font-family:var(--font-mono); font-size:var(--fs-micro); padding:8px 9px;
  border-radius:var(--r-xs); color:var(--ink-3); letter-spacing:.03em;
  transition:color .14s var(--ease), background-color .14s var(--ease);
}
.langs a:hover{ color:var(--ink); background:var(--surface-2); }
/* Gold, as an underline rather than a fill — the shipped treatment. A fill AND a
   rule together was two marks for one state. */
.langs a.on{ color:var(--ink); box-shadow:inset 0 -2px 0 var(--ink-a); font-weight:600; }

/* Icon shown is the DESTINATION, not the current state: a moon on the white sheet
   means "go dark". Both faces ship in the markup and CSS picks one, so the swap
   costs no script and cannot lag a frame behind the paint it describes. */
.theme-btn .sun{ display:none; }
html[data-theme="dark"] .theme-btn .sun{ display:block; }
html[data-theme="dark"] .theme-btn .moon{ display:none; }
.cats-btn .i-top{ display:none; }
html[data-cats="side"] .cats-btn .i-side{ display:none; }
html[data-cats="side"] .cats-btn .i-top{ display:block; }
html[dir="rtl"] .cats-btn svg{ transform:scaleX(-1); }

/* Gold. The basket is the door to Send — everything after the first add goes
   through it — and painting it the same near-black as the wordmark two
   centimetres away lost it in a masthead already carrying five controls. */
.basket-btn{
  display:inline-flex; align-items:center; gap:var(--s2); cursor:pointer;
  background:var(--ink-a); color:var(--on-gold); border:0; border-radius:var(--r-pill);
  padding:10px 16px; min-block-size:44px; font-weight:650; font-size:var(--fs-xs);
  margin-inline-start:var(--s1);
  transition:box-shadow .16s var(--ease), opacity .16s var(--ease);
}
.basket-btn:hover{ box-shadow:var(--shadow-2); }
.basket-btn svg{ inline-size:17px; block-size:17px; flex:0 0 auto; }
.basket-btn .n{
  font-family:var(--font-mono); font-size:var(--fs-micro); font-weight:600;
  background:rgba(var(--ink-rgb),.001); color:inherit;
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--on-ink) 40%, transparent);
  min-inline-size:22px; block-size:22px; border-radius:var(--r-pill);
  display:inline-grid; place-items:center; padding-inline:6px;
}
.basket-btn[hidden]{ display:none; }
.basket-btn.bump .n{ animation:bump .32s var(--ease); }
@keyframes bump{ 50%{ scale:1.3; } }

/* ── hero ────────────────────────────────────────────────────────────────────
   The front page opens on a PICTURE, not a paragraph: banners staff manage
   themselves (/ShopBanners), with the search box floating over the bottom edge.
   Both halves are optional — with no banners the stage collapses to the search
   band alone, which is still a front page rather than a hole in one.

   The slides CROSSFADE rather than sliding along a track. A track needs
   direction-aware transforms AND a direction-aware swipe, and two of this shop's
   three languages read right-to-left; a crossfade has no direction to get wrong.

   COLOUR NOTE: everything painted ON the picture — caption, arrows, dots — uses
   literal values, not the theme tokens. The photograph is the same photograph on
   both sheets, so text over it must be light in dark mode AND in light mode; a
   token here would make the caption disappear the moment a visitor switched. */
.hero{ padding-block:clamp(18px,2.6vw,30px) clamp(18px,2.6vw,28px); }

/* The strip the floating search card takes out of the TOP of the picture — the card
   itself (~75px) plus the gap it stands off the edge. One number, so the caption, the
   dots and the card can never drift into each other.

   It sits at the top of the banner (owner, 2026-08-24), not at the bottom: the search box
   is the first thing a visitor who already knows what they want reaches for, and putting
   it above the picture's own caption stops the two competing for the same edge. The
   bottom of the picture then belongs to the caption and the dots alone. */
.hero-stage{ position:relative; }
.hero-stage.has-slides{ --find-h:100px; }

.slider{
  position:relative; overflow:hidden;
  border-radius:var(--r-lg);
  background:var(--surface-2);          /* what shows while the first picture loads */
  block-size:clamp(300px,32vw,440px);
  /* No drop shadow (owner, 2026-08-24). A shadow under a picture this wide reads as a
     seam laid across the page rather than as lift, and the banner already separates
     itself from the sheet with its own ink. The search card keeps its shadow — that one
     really is floating, over a photograph nobody has vetted. */
}
.slides{ position:absolute; inset:0; }

/* visibility, not just opacity: a faded-out slide must also leave the tab order,
   or the arrows would walk a keyboard visitor through links they cannot see. */
.slide{
  position:absolute; inset:0; opacity:0; visibility:hidden;
  transition:opacity .55s var(--ease), visibility 0s linear .55s;
}
.slide.on{ opacity:1; visibility:visible; transition:opacity .55s var(--ease), visibility 0s; }
.slide > img{ position:absolute; inset:0; inline-size:100%; block-size:100%; object-fit:cover; }

/* The whole slide is the link when it has one — a picture that leads somewhere
   should not make the visitor hunt for the button. */
.slide-hit{ position:absolute; inset:0; z-index:2; }

/* The scrim is GONE (owner, 2026-08-25). It was a near-black gradient over the bottom
   84% of the picture — there only to keep a white caption readable — and on a banner
   whose own artwork fades to light it read as a shadow smeared across the bottom of the
   photograph, which is what the owner kept seeing after the drop shadow was removed.

   Legibility moved onto the TEXT instead: a tight dark halo on the caption itself (see
   .slide-h / .slide-p) holds up over a busy or a light picture without painting a band
   over the whole banner. If a future banner ever defeats it, the answer is a darker
   picture — or putting this rule back, deliberately, for that reason. */
.slide-cap{
  position:absolute; inset:0; z-index:3; pointer-events:none;   /* the link underneath still takes the click */
  display:flex; flex-direction:column; justify-content:flex-end; gap:var(--s3);
  padding:calc(var(--find-h) + var(--s6)) clamp(18px,3.4vw,40px) 34px;
  color:#FBFAF7;
}
/* Two shadows, doing two jobs, now that there is no scrim underneath: a TIGHT one that
   traces the letterforms (this is what carries white type over a light patch of the
   photograph, the way a map label survives over any terrain) and a wide soft one that
   lifts the whole block off a busy picture. */
.slide-h{
  font-family:var(--font-display); font-weight:400; line-height:1.04;
  font-size:clamp(1.9rem,4.4vw,3.1rem); max-inline-size:18ch;
  text-shadow:0 1px 3px rgba(8,7,6,.85), 0 2px 22px rgba(8,7,6,.7);
}
.slide-p{
  max-inline-size:52ch; font-size:clamp(.92rem,1.4vw,1.05rem);
  color:#FBFAF7; text-shadow:0 1px 3px rgba(8,7,6,.85), 0 1px 16px rgba(8,7,6,.7);
}
/* A span, not a button: the slide itself is the link, and a nested <a> would be
   invalid markup. It has to LOOK pressable, so it borrows the button geometry. */
.slide-cta{
  align-self:flex-start; display:inline-flex; align-items:center;
  background:var(--ink-a); color:var(--on-gold);
  border-radius:var(--r-sm); padding:11px 20px; min-block-size:44px;
  font-weight:700; font-size:var(--fs-sm);
}

.sl-arrow{
  position:absolute; z-index:4; inset-block-start:50%; translate:0 -50%;
  inline-size:40px; block-size:40px; padding:0; cursor:pointer;
  display:grid; place-items:center; border:0; border-radius:var(--r-pill);
  background:rgba(251,250,247,.88); color:#191713;
  box-shadow:var(--shadow-2);
  transition:background-color .16s var(--ease);
}
.sl-arrow:hover{ background:#FBFAF7; }
.sl-arrow svg{ inline-size:19px; block-size:19px; }
.sl-prev{ inset-inline-start:clamp(8px,1.4vw,14px); }
.sl-next{ inset-inline-end:clamp(8px,1.4vw,14px); }
/* inset-inline flips on its own; the GLYPH has to be told. */
html[dir="rtl"] .sl-arrow svg{ transform:scaleX(-1); }

.sl-dots{
  position:absolute; z-index:4; display:flex; gap:7px;
  inset-inline-start:clamp(18px,3.4vw,40px);
  inset-block-end:12px;
}
.sl-dot{
  inline-size:9px; block-size:9px; padding:0; cursor:pointer;
  border:0; border-radius:var(--r-pill);
  background:rgba(251,250,247,.44);
  transition:background-color .16s var(--ease), inline-size .16s var(--ease);
}
.sl-dot:hover{ background:rgba(251,250,247,.72); }
.sl-dot.on{ inline-size:24px; background:var(--ink-a); }
/* A 9px dot is a fine TARGET at 24px — the padding is the hit area, not the paint. */
@media (pointer:coarse){
  .sl-dots{ gap:2px; }
  .sl-dot{ box-sizing:content-box; padding-block:8px; padding-inline:5px; background-clip:content-box; }
  .sl-dot.on{ background-clip:content-box; }
}

/* The search box: a solid plate floating over the TOP edge of the picture. It is
   deliberately NOT a transparent field on the photograph — an input has to be legible
   over a picture nobody has vetted, and its focus ring has to survive. */
.hero-find{ inline-size:min(660px,100%); margin-inline:auto; }
.hero-stage.has-slides .hero-find{
  position:absolute; z-index:5;
  inset-inline:clamp(12px,3vw,28px); inset-block-start:clamp(12px,2vw,20px);
  inline-size:auto; max-inline-size:660px;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-lg); padding:var(--s3);
  box-shadow:var(--shadow-3);
}
.hero-find .finder-form{ max-inline-size:none; }

/* ── page head (once a search / filter is on) ────────────────────────────── */
.pagehead{ padding-block:var(--s7) 0; }
.pagehead-in{ display:flex; align-items:flex-end; justify-content:space-between; gap:var(--s4); flex-wrap:wrap; }
/* No negative tracking: this <h1> prints a staff-typed department name and has no
   .ar escape hatch. Tightening cursive script overlaps the glyphs. */
.pagehead h1{ font-size:var(--fs-h1); }
.pagehead .count{ color:var(--ink-3); font-size:var(--fs-xs); }

/* the trail: Shop → Department → Section */
.crumb{
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--s2);
  font-size:var(--fs-xs); color:var(--ink-3); padding-block:var(--s6) 0;
}
.crumb a{
  display:inline-flex; align-items:center; gap:5px; min-block-size:32px;
  color:var(--ink-3); border-radius:var(--r-xs); transition:color .14s var(--ease);
}
.crumb a:hover{ color:var(--ink); text-decoration:underline; text-underline-offset:3px; }
.crumb .sep{ opacity:.5; }
html[dir="rtl"] .crumb .sep,
html[dir="rtl"] .crumb-i{ transform:scaleX(-1); display:inline-block; }
.crumb .here{ color:var(--ink-2); }

/* ── the two-column body ─────────────────────────────────────────────────────
   The sidebar and the results share ONE container so the sidebar can stand beside
   the goods. In chip-row mode it is a single-column grid and nothing moves.
   `has-side` is what opens the second column: a department with no sections and a
   storefront with no colour/size attributes has nothing to put in a sidebar, and
   without the flag the results would be dealt into the empty column and squeezed
   to its width. */
.shop-body{ display:grid; grid-template-columns:minmax(0,1fr); align-items:start; }
.shop-main, .results{ min-inline-size:0; }

/* The directory page has no search band of its own — its box is the one floating over
   the hero, from the same partial — so `.shop-main` is empty there and only the
   category grid occupies the body. */

/* ── categories (the directory page only) ────────────────────────────────────
   Every department on screen at once, in a wrapping grid — never a horizontal
   scroller with two thirds of the shop hidden past the edge. This is the whole
   front page, so the tiles are given room to be looked at. Once the visitor is
   inside a department the departments leave the page entirely; the way back up
   is the breadcrumb and the sidebar's own "all departments" link.
   (Left ALONE on purpose: the horizontal strips are the DEPARTMENT PAGE's
   sections and filters. The front page was never part of that request.) */
.cats{ padding-block:var(--s5) 0; }
/* The tile width sets the icon size — the well is square, so a wider tile is a bigger
   icon, and packing more departments per row shrinks the art. 170px is the balance
   point: six to a row on a wide screen, 31 departments in six rows, and a 512px
   cut-out rendered at ~140px, where it reads as a picture rather than a thumbnail. */
.doors-row{
  display:grid; gap:var(--s4);
  grid-template-columns:repeat(auto-fill,minmax(170px,1fr));
}
.directory-head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--s4); flex-wrap:wrap;
  padding-block-end:var(--s5); margin-block-end:var(--s5);
  border-block-end:1px solid var(--line);
}
.directory-h{ font-size:var(--fs-h2); }   /* translated string — no negative tracking */
.directory-sub{ margin-block-start:4px; color:var(--ink-3); font-size:var(--fs-sm); }
.directory-n{ font-size:var(--fs-micro); color:var(--ink-3); white-space:nowrap; }

.door{
  position:relative; display:flex; flex-direction:column; gap:var(--s3);
  padding:var(--s4); border-radius:var(--r);
  background:var(--surface); border:1px solid var(--line);
  transition:border-color .16s var(--ease), box-shadow .16s var(--ease),
             background-color .16s var(--ease);
}
.door:hover{ border-color:var(--ink-3); box-shadow:var(--shadow-2); }
.door-name{
  font-size:var(--fs-h3); font-weight:650; line-height:1.3;
  overflow-wrap:break-word;
}
.door-foot{ display:flex; align-items:center; gap:var(--s2); margin-block-start:auto; }
.door-n{
  font-family:var(--font-mono); font-size:var(--fs-micro); color:var(--ink-3);
  font-variant-numeric:tabular-nums;
}

/* "This department opens onto sections." A chevron drawn from borders rather than
   a glyph: U+25BE renders as a full-height emoji on some Android builds and lands
   on the baseline of a different font on others. */
.door-subs{
  display:inline-flex; align-items:center; gap:5px; margin-inline-start:auto;
  font-family:var(--font-mono); font-size:var(--fs-micro); color:var(--ink-3);
}
.door-subs::after{
  content:""; inline-size:5px; block-size:5px; margin-block-start:-3px;
  border-inline-end:1.4px solid currentColor; border-block-end:1.4px solid currentColor;
  rotate:45deg;
}

/* The art well. A department's picture is one of two completely different things
   and they cannot share a treatment:
     · uploaded art is a transparent cut-out (a logo, a pencil) — CONTAINED, on no
       background at all, because a plate behind it prints a grey box around it;
     · a photo BORROWED from the department's own goods is photography shot on
       white — it gets the white plinth so it does not float, and it is contained
       too, because cropping a mug in half is not a category icon. */
/* Sized by RATIO, not by a fixed height, and padded.
   Category art is overwhelmingly square (the uploaded pencil is 512×512), and a square
   picture contained in a short wide box renders at exactly the box's height — touching
   the top and bottom edges with no margin at all, which reads as cropped even though
   object-fit:contain has cut nothing. It also wasted ~40% of the tile's width. A 4:3
   well is taller for the same tile width, so the icon comes out BIGGER, and the padding
   guarantees clear air on every side whatever shape the picture turns out to be. */
.door-art{
  position:relative; aspect-ratio:1/1; border-radius:var(--r-sm); overflow:hidden;
  display:grid; place-items:center; padding:var(--s3);
}
.door-art img{ inline-size:100%; block-size:100%; object-fit:contain; }
.door-art.is-photo{ background:#FFFFFF; }

/* No picture: the department prints its own initial. The plate is rendered UNDERNEATH every
   picture too and simply hidden — shop.js reveals it when an image fails, so a pasted
   external link whose host has dropped the file degrades to the plate instead of printing a
   broken-image icon across the front page. */
.door-art:not(.is-plate) .plate-ch{ display:none; }
.door-art.is-plate img{ display:none; }
.door-art.is-plate{ background:var(--surface-2); }
/* Two tones, hashed off the name — thirty identical grey squares is a wall, not a directory. */
.door-art.is-plate.pa{ background:var(--tint); }
.door-art.is-plate.pa .plate-ch{ color:var(--ink-b-text); }
.door-art .plate-ch{
  font-family:var(--font-display); font-weight:400; font-size:2.6rem;
  color:var(--ink-3); text-transform:uppercase; line-height:1; user-select:none;
}
html[dir="rtl"] .door-art .plate-ch{ font-weight:700; }

/* Same failure, same answer, on a product card: drop the broken picture and leave the well. */
.face.is-blank img{ display:none; }
.face.is-blank{
  background-image:radial-gradient(circle, rgba(var(--ink-rgb),.13) 1px, transparent 1.4px);
  background-size:11px 11px;
}

/* ── the sidebar: this department's sections, and the filters ─────────────────
   The department page's own navigation, and ONE block of markup: `data-cats` on
   <html> decides whether it stands beside the goods ("side") or folds into chip
   rows above them ("top"), and _ShopLayout resolves the configured "auto"
   against the viewport in <head>, before the first paint. Only "side" is matched
   below, so a visitor with no JavaScript keeps the chip rows — the layout that
   works at any width. */
.side{ padding-block:var(--s6) 0; display:flex; flex-direction:column; gap:var(--s5); }
.side-back{
  display:inline-flex; align-items:center; gap:6px; align-self:flex-start;
  font-size:var(--fs-xs); color:var(--ink-3); min-block-size:36px;
  transition:color .14s var(--ease);
}
.side-back:hover{ color:var(--ink); }
.side-h{
  font-family:var(--font-mono); font-size:var(--fs-micro); font-weight:500;
  letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3);
  margin-block-end:var(--s3);
}
.side-block{ min-inline-size:0; }

/* Sections. One set of markup doing two completely different jobs.
   In chip-row mode it is a ONE-LINE strip of picture cards that scrolls sideways.
   Wrapped pills grew a row for every four sections — on a phone a department with a
   dozen of them printed four lines of names, plus the filters underneath, and the
   goods started below the fold: the visitor met a wall of text before a single
   product. A strip costs one line whatever the department holds, and the picture is
   what makes a section recognisable before the name is read.
   In the sidebar (further down) the same markup unwinds into a plain column — a
   column of outlined capsules is a column of competing rectangles, and the eye has
   to read every border before it reaches a name. */
.seclist{
  display:flex; flex-wrap:nowrap; gap:var(--s3);
  overflow-x:auto; overscroll-behavior-inline:contain;
  scroll-snap-type:x proximity; scroll-padding-inline:2px;
  padding-block-end:var(--s2);          /* the scrollbar's lane, not the cards' */
  scrollbar-width:thin; scrollbar-color:rgba(var(--ink-rgb),.26) transparent;
}
.seclist::-webkit-scrollbar{ block-size:6px; }
.seclist::-webkit-scrollbar-track{ background:transparent; }
.seclist::-webkit-scrollbar-thumb{ background:rgba(var(--ink-rgb),.24); border-radius:var(--r-pill); }
/* flex:0 0 auto or the cards squeeze to fit the viewport instead of running off it —
   a flex row shrinks its children before it overflows. The li is a flex box of its
   own so every card takes the tallest card's height and the counts sit on one line
   however long the names run. */
.seclist > li{ flex:0 0 auto; display:flex; scroll-snap-align:start; }

.secitem{
  flex:1; display:flex; flex-direction:column; gap:var(--s2);
  inline-size:clamp(104px,27vw,126px); padding:var(--s2) var(--s2) var(--s3);
  border-radius:var(--r); border:1px solid var(--line-2);
  background:var(--surface); color:var(--ink-2);
  font-size:var(--fs-xs); font-weight:550; text-align:center;
  transition:border-color .14s var(--ease), color .14s var(--ease),
             background-color .14s var(--ease), box-shadow .16s var(--ease);
}
.secitem:hover{ border-color:var(--ink-3); color:var(--ink); box-shadow:var(--shadow-2); }
/* The chosen card is RINGED, never flooded with ink: a section's picture is a
   transparent cut-out and a solid plate behind it swallows the drawing whole. */
.secitem.on{
  border-color:var(--ink-b); color:var(--ink); font-weight:650;
  box-shadow:inset 0 0 0 1px var(--ink-b);
}

/* The picture well — the department tiles' rule exactly: uploaded art is a cut-out
   and gets NO background (a plate would print a grey box around every icon), a photo
   borrowed from the section's own goods gets the white plinth so it does not float,
   and a section with neither prints its own initial on a tinted plate. */
.secitem-art{
  position:relative; aspect-ratio:1/1; inline-size:100%;
  border-radius:var(--r-sm); overflow:hidden;
  display:grid; place-items:center; padding:6px;
}
.secitem-art img{ inline-size:100%; block-size:100%; object-fit:contain; }
.secitem-art.is-photo{ background:#FFFFFF; }
.secitem-art:not(.is-plate) .plate-ch{ display:none; }
.secitem-art.is-plate img{ display:none; }
.secitem-art.is-plate{ background:var(--surface-2); }
.secitem-art.is-plate.pa{ background:var(--tint); }
.secitem-art.is-plate.pa .plate-ch{ color:var(--ink-b-text); }
.secitem-art .plate-ch{
  font-family:var(--font-display); font-weight:400; font-size:1.7rem;
  color:var(--ink-3); text-transform:uppercase; line-height:1; user-select:none;
}
html[dir="rtl"] .secitem-art .plate-ch{ font-weight:700; }

.secitem-name{ min-inline-size:0; overflow-wrap:anywhere; line-height:1.25; }
/* pushed to the bottom edge of the card, so the counts read as one row of numbers
   across the strip instead of drifting with each name's line count */
.secitem-n{
  margin-block-start:auto; font-size:var(--fs-micro); opacity:.7;
  font-variant-numeric:tabular-nums;
}

.filter-group + .filter-group{ margin-block-start:var(--s4); }
.filter-lbl{
  display:block; font-size:var(--fs-xs); font-weight:600; color:var(--ink-2);
  margin-block-end:var(--s2);
}
/* Same rule as the sections strip, same reason: one line, scrolled — a storefront
   with twenty colours must not stack five rows of chips over its own goods. */
.filter-opts{
  display:flex; flex-wrap:nowrap; gap:var(--s2);
  overflow-x:auto; overscroll-behavior-inline:contain; padding-block-end:6px;
  scrollbar-width:thin; scrollbar-color:rgba(var(--ink-rgb),.26) transparent;
}
.filter-opts::-webkit-scrollbar{ block-size:6px; }
.filter-opts::-webkit-scrollbar-track{ background:transparent; }
.filter-opts::-webkit-scrollbar-thumb{ background:rgba(var(--ink-rgb),.24); border-radius:var(--r-pill); }
.filter-opts > .facet{ flex:0 0 auto; }

.facet{
  display:inline-flex; align-items:center; gap:7px; border:1px solid var(--line-2);
  border-radius:var(--r-pill); padding:6px 13px; min-block-size:36px;
  font-size:var(--fs-xs); color:var(--ink-2); background:var(--surface);
  transition:border-color .14s var(--ease), color .14s var(--ease), background-color .14s var(--ease);
}
.facet:hover{ border-color:var(--ink-3); color:var(--ink); }
.facet.on{ background:var(--ink); border-color:var(--ink); color:var(--on-ink); font-weight:600; }
.facet .dot,
.pill .dot{
  inline-size:12px; block-size:12px; border-radius:50%; flex:0 0 auto;
  border:1px solid rgba(var(--ink-rgb),.28); box-shadow:inset 0 0 0 1px rgba(255,255,255,.35);
}
.facet.on .dot{ border-color:color-mix(in srgb, var(--on-ink) 55%, transparent); }
.facet-clear{
  display:inline-flex; align-items:center; gap:6px; color:var(--ink-3);
  font-size:var(--fs-xs); padding:6px 4px; min-block-size:36px;
  text-decoration:underline; text-underline-offset:3px;
}
.facet-clear:hover{ color:var(--ink); }

/* Standing it beside the goods. Only ever matched once the browser (or the visitor's own
   toggle) has asked for it — see the head script in _ShopLayout — and only above the width
   where a sidebar can stand beside anything at all. Below 861px a stored "side" preference
   simply keeps the strip: the whole block is inside the media query, so there is nothing to
   undo further down and the two presentations cannot drift apart. */
@media (min-width:861px){
  html[data-cats="side"] .shop-body{ --mast-h:65px; --side-w:236px; }
  html[data-cats="side"] .shop-body.has-side{
    grid-template-columns:var(--side-w) minmax(0,1fr);
    column-gap:clamp(20px,3vw,44px);
  }
  html[data-cats="side"] .side{
    position:sticky; inset-block-start:calc(var(--mast-h) + 12px);
    max-block-size:calc(100vh - var(--mast-h) - 30px);   /* pre-dvh engines */
    max-block-size:calc(100dvh - var(--mast-h) - 30px);
    overflow-y:auto; overscroll-behavior:contain; scrollbar-width:thin;
    gap:var(--s6);
  }
  /* the strip unwinds: no sideways scroll, no cards, no snap */
  html[data-cats="side"] .seclist{
    flex-direction:column; flex-wrap:nowrap; gap:1px;
    overflow:visible; padding-block-end:0; scroll-snap-type:none;
  }
  html[data-cats="side"] .seclist > li{ display:block; }
  html[data-cats="side"] .secitem{
    /* flex:0 0 auto is not decoration: these are children of a column flex container with a
       capped height, and a shrinkable item gets squashed to its line box (measured: 12px)
       instead of letting the sidebar scroll. */
    position:relative; flex:0 0 auto; inline-size:100%;
    flex-direction:row; align-items:center; gap:var(--s2); text-align:start;
    border:0; background:transparent; border-radius:var(--r-sm); box-shadow:none;
    padding:8px 11px; min-block-size:38px;
    transition:background-color .13s var(--ease), color .13s var(--ease);
  }
  html[data-cats="side"] .secitem:hover{ background:rgba(var(--ink-rgb),.06); box-shadow:none; }
  html[data-cats="side"] .secitem.on{
    background:var(--tint); color:var(--ink-b-text); font-weight:650; box-shadow:none;
  }
  /* the accent edge is the "you are here" mark — a filled plate shouts, this points */
  html[data-cats="side"] .secitem.on::before{
    content:""; position:absolute; inset-block:6px; inset-inline-start:0;
    inline-size:3px; border-radius:0 2px 2px 0; background:var(--ink-b);
  }
  /* the well shrinks to a thumbnail; a section with NO picture keeps the plain row it has
     always had here rather than gaining a plate a tight column never asked for. */
  html[data-cats="side"] .secitem-art{
    inline-size:24px; block-size:24px; aspect-ratio:auto; flex:0 0 auto;
    padding:0; border-radius:var(--r-xs);
  }
  html[data-cats="side"] .secitem-art.is-plate{ display:none; }
  html[data-cats="side"] .secitem-n{ margin-inline-start:auto; margin-block-start:0; }
  html[data-cats="side"] .filter-opts{
    flex-wrap:wrap; overflow:visible; gap:6px; padding-block-end:0;
  }
}
@media (min-width:1120px){
  html[data-cats="side"] .shop-body{ --side-w:260px; }
}

/* ── search ──────────────────────────────────────────────────────────────────
   One field, and the fastest way through the whole storefront — so it takes the
   top of the results column on every page that has one, and answers to "/". */
.finder{ padding-block:var(--s6) 0; display:flex; flex-direction:column; gap:var(--s4); }
.finder-form{ display:flex; gap:var(--s2); align-items:stretch; max-inline-size:640px; }
.finder-field{
  flex:1; min-inline-size:0; display:flex; align-items:center; gap:var(--s2);
  background:var(--surface); border:1px solid var(--line-2); border-radius:var(--r-sm);
  padding-inline:14px; transition:border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.finder-field:focus-within{ border-color:var(--ink-b); box-shadow:0 0 0 3px var(--tint); }
.finder-field > svg{ inline-size:17px; block-size:17px; flex:0 0 auto; color:var(--ink-3); }
.finder-input{
  flex:1; min-inline-size:0; font:inherit; font-size:var(--fs-sm); color:var(--ink);
  background:transparent; border:0; padding-block:13px;
}
.finder-input::placeholder{ color:var(--ink-3); }
.finder-input:focus{ outline:none; }
.finder-input::-webkit-search-cancel-button{ filter:grayscale(1); opacity:.5; }
/* True only while the shortcut would actually do something, so it goes the moment the field
   is focused or already carries a value (shop.js sets .is-typing). */
.finder-key{
  flex:0 0 auto; font-family:var(--font-mono); font-size:.66rem; color:var(--ink-3);
  border:1px solid var(--line-2); border-radius:5px; padding:1px 6px; line-height:1.5;
}
.finder-field:focus-within .finder-key,
.finder-field.is-typing .finder-key{ display:none; }
@media (hover:none){ .finder-key{ display:none; } }   /* no keyboard to press it with */
.finder-go{ flex:0 0 auto; }

/* The receipt for whatever is narrowing the results, each part removable on its own — so a
   visitor looking at eleven things never has to hunt back through three lists to find out why. */
.active{ display:flex; flex-wrap:wrap; gap:var(--s2); align-items:center; }
.pill{
  display:inline-flex; align-items:center; gap:7px;
  padding:6px 11px 6px 13px; min-block-size:36px; border-radius:var(--r-pill);
  background:var(--tint); color:var(--ink-b-text);
  font-size:var(--fs-xs); font-weight:600;
  transition:background-color .14s var(--ease);
}
html[dir="rtl"] .pill{ padding:6px 13px 6px 11px; }
.pill:hover{ background:var(--tint-2); }
.pill-k{
  font-family:var(--font-mono); font-size:.62rem; letter-spacing:.09em;
  text-transform:uppercase; opacity:.72; font-weight:500;
}
.pill svg{ inline-size:13px; block-size:13px; flex:0 0 auto; opacity:.75; }
.pill:hover svg{ opacity:1; }

.result-line{
  display:flex; align-items:center; gap:var(--s3);
  padding-block:var(--s2) 0; color:var(--ink-3);
}
.result-line .rule{ flex:1; block-size:1px; background:var(--line); }

/* ── the arrangement ─────────────────────────────────────────────────────────
   The count and the sort control share one line above the goods. They wrap on a
   narrow phone rather than squeezing the select down to an unreadable stub — the
   count is the half that gives way, because it is repeated in the page heading. */
.result-row{ display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap; }
.result-row .result-line{ flex:1 1 140px; min-inline-size:0; }
.sortform{ display:flex; align-items:center; gap:var(--s2); flex:0 0 auto; }
.sort-lbl{ font-size:var(--fs-xs); color:var(--ink-3); white-space:nowrap; }
.sort-sel{
  font:inherit; font-size:var(--fs-xs); color:var(--ink); max-inline-size:200px;
  background:var(--surface); border:1px solid var(--line-2); border-radius:var(--r-sm);
  padding:7px 10px; min-block-size:36px;
  transition:border-color .14s var(--ease), box-shadow .14s var(--ease);
}
.sort-sel:hover{ border-color:var(--ink-3); }
.sort-sel:focus{ outline:none; border-color:var(--ink-b); box-shadow:0 0 0 3px var(--tint); }
/* The Apply button is the no-JS path; once shop.js is submitting on change it is a
   second press for a decision the visitor has already made. */
.sortform.js-auto .sort-go{ display:none; }

/* ── the dinar band ──────────────────────────────────────────────────────────
   Two optional bounds, and a real submit — unlike the sort control this one is
   never auto-submitted, so it needs its button in every case. Capped in width
   because in chip-row mode the block spans the whole page and a pair of number
   boxes stretched across 1200px reads as a form, not a filter. */
.priceform{ display:flex; flex-direction:column; gap:var(--s2); max-inline-size:300px; }
.price-cur{
  font-size:var(--fs-micro); letter-spacing:.08em; color:var(--ink-3);
  font-weight:400; margin-inline-start:4px;
}
.price-row{ display:flex; align-items:center; gap:var(--s2); }
.price-in{
  flex:1; inline-size:100%; min-inline-size:0; font:inherit; font-size:var(--fs-xs);
  color:var(--ink); background:var(--surface); border:1px solid var(--line-2);
  border-radius:var(--r-sm); padding:8px 10px; min-block-size:36px;
  font-variant-numeric:tabular-nums;
  transition:border-color .14s var(--ease), box-shadow .14s var(--ease);
}
.price-in::placeholder{ color:var(--ink-3); }
.price-in:hover{ border-color:var(--ink-3); }
.price-in:focus{ outline:none; border-color:var(--ink-b); box-shadow:0 0 0 3px var(--tint); }
.price-sep{ flex:0 0 auto; color:var(--ink-3); font-size:var(--fs-xs); }
.price-act{ display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap; }
.price-go{ flex:0 0 auto; }

/* ── the grid & cards ────────────────────────────────────────────────────── */
.grid{
  display:grid; gap:var(--s4); padding-block:var(--s5) 0;
  grid-template-columns:repeat(auto-fill,minmax(232px,1fr));
}
/* container-type is what lets the tile size off the CARD instead of the viewport
   (see .face-name). The grid adds COLUMNS as the viewport grows, so card width is
   near-constant while a vw-based clamp swings ~7px — the same name fitting three
   lines at one width and truncating at another, for a reason nobody can see. */
.card{
  position:relative; display:flex; flex-direction:column; overflow:hidden;
  container-type:inline-size;
  border:1px solid var(--line); border-radius:var(--r); background:var(--surface);
  transition:border-color .18s var(--ease), box-shadow .18s var(--ease), translate .18s var(--ease);
}
/* The 3px rise is the only physical response in a grid of 441 tiles, and it is
   what says the whole card — plate, name and code — is one target. */
.card:hover{ border-color:var(--ink-3); box-shadow:var(--shadow-2); translate:0 -3px; }
.face{
  position:relative; aspect-ratio:1/1; overflow:hidden;
  background:var(--surface-2); border-block-end:1px solid var(--line);
}
/* CONTAINED, never cropped (owner, 2026-08-24). These are studio shots of promotional
   goods, and `cover` was slicing the ends off anything that is not square — a pen came
   out as a bar of ink, and the customer met a different shape here than on the item page,
   which has always contained its picture. `contain` prints the photograph at exactly the
   size that fits, so the card and the detail page finally agree.

   The white ground goes with it: these photographs are shot on white and would otherwise
   letterbox onto the grey well in light mode and a near-black one in dark. Same treatment,
   same reason, as `.door-art.is-photo`. background-COLOR, not the shorthand: `.face.is-blank`
   paints a texture through background-image when a picture fails to load, and the shorthand
   here would wipe it. */
.face img{ inline-size:100%; block-size:100%; object-fit:contain; }
.face.has-shot{ background-color:#FFFFFF; }

/* A product with more than one photo shows its second on hover — the cheapest way
   to say "there is more to see here". The file is only requested once a pointer
   lands on the card (shop.js fills data-src), so a grid of forty cards still costs
   forty pictures, not eighty. */
.face-alt{ position:absolute; inset:0; opacity:0; transition:opacity .3s var(--ease); }
.card:hover .face-alt.is-loaded{ opacity:1; }
@media (hover:none){ .face-alt{ display:none; } }   /* no hover to reveal it — don't fetch it */
.face-n{
  position:absolute; inset-block-start:var(--s2); inset-inline-end:var(--s2); z-index:2;
  display:inline-flex; align-items:center; gap:4px;
  padding:3px 8px; border-radius:var(--r-pill);
  font-family:var(--font-mono); font-size:.65rem; font-weight:600;
  /* Two declarations: an engine with backdrop-filter but without color-mix drops
     the whole background and the chip loses its ground. No backdrop-filter here —
     this sits inside a repeating grid cell, and a compositing layer per card on a
     24-card grid is real cost on a low-end phone for a blur nobody can see. */
  background:var(--surface);
  background:var(--surface);
  background:color-mix(in srgb, var(--surface) 88%, transparent);
  color:var(--ink-2); box-shadow:var(--shadow-1);
}
.face-n svg{ inline-size:11px; block-size:11px; }

/* an image-less product prints its own name as the artwork — most of a young
   catalogue can look like this, so it is a design, not a placeholder */
/* No gap: the auto margin on .face-name already separates the two lines, and the
   gap only stole 8px from the artwork. */
.face-print{ position:absolute; inset:0; padding:var(--s4); display:flex; flex-direction:column; }
.face-cat{
  font-family:var(--font-mono); font-size:.65rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--ink-2);
}
.face-name{
  margin-block-start:auto; font-family:var(--font-display); font-weight:400;
  text-transform:uppercase; line-height:1.02; letter-spacing:.005em;
  /* Sized off the CARD, not the viewport — see container-type on .card. The clamp
     floor survives for engines without cqi. The square face bought ~36px of height
     and the type is what it was bought for. */
  font-size:clamp(1.4rem,9cqi,2rem); overflow-wrap:anywhere;
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;
}
html[dir="rtl"] .face-name{ font-weight:700; line-height:1.3; }
/* TWO PLATES, AND THEY HAVE TO BE TWO VALUES. On ~418 of 441 products this box IS the
   photograph, so the fa/fb alternation is the only rhythm a 24-tile grid has. The accent
   tint composited to #C9DCE5 against #EAE7DF — a 1.14:1 step that existed in the markup
   and had stopped existing on screen. Plate A is now reversed out: a genuine 14.5:1 step,
   no new colour (both tokens already exist), deployment-independent, and it answers the
   contrast objection that drove the tint in the first place — the category label lands
   ~10:1 instead of 3.8:1. A grid half-reversed reads like a type specimen. */
.face-print.fa{ background:var(--ink); }
.face-print.fa .face-name{ color:var(--on-ink); }
/* opacity, not color-mix: same 10.4:1 result, and it needs no fallback on the older
   engines the guarded backgrounds above are already written for. */
.face-print.fa .face-cat{ color:var(--on-ink); opacity:.76; }
.face-print.fb{ background:var(--surface-3); }
.face-print.fb .face-name{ color:var(--ink); }
.face-print.fb .face-cat{ color:var(--ink-2); }

/* sold out: the colour never printed */
.card.out .face{ filter:grayscale(1); opacity:.7; }
.stamp{
  position:absolute; inset-block-start:50%; inset-inline-start:50%;
  translate:-50% -50%; z-index:3;
  font-family:var(--font-mono); font-weight:600; text-transform:uppercase;
  font-size:var(--fs-micro); letter-spacing:.1em; color:var(--on-ink);
  background:rgba(var(--ink-rgb),.9); padding:7px 14px; border-radius:var(--r-pill);
  white-space:nowrap; box-shadow:var(--shadow-2);
}
html[dir="rtl"] .stamp{ translate:50% -50%; }

.card-body{ padding:var(--s4) var(--s4) 0; display:flex; flex-direction:column; gap:var(--s2); flex:1; }
.card-name{
  font-weight:600; font-size:var(--fs-sm); line-height:1.4; overflow-wrap:anywhere;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
/* The dinar price, on the products that carry one. Set in the mono face for tabular figures —
   a column of prices that do not line up reads as sloppy — but at body weight and full ink, so
   it is a PRICE and not another code. It deliberately does NOT go inside .card-code: that strip
   is the job ticket, and everything on it is something staff quote rather than something a
   customer reads. The encoded ITEM code still prints there, unchanged, beside it. */
.card-price{
  font-size:var(--fs-sm); font-weight:650; color:var(--ink);
  font-variant-numeric:tabular-nums; letter-spacing:.01em;
}
.card-price .cur{
  font-size:.68rem; font-weight:500; color:var(--ink-3);
  margin-inline-start:5px; letter-spacing:.06em;
}

.card-axes{ display:flex; align-items:center; gap:var(--s3); min-block-size:16px; flex-wrap:wrap; }
.swatches{ display:flex; gap:4px; align-items:center; }
.swatches i{
  inline-size:12px; block-size:12px; border-radius:50%; display:block;
  border:1px solid rgba(var(--ink-rgb),.22); box-shadow:inset 0 0 0 1px rgba(255,255,255,.35);
}
.swatches .more, .sizes-line .more{ font-family:var(--font-mono); font-size:.66rem; color:var(--ink-3); }
.sizes-line{ font-family:var(--font-mono); font-size:.68rem; color:var(--ink-3); letter-spacing:.04em; }

/* The card's own code plate — the same idea as the item page's, at label size. */
.card-code{
  margin-block-start:auto; display:flex; gap:var(--s4); align-items:baseline;
  border-block-start:1px solid var(--line);
  margin-inline:calc(var(--s4) * -1); padding:10px var(--s4) 11px;
  font-family:var(--font-mono); font-size:var(--fs-micro);
  background:var(--surface-2);
}
.card-code .pair{ display:flex; gap:6px; align-items:baseline; min-inline-size:0; }
.card-code .k{ color:var(--ink-3); font-size:.62rem; letter-spacing:.08em; flex:0 0 auto; }
.card-code .v{ color:var(--ink); font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ── pager ───────────────────────────────────────────────────────────────── */
.pager{
  display:flex; align-items:center; justify-content:center; gap:var(--s4);
  padding-block:var(--s9) 0;
}
.pager .sheet{
  font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--ink-3);
  letter-spacing:.06em; min-inline-size:9ch; text-align:center;
}
.pager .pg{ min-inline-size:118px; }
.pager .pg svg{ inline-size:15px; block-size:15px; }
html[dir="rtl"] .pager .pg svg{ transform:scaleX(-1); }

/* ── empty states ────────────────────────────────────────────────────────── */
.blank{ text-align:center; padding-block:var(--s11) var(--s12); max-inline-size:440px; margin-inline:auto; }
.blank-ht{
  block-size:52px; max-inline-size:210px; margin-inline:auto; margin-block-end:var(--s6);
  -webkit-mask-image:linear-gradient(#000, transparent); mask-image:linear-gradient(#000, transparent);
}
.blank h2{ font-size:var(--fs-h2); }
.blank p{ margin-block:var(--s3) var(--s6); color:var(--ink-2); }
.blank .eyebrow{ display:block; margin-block-end:var(--s2); }

/* ── item page ───────────────────────────────────────────────────────────── */
.page-item{ padding-block-end:var(--s10); }
.detail{
  display:grid; gap:clamp(24px,4vw,64px); padding-block:var(--s6) 0;
  grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr); align-items:start;
}

/* ── the gallery ─────────────────────────────────────────────────────────────
   Stage + rail. Both point at the SAME urls, so a picture is fetched once and
   every switch after that — thumbnail, arrow, swipe, full screen — is a cache hit:
   no spinner, no network, no flash of empty box. */
.detail-media{ display:flex; flex-direction:column; gap:var(--s3); position:sticky; inset-block-start:84px; }
.detail-face{
  position:relative; overflow:hidden; display:flex;
  aspect-ratio:1/1; border-radius:var(--r-lg);
  background:var(--surface-2); border:1px solid var(--line);
}
.detail-face img{ inline-size:100%; block-size:100%; object-fit:contain; }
.detail-face #hero{ cursor:zoom-in; }
.detail-face .face-print{ position:absolute; inset:0; padding:var(--s7); }
.detail-face .face-name{ font-size:clamp(1.8rem,4.2vw,3rem); -webkit-line-clamp:5; }
.detail-face.out{ filter:grayscale(1); opacity:.8; }

.shot-nav{
  position:absolute; inset-block-start:50%; translate:0 -50%; z-index:3;
  inline-size:40px; block-size:40px; display:grid; place-items:center; cursor:pointer;
  border:1px solid var(--line); border-radius:var(--r-pill); padding:0;
  background:var(--surface);
  background:color-mix(in srgb, var(--surface) 90%, transparent);
  -webkit-backdrop-filter:blur(8px);
  backdrop-filter:blur(8px); color:var(--ink); box-shadow:var(--shadow-2);
  opacity:0; transition:opacity .16s var(--ease);
}
.shot-nav svg{ inline-size:16px; block-size:16px; }
.detail-face:hover .shot-nav, .shot-nav:focus-visible{ opacity:1; }
@media (hover:none){ .shot-nav{ opacity:1; } }   /* no hover on a phone — always offer them */
.shot-prev{ inset-inline-start:var(--s3); }
.shot-next{ inset-inline-end:var(--s3); }
.shot-prev svg{ transform:scaleX(1); }
html[dir="rtl"] .shot-nav svg{ transform:scaleX(-1); }
.shot-nav[hidden]{ display:none; }

.shot-zoom{
  position:absolute; inset-block-start:var(--s3); inset-inline-end:var(--s3); z-index:3;
  inline-size:36px; block-size:36px; display:grid; place-items:center; cursor:pointer;
  border:1px solid var(--line); border-radius:var(--r-pill); padding:0;
  background:var(--surface);
  background:color-mix(in srgb, var(--surface) 90%, transparent);
  -webkit-backdrop-filter:blur(8px);
  backdrop-filter:blur(8px); color:var(--ink); box-shadow:var(--shadow-2);
  opacity:0; transition:opacity .16s var(--ease);
}
.shot-zoom svg{ inline-size:15px; block-size:15px; }
.detail-face:hover .shot-zoom, .shot-zoom:focus-visible{ opacity:1; }
@media (hover:none){ .shot-zoom{ opacity:1; } }

.shot-count{
  position:absolute; inset-block-end:var(--s3); inset-inline-end:var(--s3); z-index:3;
  padding:4px 10px; border-radius:var(--r-pill); font-size:var(--fs-micro);
  background:rgba(var(--ink-rgb),.82); color:var(--on-ink);
}
.shot-count[hidden]{ display:none; }

/* The rail sits UNDER the stage and is centred against it: three 68px thumbs hung off
   the leading edge of a square that fills the column read as a row that had lost its
   fourth, and in RTL they hung off the other edge — a picture strip centred under the
   picture it belongs to reads as one object in both directions.

   `safe center` is the whole point of the second declaration: a centred flex row whose
   content OVERFLOWS pushes its first items past the scroll origin, where no scroll can
   reach them — so a gallery of ten would lose its opening shots. `safe` falls back to
   flex-start exactly when that would happen. The plain `center` above it is the fallback
   for engines that do not parse the keyword. */
.shots{
  display:flex; gap:var(--s2); overflow-x:auto; scrollbar-width:thin;
  scroll-snap-type:x proximity; padding-block-end:var(--s1);
  justify-content:center;
  justify-content:safe center;
}
.shots.is-hidden{ display:none; }
.shot{
  flex:0 0 auto; scroll-snap-align:start; cursor:pointer; padding:0;
  inline-size:68px; block-size:68px; overflow:hidden;
  border:1px solid var(--line-2); border-radius:var(--r-sm); background:var(--surface-2);
  opacity:.7; transition:border-color .14s var(--ease), opacity .14s var(--ease);
}
.shot:hover{ opacity:1; border-color:var(--ink-3); }
.shot.on{ opacity:1; border-color:var(--ink-b); box-shadow:inset 0 0 0 1px var(--ink-b); }
.shot img{ inline-size:100%; block-size:100%; object-fit:cover; }

/* Full-screen viewer. Shows the url already on the stage, so it opens instantly. */
.lightbox{
  position:fixed; inset:0; z-index:60; display:flex; align-items:center; justify-content:center;
  padding:clamp(14px,4vw,56px); background:rgba(8,7,5,.94); cursor:zoom-out;
}
.lightbox[hidden]{ display:none; }
.lightbox img{
  max-inline-size:100%; max-block-size:100%; object-fit:contain; cursor:default;
  border-radius:var(--r-sm);
}
.lightbox-x{
  position:absolute; inset-block-start:var(--s4); inset-inline-end:var(--s4); z-index:1;
  inline-size:44px; block-size:44px; display:grid; place-items:center; cursor:pointer;
  border:0; border-radius:var(--r-pill); background:rgba(255,255,255,.14); color:#FBFAF7;
}
.lightbox-x svg{ inline-size:18px; block-size:18px; }
.lightbox-x:hover{ background:rgba(255,255,255,.26); }

/* ── the decision column ─────────────────────────────────────────────────── */
.pane-cat{ display:inline-flex; align-items:center; gap:6px; }
.pane-cat a{
  font-family:var(--font-mono); font-size:var(--fs-micro); letter-spacing:.11em;
  text-transform:uppercase; color:var(--ink-b-text);
}
.pane-cat a:hover{ text-decoration:underline; text-underline-offset:3px; }
.pane-title{ margin-block-start:var(--s3); font-size:clamp(1.7rem,3.6vw,2.5rem); }
.pane-desc{ margin-block-start:var(--s4); color:var(--ink-2); max-inline-size:54ch; }

.picker{ margin-block-start:var(--s7); }
.picker + .picker{ margin-block-start:var(--s6); }
.picker-lab{ display:flex; align-items:baseline; gap:var(--s2); margin-block-end:var(--s3); }
.picker-lab .sel{ font-size:var(--fs-sm); font-weight:650; }
.opts{ display:flex; flex-wrap:wrap; gap:var(--s2); }

/* ── the imprint — this shop's signature ─────────────────────────────────────
   There are no prices anywhere on this site: the codes ARE the product's identity
   and the thing the customer quotes on WhatsApp, so the plate that carries them is
   the one component on the page that is allowed to look like a piece of machinery.
   A press ticket: an ink strip across the head, the two fields set in monospace on
   their own stock, and a perforation before the explanation. */
.imprint{
  position:relative; margin-block-start:var(--s7);
  border:1px solid var(--line-2); border-radius:var(--r);
  background:var(--surface); overflow:hidden;
}
/* the ink strip across the head of the ticket */
.imprint::before{
  content:""; position:absolute; z-index:1;
  inset-block-start:0; inset-inline:0; block-size:3px; background:var(--ink-b);
}
.imprint-head{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s3);
  padding:12px var(--s5) 9px; background:var(--surface-2);
  border-block-end:1px solid var(--line);
}
.imprint-head button{
  background:none; border:0; cursor:pointer; padding:6px 8px; margin:-6px -8px;
  border-radius:var(--r-xs); font-weight:650; font-size:var(--fs-micro);
  font-family:var(--font-mono); letter-spacing:.06em; text-transform:uppercase;
  color:var(--ink-b-text); transition:background-color .14s var(--ease);
}
.imprint-head button:hover{ background:var(--tint); }
.imprint-codes{ display:flex; flex-wrap:wrap; gap:var(--s3) var(--s9); padding:var(--s5); }
.imprint-pair{ display:flex; flex-direction:column; gap:3px; font-family:var(--font-mono); min-inline-size:0; }
.imprint-pair .k{ font-size:.62rem; letter-spacing:.14em; color:var(--ink-3); }
/* NOT overflow-wrap:anywhere — this is the string the customer retypes into WhatsApp,
   and a code split across two lines is a code transcribed wrong. */
.imprint-pair .v{ font-size:1.22rem; font-weight:600; letter-spacing:.01em; overflow-wrap:normal; word-break:keep-all; }
.imprint-note{
  padding:var(--s3) var(--s5) var(--s4);
  font-size:var(--fs-xs); color:var(--ink-3); line-height:1.55;
  border-block-start:1px dashed var(--line-2);
}

.actions{ margin-block-start:var(--s5); display:flex; gap:var(--s2); }
.actions .btn{ flex:1; padding-block:16px; font-size:.98rem; }

.howto{ margin-block-start:var(--s8); border-block-start:1px solid var(--line); padding-block-start:var(--s6); }
.howto ol{ margin:var(--s4) 0 0; padding:0; list-style:none; counter-reset:s; display:flex; flex-direction:column; gap:var(--s3); }
.howto li{
  counter-increment:s; position:relative; padding-inline-start:var(--s8);
  color:var(--ink-2); font-size:var(--fs-sm);
}
.howto li::before{
  content:counter(s); position:absolute; inset-inline-start:0; inset-block-start:1px;
  font-family:var(--font-mono); font-size:.7rem; font-weight:600;
  color:var(--on-gold); background:var(--ink-a);   /* gold: the shop speaking, not a state */
  inline-size:24px; block-size:24px; display:grid; place-items:center; border-radius:var(--r-pill);
}

.related{ margin-block-start:var(--s10); border-block-start:1px solid var(--line); padding-block-start:var(--s7); }
.related h2{ font-size:var(--fs-h2); margin-block-end:var(--s5); }
.rel-grid{ display:grid; gap:var(--s3); grid-template-columns:repeat(auto-fill,minmax(168px,1fr)); }
.rel-grid .face{ aspect-ratio:1/1; }
.rel-grid .card-body{ padding-block-end:var(--s4); }

/* mobile buy bar — desktop keeps the button in the decision column */
.buybar{ display:none; }

/* ── the Categories modal ────────────────────────────────────────────────────
   Every department, one tap from any page. It shares the scrim with the basket
   drawer (only one of the two is ever open) and the same real-modal handling:
   the page behind goes inert, Escape and a backdrop click close it. */
.dept-btn{
  display:inline-flex; align-items:center; gap:var(--s2); cursor:pointer;
  margin-inline-start:var(--s4); padding:9px 15px; min-block-size:40px;
  border:1px solid var(--line-2); border-radius:var(--r-pill);
  background:var(--surface); color:var(--ink); font-size:var(--fs-xs); font-weight:600;
  transition:border-color .16s var(--ease), background-color .16s var(--ease);
}
.dept-btn:hover{ border-color:var(--ink-3); background:var(--surface-2); }
.dept-btn svg{ inline-size:15px; block-size:15px; flex:0 0 auto; color:var(--ink-3); }
.dept-btn[aria-expanded="true"]{ border-color:var(--ink-b); background:var(--tint); color:var(--ink-b-text); }
.dept-btn[aria-expanded="true"] svg{ color:currentColor; }

.modal{
  position:fixed; inset:0; z-index:55; display:flex; align-items:flex-start; justify-content:center;
  padding:clamp(12px,5vh,72px) clamp(12px,4vw,32px);
  overflow-y:auto; overscroll-behavior:contain;
}
.modal[hidden]{ display:none; }
.modal-card{
  inline-size:min(940px,100%); max-block-size:100%;
  display:flex; flex-direction:column; min-block-size:0;
  background:var(--paper); border:1px solid var(--line-2); border-radius:var(--r-lg);
  box-shadow:var(--shadow-3); overflow:hidden;
  /* the soft part: it settles in rather than appearing */
  opacity:0; translate:0 -10px; scale:.985;
  transition:opacity .22s var(--ease), translate .26s var(--ease), scale .26s var(--ease);
}
.modal.is-open .modal-card{ opacity:1; translate:0 0; scale:1; }
.modal-head{
  display:flex; align-items:center; gap:var(--s3);
  padding:var(--s5) var(--s6) var(--s4); border-block-end:1px solid var(--line);
}
.modal-head h2{ font-size:var(--fs-h2); }
.modal-find{ padding:var(--s4) var(--s6) 0; }
.modal-find .finder-field{ max-inline-size:360px; }
.modal-body{ padding:var(--s5) var(--s6) var(--s6); overflow-y:auto; min-block-size:0; }
.modal-body .doors-row{ grid-template-columns:repeat(auto-fill,minmax(158px,1fr)); gap:var(--s3); }
.modal-body .door-art{ padding:var(--s2); }
.modal-body .door-art .plate-ch{ font-size:1.9rem; }
.modal-body .door-name{ font-size:var(--fs-sm); }
.modal-body .door{ padding:var(--s3); gap:var(--s2); }
.modal-none{ text-align:center; color:var(--ink-3); padding-block:var(--s8); font-size:var(--fs-sm); }

/* ── the sections dialog ─────────────────────────────────────────────────────
   Same chrome as the Categories dialog above — already theme-aware, RTL-safe,
   already a bottom sheet on a phone — so the two cannot drift apart. Only three
   things differ, and each for a reason.

   1. Narrower. A department has a handful of sections, not thirty-one, and
      940px of dialog around four tiles reads as a page that failed to load. */
.secmodal .modal-card{ inline-size:min(600px,100%); }
/*   1b. And centred, where the Categories dialog hangs from the top. Thirty-one tiles
      fill the screen and must start at a predictable place; a handful of sections is a
      short card, and a short card pinned to the top of a dimmed page reads as something
      that dropped in rather than something being asked. The card still carries
      `max-block-size:100%` and scrolls internally, so centring can never clip its head. */
.secmodal{ align-items:center; }
/*   2. The heading is the DEPARTMENT's name, so it needs the small label above it
      to say what the list underneath actually is. */
.secmodal-h{ display:flex; flex-direction:column; gap:3px; min-inline-size:0; }
.secmodal-h h2{ line-height:1.15; }
.secmodal-lede{ padding:var(--s4) var(--s6) 0; color:var(--ink-3); font-size:var(--fs-sm); }
/*   3. The grid IS the body here (one element, both classes), where the Categories
      dialog nests one inside the other — so this rule cannot be a descendant one. */
.modal-body.doors-row{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:var(--s3);
}
.secmodal .modal-body{ padding-block-start:var(--s4); }
/* The server-rendered source the dialog clones from. In the document for a crawler and
   for a visitor with no script, never on screen — and [hidden] is spelled out because a
   grid/flex display declaration elsewhere would otherwise beat the UA's own rule. */
.secsrc[hidden]{ display:none; }
@media (max-width:640px){
  .modal{ padding:0; align-items:flex-end; }
  .modal-card{ inline-size:100%; max-block-size:92dvh; border-radius:var(--r-lg) var(--r-lg) 0 0; border-block-end:0; }
  .modal.is-open .modal-card{ translate:0 0; }
  .modal-card{ translate:0 14px; }
  .modal-head, .modal-find, .modal-body{ padding-inline:var(--s4); }
  .dept-lbl{
    position:absolute; inline-size:1px; block-size:1px; padding:0; margin:-1px;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
  }
  .dept-btn{ margin-inline-start:var(--s2); padding-inline:11px; }
}
/* The sidebar/chip-row toggle has nothing to offer below the width where a sidebar
   fits — "auto" already gives chip rows there, and a sidebar on a phone is wrong. */
@media (max-width:860px){
  /* The sidebar cannot stand beside anything at this width — a stored "side" preference
     would otherwise leave ~83px of goods column at 375px — and every side-mode rule is
     already inside a min-width:861px block, so there is nothing to undo here: the strip
     is simply what this width gets. Only the toggle itself has to go. */
  .cats-btn{ display:none; }
}

/* ── job bag (the basket drawer) ─────────────────────────────────────────── */
.scrim{
  position:fixed; inset:0; background:rgba(10,9,7,.5); z-index:50;
  opacity:0; pointer-events:none; transition:opacity .22s var(--ease);
}
.scrim.open{ opacity:1; pointer-events:auto; }
.bag{
  position:fixed; z-index:51; inset-block:0; inset-inline-end:0; inline-size:min(440px,100%);
  background:var(--paper); border-inline-start:1px solid var(--line);
  box-shadow:var(--shadow-3); display:flex; flex-direction:column;
  /* visibility, not just the transform: `inert` is what removes the closed drawer
     from the tab order, and where inert is unsupported an off-screen panel stays
     fully tabbable. This covers it with no script — and it is IN the transition
     list, so the slide still plays out instead of snapping. */
  translate:var(--slide,100%) 0; visibility:hidden;
  transition:translate .26s var(--ease), visibility .26s var(--ease);
}
html[dir="rtl"] .bag{ --slide:-100%; }
.bag.open{ translate:0 0; visibility:visible; }
.bag-head{
  display:flex; align-items:center; gap:var(--s3); padding:var(--s5) var(--s6) var(--s4);
  border-block-end:1px solid var(--line);
}
.bag-head h2{ font-size:var(--fs-h2); }
.bag-close{
  margin-inline-start:auto; background:none; border:0; cursor:pointer;
  inline-size:40px; block-size:40px; display:grid; place-items:center;
  color:var(--ink-3); border-radius:var(--r-pill); transition:color .14s var(--ease), background-color .14s var(--ease);
}
.bag-close svg{ inline-size:18px; block-size:18px; }
.bag-close:hover{ color:var(--ink); background:var(--surface-2); }
.bag-lines{ flex:1; overflow-y:auto; padding:0 var(--s6); }
.bag-line{ display:flex; gap:var(--s3); padding-block:var(--s4); border-block-end:1px solid var(--line); }
.bag-line:last-child{ border-block-end:0; }
.bag-thumb{
  inline-size:56px; block-size:56px; flex:0 0 auto; border:1px solid var(--line);
  border-radius:var(--r-sm); object-fit:cover; background:var(--surface-2);
}
.bag-thumb.ph{ display:grid; }
.bag-meta{ flex:1; min-inline-size:0; }
.bag-name{ font-size:var(--fs-sm); font-weight:600; line-height:1.35; overflow-wrap:anywhere; }
.bag-name a:hover{ text-decoration:underline; text-underline-offset:3px; }
.bag-opt{ font-size:var(--fs-xs); color:var(--ink-2); margin-block-start:2px; }
.bag-code{ font-family:var(--font-mono); font-size:var(--fs-micro); color:var(--ink-3); margin-block-start:4px; }
.qty{ display:inline-flex; align-items:center; gap:var(--s1); margin-block-start:var(--s2); }
.qty button{
  inline-size:32px; block-size:32px; border:1px solid var(--line-2); background:var(--surface);
  border-radius:var(--r-xs); cursor:pointer; line-height:1; font-weight:600;
  transition:border-color .14s var(--ease), background-color .14s var(--ease);
}
.qty button:hover{ border-color:var(--ink-3); background:var(--surface-2); }
.qty span{ font-family:var(--font-mono); font-size:var(--fs-sm); min-inline-size:32px; text-align:center; }
.bag-foot{
  border-block-start:1px solid var(--line); background:var(--surface);
  padding:var(--s4) var(--s6) calc(var(--s4) + env(safe-area-inset-bottom));
}
.bag-total{
  display:flex; justify-content:space-between; align-items:baseline;
  font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--ink-3); margin-block-end:var(--s3);
}
.bag-total .v{ color:var(--ink); font-weight:600; font-size:1rem; }
.bag-foot .btn{ inline-size:100%; }
.bag-nowa{
  font-size:var(--fs-xs); color:var(--ink-2); text-align:center;
  border:1px dashed var(--line-2); border-radius:var(--r-sm); padding:var(--s3) var(--s4);
}
.bag-nowa strong{ display:block; margin-block-start:4px; color:var(--ink); font-family:var(--font-mono); }
.bag-clear{
  display:block; inline-size:100%; text-align:center; background:none; border:0; cursor:pointer;
  color:var(--ink-3); font-size:var(--fs-xs); padding:var(--s3) 0 0;
  text-decoration:underline; text-underline-offset:3px;
}
.bag-clear:hover{ color:var(--ink); }
.bag-empty{ text-align:center; color:var(--ink-3); padding:var(--s11) var(--s5) var(--s8); font-size:var(--fs-sm); }
.bag-empty .ht-k{
  block-size:44px; max-inline-size:160px; margin-inline:auto; margin-block-end:var(--s4);
  -webkit-mask-image:linear-gradient(#000, transparent); mask-image:linear-gradient(#000, transparent);
}

/* ── footer ──────────────────────────────────────────────────────────────── */
.foot{ margin-block-start:var(--s11); border-block-start:1px solid var(--line); background:var(--paper-sunk); }
.foot-in{
  display:grid; gap:var(--s7) var(--s9); padding-block:var(--s9) var(--s7);
  grid-template-columns:minmax(0,1.3fr) repeat(auto-fit,minmax(180px,1fr));
}
.foot-brand .wordmark{ font-size:1.2rem; }
/* The masthead's mark shrinks to 30px on a phone because it shares a 64px bar with
   the basket and the menu. The footer's column has no such neighbours, so it prints
   the artwork at one size all the way down. */
.foot-brand .wordmark-logo{ block-size:34px; }
.foot-tagline{ margin-block-start:var(--s3); color:var(--ink-3); font-size:var(--fs-sm); max-inline-size:36ch; }
.foot-h{
  font-family:var(--font-mono); font-size:var(--fs-micro); font-weight:500;
  letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3);
  margin-block-end:var(--s4);
}
.foot-list{ display:flex; flex-direction:column; gap:var(--s1); }
.foot-list a, .foot-plain{
  display:flex; align-items:center; gap:var(--s2); min-block-size:32px;
  font-size:var(--fs-sm); color:var(--ink-2);
  transition:color .14s var(--ease);
}
.foot-list a:hover{ color:var(--ink); }
.foot-i{ inline-size:15px; block-size:15px; flex:0 0 auto; color:var(--ink-3); }
.foot-n, .foot-v{ margin-inline-start:auto; font-size:var(--fs-micro); color:var(--ink-3); text-align:end; }
.foot-plain{ align-items:flex-start; }
.foot-plain .foot-i{ margin-block-start:8px; }
.foot-plain .foot-v{ max-inline-size:22ch; line-height:1.45; padding-block:6px; }
.foot-base{
  display:flex; flex-wrap:wrap; gap:var(--s2) var(--s5); align-items:center;
  padding-block:var(--s4) var(--s6); border-block-start:1px solid var(--line);
  color:var(--ink-3); font-size:var(--fs-micro);
}
.foot-link{ color:var(--ink-3); text-decoration:underline; text-underline-offset:3px; }
.foot-link:hover{ color:var(--ink); }
.foot-sig{ margin-inline-start:auto; letter-spacing:.08em; }

/* ── the shop's own pages: header links, and the phone menu ───────────────────
   Two copies of the SAME three links — text in the bar on a desktop, a dropdown
   behind one button on a phone — because the masthead had already run out of room
   at 375px with nothing in it but a basket (see .basket-lbl). Exactly one copy is
   ever on screen: below 1024px the bar's copy and the Categories button are hidden
   and the button appears, above it the button and the dropdown are hidden. */
.mast-nav{ display:flex; align-items:center; gap:var(--s1); margin-inline-start:var(--s2); }
.mast-nav a{
  padding:7px 10px; border-radius:var(--r-sm); font-size:var(--fs-xs);
  color:var(--ink-2); white-space:nowrap;
  transition:color .14s var(--ease), background-color .14s var(--ease);
}
.mast-nav a:hover{ color:var(--ink); background:var(--surface-2); }
.mast-nav a.on{ color:var(--ink); font-weight:600; box-shadow:inset 0 -2px 0 var(--ink-a); }

/* Bigger than the other icon buttons, and deliberately so: on a phone this ONE control
   carries the language switcher, the categories, every page and the phone numbers, so
   it is the most consequential thing in the bar and was drawn the same weight as the
   theme toggle. The gap holds it off the gold basket — two round controls of similar
   size touching at var(--s1) read as one two-part widget. */
.menu-btn{ display:none; inline-size:46px; block-size:46px; margin-inline-start:var(--s3); }
.menu-btn svg{ inline-size:24px; block-size:24px; }
.menu-btn .i-shut{ display:none; }
.menu-btn[aria-expanded="true"]{ color:var(--ink-b-text); background:var(--tint); }
.menu-btn[aria-expanded="true"] .i-open{ display:none; }
.menu-btn[aria-expanded="true"] .i-shut{ display:block; }

/* ── the dim behind the open menu ────────────────────────────────────────────
   The panel is paper-on-paper against the page below it, and on the catalogue that
   page is a grid of bright product tiles — so the eye had to find the panel's edge
   to know where one ended and the other began. A wash under it separates the two at
   a glance.

   Body-level, NOT a child of .mast: the masthead runs `backdrop-filter`, which makes
   it a containing block for fixed-position descendants, so `position:fixed; inset:0`
   inside it would cover the header alone. z-index sits between the page (auto) and
   the masthead (40), which is what keeps the bar and the panel bright while
   everything else goes dark. It is NOT the shared .scrim (z 50) for that same
   reason — that one is meant to cover the header too.

   A real element rather than a ::before on the panel, because a pseudo-element's
   clicks are attributed to its owner: tapping the dim would have counted as tapping
   inside the menu and the panel would never close. */
.menu-back{
  position:fixed; inset:0; z-index:35; background:rgba(10,9,7,.45);
  opacity:0; transition:opacity .18s var(--ease);
}
.menu-back.open{ opacity:1; }
.menu-back[hidden]{ display:none; }

/* ── the same separation, on the dark sheet ──────────────────────────────────
   Dark mode needs a different MECHANISM, not a heavier dose of the light one. The
   wash is near-black and so is the sheet under it, so darkening a dark page moves
   almost nothing — and the panel is painted in --paper, which IS the page colour,
   so its edges had nothing to declare either. Three changes, each doing a job the
   others cannot:

   · the panel LIFTS to --surface, a step above the sheet — the move every card in
     this sheet makes, and the only one that works when the wash cannot go darker;
   · the wash BLURS what is behind it, so the panel's crisp type is the only sharp
     thing on screen — the separation the eye reads first, and the one that survives
     a page whose photographs are brighter than any wash;
   · its edges are drawn in --line-2 (a real control edge, ~3:1) rather than --line,
     which at 12% is invisible over a 68%-black wash.

   Light is deliberately untouched: paper against a dim already separates cleanly. */
html[data-theme="dark"] .menu-pop{
  background:var(--surface);
  border-block-end-color:var(--line-2);
  border-block-start:1px solid var(--line);
}
html[data-theme="dark"] .menu-back{
  background:rgba(0,0,0,.68);
  -webkit-backdrop-filter:blur(3px) saturate(85%);
  backdrop-filter:blur(3px) saturate(85%);
}
/* The pills would otherwise be the panel's own colour and read as holes in it.
   :not(.on) matters — an attribute-qualified rule is (0,2,1) and would otherwise
   outrank .menu-lang.on (0,2,0) and paint the CHOSEN language plain. */
html[data-theme="dark"] .menu-lang:not(.on){ background:var(--surface-2); }
/* The item page's buy bar is fixed at z 45 — above the wash — so it is faded by hand
   rather than by the backdrop, which must stay below the masthead. */
html.menu-open .buybar{ opacity:.25; pointer-events:none; }
.buybar{ transition:opacity .18s var(--ease); }

/* Hangs off the bottom edge of the masthead rather than floating: the bar is already
   sticky and blurred, so a panel flush against it reads as the bar having opened
   rather than as a card that landed on the page.

   It scrolls INSIDE itself rather than growing past the fold: with four groups open
   on a 640px-tall phone in landscape the last row would otherwise sit below the
   viewport with no way to reach it — the page behind is not scroll-locked, but the
   panel is anchored to a sticky bar and moves with it, so scrolling the page never
   brings the panel's own tail into view. */
.menu-pop{
  position:absolute; inset-inline:0; inset-block-start:100%;
  background:var(--paper); border-block-end:1px solid var(--line);
  box-shadow:var(--shadow-2);
  max-block-size:calc(100vh - 64px);
  max-block-size:calc(100dvh - 64px);
  overflow-y:auto; overscroll-behavior:contain;
}
.menu-pop[hidden]{ display:none; }
.menu-in{
  display:flex; flex-direction:column; gap:var(--s5);
  padding-block:var(--s4) calc(var(--s5) + env(safe-area-inset-bottom));
}

/* Three questions, three groups — what language, where to browse, which page — and a
   heading is what stops them reading as one undifferentiated stack of rows. Mono +
   letterspaced, the same label voice the code plates and the footer headings use. */
.menu-sec{ display:flex; flex-direction:column; gap:var(--s2); }
.menu-h{
  font-family:var(--font-mono); font-size:var(--fs-micro); font-weight:500;
  letter-spacing:.12em; text-transform:uppercase; color:var(--ink-3);
  padding-inline:2px;
}

/* The language switcher's phone face: a three-up segmented control, not the bar's
   compact pills. Each cell is a whole tap target and carries the language's own
   name, so nothing depends on recognising a two-letter code. Same marked treatment
   as .chip[aria-pressed] elsewhere in the sheet, so "chosen" looks the same
   everywhere in the shop. */
.menu-langs{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--s2); }
.menu-lang{
  display:grid; place-items:center; text-align:center;
  min-block-size:48px; padding:8px 6px;
  border:1px solid var(--line-2); border-radius:var(--r-sm); background:var(--surface);
  color:var(--ink-2); font-size:var(--fs-xs); font-weight:600; line-height:1.25;
  transition:color .14s var(--ease), border-color .14s var(--ease), background-color .14s var(--ease);
}
.menu-lang:hover{ color:var(--ink); border-color:var(--ink-3); }
.menu-lang.on{
  color:var(--ink-b-text); background:var(--tint);
  border-color:var(--ink-b); box-shadow:inset 0 0 0 1px var(--ink-b);
}

/* Rows, hairline-separated inside their group. The dividers were between EVERY row
   before, including across what are now group boundaries — which is exactly the
   flatness the headings exist to break up. */
.menu-links{ display:flex; flex-direction:column; }
.menu-link{
  display:flex; align-items:center; gap:var(--s3); inline-size:100%;
  padding:11px 10px; min-block-size:48px; text-align:start;
  border:0; border-radius:var(--r-sm); background:transparent; cursor:pointer;
  font:inherit; font-size:var(--fs-sm); color:var(--ink-2);
  transition:color .14s var(--ease), background-color .14s var(--ease);
}
.menu-links > * + *{ box-shadow:0 -1px 0 var(--line); }
.menu-link:hover{ color:var(--ink); background:var(--surface-2); }
.menu-link.on{ color:var(--ink); font-weight:600; background:var(--surface-2); }
.menu-link svg{ inline-size:18px; block-size:18px; flex:0 0 auto; color:var(--ink-3); }
.menu-link.on svg{ color:var(--ink-b); }
/* The trailing fact — a department count, a phone number. Never wraps the row onto a
   second line: it truncates, because the label is what the row is for. */
.menu-n{
  margin-inline-start:auto; font-size:var(--fs-micro); color:var(--ink-3);
  min-inline-size:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

@media (max-width:1023px){
  .mast-nav{ display:none; }
  .dept-btn{ display:none; }          /* moves inside the menu — same dialog, same button */
  /* …and so do the language pills. At 375px the bar cannot carry a wordmark, a basket
     AND three pills: the first one printed underneath the logo artwork, which is a
     control the visitor cannot see, let alone press. */
  .langs{ display:none; }
  .mast-rule{ display:none; }         /* nothing left on its far side to divide */
  /* inline-GRID, not inline-flex: .icon-btn centres its glyph with `place-items`,
     and in a flex container the justify half of that shorthand does nothing — the
     ✕ sat against the inline edge of its circle instead of in the middle of it. */
  .menu-btn{ display:inline-grid; }
}
/* A menu opened on a phone and then widened would otherwise stay hanging under a bar
   whose own links are back — the button that dismisses it is gone at this width. */
@media (min-width:1024px){
  .menu-pop{ display:none; }
}

/* ── static pages: the policy prose, and the message form ────────────────────
   One measure for reading, capped in ch so it holds at any root size and in all
   three scripts. */
.doc{ max-inline-size:68ch; padding-block:var(--s5) var(--s9); }
/* --fs-h3 is the largest size below a heading in this sheet; used here for its SIZE,
   which is what sets a lede apart from the body it introduces. */
.doc-lede{ font-size:var(--fs-h3); color:var(--ink-2); line-height:1.65; }
.doc-sec{ margin-block-start:var(--s7); }
.doc-h{ font-size:var(--fs-h3); margin-block-end:var(--s3); }
.doc-sec p{ color:var(--ink-2); line-height:1.75; }
.doc-act{ margin-block-start:var(--s7); }

.msgform{ display:flex; flex-direction:column; gap:var(--s4); margin-block-start:var(--s6); }
.msg-field{ display:flex; flex-direction:column; gap:6px; }
.msg-lbl{ font-size:var(--fs-xs); font-weight:600; color:var(--ink-2); }
.msg-opt{ font-weight:400; color:var(--ink-3); }
.msg-in{
  inline-size:100%; min-inline-size:0; font:inherit; font-size:var(--fs-sm); color:var(--ink);
  background:var(--surface); border:1px solid var(--line-2); border-radius:var(--r-sm);
  padding:12px 14px; min-block-size:46px;
  transition:border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.msg-in::placeholder{ color:var(--ink-3); }
.msg-in:hover{ border-color:var(--ink-3); }
.msg-in:focus{ outline:none; border-color:var(--ink-b); box-shadow:0 0 0 3px var(--tint); }

/* ── numeric fields in a right-to-left page ──────────────────────────────────
   A phone number and a price must stay LTR or the bidi algorithm reorders their
   groups — but `dir="ltr"` also drags `text-align:start` to the LEFT edge, so the
   field ends up starting on the opposite side from every field above it.
   Aligning to the right in RTL is what puts the box's content back where the
   reader's eye already is, while the digits themselves stay left-to-right.

   The price-range boxes carry `dir="ltr"` for exactly the same reason and had
   exactly the same problem, so they are fixed by the same rule rather than by a
   second one that could later disagree with this. */
html[dir="rtl"] .msg-num,
html[dir="rtl"] .price-in{ text-align:right; }
html[dir="rtl"] .msg-num::placeholder,
html[dir="rtl"] .price-in::placeholder{ text-align:right; }

/* The banner shown when a post came back rejected — throttled, or an empty body. */
.msg-error{
  margin-block:var(--s4) 0; padding:12px 14px; border-radius:var(--r-sm);
  border:1px solid currentColor; color:#B4472E; background:rgba(180,71,46,.08);
  display:flex; flex-direction:column; gap:3px; font-size:var(--fs-sm);
}
html[data-theme="dark"] .msg-error{ color:#E8836A; background:rgba(232,131,106,.10); }
.msg-error strong{ font-weight:650; }
.msg-error span{ color:var(--ink-2); }

/* The confirmation page's headline — the one line the visitor came back to read. */
.doc-sent{ font-size:var(--fs-h3); font-weight:650; margin-block:var(--s5) 0; }
/* resize:vertical only — a textarea dragged sideways breaks the column it sits in */
.msg-area{ resize:vertical; min-block-size:130px; line-height:1.6; }
.msg-act{ display:flex; flex-direction:column; gap:var(--s3); align-items:flex-start; }
.msg-go svg{ inline-size:17px; block-size:17px; flex:0 0 auto; margin-inline-end:7px; vertical-align:-3px; }
.msg-note{ color:var(--ink-3); font-size:var(--fs-xs); line-height:1.6; max-inline-size:52ch; }

/* ── the contact page: the message on one side, the shop's address on the other ──
   The page answers two different questions, and on a desktop the second one stacked
   under a five-field form sits below the fold — on the page a visitor opens to find
   it. Two tracks rather than one, and they stack in SOURCE order below 900px, so the
   tab order is the reading order at every width and nothing is re-ordered by CSS. */
.contact-grid{
  display:grid; align-items:start;
  grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr);
  gap:var(--s8) var(--s10);
  padding-block:var(--s5) var(--s9);
}
/* .doc carries that padding itself when it is the only thing on the page. */
.contact-grid .doc{ padding-block:0; }

/* Capped rather than left to fill its track: stacked on a tablet the card would run
   the whole 900px and stop looking like the panel it is. On a desktop the track is
   narrower than this anyway, so one rule covers both without a breakpoint. */
.visit{ max-inline-size:34rem; }
.visit-h{ font-size:var(--fs-h3); margin-block-end:var(--s4); }

.visit-card{
  border:1px solid var(--line-2); border-radius:var(--r);
  background:var(--surface); overflow:hidden;
}

/* ── the map well ──────────────────────────────────────────────────────────────
   The frame mechanics BOTH maps share — the contact card's and the front page's
   closing band. Only their geometry differs, so only the geometry is written twice.
   The well is --surface-2 rather than nothing, so the space held open before a lazy
   iframe arrives reads as a plate rather than a hole. */
.mapwell{ position:relative; background:var(--surface-2); }
.mapwell iframe{ position:absolute; inset:0; inline-size:100%; block-size:100%; border:0; display:block; }
/* Google serves the same daylight map on both sheets, and a full-brightness one is a
   floodlit rectangle on the dark page. A DIM, not a re-theme: inverting a map turns
   water into land and moves the pin's colour off Google's own legend. */
html[data-theme="dark"] .mapwell iframe{ filter:brightness(.86) contrast(1.04); }

/* The map bleeds to the card's edges — framed inside a padded card it reads as a
   screenshot OF a map. aspect-ratio reserves the box before the iframe loads, so the
   address and the buttons under it never jump. */
.visit-map{ aspect-ratio:4/3; min-block-size:220px; border-block-end:1px solid var(--line); }

.visit-body{ padding:var(--s5); display:flex; flex-direction:column; gap:var(--s4); }
.visit-addr{ display:flex; gap:var(--s2); align-items:flex-start; font-size:var(--fs-sm); color:var(--ink-2); line-height:1.55; }
.visit-addr svg{ inline-size:16px; block-size:16px; flex:0 0 auto; margin-block-start:3px; color:var(--ink-3); }

/* The plus code is the string a visitor retypes into Maps or reads out to a driver, so
   it is set the way the item codes are — monospace on its own quiet plate, copyable.
   It deliberately does NOT wear the imprint's ink strip: that stays the one plate on
   the site that means "this is the product's identity". */
.visit-plus{
  display:flex; align-items:center; gap:var(--s3);
  padding-block:8px; padding-inline:var(--s4) var(--s2);
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--r-sm);
}
.visit-plus-txt{ display:flex; flex-direction:column; gap:2px; min-inline-size:0; flex:1 1 auto; }
/* NOT overflow-wrap:anywhere, for the same reason the imprint refuses it — a code split
   across two lines is a code transcribed wrong. It may break at its own space only. */
.visit-code{ font-size:1.02rem; font-weight:600; color:var(--ink); overflow-wrap:normal; word-break:keep-all; }
/* Same trap as .msg-num: the code and the number carry dir="ltr" so bidi cannot reorder
   their halves, but dir also drags `start` to the LEFT — leaving the value flush left
   under a label that is flush right. Pulling them back to the right in RTL is what puts
   the pair back on the edge the reader's eye is already on. */
html[dir="rtl"] .visit-code,
html[dir="rtl"] .reach-v{ text-align:right; }
.visit-copy{
  flex:0 0 auto; background:none; border:0; cursor:pointer;
  padding:10px 12px; border-radius:var(--r-xs); min-block-size:44px;
  font-family:var(--font-mono); font-size:var(--fs-micro); font-weight:650;
  letter-spacing:.06em; text-transform:uppercase; color:var(--ink-b-text);
  transition:background-color .14s var(--ease);
}
.visit-copy:hover{ background:var(--tint); }
/* Tracking is a Latin luxury — letter-spacing breaks Arabic-script joining. */
html[dir="rtl"] .visit-copy{ letter-spacing:0; }

/* One decisive action, full width, and the listing underneath it as a link rather than
   a second button — two buttons side by side would make the visitor choose between two
   things that both say "Google Maps". */
.visit-act{ display:flex; flex-direction:column; align-items:flex-start; gap:var(--s2); }
.visit-act .btn{ inline-size:100%; }
.visit-go svg{ inline-size:17px; block-size:17px; flex:0 0 auto; }
.visit-link{
  display:inline-flex; align-items:center; min-block-size:44px;
  font-size:var(--fs-sm); font-weight:600; color:var(--ink-b-text);
  text-decoration:underline; text-underline-offset:3px;
  transition:color .14s var(--ease);
}
.visit-link:hover{ color:var(--ink); }

.visit-reach{ display:flex; flex-direction:column; gap:var(--s2); margin-block-start:var(--s4); }
.reach{
  display:flex; align-items:center; gap:var(--s3);
  padding-block:9px; padding-inline:var(--s4); min-block-size:56px;
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--surface); color:var(--ink);
  transition:border-color .14s var(--ease), background-color .14s var(--ease);
}
.reach:hover{ border-color:var(--ink-3); background:var(--surface-2); }
.reach svg{ inline-size:18px; block-size:18px; flex:0 0 auto; color:var(--ink-3); }
/* WhatsApp's own green, and it is allowed here where the button label was not: this is
   a decorative glyph beside a text label, so it answers to the 3:1 non-text bar (3.09:1)
   rather than the 4.5:1 the .btn-wa label had to clear. */
.reach-wa svg{ color:var(--wa); }
.reach-t{ display:flex; flex-direction:column; gap:1px; min-inline-size:0; }
.reach-k{ font-size:var(--fs-micro); color:var(--ink-3); }
.reach-v{ font-size:var(--fs-sm); font-weight:600; }

/* ── the front page's closing band ───────────────────────────────────────────────
   The directory ends on the one question a wall of departments cannot answer. It is
   composed as the page's own BOOKEND: the front page opens on a card floating over a
   picture (the hero's search box) and now closes on a card floating over a map — the
   same gesture twice, so the two frame the page instead of reading as two unrelated
   components that happen to share a stylesheet.

   Same surface as the footer, and the footer's top margin is cancelled underneath it,
   so the page ends in ONE calm field rather than grey band / white stripe / grey
   footer. A browser without :has() simply keeps the stripe — the old spacing, not a
   broken one; same posture as the :has() rule the category rail already relies on. */
.findus{ background:var(--paper-sunk); padding-block:var(--s10); }
main:has(.findus) + .foot{ margin-block-start:0; }

/* Phone first: the map, then the card lifted over its bottom edge. The map is NOT a
   positioned element, so paint order is source order and the card is on top without
   a z-index fight — the trap the hero's search card had to be rescued from. */
.findus-stage{ display:grid; }
.findus-map{
  aspect-ratio:16/10; min-block-size:230px;
  border:1px solid var(--line-2); border-radius:var(--r-lg); overflow:hidden;
}
.findus-card{
  position:relative; z-index:1;
  margin-block-start:calc(var(--s7) * -1); margin-inline:var(--s4);
  padding:var(--s6);
  background:var(--surface); border:1px solid var(--line-2); border-radius:var(--r-lg);
  box-shadow:var(--shadow-3);
  display:flex; flex-direction:column; align-items:flex-start; gap:var(--s4);
}
/* The eyebrow is the real heading — it is what belongs in the document outline — while
   the NAME carries the display type, because what this card IS is an address label: who,
   where, and the code that finds the door. They are paired in their own box so the
   greeting hugs the name instead of sitting a full card-gap away from it. */
.findus-top{ display:flex; flex-direction:column; gap:var(--s1); }
.findus-name{ font-size:var(--fs-h1); }
.findus-addr{ font-size:var(--fs-h3); color:var(--ink-2); line-height:1.5; }
.findus-card .visit-plus{ align-self:stretch; }
.findus-card .visit-act{ align-self:stretch; }

@media (min-width:1024px){
  /* The overlap is GRID, not absolute positioning: both children sit in row 1, the map
     spanning both tracks and the card in the first one. Grid resolves "first track" per
     writing direction, so the card floats over the leading edge in English and over the
     other one in Arabic and Kurdish with nothing written twice.
     Only above 1024px, because below it track 1 is too narrow to hold a display heading
     and a code plate, and a cramped floating card is worse than an honest stack. */
  .findus-stage{ grid-template-columns:minmax(300px,.40fr) minmax(0,.60fr); align-items:center; }
  .findus-map{
    grid-row:1; grid-column:1 / -1;
    aspect-ratio:auto; align-self:stretch; min-block-size:430px;
  }
  .findus-card{
    grid-row:1; grid-column:1;
    margin:0; margin-inline-start:var(--s7); padding:var(--s7);
  }
  /* Room for a row here, and a row is what keeps the primary action from looking like a
     banner across a card that is already the width of a card. */
  .findus-card .visit-act{ flex-direction:row; flex-wrap:wrap; align-items:center; gap:var(--s3) var(--s5); }
  .findus-card .visit-act .btn{ inline-size:auto; }
}

/* ── small screens ───────────────────────────────────────────────────────── */
@media (max-width:900px){
  .detail{ grid-template-columns:1fr; gap:var(--s6); }
  .detail-media{ position:static; }

  /* One column, form first — source order, so nothing about the tab order changes. */
  .contact-grid{ grid-template-columns:1fr; gap:var(--s8); }
  /* Full width, a 4:3 map is a very tall block; the shallower crop keeps the address
     and the Directions button inside the same screen as the pin. */
  .visit-map{ aspect-ratio:16/10; }
}

@media (max-width:640px){
  .mast-in{ gap:var(--s1); min-block-size:58px; }
  .wordmark{ font-size:1.1rem; }
  .mast-rule{ display:none; }
  /* …except the menu button, which keeps its 46px: it is the only way to the languages,
     the categories and the pages at this width. */
  .icon-btn:not(.menu-btn){ inline-size:38px; block-size:38px; }
  .basket-btn{ padding-inline:12px; margin-inline-start:0; }

  /* THE WHOLE PICTURE, NOT A CENTRE CROP (owner, 2026-09-01). A banner is a designed
     landscape graphic — the live set is 1920×799, so 2.4:1 — and this was a 339×240
     (1.41:1) crop window with `object-fit:cover` over it, so a phone threw away 41% of the
     width: the client's own screenshot showed the middle of the montage with both ends
     missing. A hero that quietly deletes the artwork the shop had made is the one thing
     this band must not do.

     So on a phone the PICTURE SIZES THE BOX instead of being cut to fit one. Three things
     move together for that, and each is load-bearing:

       1. the slides come back into FLOW (one grid cell, all of them stacked in it) — an
          absolutely positioned slide contributes no height, so the stage would collapse;
       2. the search card stops overlapping the picture — 20px of overlap costs an eighth of
          a picture that is now only ~155px tall, which is the same complaint again; and
       3. the caption and the dots come back down to the bottom edge, since they were only
          pushed up the picture to clear that overlap.

     Nothing changes above this width: a ~2.75:1 box against a 2.4:1 picture trims a little
     off the TOP AND BOTTOM instead of throwing the ends away — which is the version the
     client called perfect — and the floating card has 440px of picture to sit on. */
  .hero{ padding-block:var(--s4) var(--s5); }
  .hero-stage.has-slides{ --find-h:0px; display:flex; flex-direction:column; }

  /* Full bleed. Width is the ONLY thing that makes a 2.4:1 picture bigger on a phone once
     it is no longer being cropped, and the gutter is 36px of it. Same gesture as the section
     strips further down — at this width the shop already runs to the screen edge. */
  .slider{
    block-size:auto; border-radius:0;
    margin-inline:calc(var(--gutter) * -1);
  }
  /* One grid cell with every slide in it, rather than the first slide sizing the stage: the
     box is then as tall as the TALLEST banner, so a set that is not all the same shape still
     never crops, and the height cannot change under the visitor when the slideshow advances.
     `visibility:hidden` (not `display:none`) is what keeps a faded-out slide in the layout
     and makes that true. */
  .slides{ position:static; display:grid; }
  .slide{ position:relative; inset:auto; grid-area:1/1; align-self:center; }
  /* `auto 12/5` is a PLACEHOLDER, not a shape: `auto` means the picture's OWN ratio wins the
     moment it has loaded, and the 12/5 only reserves the box before that, so the categories
     below do not jump when the banner lands. 12/5 because that is what the banners are
     actually cut to (1920×799 = 2.403:1) — the closer the guess, the smaller the settle. */
  .slide > img{
    position:relative; inset:auto;
    block-size:auto; aspect-ratio:auto 12/5; object-fit:contain;
  }

  /* `order:-1` on a flex column, not a second copy of the markup: the card is the last child
     of the stage so that a screen reader and a crawler meet the picture first, and only the
     PAINT order changes here. It now sits ABOVE the picture instead of on it, with a real
     gap — the overlap read as attached when there were 240px of picture to spare, and reads
     as a lid over the artwork when there are 155. */
  .hero-stage.has-slides .hero-find{
    order:-1;
    position:relative; z-index:5; inset:auto;
    inline-size:auto; max-inline-size:none;
    margin-block-end:var(--s3); margin-inline:var(--s3);
    padding:var(--s2);
  }
  /* At a coarse pointer each dot carries 8px of invisible hit area above and below its 9px
     paint, so the caption has to clear the dots' BOX and not just their ink. Both sit low on
     the picture again now that nothing is covering its top. */
  .slide-cap{ padding-block-end:var(--s8); }
  .sl-dots{ inset-block-end:var(--s2); }
  .sl-arrow{ inline-size:34px; block-size:34px; }

  .doors-row{ grid-template-columns:repeat(auto-fill,minmax(140px,1fr)); gap:var(--s2); }
  .door{ padding:var(--s3); gap:var(--s2); }
  .door-art{ padding:var(--s2); }
  .door-art .plate-ch{ font-size:2rem; }
  .door-name{ font-size:var(--fs-sm); }
  .door-subs-w{ display:none; }      /* the chevron and the number carry it at this width */
  .side{ padding-block:var(--s5) 0; gap:var(--s4); }

  /* The strips run to the very edge of the screen: a card sliced by the viewport edge is
     what says "there is more this way". A strip that stops inside the gutter reads as a
     finished row, and then nobody scrolls it. The gutter comes back as padding so the
     first card still lines up with the headings above it. */
  .seclist, .filter-opts{
    margin-inline:calc(var(--gutter) * -1); padding-inline:var(--gutter);
    scroll-padding-inline:var(--gutter);
  }

  .grid{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s2); }
  .card-body{ padding:var(--s3) var(--s3) 0; }
  .card-code{ margin-inline:calc(var(--s3) * -1); padding-inline:var(--s3); gap:var(--s3); }
  .face-name{ font-size:1.15rem; }

  .imprint-codes{ gap:var(--s3) var(--s6); padding:var(--s4); }
  .imprint-head, .imprint-note{ padding-inline:var(--s4); }

  .actions{ display:none; }              /* the fixed buy bar replaces it */
  .buybar{
    display:flex; gap:var(--s3); align-items:center; position:fixed; z-index:45;
    inset-inline:0; inset-block-end:0; background:var(--surface);
    border-block-start:1px solid var(--line-2);
    box-shadow:0 -8px 28px rgba(0,0,0,.10);
    padding:10px var(--s4) calc(10px + env(safe-area-inset-bottom));
  }
  .buybar-code{ font-family:var(--font-mono); min-inline-size:0; flex:1; }
  .buybar-code .k{ color:var(--ink-3); font-size:.6rem; letter-spacing:.1em; }
  .buybar-code .v{ font-weight:600; font-size:var(--fs-sm); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .buybar .btn{ flex:0 0 auto; padding:13px 20px; }
  .page-item{ padding-block-end:96px; }  /* room for the buy bar */

  .foot-in{ grid-template-columns:1fr; gap:var(--s6); padding-block:var(--s7) var(--s5); }
  .foot{ margin-block-start:var(--s8); }

  .bag{
    inset-block:auto 0; inline-size:100%;
    max-block-size:88vh;      /* pre-dvh engines */
    max-block-size:88dvh;
    border-inline-start:0; border-block-start:1px solid var(--line);
    border-start-start-radius:var(--r-lg); border-start-end-radius:var(--r-lg);
    translate:0 100%;
  }
  /* NOTE: no html[dir="rtl"] .bag override here — the base .bag above already
     slides on the block axis, and an attribute-qualified rule would outrank
     .bag.open (0-2-1 vs 0-2-0) and pin the RTL drawer shut. */
  .bag.open{ translate:0 0; visibility:visible; }
  .bag-lines, .bag-head, .bag-foot{ padding-inline:var(--s4); }
}

/* Below this the masthead cannot hold the controls, a wordmark AND the word
   "Basket" on one line — so the button keeps only its count. Visually hidden
   rather than display:none, or the button would lose its accessible name. */
@media (max-width:430px){
  .basket-lbl{
    position:absolute; inline-size:1px; block-size:1px; padding:0; margin:-1px;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
  }
  .basket-btn{ padding-inline:11px; }
}

/* the fixed buy bar must not permanently cover the footer's last line */
@media (max-width:640px){
  body:has(.page-item) .foot-base{ padding-block-end:104px; }
}

/* engines without the standalone translate property would show the drawer
   permanently — fall back to a plain show/hide with no slide */
@supports not (translate: 0){
  .bag{ display:none; }
  .bag.open{ display:flex; }
}

/* ── touch targets ───────────────────────────────────────────────────────────
   The compact controls (language pills 34px, breadcrumb links 32px, section rows
   38px, icon buttons 40px) are comfortable with a mouse and clear WCAG 2.2 AA's
   24px bar — but a finger wants 44. Raised HERE rather than everywhere, because
   a 44px language pill in a 375px masthead costs width the masthead does not
   have to give; block-size is free, and pointer:coarse is exactly the population
   that needs it. */
@media (pointer:coarse){
  .langs a, .crumb a, .facet, .facet-clear, .secitem, .side-back,
  .icon-btn, .dept-btn, .foot-list a, .foot-plain{ min-block-size:44px; }
  .langs a{ display:inline-grid; place-items:center; }
  .crumb a{ align-items:center; }
}

/* ── motion discipline ───────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  *{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; }
}

/* ── the dinar price on the item page ────────────────────────────────────────
   Printway publishes a price on the products it has one for, while every other
   product still shows only its encoded code. So this block appears and vanishes
   between products, and it has to look deliberate in both states: it sits on its
   own line above the imprint with real weight, rather than being squeezed onto
   the code plate where it would read as a third code. */
.pricebox{
  display:flex; align-items:baseline; gap:7px;
  padding-block:var(--s5) 0;
}
.pricebox[hidden]{ display:none; }
.pricebox .v{
  font-size:clamp(1.5rem,3vw,2rem); font-weight:600; color:var(--ink);
  font-variant-numeric:tabular-nums; line-height:1;
}
.pricebox .cur{
  font-size:var(--fs-sm); font-weight:500; color:var(--ink-3); letter-spacing:.06em;
}
