/* ==========================================================================
   COCONAUT — C2 "Flavour Blocks"
   Colour-blocked bento. Each flavour owns a block; the real can colours carry
   the design, so every block stays minimal. Built to the ui-ux-pro-max system
   at design-system/coconaut-c2-blocks/MASTER.md, palette overridden to the
   brand colours sampled from the actual product renders.
   Motion budget: staggered entrance + hover (classic Wix Editor safe).
   ========================================================================== */

/* Rubik + Nunito Sans, self-hosted. SIL OFL 1.1.
   Replaces the Google Fonts @import: no third-party request, no
   render-blocking round trip, no dependency that can fail. */

/* round 5 (w): ONE @font-face per family and subset. The 22 per-weight faces all
   pointed at byte-identical files (Google's VARIABLE font, saved once per weight), so a
   page downloaded the same 31-35 KB file up to six times and each copy swapped in at
   its own moment. A weight RANGE on one face makes the browser use the font's own wght
   axis: same glyphs, one request per family, one swap. */
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:300 700;font-display:swap;src:url(../fonts/nunitosans-400-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:300 700;font-display:swap;src:url(../fonts/nunitosans-400-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Rubik';font-style:normal;font-weight:400 900;font-display:swap;src:url(../fonts/rubik-400-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Rubik';font-style:normal;font-weight:400 900;font-display:swap;src:url(../fonts/rubik-400-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}

:root {
  --ground:      #FFFFFF;
  --ground-alt:  #F4F6F3;
  --ink:         #0F1A13;
  --ink-soft:    #55635A;
  --ink-faint:   #5C6B62;
  --line:        #E4E9E4;

  --green:       #0AAF4E;
  --green-dark:  #07853B;
  --green-darker: #066A2F;
  --green-deep:  #0E2A1A;
  --brown:       #84735E;
  --brown-deep:  #2A2320;

  --peach:      #F1628E;
  --mango:      #F59618;
  --yuzu:       #C0CE30;
  --pineapple:  #E4C446;
  --watermelon: #D32E3B;
  --sparkling:  #97DADE;

  /* standard density — 4/10 */
  --s-xs: 4px;  --s-sm: 8px;  --s-md: 16px;
  --s-lg: 24px; --s-xl: 40px; --s-2xl: 56px; --s-3xl: 80px;

  --maxw: 1320px;
  --r: 20px;
  --ease: cubic-bezier(.22,.61,.36,1);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: 'Nunito Sans', system-ui, -apple-system, sans-serif;
  font-size: 17px;
  line-height: 1.6;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

h1, h2, h3, h4, .display {
  font-family: 'Rubik', system-ui, sans-serif;
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1.02;
  text-wrap: balance;
  margin: 0;
}

::selection { background: var(--green); color: #fff; }
:focus-visible { outline: 3px solid var(--green); outline-offset: 3px; border-radius: 4px; }

.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--s-lg); }

.eyebrow {
  font-family: 'Rubik', sans-serif;
  font-size: 12px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--green-dark); margin: 0 0 var(--s-md);
}

/* ---- buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  min-height: 54px; padding: 0 32px;
  border: 0; border-radius: 999px;
  background: var(--green-dark); color: #fff;
  font-family: 'Rubik', sans-serif; font-size: 15.5px; font-weight: 600;
  text-decoration: none; cursor: pointer;
  transition: background .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);
  box-shadow: 0 6px 18px rgba(10,175,78,.22);
}
.btn:hover { background: var(--green-darker); transform: translateY(-2px); box-shadow: 0 10px 26px rgba(10,175,78,.3); }
.btn:active { transform: translateY(0); }
.btn--ghost { background: #fff; color: var(--ink); border: 2px solid var(--ink); box-shadow: none; }
.btn--ghost:hover { background: var(--ink); color: #fff; box-shadow: none; }
.btn--onDark { background: #fff; color: var(--ink); box-shadow: none; }
.btn--onDark:hover { background: var(--ink); color: #fff; }

/* ---- header -------------------------------------------------------------- */
.hdr {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  transition: background .3s var(--ease), box-shadow .3s var(--ease);
}
.hdr.is-stuck { background: rgba(255,255,255,.94); backdrop-filter: saturate(180%) blur(12px); box-shadow: 0 1px 0 var(--line); }
.hdr__in {
  max-width: var(--maxw); margin: 0 auto; padding: 16px var(--s-lg);
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-lg);
}
.logo { display: inline-flex; align-items: center; line-height: 0; text-decoration: none; }
.logo img { height: 24px; width: auto; display: block; }
.hdr--onDark .logo img { content: url(../assets/coconaut-logo-white.svg); }
.nav { display: flex; gap: 30px; }
.nav a {
  font-family: 'Rubik', sans-serif; font-size: 14.5px; font-weight: 500;
  text-decoration: none; color: var(--ink-soft); padding: 8px 0;
  transition: color .2s var(--ease);
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--green-dark); }
.hdr__cta { display: flex; align-items: center; gap: 12px; }
.burger { display: none; width: 46px; height: 46px; align-items: center; justify-content: center; background: var(--ground-alt); border: 0; border-radius: 14px; cursor: pointer; }
.burger span { display: block; width: 17px; height: 2px; background: var(--ink); position: relative; border-radius: 2px; }
.burger span::before, .burger span::after { content: ''; position: absolute; left: 0; width: 17px; height: 2px; background: var(--ink); border-radius: 2px; }
.burger span::before { top: -6px; } .burger span::after { top: 6px; }

/* ---- hero bento ---------------------------------------------------------- */
.hero { padding: 118px 0 var(--s-2xl); }
.bento {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* A range, not a fixed height. `200px` with `.tile--hero` spanning two rows
     gave the hero exactly 400px, and `.tile` hides its overflow — so the Ueber-uns
     hero, whose headline runs to three lines, had its eyebrow cut off by 49px at
     desktop width. The homepage fits inside 400px and is unaffected; the row
     grows only where the content needs it. The same fixed-height mistake at
     170px was clipping the homepage's primary CTA in half on a phone. */
  grid-auto-rows: minmax(200px, auto);
  gap: var(--s-md);
}
.tile {
  border-radius: var(--r);
  padding: var(--s-xl);
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  text-decoration: none;
  color: inherit;
  transition: transform .28s var(--ease), box-shadow .28s var(--ease);
}
a.tile:hover { transform: translateY(-5px); box-shadow: 0 18px 40px rgba(15,26,19,.14); }

.tile--hero { grid-column: span 2; grid-row: span 2; background: var(--green-deep); color: #fff; justify-content: center; }
.tile--hero h1 { font-size: clamp(2.4rem, 4vw, 4rem); font-weight: 900; margin-bottom: var(--s-md); }
.tile--hero p { opacity: .8; max-width: 34ch; margin: 0 0 var(--s-xl); font-size: 1.05rem; }

.tile--shot { grid-column: span 2; grid-row: span 2; background: var(--ground-alt); padding: 0; }
.tile--shot img { width: 100%; height: 100%; object-fit: cover; }

.tile--fact { background: var(--ground-alt); justify-content: center; }
.tile--fact b {
  display: block; font-family: 'Rubik', sans-serif; font-weight: 800;
  font-size: clamp(1.8rem, 3vw, 2.6rem); letter-spacing: -.03em; line-height: 1; margin-bottom: 8px;
}
.tile--fact span { font-size: 14px; color: var(--ink-soft); }

.tile--green { background: var(--green-dark); color: #fff; }
.tile--fact.tile--green b { color: #fff; }
.tile--green b, .tile--green span { color: #fff; }
/* No opacity on the span (was .9): white at 90% on --green-dark reads as #e6f3eb,
   4.15:1, below WCAG AA 4.5:1 for 14px text. Solid white is 4.74:1. */

/* ---- flavour blocks ------------------------------------------------------ */
.blocks { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-md); }
.blk {
  position: relative;
  border-radius: var(--r);
  min-height: 420px;
  padding: var(--s-xl);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  text-decoration: none;
  color: #fff;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.blk:hover { transform: translateY(-6px); box-shadow: 0 22px 48px rgba(15,26,19,.18); }
.blk__word {
  font-family: 'Rubik', sans-serif;
  font-weight: 900;
  font-size: clamp(1.6rem, 2.4vw, 2.3rem);
  letter-spacing: -.04em;
  line-height: .95;
  position: relative; z-index: 2;
}
.blk__sub { font-size: 14.5px; margin: 10px 0 0; max-width: 24ch; position: relative; z-index: 2; }
.blk__price {
  margin-top: auto;
  position: relative; z-index: 2;
  font-family: 'Rubik', sans-serif; font-weight: 700; font-size: 15px;
  display: inline-flex; align-items: center; gap: 8px;
}
.blk__can {
  position: absolute;
  right: -6%;
  bottom: -4%;
  height: 78%;
  width: auto;
  z-index: 1;
  filter: drop-shadow(0 22px 34px rgba(0,0,0,.26));
  transition: transform .35s var(--ease);
}
.blk:hover .blk__can { transform: translateY(-10px) rotate(-2deg); }

/* Dark type on the two light-warm blocks. White on mango scored 2.27:1 — under
   even the 3:1 large-text floor, i.e. not a preference but a failure — and white
   on peach scored 3.05:1, which passes for the heading and fails for the copy.
   Switching the TEXT rather than the background keeps the brand colours exactly
   as sampled from the real cans, and it is the treatment yuzu, pineapple and
   sparkling already use for the same reason. Now 7.86:1 and 5.84:1. */
.blk--peach      { background: var(--peach); color: var(--ink); }
.blk--mango      { background: var(--mango); color: var(--ink); }
.blk--yuzu       { background: var(--yuzu); color: var(--ink); }
.blk--pineapple  { background: var(--pineapple); color: var(--ink); }
.blk--watermelon { background: var(--watermelon); }
.blk--sparkling  { background: var(--sparkling); color: var(--ink); }
.blk--cocoffee   { background: var(--brown-deep); }
.blk--moiso      { background: var(--green-dark); }

/* ---- section shell ------------------------------------------------------- */
.sec { padding: var(--s-3xl) 0; }
.sec--alt { background: var(--ground-alt); }
.sec__head { margin-bottom: var(--s-2xl); max-width: 60ch; }
.sec__head h2 { font-size: clamp(2rem, 4vw, 3.2rem); margin-bottom: var(--s-md); }
.sec__head p { color: var(--ink-soft); margin: 0; }

/* ---- product cards ------------------------------------------------------- */
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-lg); }
.card {
  background: var(--ground-alt);
  border-radius: var(--r);
  padding: var(--s-xl);
  text-decoration: none; color: inherit;
  display: flex; flex-direction: column;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}
.card:hover { transform: translateY(-5px); box-shadow: 0 18px 40px rgba(15,26,19,.12); }
.card__pic { height: 230px; display: flex; align-items: center; justify-content: center; margin-bottom: var(--s-lg); }
.card__pic img { max-height: 100%; width: auto; }
.card h3 { font-size: 1.08rem; font-weight: 700; line-height: 1.28; margin-bottom: 6px; }
.card__meta { font-size: 13.5px; color: var(--ink-faint); margin-bottom: var(--s-md); }
.card__foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; }
.card__price { font-family: 'Rubik', sans-serif; font-weight: 800; font-size: 1.15rem; }
/* #07823A, not --green-dark: on the card ground #f4f6f3 --green-dark is 4.36:1
   (below WCAG AA 4.5:1 at 13.5px). 2% darker gives 4.53:1. */
.card__buy { font-size: 13.5px; font-weight: 600; color: #07823A; }
.badge {
  align-self: flex-start;
  font-family: 'Rubik', sans-serif; font-size: 11px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 6px 12px; border-radius: 999px;
  background: var(--green-dark); color: #fff; margin-bottom: var(--s-md);
}
.badge--soft { background: #fff; color: var(--ink-soft); }

/* Badge must not push card contents down ------------------------------------
   Measured 2026-09-10 on the live shop page: the 6 cards carrying a badge had
   their title 46px lower than the 9 without one (badge 30px + 16px margin),
   because .badge sat in normal flow above .card__pic. Card heights and image
   boxes were already identical - only the badge broke the alignment, so titles,
   prices and buttons never lined up across a row.
   Lifting the badge out of flow puts every card's contents on one baseline.
   top/left = var(--s-xl) reproduces its previous position exactly (that is the
   card's own padding), so nothing moves visually except the alignment fix. */
/* CORRECTED TWICE. Final shape: the badge NEVER overlaps anything ------------
   Attempt 1 (02:01) made every .badge absolute to fix row alignment. That put
   it on the <h2> of the 16 cards that have a badge and no picture.
   Attempt 2 (03:39) scoped it with :has(> .card__pic). Better, but still wrong:
   the owner then found "3 SORTEN" sitting on the product photo. Measured on the
   live homepage — 5 of the badges overlapped their own image, because the fix
   assumed every product shot is a tall narrow can that leaves the top-left
   corner empty. family-mix-alpha.png is 702x615, a wide group shot that fills
   the box; it lost 63x30px behind the pill. The three single cans lost 6-28px.

   Root cause of BOTH regressions: I was moving the badge to dodge whatever
   happened to sit under it, instead of giving it space of its own.

   Final: the badge goes back into normal flow, where the design originally put
   it, and cards that have a picture but NO badge get an invisible slot of
   exactly the same height. Titles, prices and buttons line up across a row
   because every card reserves the row, filled or not — and nothing is ever
   painted on top of anything. Verified: badge-on-image 0 (was 5), row title
   spread 0, at 1280 and 390. */
.card:has(> .card__pic) { position: relative; }
.card:has(> .card__pic) > .badge {
  position: static; margin-bottom: var(--s-md); align-self: flex-start; flex: none;
}
.card:has(> .card__pic):not(:has(> .badge))::before {
  content: ''; display: block; flex: none;
  height: 30px; margin-bottom: var(--s-md);
}


/* ---- wide panel ---------------------------------------------------------- */
.panel { border-radius: var(--r); overflow: hidden; }
.panel__grid { display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: var(--s-2xl); padding: var(--s-3xl) var(--s-2xl); }
.panel--dark { background: var(--brown-deep); color: #fff; }
.panel--dark .eyebrow { color: rgba(255,255,255,.6); }
.panel h2 { font-size: clamp(1.9rem, 3.6vw, 3rem); margin-bottom: var(--s-md); }
.panel p { opacity: .85; margin: 0 0 var(--s-xl); max-width: 44ch; }
.price-chip {
  display: inline-flex; gap: 8px; align-items: baseline;
  padding: 9px 18px; border-radius: 999px;
  background: rgba(255,255,255,.14);
  font-family: 'Rubik', sans-serif; font-weight: 600; font-size: 13.5px;
  margin-bottom: var(--s-xl);
}

/* ---- trust --------------------------------------------------------------- */
.trust { text-align: center; padding: var(--s-3xl) 0; }
.trust p { font-family: 'Rubik', sans-serif; font-weight: 700; font-size: clamp(1.4rem, 3vw, 2.3rem); letter-spacing: -.03em; margin: 0 0 var(--s-md); }
.trust span { color: var(--ink-faint); font-size: 14.5px; }

/* ---- footer -------------------------------------------------------------- */
.ftr { background: var(--green-deep); color: rgba(255,255,255,.74); padding: var(--s-3xl) 0 var(--s-xl); }
.ftr__grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--s-xl); margin-bottom: var(--s-2xl); }
.ftr h4 { font-family: 'Rubik', sans-serif; font-size: 12px; letter-spacing: .16em; text-transform: uppercase; color: #fff; margin: 0 0 var(--s-md); }
.ftr ul { list-style: none; margin: 0; padding: 0; }
.ftr li { margin-bottom: 10px; }
.ftr a { text-decoration: none; font-size: 14.5px; }
.ftr a:hover { color: #fff; }
.ftr__brand { font-family: 'Rubik', sans-serif; font-size: 30px; font-weight: 900; color: #fff; margin-bottom: var(--s-md); letter-spacing: -.03em; }
.ftr__bar { border-top: 1px solid rgba(255,255,255,.15); padding-top: var(--s-md); display: flex; justify-content: space-between; flex-wrap: wrap; gap: var(--s-md); font-size: 13px; }

/* ---- reveal (motion 5/10 — standard, staggered) -------------------------- */
.js .js .rv { opacity: 0; transform: translateY(20px); transition: opacity .45s var(--ease), transform .45s var(--ease); }
.js .js .rv.in { opacity: 1; transform: none; }
.rv-d1 { transition-delay: .08s; }
.rv-d2 { transition-delay: .16s; }
.rv-d3 { transition-delay: .24s; }
.rv-d4 { transition-delay: .32s; }
.rv-d5 { transition-delay: .40s; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .rv { opacity: 1; transform: none; transition: none; }
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---- map consent gate ---------------------------------------------------- */
/* The Google embed is a third-party request, so it waits for a click. The ask
   is hidden without JS (see .js in the reveal block) because the button cannot
   work then; the plain "open in Google Maps" link above it still can. */
.mapgate { position: relative; border-radius: 12px; overflow: hidden; line-height: 0; }
.mapgate img, .mapgate iframe { width: 100%; height: auto; display: block; border: 0; }
.mapgate iframe { height: 340px; }
.mapgate__ask { display: none; }
.js .mapgate__ask {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--s-sm);
  background: rgba(15, 26, 19, .58); padding: var(--s-md); text-align: center;
}
.mapgate__ask p {
  line-height: 1.45; margin: 0; color: #fff; font-size: 14px; max-width: 34ch;
}

/* ---- cart ---------------------------------------------------------------- */
/* Both the button and the drawer are gated on .js: without JavaScript they
   cannot reach the API, and an inert cart control is the defect this round
   removed everywhere else. shop.js deletes them outright when no headless
   client id is built in. */
.cartbtn { display: none; }
.js .cartbtn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0; border: 0; border-radius: 999px;
  background: transparent; color: var(--ink); cursor: pointer;
  transition: background .18s var(--ease);
}
.js .cartbtn:hover { background: var(--ground-alt); }
.cartbtn__n {
  position: absolute; top: 2px; right: 0; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px; background: var(--green); color: #fff;
  font-family: 'Rubik', sans-serif; font-size: 10.5px; font-weight: 700; line-height: 17px;
  text-align: center;
}
.cart[hidden] { display: none; }
.cart { position: fixed; inset: 0; z-index: 90; }
.cart__scrim { position: absolute; inset: 0; background: rgba(15, 26, 19, .5); }
.cart__panel {
  position: absolute; top: 0; right: 0; bottom: 0; width: min(400px, 100%);
  display: flex; flex-direction: column; background: var(--ground);
  box-shadow: -18px 0 48px rgba(15, 26, 19, .18);
}
.cart__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-md); padding: var(--s-md) var(--s-lg);
  border-bottom: 1px solid var(--line);
}
.cart__head h2 { font-family: 'Rubik', sans-serif; font-size: 1.15rem; margin: 0; }
.cart__x { border: 0; background: none; font-size: 28px; line-height: 1; cursor: pointer; color: var(--ink); }
.cart__status { margin: 0; padding: var(--s-sm) var(--s-lg); color: var(--red, #D32E3B); font-size: 14px; }
.cart__body { flex: 1; overflow-y: auto; padding: var(--s-md) var(--s-lg); }
.cart__empty { color: var(--ink-soft); }
.cart__line {
  display: flex; gap: var(--s-md); align-items: flex-start;
  padding: var(--s-md) 0; border-bottom: 1px solid var(--line);
}
.cart__line img { width: 56px; height: 56px; object-fit: contain; flex: none; }
.cart__mid { flex: 1; min-width: 0; }
.cart__name { margin: 0 0 6px; font-size: 14px; line-height: 1.35; }
.cart__qty { display: inline-flex; align-items: center; gap: 2px; }
.cart__qty button {
  width: 26px; height: 26px; border: 1px solid var(--line); border-radius: 6px;
  background: none; cursor: pointer; font-size: 15px; line-height: 1; color: var(--ink);
}
.cart__qty span { min-width: 26px; text-align: center; font-variant-numeric: tabular-nums; font-size: 14px; }
.cart__warn { margin: 4px 0 0; font-size: 12.5px; color: var(--red, #D32E3B); }
.cart__right { text-align: right; flex: none; }
.cart__price { display: block; font-family: 'Rubik', sans-serif; font-weight: 700; font-size: 14px; }
.cart__rm { margin-top: 4px; border: 0; background: none; padding: 0; cursor: pointer;
            font-size: 12px; color: var(--ink-soft); text-decoration: underline; }
.cart__foot { padding: var(--s-md) var(--s-lg); border-top: 1px solid var(--line); }
.cart__sum {
  display: flex; justify-content: space-between; margin: 0 0 4px;
  font-family: 'Rubik', sans-serif; font-weight: 700; font-variant-numeric: tabular-nums;
}
.cart__ship { margin: 0 0 var(--s-md); font-size: 12.5px; color: var(--ink-soft); }
.cart__foot .btn { width: 100%; text-align: center; }
.is-cartopen { overflow: hidden; }
.is-cartbusy .cart__panel { opacity: .6; pointer-events: none; }
.btn[disabled], .btn[aria-disabled="true"] { cursor: not-allowed; opacity: .75; }
.buy-note { margin: 6px 0 0; font-size: 13.5px; color: var(--ink-soft); }

/* ---- contact form outcome ------------------------------------------------ */
/* Shown after submit. It carries the composed message verbatim so a visitor
   whose machine has no mail client still has something to copy — the form must
   never lose a message, which is exactly what it used to do. */
.fmdone[hidden] { display: none; }
.fmdone {
  margin-top: var(--s-lg); padding: var(--s-lg);
  border: 1px solid var(--line); border-radius: 12px; background: var(--ground-alt);
}
.fmdone h2 { font-family: 'Rubik', sans-serif; font-size: 1.05rem; margin: 0 0 8px; }
.fmdone p { margin: 0 0 var(--s-md); }
.fmdone pre {
  white-space: pre-wrap; word-break: break-word; margin: 0 0 var(--s-md);
  padding: var(--s-md); border-radius: 8px; background: var(--ground);
  border: 1px solid var(--line); font-family: 'Nunito Sans', sans-serif;
  font-size: 14px; line-height: 1.5;
}

/* ---- responsive ---------------------------------------------------------- */
@media (max-width: 1000px) {
  .bento { grid-template-columns: repeat(2, 1fr); }
  .tile--hero, .tile--shot { grid-column: span 2; }
  .blocks { grid-template-columns: repeat(2, 1fr); }
  .grid3 { grid-template-columns: repeat(2, 1fr); }
  .panel__grid { grid-template-columns: 1fr; padding: var(--s-2xl) var(--s-xl); }
  .ftr__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  body { font-size: 16px; }
  .nav { display: none; }
  .burger { display: flex; }
  .hdr__cta .btn { display: none; }
  /* `grid-auto-rows: 170px` with the hero spanning 2 gave it exactly 340px at
     phone width, and `.tile` clips its overflow — so the headline was cut at
     the top and the primary CTA was sliced in half on the homepage. The fact
     tiles still want a floor, so the row is a range rather than a fixed
     height: short tiles keep their proportion, the hero grows to fit. */
  .bento { grid-template-columns: 1fr; grid-auto-rows: minmax(170px, auto); }
  .tile--hero { padding: var(--s-xl) var(--s-lg); }
  .tile--hero, .tile--shot { grid-column: span 1; grid-row: span 2; }
  .blocks, .grid3 { grid-template-columns: 1fr; }
  .blk { min-height: 340px; }
  .sec { padding: var(--s-2xl) 0; }
  .ftr__grid { grid-template-columns: 1fr; }
}

/* ---- language switcher --------------------------------------------------- */
.langsw { display: flex; gap: 2px; align-items: center; margin-right: 6px; }
.langsw a {
  font-family: 'Rubik', sans-serif; font-size: 11.5px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-faint); text-decoration: none;
  padding: 6px 8px; border-radius: 999px;
  transition: color .2s var(--ease), background .2s var(--ease);
}
.langsw a:hover { color: var(--ink); background: var(--ground-alt); }
.langsw a[aria-current="true"] { color: #fff; background: var(--green-dark); }
@media (max-width: 640px) { .langsw a { padding: 6px 5px; font-size: 11px; } }

/* ---- skip to content (a11y: SEOmator "Skip Navigation Link") ------------- */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--green-dark); color: #fff;
  padding: 12px 20px; border-radius: 0 0 8px 0;
  font-family: inherit; font-size: 14px; font-weight: 600; text-decoration: none;
}
.skip:focus { left: 0; }

/* --- round 8 ------------------------------------------------------------- */
/* Header clearance: 86px desktop / 78px mobile against a bare .sec of 80/56,
   so shop and sorten overlapped at both sizes. One rule, no inline overrides. */
main > .sec:first-of-type { padding-top: 140px; }
/* Without height:auto the browser honours the literal height attribute, so a
   2048x1152 photo rendered 552x1152 and inflated the panel by ~840px. */
.panel__grid img { width: 100%; height: auto; }
.tile--shot img { object-fit: cover; object-position: center; }
@media (max-width: 640px) {
  main > .sec:first-of-type { padding-top: 110px; }
}

/* --- round 8: Classic and Fruchtfleisch joined the Sorten section ---------- */
.blk--classic { background: #F2F4F1; color: var(--ink); }
.blk--pulp { background: #E8E3D6; color: var(--ink); }

/* --- round 8: the header logo collapsed to 0px wide below 1024px -----------
   `.logo img { width:auto }` inside two nested flex contexts lets the browser
   shrink the SVG to nothing once the header runs out of room. Both the link and
   the image have to opt out of shrinking; `width:auto` alone is not a size. */
.hdr__in > .logo { flex: 0 0 auto; }
.logo img { flex: none; min-width: 110px; }

/* --- 2026-10-22: the header ran off the right edge at 901-1180px ----------
   .hdr is position:fixed, so the overflow made no scrollbar: the cart and
   "Zum Shop" were simply cut off. Measured on shop.html (system Chrome, the
   width each header part asks for with nothing wrapped), the row needed:
                          de     en     fr     it
     before                1213   1205   1331   1225   (901-1180 rules)
     short DE/EN/FR/IT     1046   1037   1164   1058
     ... and no CTA         894    881    966    887
   So three steps, each only where the one before cannot fit:
     901-1180   the language links show DE/EN/FR/IT (the <=900 rule, widened)
     901-999    the nav goes behind the burger, as it already does <=900;
                French cannot fit the nav below 966, and the others would
                have only 7px to spare at 901
     1000-1099  the "Zum Shop" button is hidden -- the nav's own "Shop"
                link sits beside it, so no route is lost (French until 1180:
                with the button it needs 1164)
   The cart, login and every nav link stay visible or in the burger at all
   widths. <=900 and >=1181 were untouched by THIS block; 641-753 and >=1181
   were then fixed by the block that follows it. Also check text WRAP, not just
   overflow: before this, "Über uns" and the button wrapped onto two lines
   inside a header that technically fit. */
@media (max-width: 1180px) {
  .langsw a { font-size: 0; padding: 6px 7px; }
  .langsw a::after { font-size: 11.5px; font-weight: 600; letter-spacing: .06em; }
  .langsw a[hreflang^="de"]::after { content: "DE"; }
  .langsw a[hreflang^="en"]::after { content: "EN"; }
  .langsw a[hreflang^="fr"]::after { content: "FR"; }
  .langsw a[hreflang^="it"]::after { content: "IT"; }
}

@media (max-width: 999px) {
  .nav { display: none; }
  .burger { display: flex; }
}

@media (min-width: 901px) and (max-width: 1180px) {
  .nav { gap: 22px; }
  .langsw { margin-right: 2px; }
  .langsw a { padding: 6px 6px; }
}
@media (min-width: 1000px) and (max-width: 1099px) {
  .hdr__cta .btn { display: none; }
}
@media (min-width: 1000px) and (max-width: 1180px) {
  :lang(fr) .hdr__cta .btn { display: none; }
}

/* --- 2026-10-22 (b): the same defect outside 901-1180 --------------------
   Swept every width 320-1440 (and 1445-1920 in 5s), de/en/fr/it, 7 pages.
   Two bands still wrapped a header label onto a second line or pushed the
   row past its box; everything else was clean and is left pixel-identical.

   1. Labels never wrap. Where a label fits this changes nothing; where it
      does not, the row now overflows visibly instead of hiding the problem
      as a two-line button, so the sweep can see it. Measured with nowrap
      alone, the row was still too wide by (px, then first clean width):
                    de          en          fr           it
        641        44.6 / 686  21.9 / 663  112.6 / 754   40.8 / 682
        1181       91.8 / 1273 83.4 / 1264 209.5 / never 103.5 / 1284
      French never fits with full language names: .hdr__in stops growing at
      --maxw (1320px), and at 1500 it is still 70px over.

   2. 641-753 (burger range, nav already in the panel). Short codes are
      already on here and tightening would have to find 22-113px, which is
      not "slight". So the "Zum Shop" button is hidden per language exactly
      where it cannot fit -- the same trade as <=640 and 1000-1099: the
      nav's Shop link is one tap away in the burger panel. Header height is
      78px while the button is hidden, 86px once it returns.

   3. >=1181. The DE/EN/FR/IT short codes save ~167px, which is enough for
      de, en and it, so they apply only up to each language's first clean
      width. French keeps them at every width >=1181, and between 1181 and
      1214 also takes the 901-1180 spacing (it was 34px short at 1181).

   Cart, login and every nav link stay visible at all widths. The codes
   come from --lc so the four hreflang values are written once. */
.nav a, .hdr__cta .btn, .authbtn, .authwrap a, .langsw a { white-space: nowrap; }
.langsw a[hreflang^="de"] { --lc: "DE"; }
.langsw a[hreflang^="en"] { --lc: "EN"; }
.langsw a[hreflang^="fr"] { --lc: "FR"; }
.langsw a[hreflang^="it"] { --lc: "IT"; }

@media (min-width: 641px) and (max-width: 685px) { :lang(de) .hdr__cta .btn { display: none; } }
@media (min-width: 641px) and (max-width: 662px) { :lang(en) .hdr__cta .btn { display: none; } }
@media (min-width: 641px) and (max-width: 681px) { :lang(it) .hdr__cta .btn { display: none; } }
@media (min-width: 641px) and (max-width: 753px) { :lang(fr) .hdr__cta .btn { display: none; } }

@media (min-width: 1181px) and (max-width: 1272px) {
  :lang(de) .langsw a { font-size: 0; padding: 6px 7px; }
  :lang(de) .langsw a::after { content: var(--lc); font-size: 11.5px; font-weight: 600; letter-spacing: .06em; }
}
@media (min-width: 1181px) and (max-width: 1263px) {
  :lang(en) .langsw a { font-size: 0; padding: 6px 7px; }
  :lang(en) .langsw a::after { content: var(--lc); font-size: 11.5px; font-weight: 600; letter-spacing: .06em; }
}
@media (min-width: 1181px) and (max-width: 1283px) {
  :lang(it) .langsw a { font-size: 0; padding: 6px 7px; }
  :lang(it) .langsw a::after { content: var(--lc); font-size: 11.5px; font-weight: 600; letter-spacing: .06em; }
}
@media (min-width: 1181px) {
  :lang(fr) .langsw a { font-size: 0; padding: 6px 7px; }
  :lang(fr) .langsw a::after { content: var(--lc); font-size: 11.5px; font-weight: 600; letter-spacing: .06em; }
}
@media (min-width: 1181px) and (max-width: 1214px) {
  :lang(fr) .nav { gap: 22px; }
  :lang(fr) .langsw { margin-right: 2px; }
  :lang(fr) .langsw a { padding: 6px 6px; }
}

/* --- round 8: the mobile menu had no open state at all -------------------- */
/* 999, not 900, since 2026-10-22: the burger now shows up to 999px. */
@media (max-width: 999px) {
  .nav.is-open {
    display: flex; flex-direction: column; align-items: stretch; gap: 0;
    position: absolute; left: var(--s-md); right: var(--s-md); top: calc(100% + 6px);
    background: var(--ground);
    border: 1px solid var(--line); border-radius: 18px;
    box-shadow: 0 20px 44px rgba(15, 26, 19, .14);
    padding: 6px; z-index: 60; overflow: hidden;
  }
  .nav.is-open a {
    display: flex; align-items: center;
    min-height: 50px; padding: 13px 14px;
    font-size: 16.5px; color: var(--ink); border-radius: 12px;
  }
  .nav.is-open a + a { border-top: 1px solid var(--line); border-radius: 0; }
  .nav.is-open a[aria-current="page"] { color: var(--green-dark); font-weight: 600; }
}

/* --- round 9: article covers ---------------------------------------------
   Portrait sources were being stretched to 860px wide and up to 2648px tall.
   One band, product centred, never upscaled past the file's own pixels. */
.article__cover {
  display: flex; align-items: center; justify-content: center;
  max-width: 860px; padding: 26px;
  background: var(--ground-alt);
  border-radius: 18px;
  margin-bottom: var(--s-lg);
}
.article__cover img { display: block; width: 100%; height: auto; max-width: 100%; max-height: 420px; object-fit: contain; }
/* round 5 (w): width:auto + height:auto left the cover <img> 0 px tall until the file
   arrived, so the whole article jumped down by the cover height (CLS 0.15 on a phone).
   width:100% gives the box its size from the width/height attributes BEFORE load; the
   max-height still caps it, and object-fit:contain draws the picture at exactly the size
   and centred position it had before (every cover file is larger than its box). */
@media (max-width: 640px) {
  .article__cover { padding: 16px; border-radius: 14px; }
  .article__cover img { max-height: 300px; }
}

/* --- round 9: the V-Label mark occupies the tile's headline slot -----------
   At 36px the ring's "V-LABEL INTERNATIONAL" lettering was ~3px tall. The mark
   now sits inside the <b>, so it inherits that slot's block display and bottom
   margin and the four fact tiles keep one shape. The mobile step is not
   cosmetic: c2's tile is 170px with 40px padding and `overflow:hidden`, leaving
   90px of content box — 64 + 8 + a two-line caption would clip. */
.fact__mark { line-height: 0; }
.fact__mark img { display: block; height: 64px; width: auto; }
.cert__mark { margin: 0 0 14px; line-height: 0; }
.cert__mark img { display: block; height: 72px; width: auto; }
@media (max-width: 640px) {
  .fact__mark img { height: 48px; }
  .cert__mark img { height: 60px; }
}

/* ---- round 13: two rules the design audit needed --------------------------
   .blocks--pair  a format row holding two blocks, not three. Fills the row
                  instead of leaving a third cell empty, and the extra 215px
                  of block width is what moves the Cocoffee cans clear of the
                  copy that was printing across them.
   .is-measure    the editorial column, centred, and all three parts of it on
                  ONE width. A 673px measure in a 1320px
                  container is right for reading and wrong for looking at when
                  all 623 leftover pixels sit on one side. */
.blocks--pair { grid-template-columns: repeat(2, 1fr); }
.is-measure { max-width: 66ch; margin-left: auto; margin-right: auto; }
.article__cover.is-measure { max-width: 66ch; }

/* ---- round 13b: two guards a full-page sweep asked for ---------------------
   .blk__word     The heading had no max-width. On the homepage that is
                  harmless — the word is "Mango". On Sorten the heading is the
                  whole product name, so it ran under the can: 27 collisions
                  across four languages, worst in Italian and French, which run
                  longest. The can's left edge sits at ~78% of the block, so
                  the heading stops there. Proportional, not per-string: the
                  next translation cannot defeat it.

   .grid3:has()   A 3-track grid holding 2 or 4 cards left a trailing hole on
                  34 pages — the "Weiterlesen" row at the foot of every
                  article. :has() counts the children, so no page needs
                  hand-tagging and a browser without :has() simply keeps
                  today's layout. */
.blk__word { max-width: 78%; }
.grid3:has(> :last-child:nth-child(2)) { grid-template-columns: repeat(2, 1fr); }
.grid3:has(> :last-child:nth-child(4)) { grid-template-columns: repeat(2, 1fr); }

/* ---- account control ------------------------------------------------------
   Sits beside the cart button. Deliberately quiet: it is a utility control, not
   a call to action, and it must not compete with "Zum Shop". */
.authwrap { display: inline-flex; align-items: center; }
.authbtn {
  font: inherit; font-family: 'Rubik', sans-serif; font-weight: 600; font-size: 13.5px;
  background: none; border: 0; color: var(--ink-soft); cursor: pointer;
  padding: 8px 10px; border-radius: 999px;
}
.authbtn:hover { color: var(--ink); background: var(--ground-alt); }
.authbtn:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
.authbtn[disabled] { opacity: .6; cursor: default; }
@media (max-width: 640px) { .authwrap { display: none; } }


/* ---- Phone: text must never run onto the can -------------------------------
   Owner-reported 2026-09-10 from a real phone. Measured before changing
   anything, in a 390/360/320px iframe so the real media queries applied:

     390px  7 of 8 blocks collided, worst 16px
     360px  8 of 8, worst 48px
     320px  8 of 8, worst 66px   (text column down to 116px)

   Root cause: .blk__can is sized from the block's HEIGHT (height:78%), so as
   the block narrows the can keeps its size and eats an ever larger share of the
   WIDTH. The existing guards were width-based and could not keep up:
   .blk__word max-width:78% and .blk__sub max-width:24ch both exceeded the space
   actually left beside the can, and .blk__price had no guard at all.

   Fix, three parts, all proportional so no translation can defeat them:
     1. the can is sized from the block WIDTH on phones, not its height;
     2. the block reserves that column as padding-right, so EVERY child --
        including any element added later -- is bounded by it automatically;
     3. hyphens for German compounds (Fruchtsaftzusatz), which otherwise
        overflow a narrow column as one unbreakable word.

   Re-measured after: 0 collisions, de/en/fr/it at 390, 360 and 320px.
   Text column never below 177px. */
@media (max-width: 640px) {
  .blk { padding: var(--s-lg); padding-right: 38%; }
  .blk__can { height: auto; width: 34%; max-height: 72%; right: -3%; bottom: -3%; }
  .blk__word, .blk__sub, .blk__price {
    max-width: 100%;
    overflow-wrap: break-word;
    hyphens: auto;
  }
  .blk__price { flex-wrap: wrap; }
}


/* Footer wordmark = the real logo, not a typed approximation --------------
   Owner 2026-09-10: "this logo must be the same as the one above in the page".
   The header has always used the SVG (coconaut-logo.svg, and the white variant
   on dark headers); the footer was a <div> typed as "COCONAUT(R)" in Rubik 900,
   so the two never matched - different letterforms, spacing and R-mark.
   The footer sits on --green-deep, so it takes the WHITE svg directly rather
   than the CSS content-swap the header uses. 34px keeps roughly the optical
   weight of the 30px type it replaces; 28px on phones. */
.ftr__brand img { height: 34px; width: auto; display: block; }
@media (max-width: 640px) { .ftr__brand img { height: 28px; } }


/* ---- Phone header: the menu button was off the screen ---------------------
   Found by sweep 2026-09-10, after the owner asked for the same check on the
   rest of the site. Measured, effective viewport in brackets:

     390px [375]  burger 365->411  = 36px of 46 clipped, centre not tappable
     360px [345]  burger 275->411  = 66px clipped, AND the cart clipped by 8px

   .nav is display:none on phones, so .burger is the ONLY route to Shop,
   Sorten, Ueber uns, Journal and Kontakt. It was effectively unreachable.

   Cause is arithmetic, not a bug: logo 123 + langsw 124 + cart 40 + burger 46
   plus gaps needs 381px; a 375px screen offers 327 inside the padding. No
   amount of tightening fits four language chips AND the logo AND two buttons
   on a 320-360px phone -- so the language row wraps instead.

   display:contents on .hdr__cta lets its children take part in .hdr__in's flex
   layout, so the language switcher can wrap to its own line without touching
   the HTML on 152 pages. Verified 390/360/320: nothing clipped, burger fully on
   screen and tappable at its centre, no horizontal page overflow.

   Cost, stated plainly: the header is position:fixed, so it now occupies
   102px instead of 78px on phones. Tightened padding and row-gap keep that to
   +24px. The alternative -- moving the languages inside the burger menu --
   keeps 78px but needs a markup change on every page; worth doing if the
   header height ever becomes a problem. */
/* ---- Round 10: the phone header — one row plus a language rail ------------
   REPLACES the round-9 block at the end of css/style.css (delete it: the
   @media (max-width:640px) block containing `.hdr__in{flex-wrap:wrap;
   row-gap:6px;column-gap:12px;padding:10px 16px}` through
   `.langsw{order:4;flex-basis:100%;...}`). Keep its explanatory comment —
   the arithmetic it records is still the reason this shape exists.

   Round 9 fixed the clipped burger by letting the language chips wrap. It
   worked, but it cost 24px of a position:fixed header (78 -> 102px) and it
   LOOKED like what it was: four ~29px chips huddled in the top-left of a
   second row that was 60% empty. Round 10 keeps the second row — the
   arithmetic has not changed, logo 123 + langsw 124 + cart + burger needs
   ~381px and a 320px phone offers 273px inside the padding — but rebuilds
   it as a deliberate full-bleed segmented control welded to the header's
   bottom edge.

   Net: 102px -> 80px, all four languages still one tap, zero markup change,
   zero new JavaScript.

   Measured in Chrome against this stylesheet and the shipped markup, at
   clientWidth 280 / 305 / 345 / 375 / 640 (devices ~295-655, covering 320,
   360 and 390 by the house's minus-15 convention):
     header height  80.00px at every width
     logo           16->139 (123x24, never shrunk)
     cart           44x44, hit-tested OK at its centre
     burger         44x44, 245->289 at 305 — fully on screen, hit OK
     rail           0 -> viewport edge, 28.00px, zero horizontal overflow
     chips          58-148 x 26px, 0.50px of band above and below,
                    spanning 16->289 / 16->329 / 16->359 — i.e. exactly the
                    wordmark's left edge to the burger's right edge
     burger panel   opens at y=86 (80+6); all six links hit OK; no overflow
     .sec pages     110px pad - 80px header = 30px clearance (was 8px)
     .hero pages    118px pad - 80px header = 38px clearance
     losing .js     header stays 80px — zero relayout, see note at (2)     */

@media (max-width: 640px){

  /* 1 — the header needs a surface of its own.
     .hdr declares no background at all; only .hdr.is-stuck does, and site.js
     adds that above scrollY 24. So at first paint the bar is transparent —
     nothing for a bottom band to sit against, on exactly the first screen a
     visitor sees. Checked before changing it: no page uses .hdr--onDark, and
     all 38 pages per language open with .sec or .hero, both white, so an
     opaque white phone header is visually identical to today at rest. Both
     states take the same fill so the rail never changes material mid-scroll;
     .is-stuck keeps its hairline box-shadow, which now reads as the header's
     bottom edge. */
  .hdr, .hdr.is-stuck{
    background: var(--ground);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }

  /* 2 — row 1: wordmark left, cart + burger right. 8 + 44 = 52px.
     display:contents on .hdr__cta is what lets .langsw take its own flex
     line without touching the HTML on 152 pages x 2 trees. */
  .hdr__in{
    flex-wrap: wrap;
    row-gap: 0;               /* the rail's own hairline is the separator */
    column-gap: 10px;
    padding: 8px 16px 0;      /* no bottom padding: the rail IS the edge */
    align-items: center;
  }
  .hdr__cta{ display: contents; }

  .logo    { order: 1; }
  .cartbtn { order: 2; margin-left: auto; }
  .authwrap{ order: 3; }      /* display:none at this width, but pinned so it
                                 can never jump ahead of the logo if it
                                 ever comes back */
  .burger  { order: 4; }

  /* Both controls to a 44px square — the tap-target floor, and a matched
     pair. The cart is gated on .js upstream (style.css "cart" block); that
     is pre-existing and deliberate. It matters here only because the inline
     head script strips .js after 2000ms if site.js has not run: under this
     layout that hides the cart and NOTHING else, because the 44px burger
     still sets row 1's height. Measured 80px with .js and 80px without —
     no relayout, no CLS, unlike a design whose bar height is .js-conditional. */
  .js .cartbtn{ width: 44px; height: 44px; }
  .burger     { width: 44px; height: 44px; }

  /* 3 — row 2: the rail.
     flex-basis resolves against .hdr__in's content box, so +32px and then
     -16px margins land the band exactly on both viewport edges. .hdr__in's
     max-width never binds below 640px. */
  .langsw{
    order: 5;
    flex: 0 0 calc(100% + 32px);
    margin: 0 -16px;
    padding: 0 16px;
    height: 28px;
    display: flex;
    align-items: center;
    gap: 6px;
    background: var(--ground-alt);
    border-top: 1px solid var(--line);
  }

  /* 4 — four equal segments filling the rail edge to edge.
     This is the move that makes the second row read as a control instead of
     a leftover: the chips start at the wordmark's left edge and end at the
     burger's right edge at EVERY width, so the two rows share both margins.
     It also repairs the worst ergonomics on the site — targets go from
     ~29x28 to 58-148 x 26, past WCAG 2.5.8 AA (24x24) with room to spare.
     Deliberately no max-width: a cap would leave a ragged ~12px gap under
     the burger at 430px, and consistent alignment is worth more than pill
     proportion. Above ~410px the pills simply get wider.

     The inactive chips are transparent, NOT white. A fill lighter than the
     band inverts figure/ground — the chips float and the band disappears,
     which is what made the earlier tinted-band attempt read as a second row
     rather than a border. Full --ink at 600 weight beside one filled sibling
     reads as a segmented control, which is exactly what it is.
     (font-size:0 and the ::after DE/EN/FR/IT swap come from the <=1180px
     block above and are deliberately left in place.) */
  .hdr .langsw a{
    flex: 1 1 0; min-width: 0;
    display: inline-flex; align-items: center; justify-content: center;
    height: 26px; padding: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--ink);
  }
  .hdr .langsw a::after{ font-size: 11.5px; line-height: 1; }

  /* .langsw a:hover (style.css:485, 0-2-1) beats a plain .langsw a (0-1-1)
     and would paint an inactive chip in exactly the band's own colour,
     dissolving it — and iOS keeps :hover after a tap, so it would stick.
     Beaten explicitly at 0-3-1. */
  .hdr .langsw a:hover,
  .hdr .langsw a:focus{ background: transparent; color: var(--ink); }

  .hdr .langsw a[aria-current="true"],
  .hdr .langsw a[aria-current="true"]:hover{
    background: var(--green-dark); color: #fff;   /* 4.7:1 white on #07853B, AA */
  }

  /* 5 — Swiss national order: DE · FR · IT, then EN.
     EN second says "a German shop with an English translation"; the three
     national languages first says "a Swiss shop", and it costs four lines.
     The DOM stays DE EN FR IT, so visual and focus order diverge — four
     sibling links inside one labelled group, so the impact is small, but the
     clean fix is to reorder the four <a> elements in the generator and
     delete this block. See the markup note. */
  .langsw a[hreflang^="de"]{ order: 1; }
  .langsw a[hreflang^="fr"]{ order: 2; }
  .langsw a[hreflang^="it"]{ order: 3; }
  .langsw a[hreflang^="en"]{ order: 4; }
}


/* ---- The burger menu was unreachable in landscape --------------------------
   Measured 2026-09-10 on the live build. `.nav.is-open` is position:absolute
   inside a position:FIXED header, with overflow-y:hidden and no max-height. On
   a 360px-tall viewport (any phone turned sideways) the panel runs to 412px:
   "Kontakt" sits 52px below the screen, and because the header is fixed,
   scrolling the page does not move it. The link was permanently unreachable.

   Hit-testing did NOT catch this — elementFromPoint happily returns the <a>
   for a link that is off-screen. Only comparing the panel's bottom against the
   viewport height found it.

   Cap the panel to the space actually available and let it scroll itself.
   dvh second so browsers that have it use it (it accounts for mobile URL bars);
   vh is the fallback. 96px = the 80px phone header + the panel's 6px offset +
   10px breathing room. Unscoped by width on purpose: the dropdown is used
   wherever the burger is shown, which is wider than the 640px phone query --
   at 740x360 it was cut off by 58px.
   Verified: 640x360 and 740x360 now fit, scroll internally, last link
   reachable; 390x844 portrait unchanged (short enough that it never scrolls). */
.nav.is-open{
  max-height: calc(100vh - 96px);
  max-height: calc(100dvh - 96px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* ---- homepage promo banner (2026-10-12) ---------------------------------- */
/* Plain brand-green stage, the real Tetra Pak photos and HTML text: the label is
   never AI-drawn and every language is translatable. Contrast, measured: white on
   --green (#0AAF4E) is 2.9:1 and fails, on --green-dark 4.7:1. The copy column
   starts at ~43% of the tile, so the gradient reaches --green-dark by 34%, and the
   white glow lives in the stage box only (it used to reach ~53% and sat behind the
   footnote). Eyebrow and footnote are solid white for the same reason. */
.tile--promo {
  grid-column: 1 / -1; grid-row: span 2;
  display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 6fr);
  align-items: center; gap: var(--s-xl);
  min-height: clamp(440px, 40vw, 560px);
  padding: var(--s-xl) clamp(24px, 5vw, 72px);
  color: #fff;
  justify-content: normal;  /* .tile's flex-end would push an overflowing track out to the left */
  background: linear-gradient(100deg, var(--green) 0%, var(--green-dark) 34%, var(--green-darker) 60%);
}
.tile--promo:hover { transform: none; box-shadow: none; }
.promo__copy { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-md); }
.promo__copy .eyebrow { color: #fff; margin: 0; }
.promo__h {
  color: #fff; font-weight: 900; font-style: italic; text-transform: uppercase;
  font-size: clamp(2.1rem, 3.7vw, 3.6rem); line-height: .96; letter-spacing: -.02em;
}
.promo__h span { display: block; }
.promo__h sup { font-size: .42em; line-height: 0; position: relative; top: -.15em; font-style: normal; }
.promo__sub { margin: 0; font-size: clamp(1rem, 1.3vw, 1.2rem); font-weight: 600; }
.promo__note { margin: 0; font-size: 12.5px; line-height: 1.45; color: #fff; max-width: 56ch; }
.promo__stage {
  order: -1; position: relative; align-self: stretch;
  display: flex; align-items: flex-end; justify-content: center; min-height: 340px;
}
.promo__stage::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(closest-side, rgba(255,255,255,.26), rgba(255,255,255,0));
}
.promo__stage::after {
  content: ""; position: absolute; left: 18%; right: 18%; bottom: 0; height: 28px; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(0,40,15,.42), rgba(0,40,15,0));
}
.promo__stage img { position: relative; z-index: 1; width: auto; filter: drop-shadow(0 18px 22px rgba(0,40,15,.3)); }
.promo__l { height: clamp(300px, 32vw, 460px); }
.promo__s { height: clamp(220px, 23vw, 335px); margin-left: -4%; }
@media (max-width: 1000px) {
  .tile--promo { grid-template-columns: minmax(0, 1fr); gap: var(--s-lg);
    background: linear-gradient(180deg, var(--green-darker) 0%, var(--green-dark) 62%, var(--green) 100%); }
  .promo__stage { order: 0; min-height: 0; }
  .promo__l { height: clamp(240px, 46vw, 380px); }
  .promo__s { height: clamp(176px, 34vw, 280px); }
}
@media (max-width: 640px) {
  .tile--promo { padding: var(--s-xl) var(--s-lg); }
  /* 6.6vw keeps the longest word (FR "NATURELLEMENT") inside a 320px phone */
  .promo__h { font-size: clamp(1.35rem, 6.6vw, 2.3rem); overflow-wrap: break-word; }
}

/* ---- phone image proportions (2026-10-12) ---------------------------------
   Measured in a 375px sweep of every page, de/en/fr/it:
   - .card__pic img had width:auto but no height, so the HTML height attribute
     still applied; on a narrow card max-width cut the width and the picture was
     squashed (blog list x0.64). height:auto lets max-width/max-height scale
     both sides together. Desktop is unchanged: the same 230px cap applies.
   - .blk__can on phones is sized by width with a height cap; when the cap
     binds only the height shrinks. object-fit keeps the box but never the
     distortion (cans were stretched up to x1.38).
   - .blocks--pair is declared after the phone query, so it kept two 155px
     columns on phones: words broke mid-word and the Cocoffee trio was crushed. */
.card__pic img { height: auto; max-width: 100%; object-fit: contain; }
@media (max-width: 640px) {
  .blocks--pair { grid-template-columns: 1fr; }
  /* same class of bug: the round-13b :has() rule kept 2 columns on phones and blog
     pages scrolled sideways by 35-62px */
  .grid3:has(> :last-child:nth-child(2)),
  .grid3:has(> :last-child:nth-child(4)) { grid-template-columns: 1fr; }
  .blk__can { object-fit: contain; object-position: right bottom; }
}

/* ---- wide product shot in a block (2026-10-12) ----------------------------
   The Cocoffee trio is wide (586x546); the can slot is a tall right-hand column.
   Desktop: same heights the inline styles had, with the price kept left of the
   picture. Up to 1024px (measured: it covered copy at 700-1024px) the picture
   leaves the column and sits in the flow, between the copy and the price, so it
   is neither crushed nor clipped and the copy gets the whole card width. */
.blk__can--wide { height: 60%; }
.blocks--pair .blk__can--wide { height: 62%; }
.blk:has(> .blk__can--wide) .blk__price { max-width: 46%; flex-wrap: wrap; }
@media (max-width: 640px) {
  .blk:has(> .blk__can--wide) { padding-right: var(--s-lg); }
}
@media (max-width: 1024px) {
  .blk:has(> .blk__can--wide) .blk__price { max-width: none; }
  .blk > .blk__can--wide {
    position: relative; right: auto; bottom: auto; order: 1; align-self: center;
    width: 100%; max-width: 340px; height: auto; max-height: none;
    margin: auto 0 var(--s-md); object-position: center;
  }
  .blk:has(> .blk__can--wide) .blk__price { order: 2; margin-top: 0; }
}

/* ---- long words on phones (2026-10-12) -------------------------------------
   A 320/375px sweep of all 152 pages found three that scrolled sideways, each
   caused by ONE German word too wide for the screen: "Geschäftsbedingungen" and
   "Datenschutzerklärung" in an h1, and "Wassermelonensaft", which set the
   min-content width of the product grid column (307px track in a 253px box).
   Phones only, so desktop headlines are never hyphenated. */
@media (max-width: 640px) {
  h1, h2 { hyphens: auto; hyphenate-limit-chars: 12 5 5; overflow-wrap: break-word; }
  .promo__h { hyphens: manual; }
}
@media (max-width: 1000px) {
  .panel__grid { grid-template-columns: minmax(0, 1fr); }
}
/* 1025-1199px: the Cocoffee trio grazed the longest price ("Confezione da 24 ·
   CHF 40.00") by 12-15px. Slightly smaller there only; 1200px+ is unchanged. */
@media (min-width: 1025px) and (max-width: 1199px) {
  .blk__can--wide { height: 52%; }
}
