/* Omnisio Shop — one stylesheet for shop.html, checkout.html and order.html.
   It was inline in shop.html while the shop was a single page. Two more pages
   now share these tokens, and three copies of a palette is how three pages come
   to disagree about what --primary is. Nothing below was restyled on the way
   out: the block is the same rules, plus the new ones at the end. */

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* 🔴 `hidden` MUST WIN. The UA sheet's `[hidden]{display:none}` is a type-less
   selector, so ANY class that sets `display` beats it — and this stylesheet is
   full of them. Measured on the live pages before this rule existed:

     #hygiene-consent   hidden=true, .consent{display:flex}  → 241×148 px, VISIBLE
     #checkout-grid     hidden=true, .checkout-grid{display:grid} → 3357 px tall

   The first is the one that matters: every shopper, on every order, was shown
   "opening the seal on a hygiene item ends the 14-day right of withdrawal" — a
   statement about their withdrawal rights, on a cart that contained no such
   item. The second put the whole address form under the "your basket is empty"
   message.

   Two earlier patches (`.overlay[hidden]`, `.drawer[hidden]` further down) fixed
   this per-component and were left behind three times. `!important` is correct
   here and nowhere else: `hidden` is an assertion that the element is not
   relevant, not a style preference, and a rule a later `display:` can silently
   defeat is how this bug came back twice. */
[hidden]{display:none!important}
:focus-visible{outline:2px solid var(--primary);outline-offset:2px;border-radius:6px}
:root{
  /* Verbatim from index.html:22-42 — the shop extends the live palette, it does not invent one. */
  --bg:#0A0A0F; --card-bg:#12121A; --primary:#00E5CC; --primary-dark:#00B3A0;
  --gold:#C9A84C; --gold-dark:#B8941F; --white:#FFFFFF;
  --text-secondary:#94A3B8; --text-muted:#889AAE; --border:rgba(255,255,255,0.08);
  --success:#22C55E; --warning:#F59E0B; --error:#EF4444;
  /* ONE ACCENT. --cta-gradient keeps its name so every call site still reads,
     but it is a flat colour now, exactly as on the landing: a two-stop teal
     ramp on the one control a shopper is meant to press adds a second hue to a
     page whose whole emphasis system is the single teal. */
  --cta-gradient:var(--primary);
  --gold-gradient:linear-gradient(135deg,#C9A84C,#B8941F);

  /* FACES. Body moves off Inter for the reason the landing moved off it. The
     stack keeps Inter as the SECOND name rather than dropping it, because
     checkout.html, order.html and account.html share this stylesheet and still
     request Inter in their own <head>: a stack that named only Jakarta would
     drop those three pages to a system face the day this line changed. They
     each need the same link swap; until then this chain is what keeps every
     page on a drawn face. */
  --font-head:'Space Grotesk',ui-sans-serif,system-ui,sans-serif;
  --font-body:'Plus Jakarta Sans','Inter',ui-sans-serif,system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --radius-card:20px; --radius-btn:14px;
  /* Five derived tokens. No new hues. */
  --surface-raised:#171722; --border-strong:rgba(255,255,255,0.16);
  --accent-soft:rgba(0,229,204,0.10); --radius-input:12px; --radius-chip:999px;

  /* FLUID TYPE, one scale, interpolating on viewport width between a phone
     floor and a desktop ceiling. The shop is four languages and Russian runs
     15-20% longer than English on this content, so a size chosen while looking
     at English becomes a wrapped card name at a width nobody screenshots.
     Same steps as the landing, so the two pages are one voice. */
  --t-mono:clamp(.64rem,.6rem + .12vw,.72rem);
  --t-xs:clamp(.76rem,.73rem + .12vw,.83rem);
  --t-sm:clamp(.84rem,.81rem + .14vw,.92rem);
  --t-base:clamp(.94rem,.9rem + .2vw,1.03rem);
  --t-lead:clamp(1.02rem,.96rem + .3vw,1.18rem);
  --t-h3:clamp(1.06rem,.98rem + .38vw,1.28rem);
  --t-h2:clamp(1.65rem,1.32rem + 1.5vw,2.55rem);
  --t-h1:clamp(2.05rem,1.55rem + 2.4vw,3.5rem);
}
html{scroll-behavior:smooth;font-size:16px}
body{background:var(--bg);color:var(--white);font-family:var(--font-body);font-size:var(--t-base);line-height:1.6;-webkit-font-smoothing:antialiased;overflow-x:hidden}
img{max-width:100%;display:block}
a{color:var(--primary);text-decoration:none;transition:opacity .12s ease-out}
a:hover{opacity:.8}
::-webkit-scrollbar{width:4px}
::-webkit-scrollbar-track{background:var(--bg)}
::-webkit-scrollbar-thumb{background:var(--primary);border-radius:2px}
.skip-nav{position:absolute;left:-9999px}
.skip-nav:focus{position:fixed;left:16px;top:16px;padding:12px 20px;background:var(--primary);color:#000;z-index:9999;border-radius:8px;font-weight:600}

/* ---- type scale ----
   Every step below is a token, and there is no per-breakpoint font-size
   override anywhere in this file: the clamp already handles size, so a media
   query only ever changes how many columns a thing occupies. Weights are 700
   at the top rather than 800 because Space Grotesk stops at 700 — asking for
   800 got a synthesised face, not a heavier one. */
h1,h2,h3{font-family:var(--font-head);letter-spacing:-.02em;line-height:1.1}
h1{font-weight:700;font-size:var(--t-h1);line-height:1.05;text-wrap:balance}
h2{font-weight:700;font-size:var(--t-h2);text-wrap:balance}
h3{font-weight:600;font-size:var(--t-h3);letter-spacing:-.01em;line-height:1.2}
.eyebrow{font-family:var(--font-mono);font-weight:500;font-size:var(--t-mono);letter-spacing:.14em;text-transform:uppercase;color:var(--text-secondary);line-height:1}
/* The measure is in em, not px or ch, and the unit is the point: a token is
   substituted as text, so 36em resolves against the font-size of whichever
   element uses it and stays ~70 characters at 16px AND at 13px. */
.lead{font-size:var(--t-lead);color:var(--text-secondary);max-width:36em}
.prose{max-width:38em;color:var(--text-secondary)}
.meta{font-size:var(--t-xs);color:var(--text-muted)}
/* Legally weighted copy never drops to --text-muted (6.8:1): small type at low
   contrast reads as something we tried to hide. */
.legal{font-size:var(--t-sm);color:var(--text-secondary)}
.legal+.legal{margin-top:10px}
.legal b{color:var(--white);font-weight:600}
.price{font-family:var(--font-head);font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.01em}

/* ---- layout ---- */
.wrap{max-width:1200px;margin:0 auto;padding:0 5%}
section{padding:96px 0}
@media(max-width:1024px){section{padding:72px 0}}
@media(max-width:768px){section{padding:48px 0}}
.rule{border:0;border-top:1px solid var(--border);margin:0}

/* ---- nav ---- */
#navbar{position:fixed;top:0;left:0;right:0;z-index:900;padding:16px 5%;display:flex;align-items:center;justify-content:space-between;gap:16px;transition:background .12s ease-out,padding .12s ease-out}
#navbar.scrolled{background:rgba(10,10,15,.92);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid var(--border);padding:12px 5%}
/* Wordmark verbatim from index.html:59-62. The shop had its own treatment —
   white, 700, tight tracking — and read as a different site sitting on the same
   domain. Only the SHOP suffix is added to it. */
.nav-logo{display:flex;align-items:center;gap:12px;flex-shrink:0}
.nav-logo:hover{opacity:1}
.nav-logo img{width:32px;height:32px;object-fit:contain}
.nav-logo-text{font-family:var(--font-head);font-weight:400;font-size:1.15rem;letter-spacing:.36em;color:var(--primary);text-transform:uppercase;white-space:nowrap}
.nav-logo-text i{font-style:normal;font-weight:400;font-size:var(--t-mono);letter-spacing:.16em;color:var(--text-secondary)}
.nav-right{display:flex;align-items:center;gap:10px}
/* Same weight and colour as the anchors in the landing header (index.html:63).
   The logo already goes home; this is the version a visitor actually reads as
   a way back. */
.nav-back{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-body);font-size:var(--t-sm);font-weight:500;color:var(--text-secondary);min-height:44px;padding:0 4px}
.nav-back:hover{color:var(--white);opacity:1}
.nav-back svg{width:16px;height:16px;flex-shrink:0}
.lang-switcher{display:flex;gap:2px;background:var(--card-bg);border:1px solid var(--border);border-radius:var(--radius-chip);padding:3px}
.lang-btn{background:none;border:0;color:var(--text-secondary);font-family:var(--font-mono);font-size:var(--t-mono);font-weight:500;padding:6px 9px;border-radius:var(--radius-chip);cursor:pointer}
.lang-btn.active{background:var(--primary);color:#000}
/* At 375px the wordmark plus four language buttons plus a labelled back link
   crowd the right edge. */
@media(max-width:640px){
  /* Taken out of sight, not out of the accessibility tree: display:none on the
     only text in this control leaves an unnamed icon button. */
  .nav-back-label{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
  /* With its label out of sight the chevron alone is a 24px-wide target. */
  .nav-back{min-width:44px;justify-content:center;padding:0}
  .lang-btn{padding:6px 7px}
  #navbar{gap:8px}
  .nav-logo-text i{display:none}
}
@media(max-width:430px){.nav-logo-text{display:none}}

/* ---- buttons ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:var(--font-head);font-weight:700;font-size:var(--t-sm);letter-spacing:.04em;text-transform:uppercase;padding:15px 28px;border-radius:var(--radius-btn);border:1px solid transparent;cursor:pointer;min-height:52px;white-space:normal;text-align:center;transition:transform .12s ease-out,border-color .12s ease-out}
/* Never white on the CTA gradient: that measures 2.5:1. Black measures 8.5:1. */
.btn-primary{background:var(--cta-gradient);color:#000}
.btn-primary:hover{transform:translateY(-1px);opacity:1}
.btn-secondary{background:transparent;color:var(--white);border-color:var(--border-strong)}
.btn-secondary:hover{border-color:var(--primary);opacity:1}
.btn:disabled{opacity:.45;cursor:not-allowed;transform:none}
/* Turkish uppercases i to İ, so "Horizon" becomes "HORİZON". */
.btn-brandcase{text-transform:none;letter-spacing:.01em}

/* ---- chips & notices ---- */
.chip{display:inline-flex;align-items:center;font-family:var(--font-mono);font-size:var(--t-mono);font-weight:500;letter-spacing:.07em;text-transform:uppercase;border:1px solid var(--border-strong);border-radius:var(--radius-chip);padding:4px 10px;line-height:1.4;align-self:flex-start}
.chip-strap{color:#FCD9A0;border-color:rgba(245,158,11,.5);background:rgba(245,158,11,.10)}
.notice{border:1px solid var(--border-strong);background:var(--card-bg);border-radius:var(--radius-card);padding:24px 28px;display:flex;gap:20px;align-items:flex-start}
.notice-accent{border-left:3px solid var(--warning)}
.notice h3{font-size:var(--t-h3);margin-bottom:10px}
.notice-icon{font-family:var(--font-mono);font-size:var(--t-mono);letter-spacing:.1em;color:var(--warning);padding-top:4px;flex-shrink:0}
@media(max-width:600px){.notice{flex-direction:column;gap:12px;padding:20px}}

/* A .todo style lived here and dressed seven internal Turkish authoring markers
   that every visitor read, untranslated, in all four languages. There is no
   visitor-facing way to say "we have not filled this in yet", so the style is
   gone with them: a gap is either stated in the copy or the element does not
   ship. */
/* Where a price would be. It occupies the price line rather than sitting beside
   it, so nothing on a card can be mistaken for a figure — and it stays quiet:
   a warning colour here would read as an error rather than a state. */
.price-soon{font-family:var(--font-mono);font-size:var(--t-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);align-self:flex-start}

/* ---- hero ---- */
#hero{padding-top:132px;padding-bottom:72px;min-height:70vh;display:flex;align-items:center}
.hero-grid{display:grid;grid-template-columns:1fr 44%;gap:56px;align-items:center;width:100%}
.hero-copy h1{margin:18px 0 20px}
.hero-actions{display:flex;align-items:center;gap:24px;flex-wrap:wrap;margin-top:32px}
.hero-actions .textlink{font-family:var(--font-head);font-weight:600;font-size:var(--t-sm)}
.hero-media img{width:100%;height:auto;border-radius:var(--radius-card)}
.hero-caption{margin-top:14px;font-size:var(--t-xs);color:var(--text-muted);text-align:center;max-width:38ch;margin-left:auto;margin-right:auto}
@media(max-width:900px){
  #hero{padding-top:104px;padding-bottom:48px;min-height:0}
  .hero-grid{grid-template-columns:1fr;gap:28px}
  .hero-media{order:-1;max-width:340px;margin:0 auto}
}
/* The next section has to be visible on the first screen. A hero taller than
   the viewport is the mistake this store is built to avoid. */
@media(max-width:600px){
  #hero{padding-top:88px;padding-bottom:28px}
  .hero-grid{gap:18px}
  .hero-media{max-width:190px}
  .hero-caption{margin-top:10px}
  /* The per-breakpoint h1 override that stood here is GONE, and that is the
     point of the fluid scale rather than an oversight. It existed because
     Russian headlines run 15-30% longer and a fixed desktop size pushed the
     first product a screen down; --t-h1 interpolates on viewport width and has
     no such cliff, so a second size at a second breakpoint is now two sources
     of truth for one decision. Nothing in this file overrides a font-size in a
     media query any more - keep it that way. */
}

/* ---- membership ---- */
.plans{display:grid;grid-template-columns:repeat(2,1fr);gap:24px;align-items:stretch;margin-top:40px}
@media(max-width:900px){.plans{grid-template-columns:1fr;margin-top:32px}}
.plan{position:relative;background:var(--card-bg);border:1px solid var(--border);border-radius:var(--radius-card);padding:32px;display:flex;flex-direction:column}
.plan-featured{border-color:rgba(0,229,204,.2);background:linear-gradient(180deg,var(--accent-soft),transparent 40%),var(--card-bg)}
.plan-badge{position:absolute;top:-13px;left:50%;transform:translateX(-50%);background:var(--primary);color:#000;font-family:var(--font-mono);font-size:var(--t-mono);font-weight:500;letter-spacing:.1em;text-transform:uppercase;padding:5px 14px;border-radius:var(--radius-chip);white-space:nowrap}
/* Both cards must show the price on the same line, so the promise reserves the
   same height in every language rather than pushing one card's price down. */
.plan-promise{color:var(--text-secondary);margin-top:8px;font-size:var(--t-sm);min-height:4.5em}
@media(max-width:900px){.plan-promise{min-height:0}}
.plan-price{margin-top:24px;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.plan-price .price{font-size:clamp(1.85rem,1.5rem + 1.5vw,2.35rem)}
.plan-price .per{color:var(--text-secondary);font-size:var(--t-base)}
.plan-monthly{font-size:var(--t-sm);color:var(--text-muted);margin-top:4px}
.plan-note{font-size:var(--t-xs);color:var(--text-muted);margin-top:10px;max-width:44ch}
.plan-list{list-style:none;margin:24px 0;border-top:1px solid var(--border);padding-top:20px;display:flex;flex-direction:column;gap:10px;flex-grow:1}
.plan-list li{font-size:var(--t-base);padding-left:18px;position:relative;color:var(--text-secondary)}
.plan-list li::before{content:"";position:absolute;left:0;top:.62em;width:5px;height:5px;border-radius:50%;background:var(--primary)}
.plan .btn{width:100%}
.plan-disclaimer{font-size:var(--t-mono);color:var(--text-muted);margin-top:12px;text-align:center}
/* The Family card spans both columns instead of taking a third one: it is not a
   third tier, it is Horizon bought for several people, and a third column would
   read as a third product to choose between. */
.plan-wide{grid-column:1/-1}
/* Not `.state-badge` — that one is absolutely positioned onto a photograph and
   would land on top of this card's heading. Here it sits in the flow, above the
   button, where the two written cards carry their App Store note. */
.plan-state{align-self:flex-start;margin-bottom:14px}
.swatches{display:flex;gap:10px;align-items:center;margin-top:12px;flex-wrap:wrap}
.swatch{width:44px;height:44px;border-radius:var(--radius-chip);border:1px solid var(--border-strong);background:none;cursor:pointer;position:relative;padding:0}
/* A flat near-black fill disappears against --card-bg and reads as an empty
   ring, so the black finish is drawn with the same highlight a real anodised
   surface has. */
.swatch i{position:absolute;inset:6px;border-radius:var(--radius-chip);box-shadow:inset 0 1px 0 rgba(255,255,255,.14)}
.swatch[aria-checked="true"]{border-color:var(--primary);box-shadow:0 0 0 1px var(--primary)}
.swatch-label{font-size:var(--t-sm);color:var(--text-secondary)}
fieldset{border:0;margin-top:24px}
legend{font-family:var(--font-mono);font-size:var(--t-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--text-secondary);padding:0}

/* ---- included panel ---- */
.included{background:var(--card-bg);border:1px solid var(--border);border-radius:var(--radius-card);padding:32px;margin-top:24px}
.included h3{margin-bottom:16px}
.incl-row{display:flex;align-items:center;gap:16px;min-height:56px;border-top:1px solid var(--border);padding:8px 0}
.incl-row:first-of-type{border-top:0}
.incl-name{font-weight:500;flex:0 0 auto;min-width:160px}
.incl-meta{color:var(--text-muted);font-size:var(--t-sm);flex:1}
.incl-val{color:var(--primary);font-family:var(--font-mono);font-size:var(--t-mono);letter-spacing:.08em;text-transform:uppercase}
.incl-foot{margin-top:20px;padding-top:20px;border-top:1px solid var(--border);color:var(--text-secondary);font-size:var(--t-sm)}
@media(max-width:600px){.included{padding:22px}.incl-row{flex-wrap:wrap;gap:2px 12px;padding:12px 0}.incl-name{min-width:0;flex:1 0 100%}}

/* ---- collection ---- */
.family{margin-top:56px}
.family-head{border-top:1px solid var(--border);padding-top:24px;margin-bottom:24px}
.family-head h3{margin-bottom:8px}
.family-head p{color:var(--text-secondary);max-width:68ch}
.grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px}
@media(max-width:1024px){.grid{grid-template-columns:repeat(3,1fr)}}
/* minmax(0,…) not 1fr: a bare 1fr floors at min-content, and the Russian card
   names ("Серебристое — зарезервировано") do not break, so the column widened
   past the viewport and pushed the page 44px sideways at 320px. */
@media(max-width:768px){.grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}}
.card{background:var(--card-bg);border:1px solid var(--border);border-radius:var(--radius-card);overflow:hidden;text-align:left;cursor:pointer;color:inherit;font:inherit;padding:0;display:flex;flex-direction:column;transition:border-color .12s ease-out,transform .12s ease-out}
.card:hover{border-color:var(--border-strong);transform:translateY(-2px)}
.card-media{aspect-ratio:1/1;background:var(--bg);position:relative}
.card-media img{width:100%;height:100%;object-fit:cover}
.card-body{padding:16px 18px 20px;display:flex;flex-direction:column;gap:6px;flex-grow:1}
.card-name{font-family:var(--font-head);font-weight:600;font-size:var(--t-lead);letter-spacing:-.01em}
.card-meta{font-size:var(--t-xs);color:var(--text-muted)}
.card-price{font-family:var(--font-head);font-weight:600;font-variant-numeric:tabular-nums;font-size:var(--t-base)}
.card-chip{margin-top:4px}
@media(max-width:768px){.card-body{padding:14px 14px 16px}.card-name{font-size:var(--t-sm)}}

/* Reserved slot: flat outline, no fill, no shadow, no gradient. It has to read
   as an empty frame at a glance, never as a bad photograph. */
.slot{border:1px dashed var(--border-strong);border-radius:var(--radius-card);display:flex;flex-direction:column;color:var(--text-muted)}
.slot-media{aspect-ratio:1/1;display:flex;align-items:center;justify-content:center}
.slot-media svg{width:52%;height:52%;opacity:.32}
.slot-body{padding:0 18px 20px;display:flex;flex-direction:column;gap:6px}
.slot-body .card-name{color:var(--white)}

/* ---- faq ---- */
.faq{margin-top:32px;max-width:68ch}
.faq details{border-bottom:1px solid var(--border)}
.faq summary{cursor:pointer;padding:18px 0;font-family:var(--font-head);font-weight:600;font-size:var(--t-lead);list-style:none;display:flex;justify-content:space-between;gap:16px;min-height:44px;align-items:center}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";color:var(--primary);font-size:1.25rem;flex-shrink:0}
.faq details[open] summary::after{content:"–"}
.faq details p{padding:0 0 20px;color:var(--text-secondary)}

/* ---- footer ---- */
footer{border-top:1px solid var(--border);padding:64px 0 48px;background:var(--card-bg)}
.foot-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}
@media(max-width:900px){.foot-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.foot-grid{grid-template-columns:1fr}}
.foot-grid h4{font-family:var(--font-mono);font-weight:500;font-size:var(--t-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--text-secondary);margin-bottom:14px}
.foot-grid ul{list-style:none;display:flex;flex-direction:column;gap:9px}
.foot-grid a{font-size:var(--t-sm);color:var(--text-secondary)}
.foot-grid a:hover{color:var(--primary);opacity:1}
/* A 19px-tall link in a stacked list is not a touch target. */
@media(max-width:768px){
  .foot-grid ul{gap:0}
  .foot-grid a{display:flex;align-items:center;min-height:44px}
  .hero-actions .textlink{display:inline-flex;align-items:center;min-height:44px}
}
/* PAYMENT MARKS — iyzico's own footer band, unmodified, above the rule that
   .foot-bottom draws. White variant: measured against this palette's --bg
   (#0A0A0F) the coloured band's Visa wordmark and troy mark sit at 1.59:1 and
   1.80:1, under the 3:1 floor for graphical objects; the white one is 19.7:1.
   🔴 Five trademark holders share this one file (iyzico, Mastercard, Visa,
   American Express, troy). Size it and nothing else — no recolour, no crop, no
   overlay, no separating a card mark out of the band. */
.foot-pay{margin-top:40px}
.foot-pay img{display:block;width:456px;max-width:100%;height:auto}
.foot-bottom{border-top:1px solid var(--border);margin-top:40px;padding-top:24px;font-size:var(--t-xs);color:var(--text-muted);max-width:80ch}
.foot-bottom p+p{margin-top:10px}
/* SELLER IDENTITY. Statutory text. It keeps .foot-bottom's inherited colour —
   --text-muted (#889AAE) on --card-bg (#12121A) measures 6.46:1, which already
   clears AA and AAA for body copy, so there is nothing here to correct. Only
   the spacing is set: the four lines are one address block, not four
   paragraphs, and the 10px gap .foot-bottom gives sibling <p>s pulls them
   apart. Do not restyle this into a colour of its own without measuring. */
.foot-seller{margin:14px 0 16px}
.foot-seller p+p{margin-top:4px}

/* ---- modal ---- */
.overlay{position:fixed;inset:0;background:rgba(4,4,8,.74);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);z-index:1000;display:flex;align-items:center;justify-content:center;padding:24px}
.overlay[hidden]{display:none}
.modal{background:var(--card-bg);border:1px solid var(--border-strong);border-radius:var(--radius-card);max-width:960px;width:100%;max-height:90vh;overflow-y:auto;position:relative}
.modal-grid{display:grid;grid-template-columns:1fr 1fr}
@media(max-width:820px){.modal-grid{grid-template-columns:1fr}}
.modal-media{background:var(--bg);padding:20px}
.modal-body{padding:32px}
.modal-close{position:absolute;top:12px;right:12px;background:var(--surface-raised);border:1px solid var(--border-strong);color:var(--white);width:40px;height:40px;border-radius:var(--radius-chip);font-size:1.25rem;line-height:1;cursor:pointer;z-index:2}
@media(max-width:600px){.modal-body{padding:22px}.overlay{padding:0}.modal{border-radius:0;max-height:100vh;min-height:100vh}}
.spec{border-top:1px solid var(--border);margin-top:24px;padding-top:12px}
.spec-row{display:flex;gap:16px;padding:7px 0;font-size:var(--t-sm)}
.spec-row dt{color:var(--text-muted);flex:0 0 36%}
.spec-row dd{color:var(--text-secondary);flex:1}
/* Sizing notes sit under the table, not in it. No rule and no new type scale:
   they are `.meta` paragraphs, and this only spaces them off the last row. */
.spec-notes{margin-top:14px;display:grid;gap:8px}
/* Explicit, not left to margin collapsing: the host div is always in the DOM,
   and a product with no notes must add no space above the buy button. */
.spec-notes:empty{display:none}

/* ═══════════════════════════════════════════════════════════════════════════
   NEW BELOW THIS LINE
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---- 🔴 THE MODAL IMAGE FRAME — defect: "görseller kayıyor" ---------------
   MEASURED before the fix, at 1280×720 with an 800×800 source:
     computed  437 × 800 px      (object-fit: fill → a 1.83× vertical stretch)
     .modal-media   873 px tall inside a modal capped at 646 px
     .modal.scrollTop  0 → 32   synchronously, inside openModal()
     the caption at shop.html "The device is not included" — NOT VISIBLE
   Three causes, one shape of bug: nothing ever gave the image a box.

   1. `<img width="800" height="800">` maps to PRESENTATIONAL HINTS for both
      properties. The old rule set `width:100%` and left `height` alone, so the
      hint survived as a literal `height:800px` that no viewport width could
      talk to. Setting `height` in CSS is what overrides a presentational hint;
      `width:100%` alone never touches it.
   2. An 800px-tall media column inside a 90vh modal overflows it, which is what
      pushed the disclosure caption off the bottom.
   3. An overflowing scroll container plus `.focus()` on the close button = the
      browser scrolls it into view, and the whole modal jumps 32px on open.

   The fix is a FRAME with its own aspect ratio, so the box exists before the
   first byte of the image arrives and never changes afterwards — including for
   the accessory shots, which are not square (charger 800×790, cable 800×616,
   band colours 800×411). `contain` letterboxes them against --bg on purpose:
   a stable letterbox is honest, a stretched product photograph is not. */
/* An option value with no colour to show — a size, a length, a fitting. The old
   picker was a hard-coded S/M · L/XL pair; these are built from the product's
   own `option_types`, so they carry words of unpredictable length in four
   languages and cannot be a 44px circle. */
.optchip{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:0 18px;width:auto;height:auto;border-radius:var(--radius-chip);border:1px solid var(--border-strong);background:none;color:var(--white);font-family:var(--font-body);font-size:var(--t-sm);font-weight:500;cursor:pointer;white-space:nowrap}
.optchip[aria-checked="true"]{border-color:var(--primary);box-shadow:0 0 0 1px var(--primary);color:var(--primary)}

.modal-frame{aspect-ratio:1/1;border-radius:12px;overflow:hidden;background:var(--bg);display:flex;align-items:center;justify-content:center}
.modal-frame img{width:100%;height:100%;object-fit:contain}
/* Belt and braces: any image inside the media column, however it got there,
   sizes from its width and never from a stale height attribute. */
.modal-media img{width:100%;height:auto;border-radius:12px}
.modal-frame img{height:100%}

/* ---- stock state: the card must LOOK depleted before it is read ----------
   The photograph fades; the TEXT does not. Dimming the whole card is the
   obvious move and the wrong one — it takes --text-secondary (#94A3B8 on
   #12121A ≈ 8.0:1) below the 4.5:1 floor and makes the product name harder to
   read exactly when a shopper is scanning for an alternative. So the filter is
   scoped to .card-media, and the state is additionally SAID, in words, in the
   badge and in the price line. Colour is never the only carrier. */
.card[data-stock="sold_out"] .card-media img,
.card[data-stock="sold_out"] .curtain{filter:grayscale(1) brightness(.42) contrast(.92)}
.card[data-stock="sold_out"]{border-color:rgba(255,255,255,.05);background:#0E0E15}
.card[data-stock="sold_out"] .card-price{color:var(--text-secondary)}
.card[data-stock="low_stock"] .card-media img{filter:saturate(.86)}
/* Hover lift is a "you can have this" gesture. A sold-out card keeps its focus
   ring and its click (the modal still explains itself) but drops the lift. */
.card[data-stock="sold_out"]:hover{transform:none;border-color:var(--border-strong)}

/* The state badge sits ON the photograph, not next to "Strap only" in the body:
   two amber chips stacked in the same column read as one noisy block, and the
   state is the thing that must survive a two-second scan. */
.state-badge{position:absolute;top:10px;left:10px;z-index:1;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);max-width:calc(100% - 20px)}
.chip-low{color:#FFDCA8;border-color:rgba(245,158,11,.55);background:rgba(28,20,6,.72)}
.chip-out{color:#D7DEE8;border-color:rgba(255,255,255,.22);background:rgba(10,10,15,.78)}
.chip-soon{color:#7DF5E4;border-color:rgba(0,229,204,.42);background:rgba(4,22,21,.78)}
/* Long in Russian ("Распродано — может вернуться"), so it wraps rather than
   running off the tile. */
.state-badge.chip{white-space:normal;text-align:left;line-height:1.3}

/* ---- the coming-soon curtain --------------------------------------------
   The owner asked for "a curtain about to reveal". Drawn here, in CSS, from the
   two things the brand already owns: an obsidian ground and one teal line. It
   is deliberately NOT a picture — there is no photograph of the next strap, and
   a blurred or AI-rendered stand-in would be a fabricated product shot on a
   page whose whole argument is that it does not fabricate.

   Two panels meet on a lit seam. On hover/focus they part by 7px and the seam
   brightens: enough to say something is behind it, never enough to claim what.
   No date, no name, no spec — see the copy keys, which promise none of the
   three. */
.curtain{position:absolute;inset:0;overflow:hidden;background:
  radial-gradient(120% 80% at 50% 50%, #14141D 0%, #0A0A0F 68%)}
.curtain-panel{position:absolute;top:0;bottom:0;width:calc(50% + 1px);
  background:linear-gradient(100deg,#0C0C13 0%,#15151F 46%,#0D0D14 100%);
  transition:transform .42s cubic-bezier(.22,.61,.36,1)}
/* The fold: a soft vertical shadow so each panel reads as cloth with weight,
   not as two flat rectangles. */
.curtain-panel::after{content:"";position:absolute;top:0;bottom:0;width:38%;
  background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.45) 100%)}
.curtain-l{left:0}
.curtain-l::after{right:0}
.curtain-r{right:0;transform:scaleX(-1)}
.curtain-r::after{right:0}
/* The seam. One line of --primary, the same accent the whole site is built on. */
.curtain-seam{position:absolute;top:12%;bottom:12%;left:50%;width:1px;
  transform:translateX(-.5px);
  background:linear-gradient(180deg,rgba(0,229,204,0) 0%,var(--primary) 50%,rgba(0,229,204,0) 100%);
  box-shadow:0 0 18px 2px rgba(0,229,204,.32);
  transition:opacity .42s ease-out,box-shadow .42s ease-out;opacity:.85}
.card:hover .curtain-l,.card:focus-visible .curtain-l{transform:translateX(-7px)}
.card:hover .curtain-r,.card:focus-visible .curtain-r{transform:scaleX(-1) translateX(-7px)}
.card:hover .curtain-seam,.card:focus-visible .curtain-seam{opacity:1;box-shadow:0 0 26px 4px rgba(0,229,204,.5)}

/* ---- accessory / colour-range imagery -----------------------------------
   The four-strap shots are 800×411 — wide, transparent-backed, cut for a dark
   page. Forced into the square product tile they would sit in a sea of empty
   background, so they get their own full-width band instead, where the aspect
   ratio is the point. */
.colourband{margin-top:32px;border:1px solid var(--border);border-radius:var(--radius-card);background:linear-gradient(180deg,#101019,#0B0B11);overflow:hidden}
.colourband-media{aspect-ratio:1400/719;background:var(--bg)}
.colourband-media img{width:100%;height:100%;object-fit:contain}
.colourband-body{padding:20px 28px 26px;display:flex;flex-wrap:wrap;gap:8px 28px;align-items:baseline}
.colourband-body h3{font-size:var(--t-h3)}
.colourband-body p{color:var(--text-secondary);font-size:var(--t-sm);flex:1 1 24ch;min-width:0}
@media(max-width:600px){.colourband-body{padding:16px 18px 20px}}

/* ---- order drawer (restored with the checkout) --------------------------- */
.nav-order{position:relative;display:inline-flex;align-items:center;gap:8px;background:var(--surface-raised);border:1px solid var(--border-strong);color:var(--white);border-radius:var(--radius-chip);min-height:44px;padding:0 16px;font-family:var(--font-head);font-weight:600;font-size:var(--t-xs);cursor:pointer}
.nav-order:hover{border-color:var(--primary)}
.nav-order-count{font-family:var(--font-mono);font-size:var(--t-mono);background:var(--primary);color:#000;border-radius:var(--radius-chip);min-width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;padding:0 6px}
.nav-order[data-empty="true"] .nav-order-count{display:none}

.drawer{position:fixed;inset:0;z-index:1100;display:flex;justify-content:flex-end}
.drawer[hidden]{display:none}
.drawer-scrim{position:absolute;inset:0;background:rgba(4,4,8,.7);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
.drawer-panel{position:relative;background:var(--card-bg);border-left:1px solid var(--border-strong);width:min(440px,100%);display:flex;flex-direction:column;max-height:100%}
.drawer-head{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px;border-bottom:1px solid var(--border)}
.drawer-head h2{font-size:var(--t-h3)}
.drawer-lines{overflow-y:auto;flex:1;padding:8px 24px}
.drawer-foot{border-top:1px solid var(--border);padding:20px 24px;display:flex;flex-direction:column;gap:12px}
.drawer-empty{color:var(--text-secondary);padding:32px 0}

.line{display:flex;gap:14px;padding:16px 0;border-bottom:1px solid var(--border)}
.line:last-child{border-bottom:0}
.line-media{width:64px;height:64px;flex:0 0 64px;border-radius:12px;overflow:hidden;background:var(--bg)}
.line-media img{width:100%;height:100%;object-fit:cover}
.line-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.line-name{font-family:var(--font-head);font-weight:600;font-size:var(--t-sm)}
.line-opt{font-size:var(--t-xs);color:var(--text-muted)}
.line-price{font-variant-numeric:tabular-nums;font-size:var(--t-sm);color:var(--text-secondary)}
.line-side{display:flex;flex-direction:column;align-items:flex-end;justify-content:space-between;gap:8px}
.qty{display:inline-flex;align-items:center;border:1px solid var(--border-strong);border-radius:var(--radius-chip);overflow:hidden}
.qty button{background:none;border:0;color:var(--white);width:34px;height:34px;font-size:1rem;cursor:pointer;line-height:1}
.qty button:disabled{opacity:.35;cursor:not-allowed}
.qty span{min-width:26px;text-align:center;font-family:var(--font-mono);font-size:var(--t-xs)}
.line-remove{background:none;border:0;color:var(--text-muted);font-size:var(--t-xs);cursor:pointer;text-decoration:underline;padding:4px 0;min-height:32px}
.line-remove:hover{color:var(--white)}
/* A line whose variant the server no longer sells. Never silently dropped —
   the shopper put it there and is owed the sentence. */
.line[data-gone="true"]{opacity:.72}
.line[data-gone="true"] .line-media{filter:grayscale(1) brightness(.5)}

.sumrow{display:flex;justify-content:space-between;gap:16px;font-size:var(--t-sm);color:var(--text-secondary)}
.sumrow b{color:var(--white);font-family:var(--font-head);font-variant-numeric:tabular-nums}
.sumrow-total{font-size:var(--t-lead);padding-top:12px;border-top:1px solid var(--border);color:var(--white)}
.sumrow-total b{font-size:var(--t-h3)}

/* ---- forms (checkout + order lookup) ------------------------------------- */
.form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px 20px}
@media(max-width:600px){.form-grid{grid-template-columns:1fr}}
.field{display:flex;flex-direction:column;gap:6px;min-width:0}
.field-wide{grid-column:1/-1}
.field label{font-size:var(--t-xs);color:var(--text-secondary);font-weight:500}
.field .req{color:var(--primary)}
.field input,.field select,.field textarea{background:var(--bg);border:1px solid var(--border-strong);border-radius:var(--radius-input);color:var(--white);font-family:var(--font-body);font-size:1rem;padding:13px 14px;min-height:50px;width:100%}
.field textarea{min-height:88px;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--primary);outline:none;box-shadow:0 0 0 1px var(--primary)}
.field-hint{font-size:var(--t-mono);color:var(--text-muted)}
.field-error{font-size:var(--t-xs);color:#FFB4AC}
.field[data-invalid="true"] input,.field[data-invalid="true"] select{border-color:var(--error)}

.choice{display:flex;gap:14px;align-items:flex-start;border:1px solid var(--border-strong);border-radius:var(--radius-input);padding:16px 18px;cursor:pointer}
.choice input{margin-top:3px;accent-color:var(--primary);width:18px;height:18px;flex:0 0 auto}
.choice-main{display:flex;flex-direction:column;gap:4px;min-width:0}
.choice-name{font-weight:600}
.choice-meta{font-size:var(--t-xs);color:var(--text-muted)}
.choice-price{margin-left:auto;font-family:var(--font-head);font-weight:600;white-space:nowrap}
.choice:has(input:checked){border-color:var(--primary);background:var(--accent-soft)}

.consent{display:flex;gap:12px;align-items:flex-start;padding:14px 0}
.consent input{margin-top:3px;accent-color:var(--primary);width:18px;height:18px;flex:0 0 auto}
.consent span{font-size:var(--t-sm);color:var(--text-secondary)}

/* ---- checkout page shell ------------------------------------------------- */
.page{padding-top:112px;padding-bottom:80px}
@media(max-width:600px){.page{padding-top:92px;padding-bottom:56px}}
.checkout-grid{display:grid;grid-template-columns:1fr 400px;gap:40px;align-items:start;margin-top:36px}
@media(max-width:980px){.checkout-grid{grid-template-columns:1fr;gap:28px}}
.panel{background:var(--card-bg);border:1px solid var(--border);border-radius:var(--radius-card);padding:28px}
.panel+.panel{margin-top:20px}
.panel h2{font-size:var(--t-h3);margin-bottom:6px}
.panel-lead{color:var(--text-secondary);font-size:var(--t-sm);margin-bottom:20px}
@media(max-width:600px){.panel{padding:20px}}
.summary{position:sticky;top:96px}
@media(max-width:980px){.summary{position:static}}
.stepno{font-family:var(--font-mono);font-size:var(--t-mono);letter-spacing:.14em;color:var(--primary);text-transform:uppercase;display:block;margin-bottom:8px}

/* ---- status blocks: loading, error, empty -------------------------------
   A storefront that cannot reach its catalogue says so. It does NOT fall back
   to a bundled copy of the products: a stale price rendered as a live one is
   the single worst failure mode this page has. */
.status{border:1px solid var(--border-strong);border-radius:var(--radius-card);background:var(--card-bg);padding:28px;display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.status h3{font-size:var(--t-lead)}
.status p{color:var(--text-secondary);font-size:var(--t-sm);max-width:60ch}
.status-error{border-left:3px solid var(--error)}
.status-ok{border-left:3px solid var(--success)}
.status-wait{border-left:3px solid var(--primary)}
.status .btn{margin-top:6px}
.status-code{font-family:var(--font-mono);font-size:var(--t-mono);color:var(--text-muted);letter-spacing:.04em}

/* Skeleton tiles while the catalogue is in flight. Same box as a real card, so
   nothing moves when the answer lands — the whole point of the frame fix above,
   applied to the grid as well. */
.skeleton{background:var(--card-bg);border:1px solid var(--border);border-radius:var(--radius-card);overflow:hidden;display:flex;flex-direction:column}
.skeleton-media{aspect-ratio:1/1;background:linear-gradient(100deg,#101018 30%,#16161F 50%,#101018 70%);background-size:280% 100%;animation:sheen 1.4s linear infinite}
.skeleton-body{padding:16px 18px 20px;display:flex;flex-direction:column;gap:10px}
.skeleton-line{height:12px;border-radius:6px;background:#181822}
.skeleton-line.short{width:52%}
@keyframes sheen{from{background-position:180% 0}to{background-position:-80% 0}}

/* ---- the membership address, echoed back --------------------------------
   Typeset like the order reference and NOT like a form value: this is the one
   thing on the checkout that has to be read rather than skimmed, and a line of
   body text under a filled-in box gets skimmed. Monospace so an l/1 and an
   O/0 are distinguishable, and `word-break` because a long address must wrap
   rather than be clipped — a truncated address is worse than none here. */
.identity{margin-top:20px;padding:16px 18px;background:var(--bg);border:1px solid var(--border-strong);border-radius:var(--radius-input)}
.identity-label{font-family:var(--font-mono);font-size:var(--t-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--text-secondary)}
.identity-value{font-family:var(--font-mono);font-size:var(--t-lead);color:var(--primary);margin-top:8px;word-break:break-all;line-height:1.45}
.identity-value[data-empty="true"]{color:var(--text-muted);font-size:var(--t-sm);word-break:normal}
.identity-change{background:none;border:0;color:var(--text-secondary);font-size:var(--t-xs);text-decoration:underline;cursor:pointer;padding:8px 0;min-height:36px}
.identity-change:hover{color:var(--white)}

/* ---- what happens next, on the confirmation ------------------------------ */
.nextlist{list-style:none;margin-top:18px;display:flex;flex-direction:column;gap:18px}
.nextlist li{display:grid;grid-template-columns:auto 1fr;gap:4px 14px}
.nextstep-no{font-family:var(--font-mono);font-size:var(--t-xs);color:var(--primary);grid-row:1/3;padding-top:3px}
.nextstep-h{font-family:var(--font-head);font-weight:600;font-size:var(--t-sm)}
.nextstep-b{font-size:var(--t-sm);color:var(--text-secondary)}

/* ---- confirmation -------------------------------------------------------- */
.reference{font-family:var(--font-mono);font-size:1.5rem;letter-spacing:.16em;color:var(--primary);background:var(--bg);border:1px dashed rgba(0,229,204,.4);border-radius:var(--radius-input);padding:14px 18px;display:inline-block;word-break:break-all}
.kv{display:grid;grid-template-columns:auto 1fr;gap:8px 20px;font-size:var(--t-sm)}
.kv dt{color:var(--text-muted)}
.kv dd{color:var(--text-secondary)}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
  /* The curtain's whole idea is the movement. With motion off it holds the
     parted position statically rather than snapping — the image still reads as
     "about to open", which is what it is for. */
  .curtain-l{transform:translateX(-7px)}
  .curtain-r{transform:scaleX(-1) translateX(-7px)}
  .skeleton-media{animation:none;background:#101018}
}
