/* ============================================================================
   nordkraft.css — the redesigned design system.
   Approved 2026-07-30: Fraunces display, dark hero + alternating page,
   specimen wells, gradients replacing borders.

   This is the only stylesheet. The pre-redesign css/style.css was deleted on
   2026-07-30 once all seven pages had been converted; recover it from commit
   900426b if a rule ever needs checking against the old design.
   ========================================================================== */

:root{
  /* ---------- neutrals: warm, never grey ---------- */
  --paper:      #F7F5F0;
  --paper-raise:#FFFFFF;
  --paper-sink: #EFEBE2;
  --line:       #E2DDD1;
  --line-strong:#CFC8B8;

  /* ---------- ink & forest ---------- */
  --ink:        #0A0F0C;
  --forest-950: #0B1A12;
  --forest-900: #0E2E1E;
  --forest-800: #123A26;
  --forest-700: #1F5136;
  --forest-400: #6E9480;

  /* ---------- gold: four roles, split by contrast requirement.
     NEVER put text on --gold. Text uses --gold-text (light bg)
     or --gold-pale (dark bg). Verified by calculation, all >= 4.5:1. ---- */
  --gold:       #C9A227;
  --gold-lo:    #B8931F;
  --gold-text:  #846818;
  --gold-pale:  #E8D28A;

  --muted:      #55635B;
  --muted-dark: #9DAEA4;

  /* ---------- gradients: these replace borders ---------- */
  --grad-deep:  linear-gradient(158deg,#123A26 0%,#0B1A12 62%,#060D09 100%);
  --grad-gold:  linear-gradient(101deg,#E8D28A 0%,#C9A227 46%,#A8811A 100%);
  --grad-dawn:  linear-gradient(180deg,#F7F5F0 0%,#EFEBE2 48%,#E4DED2 100%);
  --grad-veil:  linear-gradient(180deg,rgba(11,26,18,0) 0%,rgba(11,26,18,.72) 58%,#0B1A12 100%);

  /* ---------- spacing scale — no one-off values ---------- */
  --s-1:4px;  --s-2:8px;  --s-3:12px; --s-4:16px; --s-5:24px; --s-6:32px;
  --s-7:48px; --s-8:64px; --s-9:96px; --s-10:128px; --s-11:180px;

  --r-xs:3px; --r-sm:6px; --r-md:10px; --r-lg:18px; --r-xl:28px; --r-full:999px;

  --sh-1:0 1px 2px rgba(10,15,12,.04), 0 2px 6px rgba(10,15,12,.04);
  --sh-2:0 2px 4px rgba(10,15,12,.04), 0 8px 20px rgba(10,15,12,.06);
  --sh-3:0 4px 8px rgba(10,15,12,.05), 0 18px 44px rgba(10,15,12,.10);
  --sh-glow:0 0 0 1px rgba(201,162,39,.28), 0 12px 40px rgba(201,162,39,.16);

  --t-fast:140ms cubic-bezier(.4,0,.2,1);
  --t-base:200ms cubic-bezier(.4,0,.2,1);
  --t-slow:320ms cubic-bezier(.22,1,.36,1);

  --font-display:"Fraunces","Iowan Old Style",Georgia,serif;
  --font-body:"Instrument Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,sans-serif;
  --font-mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  --wrap:1240px;
  --measure:66ch;
  --header-h:76px;
  --header-h-sm:60px;
}

/* ============================ base ============================ */
*,*::before,*::after{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:16.5px; line-height:1.62;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  /* Both, in this order, on purpose. overflow-x:hidden turns the body into a
     scroll container, which silently stops position:sticky working ANYWHERE
     on the page — the sticky opening line on the about page simply scrolled
     away. overflow-x:clip prevents the same horizontal overflow (the hero
     bottle sits partly outside the viewport) without creating that scroll
     container. Browsers that do not know `clip` fall back to `hidden` and are
     no worse off than before. */
  overflow-x:hidden;
  overflow-x:clip;
}
h1,h2,h3,h4,h5{
  margin:0; font-family:var(--font-display); font-weight:600;
  line-height:1.04; letter-spacing:-.025em;
}
p{margin:0}
ul,ol{margin:0; padding:0; list-style:none}
img,svg,video{display:block; max-width:100%}
a{color:var(--gold-text); text-decoration:none}
button{font:inherit; color:inherit}
:focus-visible{outline:2px solid var(--gold); outline-offset:3px}
.hidden{display:none !important}
.visually-hidden{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap;
}

.skip-link{
  position:absolute; left:var(--s-4); top:-9999px; z-index:200;
  background:var(--grad-gold); color:var(--ink);
  font-family:var(--font-mono); font-size:12px; font-weight:600;
  letter-spacing:.1em; text-transform:uppercase;
  padding:var(--s-3) var(--s-5); border-radius:var(--r-full); box-shadow:var(--sh-3);
}
.skip-link:focus{top:var(--s-4)}

/* ============================ layout ============================ */
.wrap{width:100%; max-width:var(--wrap); margin:0 auto; padding:0 var(--s-6)}
.wrap--narrow{max-width:900px}
.section{padding:var(--s-10) 0; position:relative}
.section--tight{padding:var(--s-9) 0}
/* Two adjacent sections of the SAME colour stack both paddings, which reads as
   dead space because no colour change justifies it. Pull the second one up.
   Not needed where the colour changes — there the gap does visible work. */
.section--pull{padding-top:var(--s-5)}
.section--dark{background:var(--grad-deep); color:#F2EFE7}
.section--sink{background:var(--paper-sink)}

/* section framing: eyebrow + heading + support line. Never a bare h2. */
.eyebrow{
  font-family:var(--font-mono); font-size:11.5px; font-weight:500;
  letter-spacing:.19em; text-transform:uppercase; color:var(--gold-text);
  display:flex; align-items:center; gap:var(--s-3); margin-bottom:var(--s-4);
}
.eyebrow::after{content:""; flex:1; max-width:140px; height:1px; background:var(--line-strong)}
.section--dark .eyebrow{color:var(--gold-pale)}
.section--dark .eyebrow::after{background:rgba(110,148,128,.45)}

.section__head{max-width:var(--measure); margin-bottom:var(--s-8)}
.section__title{font-size:clamp(30px,4.4vw,50px); margin-bottom:var(--s-4)}
.section__sub{color:var(--muted); font-size:17px}
.section--dark .section__sub{color:var(--muted-dark)}

/* ============================ header ============================ */
.site-header{
  position:sticky; top:0; z-index:100;
  height:var(--header-h);
  display:flex; align-items:center;
  background:transparent;
  transition:height var(--t-base),background var(--t-base),box-shadow var(--t-base),
             backdrop-filter var(--t-base),border-color var(--t-base);
  border-bottom:1px solid transparent;
}
/* over the dark hero the header is invisible chrome; once scrolled it
   condenses into a blurred bar. .is-stuck is set by js/header.js */
.site-header__in{
  width:100%; max-width:var(--wrap); margin:0 auto; padding:0 var(--s-6);
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-5);
}
.site-header.is-stuck{
  height:var(--header-h-sm);
  background:rgba(247,245,240,.86);
  backdrop-filter:blur(16px) saturate(1.5);
  -webkit-backdrop-filter:blur(16px) saturate(1.5);
  border-bottom-color:var(--line);
  box-shadow:0 1px 20px rgba(10,15,12,.05);
}

.nav__word{
  font-family:var(--font-display); font-weight:700; font-size:19px;
  letter-spacing:.15em; text-transform:uppercase; color:#F2EFE7;
  transition:color var(--t-base),font-size var(--t-base);
}
.site-header.is-stuck .nav__word{color:var(--ink); font-size:17px}

.nav__links{display:flex; align-items:center; gap:var(--s-6)}
.nav__links a{
  position:relative; font-size:14.5px; color:rgba(242,239,231,.86);
  transition:color var(--t-fast);
}
.nav__links a::after{
  content:""; position:absolute; left:0; right:0; bottom:-6px; height:1px;
  background:var(--gold-pale); transform:scaleX(0); transform-origin:left;
  transition:transform var(--t-base);
}
.nav__links a:hover,.nav__links a:focus-visible{color:#F2EFE7}
.nav__links a:hover::after,.nav__links a[aria-current="page"]::after{transform:scaleX(1)}
.nav__links a[aria-current="page"]{color:var(--gold-pale)}

.site-header.is-stuck .nav__links a{color:var(--muted)}
.site-header.is-stuck .nav__links a:hover,
.site-header.is-stuck .nav__links a:focus-visible{color:var(--ink)}
.site-header.is-stuck .nav__links a[aria-current="page"]{color:var(--gold-text)}
.site-header.is-stuck .nav__links a::after{background:var(--gold)}

.nav__cart{display:inline-flex; align-items:center; gap:var(--s-2)}
.cart-badge{
  min-width:21px; height:21px; padding:0 6px; border-radius:var(--r-full);
  background:var(--grad-gold); color:var(--ink);
  font-family:var(--font-mono); font-size:11.5px; font-weight:600;
  font-variant-numeric:tabular-nums;
  display:inline-flex; align-items:center; justify-content:center;
}
.cart-badge--empty{display:none}

/* States the currency actually charged, under the checkout order summary.
   Quiet by design — it is a clarification, not a warning. */
.checkout-charged{
  margin-top:var(--s-3); padding-top:var(--s-3); border-top:1px solid var(--line);
  font-size:12.5px; line-height:1.5; color:var(--muted);
}

/* ---------------------------- language switcher ----------------------------
   Sits as the last item inside .nav__links, so it inherits the header's two
   colour states: pale over the dark hero, dark once .is-stuck. Built as a
   <button>, not an <a>, so none of the .nav__links a rules reach it — those
   carry the underline-sweep hover, which would look wrong on a control that
   opens a menu rather than navigating. */
.lang{position:relative; display:inline-flex}
.lang__btn{
  display:inline-flex; align-items:center; gap:var(--s-2);
  padding:5px 9px; border:1px solid rgba(242,239,231,.28);
  border-radius:var(--r-sm); background:transparent; cursor:pointer;
  font-family:var(--font-mono); font-size:11.5px; font-weight:600;
  letter-spacing:.1em; text-transform:uppercase;
  color:rgba(242,239,231,.86);
  transition:color var(--t-fast),border-color var(--t-fast),background var(--t-fast);
}
.lang__btn:hover,.lang__btn:focus-visible{
  color:#F2EFE7; border-color:var(--gold-pale); background:rgba(232,210,138,.10);
}
.lang__chev{transition:transform var(--t-base)}
.lang__btn[aria-expanded="true"] .lang__chev{transform:rotate(180deg)}

.site-header.is-stuck .lang__btn{color:var(--muted); border-color:var(--line-strong)}
.site-header.is-stuck .lang__btn:hover,
.site-header.is-stuck .lang__btn:focus-visible{
  color:var(--ink); border-color:var(--gold); background:rgba(201,162,39,.08);
}

.lang__menu{
  position:absolute; top:calc(100% + 10px); right:0; z-index:200;
  min-width:190px; padding:var(--s-2); margin:0; list-style:none;
  background:var(--paper-raise); border:1px solid var(--line);
  border-radius:var(--r-md); box-shadow:var(--sh-3);
  opacity:0; visibility:hidden; transform:translateY(-4px);
  transition:opacity var(--t-base),transform var(--t-base),visibility var(--t-base);
}
.lang__menu.is-open{opacity:1; visibility:visible; transform:translateY(0)}
/* Selector is .lang__menu .lang__item, not .lang__item, on purpose: these are
   <a> elements sitting inside .nav__links, whose rule `.nav__links a` paints
   links pale cream for the dark header. On this white menu that renders the
   languages almost invisible. Two classes outrank one class + one element. */
.lang__menu .lang__item{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-4);
  padding:8px 10px; border-radius:var(--r-sm);
  font-size:14px; color:var(--ink); white-space:nowrap;
  transition:background var(--t-fast),color var(--t-fast);
}
.lang__menu .lang__item:hover,
.lang__menu .lang__item:focus-visible{background:var(--paper-sink); color:var(--ink)}
/* Kill the header's underline-sweep: it belongs on nav links, not menu rows. */
.lang__menu .lang__item::after{display:none}
/* --gold-text, not --gold: this is text on a light background. */
.lang__menu .lang__item[aria-current="true"]{color:var(--gold-text); font-weight:600}
.lang__cur{
  font-family:var(--font-mono); font-size:10.5px; font-weight:600;
  letter-spacing:.08em; color:var(--muted);
}
.lang__menu .lang__item[aria-current="true"] .lang__cur{color:var(--gold-text)}

/* Mobile nav: no hamburger — there are only four links, so below 560px the
   header becomes two rows (wordmark, then the links spread across the full
   width). Every destination stays reachable and no JS toggle is needed.
   --header-h is overridden too, because the hero's negative margin and top
   padding are both derived from it. */
/* Product search, in the header between the wordmark and the nav. Because
   .site-header__in is space-between with three children, flexbox centres it in
   the gap on its own.

   Like the nav links, it has to survive two header states: pale on the dark
   hero, dark once .is-stuck paints the bar light. */
.search{position:relative; display:flex; align-items:center}
.search--header{flex:0 1 340px; min-width:0}
.search__icon{
  position:absolute; left:13px; color:rgba(242,239,231,.6);
  pointer-events:none; transition:color var(--t-fast);
}
.search__in{
  width:100%; font-family:var(--font-body); font-size:14.5px;
  color:#F2EFE7; padding:8px 34px 8px 36px;
  background:rgba(242,239,231,.08); border:1px solid rgba(242,239,231,.22);
  border-radius:var(--r-full);
  transition:border-color var(--t-fast),background var(--t-fast),box-shadow var(--t-fast);
  -webkit-appearance:none; appearance:none;
}
.search__in::placeholder{color:rgba(242,239,231,.55)}
.search__in:focus{
  outline:none; border-color:var(--gold-pale);
  background:rgba(242,239,231,.13); box-shadow:none;
}
.search:focus-within .search__icon{color:var(--gold-pale)}
/* The browser's own clear affordance is inconsistent across engines, so it is
   suppressed and replaced with one that matches the rest of the controls. */
.search__in::-webkit-search-cancel-button{-webkit-appearance:none; appearance:none}
.search__clear{
  position:absolute; right:6px; display:flex; align-items:center;
  justify-content:center; width:22px; height:22px; padding:0;
  background:transparent; border:0; border-radius:var(--r-full);
  color:rgba(242,239,231,.7); cursor:pointer;
  transition:background var(--t-fast),color var(--t-fast);
}
.search__clear:hover{background:rgba(242,239,231,.14); color:#F2EFE7}

/* Results drop under the box on every page. Always a light panel, like the
   language menu — the header has two colour states but a floating surface
   reads better as one consistent thing. */
.search__results{
  position:absolute; top:calc(100% + 8px); left:0; right:0; z-index:200;
  max-height:min(60vh,420px); overflow-y:auto;
  padding:var(--s-2);
  background:var(--paper-raise); border:1px solid var(--line);
  border-radius:var(--r-md); box-shadow:var(--sh-3);
}
.search__results[hidden]{display:none}
.search__result{
  display:flex; align-items:center; gap:var(--s-3);
  padding:7px var(--s-2); border-radius:var(--r-sm);
  color:var(--ink); text-decoration:none;
  transition:background var(--t-fast);
}
/* .is-active is set by keyboard and hover alike, so arrow keys and the mouse
   cannot disagree about which row is current. */
.search__result.is-active{background:var(--paper-sink)}
.search__thumb{
  flex:0 0 auto; width:34px; height:34px; border-radius:var(--r-sm);
  background:var(--grad-deep); display:flex; align-items:center;
  justify-content:center; overflow:hidden;
}
.search__thumb img{width:74%; height:auto}
.search__resultText{display:flex; flex-direction:column; min-width:0; flex:1}
.search__resultName{
  font-family:var(--font-display); font-size:15px; font-weight:600;
  line-height:1.2; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.search__resultCat{
  font-family:var(--font-display); font-style:italic; font-size:12.5px;
  color:var(--gold-text);
}
.search__resultPrice{
  flex:0 0 auto; font-family:var(--font-mono); font-size:12.5px;
  font-weight:600; font-variant-numeric:tabular-nums; color:var(--muted);
}
.search__empty{
  padding:var(--s-3) var(--s-2); color:var(--muted); font-size:14px;
}

/* condensed light header */
.site-header.is-stuck .search__in{
  color:var(--ink); background:var(--paper-raise); border-color:var(--line-strong);
}
.site-header.is-stuck .search__in::placeholder{color:var(--muted)}
.site-header.is-stuck .search__icon{color:var(--muted)}
.site-header.is-stuck .search__in:focus{border-color:var(--gold); background:var(--paper-raise)}
.site-header.is-stuck .search:focus-within .search__icon{color:var(--gold-text)}
.site-header.is-stuck .search__clear{color:var(--muted)}
.site-header.is-stuck .search__clear:hover{background:var(--paper-sink); color:var(--ink)}

/* The search gives ground before the nav does — a nav item that has wrapped is
   broken, a shorter search box is merely smaller. Between 861px and 980px that
   is still the right trade. Below 861px it stops being winnable: the five nav
   labels total 45 characters in Finnish against 27 in English, so no amount of
   shrinking fits every language into one row. The phone header below gives each
   label a row of its own instead, which removes label length from the problem
   for good. */
@media (max-width:980px){
  .search--header{flex:0 1 240px}
}

/* =========================== phone header ===========================
   Hidden above 860px. .header__tools carries the three controls; the nav
   itself moves into .mnav, which is a sibling of <header> — see the comment
   on .mnav for why it cannot live inside it. */
.header__tools{display:none; align-items:center; gap:2px; margin-left:auto}
.header__tool{
  position:relative; display:flex; align-items:center; justify-content:center;
  width:44px; height:44px; padding:0; flex:0 0 auto;
  background:transparent; border:0; border-radius:var(--r-full);
  color:rgba(242,239,231,.86); cursor:pointer; text-decoration:none;
  -webkit-tap-highlight-color:transparent;
  transition:color var(--t-fast),background var(--t-fast);
}
.header__tool:hover,.header__tool:focus-visible{
  color:#F2EFE7; background:rgba(242,239,231,.10);
}
.site-header.is-stuck .header__tool{color:var(--muted)}
.site-header.is-stuck .header__tool:hover,
.site-header.is-stuck .header__tool:focus-visible{
  color:var(--ink); background:var(--paper-sink);
}
/* The badge rides the corner of the icon rather than sitting beside it, so the
   control stays a 44px square whatever the count. */
.header__tool--cart .cart-badge{
  position:absolute; top:2px; right:0;
  min-width:17px; height:17px; padding:0 4px; font-size:10.5px;
}

@media (max-width:860px){
  /* One row, 56px, down from three rows and 152px. Every offset below the
     header — the hero's negative margin, the sticky sidebars, the section
     padding — derives from these two variables, so this is the only place the
     height is stated. */
  :root{--header-h:56px; --header-h-sm:56px}

  .site-header__in{position:relative; padding:0 var(--s-4); gap:var(--s-3)}
  .nav__word{font-size:16px; letter-spacing:.12em}
  .site-header.is-stuck .nav__word{font-size:16px}

  .nav__links{display:none}
  .header__tools{display:flex}

  /* Search collapses to a toggle. Open, it covers the header row instead of
     adding one, so the header height never changes and nothing below it moves.
     Absolute rather than fixed: it should track the header — and .is-stuck's
     backdrop-filter would trap a fixed child here regardless. */
  .search--header{
    position:absolute; z-index:2;
    left:var(--s-4); right:calc(var(--s-4) + 48px);
    top:50%; transform:translateY(-50%);
    flex:0 0 auto; width:auto; margin:0;
    opacity:0; visibility:hidden; pointer-events:none;
    /* Only opacity is transitioned. Putting visibility in here delays the flip
       to `visible` until the transition runs, and focus() on a still-hidden
       field is silently ignored — which meant the keyboard did not open when
       the visitor tapped search, and the field had to be tapped again. */
    transition:opacity var(--t-fast);
  }
  .site-header.is-searching .search--header{
    opacity:1; visibility:visible; pointer-events:auto;
  }
  /* While the field is open the wordmark sits underneath it, and the cart and
     menu would crowd it. Only the search toggle stays, now labelled "close". */
  .site-header.is-searching .nav__word{opacity:0}
  .site-header.is-searching .header__tool--cart,
  .site-header.is-searching [data-mnav-open]{display:none}

  .search__clear{width:32px; height:32px; right:4px}
}

/* ====================== phone navigation panel ======================
   The markup for this sits directly inside <body>, after </header>, and must
   stay there. .site-header gains backdrop-filter when it condenses, and
   backdrop-filter makes an element a containing block for position:fixed
   descendants — nested in the header, this panel would anchor to the header
   rather than the viewport, but only once scrolled. Same shape of bug as
   overflow-x:hidden quietly disabling position:sticky.

   z-index sits above the header (100) and the search results (200). */
.mnav{display:none; position:fixed; inset:0; z-index:300}
.mnav[hidden]{display:none}
@media (max-width:860px){
  .mnav{display:block}
  .mnav[hidden]{display:none}
}

.mnav__scrim{
  position:absolute; inset:0;
  background:rgba(6,13,9,.55);
  opacity:0; transition:opacity .28s ease;
}
.mnav.is-open .mnav__scrim{opacity:1}

.mnav__panel{
  position:absolute; top:0; right:0; bottom:0;
  width:min(84vw,340px);
  display:flex; flex-direction:column;
  background:var(--paper-raise);
  border-left:1px solid var(--line);
  box-shadow:var(--sh-3);
  overflow-y:auto; overscroll-behavior:contain;
  /* Translate rather than animating `right`, so the slide runs on the
     compositor and creates no horizontal overflow on the way in or out. */
  transform:translateX(100%);
  transition:transform .3s cubic-bezier(.32,.72,0,1);
}
.mnav.is-open .mnav__panel{transform:none}

.mnav__top{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s-3); padding:var(--s-3) var(--s-4) var(--s-3) var(--s-5);
  border-bottom:1px solid var(--line);
}
.mnav__word{
  font-family:var(--font-display); font-weight:700; font-size:16px;
  letter-spacing:.15em; text-transform:uppercase; color:var(--ink);
}
.mnav__close{
  display:flex; align-items:center; justify-content:center;
  width:44px; height:44px; flex:0 0 auto; padding:0;
  background:transparent; border:0; border-radius:var(--r-full);
  color:var(--muted); cursor:pointer;
  transition:color var(--t-fast),background var(--t-fast);
}
.mnav__close:hover,.mnav__close:focus-visible{
  background:var(--paper-sink); color:var(--ink);
}

.mnav__links{display:flex; flex-direction:column; padding:var(--s-2) 0}
.mnav__links a{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s-3); min-height:48px; padding:0 var(--s-5);
  font-family:var(--font-display); font-size:17px; color:var(--ink);
  text-decoration:none;
  /* A label on its own row cannot wrap at any width the panel reaches, in any
     of the six languages. This is the rule that retires the wrapping bug. */
  white-space:nowrap;
  transition:background var(--t-fast),color var(--t-fast);
}
.mnav__links a:hover,.mnav__links a:focus-visible{background:var(--paper-sink)}
.mnav__links a[aria-current="page"]{
  color:var(--gold-text); box-shadow:inset 3px 0 0 var(--gold);
}
.mnav__cart{gap:var(--s-3)}

.mnav__foot{
  margin-top:auto;
  padding:var(--s-4) var(--s-5) calc(var(--s-4) + env(safe-area-inset-bottom));
  border-top:1px solid var(--line);
}
/* The switcher is built for the header's two colour states; inside the panel
   it is always on paper, so it needs the condensed treatment unconditionally. */
.mnav__foot .lang{width:100%}
.mnav__foot .lang__btn{
  width:100%; min-height:44px; justify-content:space-between;
  color:var(--muted); border-color:var(--line-strong);
}
.mnav__foot .lang__btn:hover,.mnav__foot .lang__btn:focus-visible{
  color:var(--ink); border-color:var(--gold); background:rgba(201,162,39,.08);
}
.mnav__foot .lang__menu{right:0; left:0; bottom:calc(100% + 6px); top:auto}

/* Scroll lock while the panel is open. On the root element rather than <body>:
   body{overflow:hidden} is the more common spelling and it is also what
   disables position:sticky for the whole page, which this project has already
   been bitten by once. */
html.has-mnav{overflow:hidden}

@media (prefers-reduced-motion:reduce){
  .mnav__panel,.mnav__scrim{transition:none}
}

/* ======================== phone touch layer ========================
   Below 860px every control has to clear the 44px minimum both Apple and
   Google publish, and every text field has to be exactly 16px. */
@media (max-width:860px){
  /* Safari on iOS zooms the viewport whenever a focused field is under 16px,
     and the visitor has to pinch back out — worst on checkout, which has eight
     of them. Deliberately NOT fixed with maximum-scale=1 on the viewport meta:
     that stops the zoom by disabling pinch-zoom for everyone, including people
     who rely on it to read. Font size is the honest fix. */
  input,select,textarea{font-size:16px}
  .search__in{font-size:16px; padding:11px 38px 11px 38px}

  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea,
  button,
  .btn{min-height:44px}
}

/* ============================ buttons ============================ */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s-2);
  font-family:var(--font-body); font-size:14.5px; font-weight:600; letter-spacing:.01em;
  padding:14px var(--s-6); border-radius:var(--r-full); border:0; cursor:pointer;
  text-decoration:none; text-align:center;
  transition:transform var(--t-fast),box-shadow var(--t-base),
             background var(--t-base),color var(--t-base);
}
.btn--primary{background:var(--grad-gold); color:var(--ink); box-shadow:var(--sh-2)}
.btn--primary:hover{transform:translateY(-1px); box-shadow:var(--sh-3)}
.btn--dark{background:var(--forest-700); color:#F2EFE7; box-shadow:var(--sh-2)}
.btn--dark:hover{background:var(--forest-800); transform:translateY(-1px); box-shadow:var(--sh-3)}
.btn--ghost{background:transparent; color:var(--ink); box-shadow:inset 0 0 0 1px var(--line-strong)}
.btn--ghost:hover{box-shadow:inset 0 0 0 1px var(--ink); background:rgba(10,15,12,.03)}
.btn--quiet{background:transparent; color:var(--muted); padding:var(--s-2) var(--s-3); font-weight:500}
.btn--quiet:hover{color:var(--ink)}
.btn--wide{width:100%}
.btn--disabled,.btn[disabled]{opacity:.42; cursor:not-allowed; transform:none; box-shadow:none}
.section--dark .btn--ghost,.hero .btn--ghost{
  color:#F2EFE7; box-shadow:inset 0 0 0 1px rgba(242,239,231,.28);
}
.section--dark .btn--ghost:hover,.hero .btn--ghost:hover{
  box-shadow:inset 0 0 0 1px #F2EFE7; background:rgba(242,239,231,.07);
}

/* ============================ small components ============================ */
.tag{
  display:inline-flex; align-items:center; gap:5px;
  font-family:var(--font-mono); font-size:10.5px; font-weight:500;
  letter-spacing:.13em; text-transform:uppercase;
  padding:5px 11px; border-radius:var(--r-full);
}
.tag--gold{background:rgba(201,162,39,.14); color:var(--gold-text)}
.tag--forest{background:rgba(31,81,54,.1); color:var(--forest-700)}
.section--dark .tag--gold{background:rgba(232,210,138,.14); color:var(--gold-pale)}
.section--dark .tag--forest{background:rgba(242,239,231,.1); color:#F2EFE7}

.rating{display:inline-flex; align-items:center; gap:var(--s-2)}
.rating__num{
  font-family:var(--font-mono); font-size:13px; font-weight:600;
  font-variant-numeric:tabular-nums;
}
/* Five stars, filled to the exact score. Two stacked rows: a muted row of five
   and a gold row on top clipped by width, so 4.5 renders as a real half star
   rather than rounding. The fill row is aria-hidden by virtue of the wrapper
   carrying role="img" and the score as its label. */
.rating__stars{position:relative; display:inline-block; height:14px; line-height:0}
.rating__starsBase,.rating__starsFill{display:flex; gap:2px; height:14px}
/* flex:0 0 auto is load-bearing. The fill row is a flex container narrowed to
   the score (width:90% for 4.5), and flex children shrink by default — so the
   gold stars squashed to fit that width instead of being clipped by it, ending
   up narrower than the grey ones and drifting further out of line with every
   star. They must keep their size and let overflow:hidden do the cutting. */
.rating__starsBase svg,.rating__starsFill svg{
  width:14px; height:14px; display:block; flex:0 0 auto;
}
.rating__starsBase{fill:var(--line-strong)}
.rating__starsFill{
  position:absolute; inset:0 auto 0 0; overflow:hidden;
  fill:var(--gold); white-space:nowrap;
}
.section--dark .rating__num{color:var(--gold-pale)}
.section--dark .rating__starsBase{fill:rgba(242,239,231,.28)}
.section--dark .rating__starsFill{fill:var(--gold-pale)}

.price{font-family:var(--font-mono); font-weight:600; font-variant-numeric:tabular-nums}
.price--lg{font-size:26px}
.price--old{font-weight:400; text-decoration:line-through; color:var(--muted); font-size:13px}

/* ============================ the specimen well ============================
   Signature element. Deep gradient well, hue-keyed per category, gold bloom
   behind the product. Replaces the 1px bordered box entirely.
   ========================================================================== */
.well{position:relative; overflow:hidden; background:var(--forest-900)}
.well--somn  {background:linear-gradient(158deg,#0A2A26 0%,#061519 70%,#040E12 100%)}
.well--aterhamtning{background:linear-gradient(158deg,#0E2E1E 0%,#071810 70%,#050F0A 100%)}
.well--prestation{background:linear-gradient(158deg,#1A2E12 0%,#0D1A08 70%,#081205 100%)}
.well--energi{background:linear-gradient(158deg,#26290E 0%,#151407 70%,#0E0D05 100%)}

.well::before{                       /* the gold bloom — the light source */
  content:""; position:absolute; left:50%; top:56%; width:135%; aspect-ratio:1;
  transform:translate(-50%,-50%); pointer-events:none;
  background:radial-gradient(circle,rgba(201,162,39,.32) 0%,rgba(201,162,39,.10) 34%,transparent 62%);
  transition:transform var(--t-slow),opacity var(--t-slow);
}
.well::after{                        /* contour rings — the brand's own motif */
  content:""; position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(circle at 50% 56%,transparent 27%,rgba(232,210,138,.13) 27.4%,transparent 28.2%),
    radial-gradient(circle at 50% 56%,transparent 40%,rgba(232,210,138,.09) 40.4%,transparent 41.2%),
    radial-gradient(circle at 50% 56%,transparent 54%,rgba(232,210,138,.06) 54.4%,transparent 55.2%);
}
.well__art{
  position:absolute; inset:0; z-index:2;
  display:flex; align-items:center; justify-content:center;
}
/* Two layers: the SVG vessel always renders, a product photo layers on top and
   is only revealed once it has actually decoded (.is-loaded, set by
   product-card.js). A missing photo therefore degrades to the SVG with no
   blank box and no flash of empty space. */
.well__svg,.well__photo{
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  width:54%; height:auto; max-height:78%;
  filter:drop-shadow(0 10px 22px rgba(0,0,0,.42));
  transition:transform var(--t-slow),opacity var(--t-slow);
}
/* The bottles drift, like the hero specimen but with more travel.
   Animated on .well__art, NOT on the artwork itself: .well__svg/.well__photo
   already own a transform (centring, and the hover scale), and an animation on
   the same element would win over the hover and kill it. Animating the wrapper
   keeps both. A global prefers-reduced-motion rule further down stops all of
   this for anyone who has asked for less movement. */
@keyframes bottle-float{
  0%,100%{transform:translateY(0) rotate(-2deg)}
  50%    {transform:translateY(-13px) rotate(2deg)}
}
@keyframes bottle-float-lg{
  0%,100%{transform:translateY(0) rotate(-2.5deg)}
  50%    {transform:translateY(-20px) rotate(2.5deg)}
}
.well__art{animation:bottle-float 6s ease-in-out infinite}
/* Negative delays start each bottle mid-cycle, so a grid of eight drifts out
   of step instead of pulsing in unison — which reads as a glitch, not motion. */
.grid--products .card:nth-child(4n+2) .well__art{animation-delay:-1.5s}
.grid--products .card:nth-child(4n+3) .well__art{animation-delay:-3s}
.grid--products .card:nth-child(4n+4) .well__art{animation-delay:-4.5s}
.grid--products .card:nth-child(n+5) .well__art{animation-duration:6.8s}

.well__svg{display:block}
.well__svg svg{width:100%; height:auto}
.well__photo{width:72%; max-height:84%; object-fit:contain; opacity:0}
.well__photo.is-loaded{opacity:1}
/* has-photo is set on the wrapper by product-card.js once the photo decodes.
   Done in JS rather than with :has() so the fallback never depends on
   selector support. */
.well__art.has-photo .well__svg{opacity:0}
/* "Demo" badge — rendered here, never baked into the image, so it stays
   crisp and can be removed for a real client without new art. */
.well__demo{
  position:absolute; top:var(--s-3); right:var(--s-3); z-index:3;
  font-family:var(--font-mono); font-size:9.5px; font-weight:500;
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--gold-pale); background:rgba(6,13,9,.55);
  border:1px solid rgba(232,210,138,.3);
  padding:3px 8px; border-radius:var(--r-full);
  backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px);
}

/* ============================ product card ============================ */
.grid{display:grid; gap:var(--s-5)}
.grid--products{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}
.grid--bundles{grid-template-columns:repeat(auto-fill,minmax(290px,1fr))}

.card{
  position:relative; display:flex; flex-direction:column;
  background:var(--paper-raise); border-radius:var(--r-lg); overflow:hidden;
  box-shadow:var(--sh-1); color:inherit;
  transition:transform var(--t-base),box-shadow var(--t-base);
}
.card:hover{transform:translateY(-4px); box-shadow:var(--sh-3)}
.card:focus-within{box-shadow:var(--sh-3)}
.card .well{aspect-ratio:4/3}
.card:hover .well::before{transform:translate(-50%,-50%) scale(1.1)}
/* must repeat the centring translate — a bare scale() would drop it and the
   art would jump to the corner on hover */
.card:hover .well__svg,.card:hover .well__photo{transform:translate(-50%,-50%) scale(1.04)}

.card__body{display:flex; flex-direction:column; gap:var(--s-3); padding:var(--s-5); flex:1}
/* Matches the filter chips: same serif, same sentence case, so a category
   reads the same wherever it appears. */
.card__cat{
  font-family:var(--font-display); font-size:13.5px; font-style:italic;
  font-weight:500; letter-spacing:0; color:var(--gold-text);
}
.card__name{font-family:var(--font-display); font-size:19px; font-weight:600; letter-spacing:-.01em}
.card__link{color:inherit; text-decoration:none}
/* the whole card is clickable via a stretched pseudo-element on the title
   link, so the add button stays independently focusable */
.card__link::after{content:""; position:absolute; inset:0; z-index:4}
.card__desc{font-size:13.5px; color:var(--muted); line-height:1.5; flex:1}
.card__foot{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-3);
  padding-top:var(--s-3); border-top:1px solid var(--line);
}
.card__price{font-family:var(--font-mono); font-size:17px; font-weight:600; font-variant-numeric:tabular-nums}

/* quiet circular add control — fills with gold on card hover.
   Deliberately not a full-width button: eight of those shouting at once is
   what made the old grid feel cheap. */
.card__add{
  position:relative; z-index:5; flex:0 0 auto;
  width:38px; height:38px; border-radius:var(--r-full);
  border:1px solid var(--line-strong); background:transparent; color:var(--ink);
  display:inline-flex; align-items:center; justify-content:center; cursor:pointer;
  transition:background var(--t-base),border-color var(--t-base),
             color var(--t-base),transform var(--t-fast);
}
.card:hover .card__add{background:var(--grad-gold); border-color:transparent}
.card__add:hover{transform:scale(1.08)}
.card__add.is-added{background:var(--forest-700); border-color:transparent; color:#F2EFE7}

/* ============================ bundle card ============================
   Same shape as a product card: a hue-keyed specimen well on top, then the
   body. The well shows one vessel per product in the bundle. Both bundle bands
   are light, so there is no dark variant — reintroduce one only if a bundle
   grid ever moves onto .section--dark again. */
.bundle{
  position:relative; display:flex; flex-direction:column;
  background:var(--paper-raise); border-radius:var(--r-lg); overflow:hidden;
  box-shadow:var(--sh-1);
  transition:box-shadow var(--t-base),transform var(--t-base);
}
.bundle:hover{transform:translateY(-4px); box-shadow:var(--sh-3)}
.bundle .well{aspect-ratio:16/10}
.bundle:hover .well::before{transform:translate(-50%,-50%) scale(1.1)}
.bundle:hover .well__group{transform:scale(1.04)}

.bundle__body{display:flex; flex-direction:column; gap:var(--s-3); padding:var(--s-5); flex:1}
.bundle__name{font-size:21px; color:var(--ink)}
.bundle__contents{font-size:13.5px; color:var(--muted); line-height:1.5; flex:1}
.bundle__prices{display:flex; align-items:baseline; gap:var(--s-3); flex-wrap:wrap}
.bundle__new{font-family:var(--font-mono); font-size:21px; font-weight:600; color:var(--ink)}
.bundle__old{font-family:var(--font-mono); font-size:13px; text-decoration:line-through; color:var(--muted)}

/* ---------- clustered vessels inside a bundle well ---------- */
.well__group{
  position:absolute; inset:0; z-index:2;
  display:flex; align-items:center; justify-content:center;
  transition:transform var(--t-slow);
}
.well__group > span{
  flex:0 0 auto; width:26%;
  filter:drop-shadow(0 10px 22px rgba(0,0,0,.45));
}
/* Per-member photo layer, mirroring .well__svg / .well__photo on a single
   product card. The SVG vessel sets the box; the photo is laid over it and
   only revealed once decoded, so a missing file degrades to the vessel. */
/* Bundle members drift individually and out of phase, so a pair reads as two
   separate objects rather than one rocking slab. Animated on the member, not
   .well__group, because the group owns the bundle's hover scale. */
.well__member{
  position:relative; display:block;
  animation:bottle-float 6.4s ease-in-out infinite;
}
.well__group > span:nth-child(2){animation-delay:-2.4s}
.well__group > span:nth-child(3){animation-delay:-4.1s}
.well__memberSvg{display:block; transition:opacity var(--t-slow)}
.well__memberSvg svg{width:100%; height:auto; display:block}
.well__memberPhoto{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:contain; opacity:0; transition:opacity var(--t-slow);
}
.well__memberPhoto.is-loaded{opacity:1}
.well__member.has-photo .well__memberSvg{opacity:0}
.well__group > span + span{margin-left:-5%}
/* stagger so the cluster reads as depth rather than a flat row; the centre
   vessel sits forward */
.well__group > span:nth-child(odd){transform:translateY(4%) scale(.9)}
.well__group > span:nth-child(even){position:relative; z-index:2; transform:translateY(-3%) scale(1.04)}
/* fewer vessels can afford to be bigger; three or more need the smaller base */
.well__group[data-count="2"] > span{width:35%}
.well__group[data-count="1"] > span{width:44%}
.well__group > span svg{width:100%; height:auto}

/* ============================ hero ============================ */
.hero{
  position:relative; overflow:hidden;
  background:var(--grad-deep); color:#F2EFE7;
  margin-top:calc(var(--header-h) * -1);
  padding:calc(var(--header-h) + var(--s-10)) 0 var(--s-10);
  min-height:min(88vh,860px);
  display:flex; align-items:center;
}
.hero__in{position:relative; z-index:3; max-width:660px}
.hero__eyebrow{
  display:block; font-family:var(--font-mono); font-size:11.5px;
  letter-spacing:.2em; text-transform:uppercase; color:var(--gold-pale);
  margin-bottom:var(--s-5);
}
.hero__h{font-size:clamp(42px,7vw,82px); line-height:.95; letter-spacing:-.04em; margin-bottom:var(--s-5)}
.hero__h em{font-style:italic; font-weight:400}
.hero__p{font-size:18px; max-width:46ch; color:var(--muted-dark); margin-bottom:var(--s-7)}
.hero__cta{display:flex; gap:var(--s-4); flex-wrap:wrap; align-items:center}
.hero__trust{
  display:flex; gap:var(--s-6); flex-wrap:wrap; margin-top:var(--s-9);
  padding-top:var(--s-5); border-top:1px solid rgba(110,148,128,.3);
  font-family:var(--font-mono); font-size:11px; letter-spacing:.13em;
  text-transform:uppercase; color:var(--muted-dark);
}
/* ambient bloom + contour rings, right side */
.hero::before{
  content:""; position:absolute; right:-10%; top:-16%; width:64%; aspect-ratio:1;
  pointer-events:none;
  background:radial-gradient(circle,rgba(201,162,39,.26) 0%,rgba(201,162,39,.07) 38%,transparent 64%);
}
.hero::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(circle at 74% 42%,transparent 20%,rgba(232,210,138,.11) 20.3%,transparent 20.9%),
    radial-gradient(circle at 74% 42%,transparent 30%,rgba(232,210,138,.08) 30.3%,transparent 30.9%),
    radial-gradient(circle at 74% 42%,transparent 41%,rgba(232,210,138,.055) 41.3%,transparent 41.9%),
    radial-gradient(circle at 74% 42%,transparent 53%,rgba(232,210,138,.035) 53.3%,transparent 53.9%);
}
/* the hero's own specimen, floating in the ring centre */
.hero__specimen{
  position:absolute; right:6%; top:50%; transform:translateY(-50%);
  width:min(30vw,380px); z-index:2; pointer-events:none;
  filter:drop-shadow(0 30px 60px rgba(0,0,0,.5));
  animation:hero-float 9s ease-in-out infinite;
}
@keyframes hero-float{
  0%,100%{transform:translateY(-50%) rotate(-3deg)}
  50%    {transform:translateY(calc(-50% - 14px)) rotate(-1deg)}
}
@media (max-width:980px){
  .hero__specimen{display:none}
  .hero{min-height:auto; padding:calc(var(--header-h) + var(--s-8)) 0 var(--s-8)}
}

/* ============================ dosing principles ============================ */
.principles{display:grid; grid-template-columns:repeat(auto-fit,minmax(250px,1fr)); gap:var(--s-7)}
.principle{position:relative; padding-top:var(--s-6)}
.principle::before{
  content:""; position:absolute; top:0; left:0; width:44px; height:2px; background:var(--grad-gold);
}
.principle__n{
  font-family:var(--font-mono); font-size:11px; font-weight:600; letter-spacing:.16em;
  color:var(--gold-text); display:block; margin-bottom:var(--s-3);
}
.section--dark .principle__n{color:var(--gold-pale)}
.principle__h{font-size:21px; margin-bottom:var(--s-3)}
.principle__p{font-size:15px; color:var(--muted)}
.section--dark .principle__p{color:var(--muted-dark)}

/* ============================ newsletter ============================ */
.news{
  position:relative; overflow:hidden;
  border-radius:var(--r-xl); padding:var(--s-9) var(--s-7);
  background:var(--grad-deep); color:#F2EFE7; text-align:center;
}
.news::before{
  content:""; position:absolute; left:50%; top:-40%; width:80%; aspect-ratio:1;
  transform:translateX(-50%); pointer-events:none;
  background:radial-gradient(circle,rgba(201,162,39,.2) 0%,transparent 60%);
}
.news__in{position:relative; z-index:2; max-width:520px; margin:0 auto}
.news__h{font-size:clamp(26px,3.4vw,38px); margin-bottom:var(--s-4)}
.news__p{color:var(--muted-dark); margin-bottom:var(--s-6)}
.news__form{display:flex; gap:var(--s-3); flex-wrap:wrap; justify-content:center}
.field__in{
  flex:1 1 240px; min-width:0;
  font-family:inherit; font-size:15.5px; color:#F2EFE7;
  background:rgba(242,239,231,.07); border:1px solid rgba(110,148,128,.4);
  border-radius:var(--r-full); padding:13px var(--s-5);
  transition:border-color var(--t-fast),box-shadow var(--t-fast),background var(--t-fast);
}
.field__in::placeholder{color:#7E8F85}
.field__in:focus{
  outline:0; border-color:var(--gold); background:rgba(242,239,231,.1);
  box-shadow:0 0 0 3px rgba(201,162,39,.18);
}
.news__note{
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--muted-dark); margin-top:var(--s-5);
}

/* ============================ footer ============================ */
.site-footer{background:var(--forest-950); color:var(--muted-dark); padding:var(--s-9) 0 var(--s-6)}
.site-footer__grid{
  display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:var(--s-7);
  padding-bottom:var(--s-8); border-bottom:1px solid rgba(110,148,128,.22);
}
.site-footer__word{
  font-family:var(--font-display); font-weight:700; font-size:20px;
  letter-spacing:.15em; text-transform:uppercase; color:#F2EFE7;
  display:block; margin-bottom:var(--s-4);
}
.site-footer__blurb{font-size:14px; max-width:34ch}
.site-footer h4{
  font-family:var(--font-mono); font-size:10.5px; font-weight:500;
  letter-spacing:.16em; text-transform:uppercase; color:var(--gold-pale);
  margin-bottom:var(--s-4);
}
.site-footer li{margin-bottom:var(--s-3)}
.site-footer a{color:var(--muted-dark); font-size:14px; transition:color var(--t-fast)}
.site-footer a:hover{color:#F2EFE7}
.site-footer__base{
  display:flex; justify-content:space-between; gap:var(--s-4); flex-wrap:wrap;
  padding-top:var(--s-5);
  font-family:var(--font-mono); font-size:11px; letter-spacing:.08em;
}
@media (max-width:860px){
  .site-footer__grid{grid-template-columns:1fr 1fr; gap:var(--s-6)}
}
@media (max-width:520px){
  .site-footer__grid{grid-template-columns:1fr}
}

/* ============================ demo notice ============================ */
.demo-notice{
  display:flex; gap:var(--s-3); align-items:flex-start;
  padding:var(--s-4) var(--s-5); border-radius:var(--r-md);
  background:rgba(201,162,39,.09); box-shadow:inset 0 0 0 1px rgba(201,162,39,.3);
  font-size:14px;
}
.demo-notice strong{color:var(--gold-text)}

/* ============================ scroll reveal ============================
   Restrained: 14px rise + fade, staggered by index, once only.
   js/motion.js adds .is-in. No reveal at all if reduced motion is set.
   ========================================================================== */
.reveal{opacity:0; transform:translateY(14px); transition:opacity 520ms ease,transform 520ms cubic-bezier(.22,1,.36,1)}
.reveal.is-in{opacity:1; transform:none}
.no-motion .reveal{opacity:1; transform:none; transition:none}

/* fade-in for JS-rendered content, so grids don't flash blank then pop */
.js-render{opacity:0; transition:opacity var(--t-slow)}
.js-render.is-ready{opacity:1}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
  }
  .reveal{opacity:1; transform:none}
}

/* ============================ responsive ============================ */
@media (max-width:720px){
  :root{--s-9:64px; --s-10:80px}
  .wrap{padding:0 var(--s-5)}
  .news{padding:var(--s-8) var(--s-5); border-radius:var(--r-lg)}
  .hero__trust{gap:var(--s-4); font-size:10px}
}

/* ============================================================================
   INTERIOR PAGES
   Added when the remaining six pages were converted off the old stylesheet.
   ========================================================================== */

/* ---------- interior page framing header ---------- */
/* Every interior page opens with eyebrow + heading + one supporting line on a
   soft gradient band. A bare <h1> on flat background is what made these pages
   read as unfinished. */
/* No negative margin here, unlike .hero: interior pages have no dark hero, so
   header.js starts the header condensed and opaque. It occupies real space and
   the band simply sits below it. Pulling up by --header-h would overlap, since
   the condensed header is --header-h-sm tall. */
.page-head{
  background:var(--grad-dawn); border-bottom:1px solid var(--line);
  padding:var(--s-9) 0 var(--s-8);
  position:relative; overflow:hidden;
}
.page-head::after{                   /* faint contour echo of the brand motif */
  content:""; position:absolute; right:-6%; top:-60%; width:44%; aspect-ratio:1;
  pointer-events:none;
  background:
    radial-gradient(circle,transparent 46%,rgba(132,104,24,.09) 46.4%,transparent 47.2%),
    radial-gradient(circle,transparent 62%,rgba(132,104,24,.07) 62.4%,transparent 63.2%),
    radial-gradient(circle,transparent 78%,rgba(132,104,24,.05) 78.4%,transparent 79.2%);
}
.page-head__in{position:relative; z-index:2; max-width:var(--measure)}
.page-head h1{font-size:clamp(34px,5.2vw,58px); margin-bottom:var(--s-4)}
.page-head__sub{color:var(--muted); font-size:17px}

/* Dark variant — opt-in, currently only produkter.html.
   COLOUR ONLY. It inherits .page-head's box untouched: no margin, no padding
   override, same border. The band is therefore pixel-identical in position,
   height and width to the light bands on about/contact/kundvagn/checkout, and
   the header stays condensed above it exactly as it does there. Do not give
   this a negative top margin to slide it under the header — that makes the
   band taller than the others, which is the thing this variant must not do. */
.page-head--dark{
  background:var(--grad-deep); color:#F2EFE7;
}
.page-head--dark h1{color:#F2EFE7}
.page-head--dark .page-head__sub{color:var(--muted-dark)}
.page-head--dark .eyebrow{color:var(--gold-pale)}
.page-head--dark .eyebrow::after{background:rgba(110,148,128,.45)}
/* the contour echo needs pale gold on dark, not the light-background gold */
.page-head--dark::after{
  background:
    radial-gradient(circle,transparent 46%,rgba(232,210,138,.1) 46.4%,transparent 47.2%),
    radial-gradient(circle,transparent 62%,rgba(232,210,138,.075) 62.4%,transparent 63.2%),
    radial-gradient(circle,transparent 78%,rgba(232,210,138,.05) 78.4%,transparent 79.2%);
}

/* Interior pages have no dark hero, so the header must start condensed —
   header.js handles the class, this just keeps the spacing honest. */
.page-head + .section{padding-top:var(--s-9)}

/* ---------- panels: one shared surface for boxed content ---------- */
.panel{
  background:var(--paper-raise); border-radius:var(--r-lg);
  box-shadow:var(--sh-1); padding:var(--s-6);
}
.panel--raised{box-shadow:var(--sh-2)}
.panel__h{font-size:20px; margin-bottom:var(--s-4)}

/* ---------- light form fields ---------- */
.input{
  width:100%; font-family:inherit; font-size:15.5px; color:var(--ink);
  background:var(--paper-raise); border:1px solid var(--line-strong);
  border-radius:var(--r-md); padding:12px var(--s-4);
  transition:border-color var(--t-fast),box-shadow var(--t-fast);
}
.input::placeholder{color:#98A29B}
.input:focus{outline:0; border-color:var(--gold); box-shadow:0 0 0 3px rgba(201,162,39,.18)}
.label{
  display:block; font-family:var(--font-mono); font-size:10.5px;
  letter-spacing:.14em; text-transform:uppercase; color:var(--muted);
  margin-bottom:var(--s-2);
}
.field{margin-bottom:var(--s-4)}
.field-row{display:grid; grid-template-columns:1fr 1fr; gap:var(--s-4)}

/* ---------- filters (products listing) ---------- */
.filters{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s-5); flex-wrap:wrap; margin-bottom:var(--s-7);
  padding-bottom:var(--s-5); border-bottom:1px solid var(--line);
}
.chips{display:flex; gap:var(--s-2); flex-wrap:wrap}
/* Category names are set in the display serif, sentence case — the mono
   uppercase they used to carry read as a lab label rather than a shop. The
   sort control beside them stays mono on purpose: these are content, that is
   a mechanism, and the two should not look like the same kind of thing. */
.chip{
  font-family:var(--font-display); font-size:15px; font-weight:500;
  letter-spacing:0; padding:7px var(--s-5); border-radius:var(--r-full);
  border:1px solid var(--line-strong); background:transparent; color:var(--muted);
  cursor:pointer; transition:all var(--t-fast);
}
.chip:hover{border-color:var(--ink); color:var(--ink)}
.chip[aria-pressed="true"]{
  background:var(--ink); border-color:var(--ink); color:var(--paper);
}
.sort{display:flex; align-items:center; gap:var(--s-3)}
.sort__label{
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--muted);
}
.sort__select{
  font-family:var(--font-body); font-size:14px; color:var(--ink);
  background:var(--paper-raise); border:1px solid var(--line-strong);
  border-radius:var(--r-full); padding:9px var(--s-4); cursor:pointer;
}
.sort__select:focus{outline:0; border-color:var(--gold); box-shadow:0 0 0 3px rgba(201,162,39,.18)}
.result-count{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--muted); margin-bottom:var(--s-5);
}
.empty-state{
  padding:var(--s-9) var(--s-5); text-align:center; color:var(--muted);
  background:var(--paper-sink); border-radius:var(--r-lg);
}

/* ---------- product detail ---------- */
.pdp{display:grid; grid-template-columns:1fr 1fr; gap:var(--s-8); align-items:start}
.pdp__visual{
  position:sticky; top:calc(var(--header-h-sm) + var(--s-5));
  border-radius:var(--r-xl); overflow:hidden; box-shadow:var(--sh-2);
}
.pdp__visual .well{aspect-ratio:1}
.pdp__visual .well__svg,.pdp__visual .well__photo{width:52%}
/* The product page bottle is the biggest on the site and has the most room,
   so it gets the longer travel. */
.pdp__visual .well__art{animation:bottle-float-lg 5.6s ease-in-out infinite}
.pdp__cat{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.18em;
  text-transform:uppercase; color:var(--gold-text); display:block; margin-bottom:var(--s-4);
}
.pdp__name{font-size:clamp(30px,4.2vw,46px); margin-bottom:var(--s-4)}
.pdp__meta{display:flex; align-items:center; gap:var(--s-5); flex-wrap:wrap; margin-bottom:var(--s-5)}
.pdp__price{font-family:var(--font-mono); font-size:30px; font-weight:600; font-variant-numeric:tabular-nums}
.pdp__buy{display:flex; gap:var(--s-4); align-items:center; flex-wrap:wrap; margin-bottom:var(--s-5)}
.pdp__sold{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--muted);
}
.pdp__trust{
  display:grid; gap:var(--s-3); margin-top:var(--s-6);
  padding-top:var(--s-5); border-top:1px solid var(--line);
}
.pdp__trust li{
  display:flex; gap:var(--s-3); align-items:flex-start;
  font-size:14px; color:var(--muted);
}
.pdp__trust svg{flex:0 0 auto; margin-top:3px; color:var(--gold-text)}
@media (max-width:860px){
  .pdp{grid-template-columns:1fr; gap:var(--s-6)}
  .pdp__visual{position:static}
}

/* ---------- reviews ---------- */
.reviews{display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:var(--s-5)}
.review{
  background:var(--paper-raise); border-radius:var(--r-lg); padding:var(--s-6);
  box-shadow:var(--sh-1); display:flex; flex-direction:column; gap:var(--s-3);
}
.review__top{display:flex; align-items:center; justify-content:space-between; gap:var(--s-3)}
.review__author{font-family:var(--font-display); font-size:17px; font-weight:600}
.review__text{color:var(--muted); font-size:15px}

/* ---------- FAQ accordion ---------- */
/* ---------- long-form product copy ---------- */
/* Sits inside the buy column, between the add-to-cart button and the delivery
   promises, so it is already measure-constrained by the column. The cap only
   bites on a very wide screen. */
.prose{max-width:var(--measure); margin-top:var(--s-6)}
/* Weight 700 — the mono face is loaded at 400/500/600/700 for this. Without the
   700 in the font URL the browser fakes the bold by smearing the 600, which at
   11px uppercase turns the letters to mud. */
.prose__h{
  font-family:var(--font-mono); font-size:11.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--gold-text); font-weight:700;
  padding-bottom:var(--s-3); margin-bottom:var(--s-4);
  border-bottom:1px solid var(--line);
}
.prose__h + .prose__p{margin-bottom:var(--s-6)}
.prose__p:last-child{margin-bottom:0}
/* --muted, like the FAQ answers, but looser: these are paragraphs someone reads
   through, not a line they scan. */
.prose__p{font-size:15.5px; line-height:1.7; color:var(--muted)}

.faq{display:grid; gap:var(--s-2); max-width:760px}
.faq__item{
  background:var(--paper-raise); border-radius:var(--r-md);
  box-shadow:var(--sh-1); overflow:hidden;
}
.faq__q{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-4);
  padding:var(--s-5); cursor:pointer; list-style:none;
  font-family:var(--font-display); font-size:17px; font-weight:600;
  transition:background var(--t-fast);
}
.faq__q::-webkit-details-marker{display:none}
.faq__q:hover{background:var(--paper-sink)}
.faq__q::after{
  content:""; flex:0 0 auto; width:10px; height:10px;
  border-right:1.6px solid var(--gold-text); border-bottom:1.6px solid var(--gold-text);
  transform:rotate(45deg) translateY(-2px); transition:transform var(--t-base);
}
.faq__item[open] .faq__q::after{transform:rotate(-135deg) translateY(-2px)}
.faq__a{padding:0 var(--s-5) var(--s-5); color:var(--muted); font-size:15px; max-width:62ch}
/* Closing move on the FAQ page: a list of questions that simply stops is a
   dead end, so it hands the visitor somewhere to go. */
.faq__foot{
  margin-top:var(--s-8); padding-top:var(--s-7);
  border-top:1px solid var(--line);
  display:flex; flex-wrap:wrap; align-items:center;
  justify-content:space-between; gap:var(--s-5);
}
.faq__foot p{color:var(--muted); font-size:15.5px; max-width:46ch}

/* ---------- sticky mobile buy bar ---------- */
.buybar{
  position:fixed; left:0; right:0; bottom:0; z-index:90;
  display:none; align-items:center; justify-content:space-between; gap:var(--s-4);
  padding:var(--s-3) var(--s-5);
  background:rgba(247,245,240,.92); backdrop-filter:blur(16px) saturate(1.5);
  -webkit-backdrop-filter:blur(16px) saturate(1.5);
  border-top:1px solid var(--line); box-shadow:0 -4px 24px rgba(10,15,12,.08);
}
.buybar__name{font-size:13px; color:var(--muted); display:block}
.buybar__price{font-family:var(--font-mono); font-size:17px; font-weight:600}
@media (max-width:860px){
  .buybar{display:flex}
  body:has(.buybar){padding-bottom:78px}
}

/* ---------- cart table ---------- */
.cart-table{width:100%; border-collapse:collapse}
.cart-table__row td{padding:var(--s-4) var(--s-2); border-bottom:1px solid var(--line); vertical-align:top}
.cart-table__row:last-child td{border-bottom:0}
.cart-table__name{font-weight:600}
.cart-table__savings{
  display:block; margin-top:var(--s-1);
  font-family:var(--font-mono); font-size:11px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--gold-text); font-weight:400;
}
.cart-table__price{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  text-align:right; white-space:nowrap;
}
/* Automatic bundle discount earned by loose products. Tinted so it reads as a
   credit rather than another thing being charged for. */
.cart-table__row--discount td{background:var(--paper-sink)}
.cart-table__row--discount .cart-table__name,
.cart-table__row--discount .cart-table__price{color:var(--gold-text)}
.cart-table__remove{
  background:none; border:0; color:var(--muted); cursor:pointer;
  font-size:13px; padding:var(--s-1) var(--s-2); border-radius:var(--r-sm);
  transition:color var(--t-fast),background var(--t-fast);
}
.cart-table__remove:hover{color:var(--ink); background:var(--paper-sink)}
.cart-total{
  display:flex; align-items:baseline; justify-content:space-between;
  padding-top:var(--s-5); margin-top:var(--s-4); border-top:2px solid var(--ink);
}
.cart-total__label{font-family:var(--font-mono); font-size:11px; letter-spacing:.16em; text-transform:uppercase}
.cart-total__value{font-family:var(--font-mono); font-size:24px; font-weight:600; font-variant-numeric:tabular-nums}
.cart-empty{color:var(--muted); margin-bottom:var(--s-5)}

/* ---------- cart / checkout layout ---------- */
.shop-layout{display:grid; grid-template-columns:1.5fr 1fr; gap:var(--s-8); align-items:start}
.shop-layout__side{position:sticky; top:calc(var(--header-h-sm) + var(--s-5)); display:grid; gap:var(--s-4)}
@media (max-width:900px){
  .shop-layout{grid-template-columns:1fr; gap:var(--s-6)}
  .shop-layout__side{position:static}
}
.checkout-section{margin-bottom:var(--s-7)}
.checkout-section__h{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--muted); font-weight:500;
  padding-bottom:var(--s-3); margin-bottom:var(--s-5);
  border-bottom:1px solid var(--line);
}

/* ---------- upsell ---------- */
.upsell{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s-4); flex-wrap:wrap; padding:var(--s-5);
  border-radius:var(--r-lg); background:rgba(201,162,39,.08);
  box-shadow:inset 0 0 0 1px rgba(201,162,39,.28);
}
.upsell__label{
  font-family:var(--font-mono); font-size:10px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--gold-text); display:block; margin-bottom:var(--s-2);
}
.upsell__name{font-family:var(--font-display); font-size:16px; font-weight:600}
.upsell__price{font-family:var(--font-mono); font-weight:400; color:var(--muted); margin-left:.45em}
/* Thumbnail of the recommended product: the same .well as the product cards,
   so the customer sees the bottle and not only its name. No .well__demo badge
   here — at 64px it would cover the vessel. */
.upsell__well{
  flex:0 0 auto; width:64px; aspect-ratio:4/5; border-radius:var(--r-md);
}
/* Larger than the .well default (54%): at 64px the vessel would otherwise be
   about 35px wide and read as a smudge rather than a bottle. */
.upsell__well .well__svg{width:64%; max-height:80%}
/* The summary column is narrow. Letting the thumbnail, the text and the button
   share one row squeezed the label onto two lines, so the button gets its own
   full-width row — which also makes it a much bigger target. */
.upsell__text{flex:1 1 8rem; min-width:0}
.upsell__cta{flex:1 1 100%}

/* ---------- confirmation ---------- */
.confirmation{
  position:relative; overflow:hidden; text-align:center;
  padding:var(--s-10) var(--s-6); border-radius:var(--r-xl);
  background:var(--grad-deep); color:#F2EFE7;
}
.confirmation::before{
  content:""; position:absolute; left:50%; top:-38%; width:78%; aspect-ratio:1;
  transform:translateX(-50%); pointer-events:none;
  background:radial-gradient(circle,rgba(201,162,39,.24) 0%,transparent 60%);
}
.confirmation__in{position:relative; z-index:2; max-width:520px; margin:0 auto}
.confirmation__h{font-size:clamp(28px,4vw,42px); margin-bottom:var(--s-4)}
.confirmation p{color:var(--muted-dark); margin-bottom:var(--s-4)}
.confirmation__total{
  font-family:var(--font-mono); font-size:20px; font-weight:600;
  color:var(--gold-pale) !important; margin:var(--s-6) 0 !important;
}
.confirmation:focus-visible{outline:2px solid var(--gold); outline-offset:4px}

/* ---------- about ---------- */
/* The opening statement. It used to sit at body size in a half-width column
   with three hundred pixels of nothing under it, which read as a mistake
   rather than as space. Now it is sized like the statement it is, carries the
   gold rule the rest of the site uses to open a thought, and sticks while the
   story scrolls past it — so the column is occupied the whole way down and the
   space beneath it is doing a job. */
/* Less air under the opening block than a standard section, so the top edge of
   the next one clears the first screen. That sliver of dark shape is what tells
   a visitor the page continues — the fold used to land on empty background,
   which reads as the end of the page.
   Tuned against the LONGEST translation, not the Swedish. At --s-6 the Swedish
   cleared the fold by 25px while every other language missed it by 2px, which
   is exactly the kind of thing that only shows up if you check all six. */
.about-intro{padding-bottom:var(--s-3)}

.about-lede{
  position:sticky; top:calc(var(--header-h) + var(--s-6));
  font-family:var(--font-display); font-size:clamp(24px,3.1vw,35px);
  line-height:1.2; letter-spacing:-.024em; max-width:16ch; color:var(--ink);
}
.about-lede::before{
  content:""; display:block; width:54px; height:2px;
  background:var(--grad-gold); margin-bottom:var(--s-5);
}
.about-grid{display:grid; grid-template-columns:1fr 1.1fr; gap:var(--s-8); align-items:start}
/* Stretching this one column is what gives position:sticky room to work — a
   sticky element only travels inside its own containing block, and an
   auto-height column is exactly as tall as the text in it. */
.about-grid__aside{align-self:stretch; display:flex; flex-direction:column}
/* A hairline running down the rest of the column. It does two jobs: it fills
   the space under the short opening line, which otherwise read as a mistake,
   and it runs off the bottom of the first screen — which is the only thing
   telling a visitor the page continues, since the fold used to land on plain
   background. Purely graphic, so it needs no translation. */
.about-grid__aside::after{
  content:""; display:block; flex:1; width:1px; min-height:120px;
  margin:var(--s-6) 0 0 2px;
  background:linear-gradient(to bottom,
    var(--gold) 0, rgba(201,162,39,.35) 45%, rgba(201,162,39,0) 100%);
}
.about-grid p{margin-bottom:var(--s-5); color:var(--muted); max-width:58ch}
/* Same trick as the opening column: a stretched wrapper gives the figure room
   to stick, so it stays beside the three principles instead of ending early and
   leaving the column empty for the rest of the section. */
.about-grid__figcol{align-self:stretch}
.about-grid__figcol .about-figure{position:sticky; top:calc(var(--header-h) + var(--s-6))}

/* Square, not 4:5. At 4:5 the placeholder stood 662px tall beside 570px of
   text and dragged the whole section open on its own. */
.about-figure{
  position:relative; aspect-ratio:1; border-radius:var(--r-xl);
  overflow:hidden; background:var(--grad-deep); box-shadow:var(--sh-3);
}
.about-figure::before{
  content:""; position:absolute; left:50%; top:50%; width:150%; aspect-ratio:1;
  transform:translate(-50%,-50%);
  background:radial-gradient(circle,rgba(201,162,39,.26) 0%,rgba(201,162,39,.08) 36%,transparent 62%);
}
.about-figure::after{
  content:""; position:absolute; inset:0;
  background:
    radial-gradient(circle at 50% 50%,transparent 24%,rgba(232,210,138,.14) 24.4%,transparent 25.2%),
    radial-gradient(circle at 50% 50%,transparent 36%,rgba(232,210,138,.1) 36.4%,transparent 37.2%),
    radial-gradient(circle at 50% 50%,transparent 48%,rgba(232,210,138,.07) 48.4%,transparent 49.2%),
    radial-gradient(circle at 50% 50%,transparent 60%,rgba(232,210,138,.05) 60.4%,transparent 61.2%);
}
/* The chart IS the artwork now, so the concentric-ring motif underneath is
   turned off for this figure — rings behind a line chart is just noise. The
   gold bloom (::before) stays as the light source. */
.about-grid__figcol .about-figure::after{display:none}
.about-figure__chart{
  position:absolute; inset:0; z-index:1;
  width:100%; height:100%;
}

.about-figure__cap{
  position:absolute; left:var(--s-5); bottom:var(--s-5); z-index:2;
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--gold-pale);
}
@media (max-width:860px){ .about-grid{grid-template-columns:1fr; gap:var(--s-6)} }

/* ---------- contact ---------- */
.contact-grid{display:grid; grid-template-columns:1fr 1fr; gap:var(--s-7); align-items:start}
.contact-list{display:grid; gap:var(--s-5); margin:0}
.contact-list dt{
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--muted); margin-bottom:var(--s-2);
}
.contact-list dd{margin:0; font-size:17px}
.contact-list dd a{color:var(--gold-text); border-bottom:1px solid transparent; transition:border-color var(--t-fast)}
.contact-list dd a:hover{border-bottom-color:var(--gold)}
.policy-grid{display:grid; grid-template-columns:1fr 1fr; gap:var(--s-5)}
.policy__h{
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--gold-text); margin-bottom:var(--s-3);
}
.policy p{color:var(--muted); font-size:15px}
@media (max-width:760px){
  .contact-grid,.policy-grid,.field-row{grid-template-columns:1fr}
}

/* ---------- error page ---------- */
/* Same reasoning as .page-head — no negative margin, the condensed header
   sits above this band rather than over it. */
.error-page{
  min-height:60vh; display:flex; align-items:center;
  background:var(--grad-deep); color:#F2EFE7;
  padding:var(--s-10) 0;
  position:relative; overflow:hidden;
}
.error-page::before{
  content:""; position:absolute; right:-8%; top:-20%; width:56%; aspect-ratio:1;
  pointer-events:none;
  background:radial-gradient(circle,rgba(201,162,39,.2) 0%,transparent 62%);
}
.error-page__in{position:relative; z-index:2; max-width:560px}
.error-page__code{
  font-family:var(--font-mono); font-size:11.5px; letter-spacing:.2em;
  text-transform:uppercase; color:var(--gold-pale); margin-bottom:var(--s-5);
}
.error-page h1{font-size:clamp(34px,5.4vw,58px); margin-bottom:var(--s-4)}
.error-page__sub{color:var(--muted-dark); font-size:17px; margin-bottom:var(--s-7)}
.error-page__actions{display:flex; gap:var(--s-4); flex-wrap:wrap}

/* ==================================================================
   PHONE LAYOUT — everything below 860px that has to beat an earlier rule.

   Deliberately at the foot of the file. Media queries carry no extra
   specificity in this stylesheet, so a `max-width:860px` rule placed above a
   plain rule for the same selector quietly loses; putting these last makes
   source order do the work. Two of them depend on it outright: the hero
   specimen is hidden by a `max-width:980px` block near line 880, and
   .card__add fixes its size around line 752.
   ================================================================== */
/* Absent above 860px, and hidden by the utility class until product-detail.js
   resolves the product. Stated before the media query, not after it: both
   declarations are a single class, so whichever comes last would win outright
   — and `display:none` last would mean the bar never appears at all. */
.buybar{display:none}

@media (max-width:860px){

  /* ------------------------- hero specimen -------------------------
     The bottle is the only distinctive image on the site and it was absent
     from every phone. It sits beside the headline, not beneath it.

     Anchored to .hero rather than placed in flow, because the specimen is a
     sibling of .wrap — it is not inside .hero__in and so cannot simply float
     next to the heading. .hero is the nearest positioned ancestor, and its
     top padding is the same expression used here, which lines the bottle's top
     edge up with the eyebrow's without hard-coding a pixel offset.

     Only the eyebrow and the headline reserve room for it. The paragraph and
     the buttons keep the full column width: a text column narrow enough to sit
     beside the bottle turns that paragraph into seven cramped lines and stacks
     the two call-to-action buttons. */
  /* One number drives both the bottle and the space reserved for it, so they
     cannot drift apart. The +26px covers the gap plus the float animation's
     rotation, which inflates the rendered box about 6% wider than the
     declared width.

     On :root, not bare inside the media block — a declaration that is not
     inside a rule is a parse error, and the browser discards it along with
     whatever it swallows next. */
  :root{--hero-bottle:112px}

  .hero__specimen{
    display:block;
    position:absolute;
    top:calc(var(--header-h) + var(--s-8));
    /* Matches .wrap's own side padding, which drops to --s-5 below 720px.
       Anchoring to --s-6 instead put the bottle 8px inside the text's right
       edge, and the rotation then pushed it into the reserved column — the
       Norwegian headline collided with it by 4px. */
    right:var(--s-5); left:auto;
    transform:none;
    width:var(--hero-bottle); margin:0;
    /* The absolute version's keyframes bake in translateY(-50%) for centring.
       Reusing them here would shove the bottle half its height off the top. */
    animation:hero-float-sm 9s ease-in-out infinite;
  }
  .hero__eyebrow,.hero__h{padding-right:calc(var(--hero-bottle) + 26px)}
  /* Sized so the longest headline word still clears the narrowed column —
     Finnish "ravintolisää." and English "supplements." are the binding cases,
     not the Swedish this was first eyeballed against. */
  .hero__h{font-size:clamp(29px,8.2vw,38px)}

  /* ------------------------- product cards -------------------------
     Cards lie down: art left, text right. Chosen over a two-up grid because
     the text column never narrows, so no language can wrap a product name.
     Takes the product list from 9.3 screens to roughly 4.5. */
  .grid--products{grid-template-columns:1fr; gap:var(--s-3)}
  .grid--products .card{flex-direction:row; align-items:stretch}
  .grid--products .card .well{
    flex:0 0 116px; width:116px; aspect-ratio:auto; align-self:stretch;
  }
  .grid--products .card__body{
    flex:1; min-width:0; gap:var(--s-2); padding:var(--s-4);
    justify-content:center;
  }
  /* The blurb is what made a full-width card a screen tall. Name, rating and
     price carry the browsing decision; the blurb is one tap away on the
     product page. */
  .grid--products .card__desc{display:none}
  .grid--products .card__name{font-size:17px}
  .grid--products .card__foot{padding-top:var(--s-3)}
  /* On a 116px-wide panel the badge's usual top-right corner lands squarely on
     the bottle's lid. Tucked into the bottom-left it sits on empty backdrop. */
  .grid--products .well__demo{
    top:auto; bottom:6px; right:auto; left:6px;
    font-size:8.5px; letter-spacing:.1em; padding:2px 6px;
  }
  /* 38px was under the 44px minimum both Apple and Google publish. */
  .card__add{width:44px; height:44px}

  /* --------------------- fields and tap targets ---------------------
     The bare `input,select,textarea{font-size:16px}` in the touch-layer block
     above loses to every one of these, because a single class outranks a bare
     element selector no matter which comes last. Each field style therefore has
     to be named. Checkout's eight fields all run through .input. */
  .field__in,.input,.sort__select,.search__in{font-size:16px}
  .input,.field__in,.sort__select{min-height:44px}

  /* One pixel short of the minimum, and it is the first thing a keyboard or
     screen-reader user reaches. */
  .skip-link{min-height:44px; display:inline-flex; align-items:center}

  /* The wordmark is the home link and was a 26px-tall target. */
  .nav__word{display:inline-flex; align-items:center; min-height:44px}

  /* mailto: and tel: links — the two things a visitor on a phone is most
     likely to actually tap on this page. */
  .contact-list dd a{
    display:inline-flex; align-items:center; min-height:44px;
  }

  /* -------------------------- sticky buy bar ------------------------ */
  .buybar{
    display:flex; align-items:center; gap:var(--s-4);
    position:fixed; left:0; right:0; bottom:0; z-index:250;
    padding:var(--s-3) var(--s-4);
    padding-bottom:calc(var(--s-3) + env(safe-area-inset-bottom));
    background:rgba(247,245,240,.94);
    -webkit-backdrop-filter:blur(16px) saturate(1.5);
    backdrop-filter:blur(16px) saturate(1.5);
    border-top:1px solid var(--line);
    box-shadow:0 -2px 24px rgba(10,15,12,.10);
    transform:translateY(100%);
    transition:transform .28s ease;
  }
  .buybar.is-visible{transform:none}
  .buybar__text{display:flex; flex-direction:column; min-width:0; flex:1}
  .buybar__name{
    font-family:var(--font-display); font-size:14.5px; font-weight:600;
    color:var(--ink);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  .buybar__price{
    font-family:var(--font-mono); font-size:13px; font-weight:600;
    font-variant-numeric:tabular-nums; color:var(--muted);
  }
  .buybar__btn{flex:0 0 auto; padding:12px var(--s-5); font-size:14px}
  /* Reserve the bar's height at the foot of the page so it never covers the
     last line of the footer. */
  html.has-buybar .site-footer{
    padding-bottom:calc(var(--s-10) + 72px + env(safe-area-inset-bottom));
  }
}

/* Two classes, so this beats the single-class display:flex above at any width
   and the bar stays down until the product actually resolves. */
.buybar.hidden{display:none}

@keyframes hero-float-sm{
  0%,100%{transform:translateY(0) rotate(-3deg)}
  50%    {transform:translateY(-10px) rotate(-1deg)}
}

/* Very narrow phones (iPhone SE 1st gen and similar). Reserving room for the
   bottle leaves a text column of about 134px, and no headline size that still
   reads as a headline fits English "supplements." or Finnish "ravintolisää."
   into it — they overhang the bottle by 20-40px. Below 360px the bottle
   therefore goes back under the text, where width stops mattering. */
@media (max-width:359px){
  .hero{flex-direction:column; align-items:stretch}
  .hero__specimen{
    position:static; top:auto; right:auto;
    width:min(44vw,150px);
    margin:var(--s-5) auto 0;
  }
  .hero__eyebrow,.hero__h{padding-right:0}
  .hero__h{font-size:clamp(32px,10vw,40px)}
}
