/* ============================================================
   bieren.css — PAGE-SPECIFIC layer for /bieren/, loaded via the
   master's `contentCss` option AFTER css/master.css.

   Holds ONLY the beer-catalogue components that aren't part of the
   shared blueprint: the per-beer animated hero, the sticky pill nav,
   and the beer detail panels. All design tokens, chrome (preloader,
   bar, menu, guide, footer, .ask) and the `.r` reveal system come
   from master.css — do NOT redefine them here.
============================================================ */

/* ── unified beer hero (canvas + live beer name) ───────── */
.bieren-hero {
  position: relative; overflow: hidden;
}
.hero-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
}
.hero-overlay {
  position: relative; z-index: 2;
  display: flex; align-items: center;
  /* The beer NAME is deliberately not shown here — the 3D label right below
     already carries it, so repeating it in a display-size H1 just pushed the
     actual beer story below the fold. Only the bar-clearance remains, so the
     hero is now a slim ambient colour strip. */
  padding: 12px var(--gutter);
  border-bottom: 1px solid var(--beer-color, var(--gold));
  background: linear-gradient(to bottom,
    rgba(15,14,11,0.55) 0%,
    rgba(15,14,11,0.75) 100%);
}
/* Kept in the DOM (and still updated per beer by the tab switcher) so the page
   keeps ONE real <h1> for SEO/screen readers — visually hidden only. */
.hero-name {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* ── sticky pill nav ────────────────────────────────────── */
.beer-tabs-wrap {
  position: sticky; top: 0; z-index: 220;
  background: var(--ink);
  border-bottom: 1px solid var(--line-dark);
  transition: box-shadow 0.3s;
}
.beer-tabs-wrap.is-scrolled {
  box-shadow: 0 4px 28px rgba(0,0,0,0.5);
}
.beer-tabs {
  display: flex; overflow-x: auto; scrollbar-width: none; gap: 0;
  padding: 0 var(--gutter);
}
.beer-tabs::-webkit-scrollbar { display: none; }
.beer-tab {
  flex-shrink: 0; font-family: var(--sans); font-size: 17px;
  letter-spacing: 0.01em; text-transform: none;
  /* 0.45 measured 4.09:1 against the tab bar — under AA, and these are the
     controls the whole page is navigated with. 0.58 clears it while still
     reading as "not the selected one". */
  color: rgba(244,239,227,0.58); background: none; border: none;
  border-bottom: 2px solid transparent;
  padding: 16px 18px 14px; cursor: pointer; white-space: nowrap;
  transition: color 0.2s, border-color 0.2s;
}
.beer-tab:hover { color: var(--cream); }
.beer-tab[aria-selected="true"] { color: var(--beer-color, var(--gold)); border-bottom-color: var(--beer-color, var(--gold)); }

/* ── tab label swap: style ⇄ beer name ──────────────────────
   "Blond" on load; the real name ("Gouden Gilbert") on hover, on keyboard
   focus, and on the selected tab — so touch devices get it via tap-to-select,
   not hover alone. Both labels occupy the SAME 1×1 grid cell, so the button
   is permanently sized to the wider of the two and the swap cannot reflow the
   strip (which would shift the tabs under the user's finger mid-tap). */
.beer-tab .bt-swap { display: grid; }
.beer-tab .bt-style,
.beer-tab .bt-name {
  grid-area: 1 / 1;
  transition: opacity 0.22s var(--ease);
}
.beer-tab .bt-name { opacity: 0; }
.beer-tab:hover .bt-style,
.beer-tab:focus-visible .bt-style,
.beer-tab[aria-selected="true"] .bt-style { opacity: 0; }
.beer-tab:hover .bt-name,
.beer-tab:focus-visible .bt-name,
.beer-tab[aria-selected="true"] .bt-name { opacity: 1; }

/* ── beer panels ────────────────────────────────────────── */
.beer-panels {
  padding: 0 0 clamp(56px,9vw,96px);
}
.beer-panel {
  display: none; opacity: 0; transform: translateY(10px);
}
.beer-panel.is-active { display: block; }
.beer-panel.is-visible {
  opacity: 1; transform: none;
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}

/* ── beer body (below header) ──────────────────────────── */
.bp-body {
  padding: clamp(24px,4vw,40px) var(--gutter) 0;
  display: grid;
  grid-template-columns: minmax(0, 40%) 1fr;
  gap: clamp(26px,3.5vw,52px);
  align-items: stretch;
}

.bp-photo {
  border-radius: 4px; overflow: hidden;
  position: relative; min-height: 320px;
  background-size: cover; background-position: center;
  border: 1px solid var(--line-dark);
}

/* detail column */
.bp-detail { display: flex; flex-direction: column; gap: 20px; }

/* verhaal block */
.bp-verhaal {
  font-size: var(--fs-body); line-height: 1.7;
  color: rgba(244,239,227,0.74); font-style: italic;
  border-left: 2px solid var(--beer-color, var(--gold));
  padding-left: 18px; margin: 0;
}

/* detail */
.bp-kicker { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bp-style {
  font-size: var(--fs-label); letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(244,239,227,0.45); background: rgba(244,239,227,0.05);
  border: 1px solid var(--line-dark); border-radius: 4px; padding: 4px 10px;
}
.bp-abv {
  font-size: var(--fs-label); letter-spacing: 0.06em; color: var(--beer-color, var(--gold));
  background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.12);
  border-radius: 4px; padding: 4px 10px;
}

.bp-beschrijving {
  font-size: var(--fs-body); line-height: 1.75; color: rgba(244,239,227,0.88);
  border-top: 1px solid var(--line-dark); padding-top: 22px; margin: 0;
}

.bp-section-label {
  font-size: var(--fs-label); letter-spacing: var(--ls-label);
  text-transform: uppercase; color: rgba(244,239,227,0.35); margin-bottom: 10px;
}
.bp-tags {
  display: flex; flex-wrap: wrap; gap: 8px;
  list-style: none; margin: 0; padding: 0;
}
.bp-tags li {
  font-size: var(--fs-label); letter-spacing: 0.04em;
  color: rgba(244,239,227,0.7);
  background: rgba(244,239,227,0.04);
  border: 1px solid var(--line-dark);
  border-radius: 999px; padding: 5px 14px;
}

.bp-divider { border: none; border-top: 1px solid var(--line-dark); margin: 0; }

.bp-rotating-note {
  border: 1px solid rgba(255,217,0,0.14); border-radius: 10px;
  padding: 18px 22px; font-size: var(--fs-body); line-height: 1.65;
  color: rgba(244,239,227,0.72); background: rgba(255,217,0,0.04);
}
.bp-rotating-note strong { color: var(--gold); }

/* LAYOUT ONLY. `.bp-btn-primary` and `.bp-btn-secondary` used to be declared here — the seventh
   copy of the pill, written three weeks after css/button.css was created to stop exactly that.
   Measured against the shared button they were 56px tall against 50px, the ghost carried a 0.14
   border against 0.30, and the arrow was typed into the label instead of drawn. The buttons are
   `.cbtn` now; this row keeps its layout job, which genuinely belongs to the page. */
.bp-actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* ── mobile ─────────────────────────────────────────────── */
@media (max-width: 700px) {
  .beer-tab { padding: 13px 13px 11px; }
  /* name is visually hidden — only bar-clearance needed (see .hero-overlay) */
  .hero-overlay { padding: 68px var(--gutter) 10px; }
  .bp-body { grid-template-columns: 1fr; }
  .bp-photo { min-height: 200px; }
  /* The row stacks on a phone, so the buttons go full width and centre their label. This is
     layout, not look — the shared button keeps every other property. */
  .bp-actions { flex-direction: column; }
  .bp-actions .cbtn { justify-content: center; }

  /* NO VERTICAL BIAS HERE. This rule used to set `object-position: 50% 15%` to "bias the crop
     upward", on the reasoning that a tall panel crops top and bottom. It is the other way round:
     a 2:1 sheet in a taller-than-2:1 box is fitted BY HEIGHT, so the full height always shows and
     only the sides are cropped. Measured on the reported screenshot, the artwork's top and bottom
     edges both sat inside the panel — the 15% had no effect at all. The horizontal offset that
     was actually visible is handled on .bp-3d-branding above. */
}

/* ── 3D bottle (bottle3d.js) — canvas overlay + orbiting feature chips ── */
.bp-photo--3d { min-height: 520px; }

/* NO VERTICAL TRAVEL ON THE PANEL BODY (2026-09-01).
   The shared reveal (.r) fades content in from `translateY(34px)`. Everywhere
   else that is right; here it is the "step 3" the owner reported:

     1. page loads   2. label appears   3. label shifts slightly   4. label
     transforms onto the bottle

   Step 3 was this 34px slide — measured, the photo's top moved 226 -> 192 in the
   same window as the canvas fade-in. Two entrances were running over each other:
   the panel's arrival and the bottle's own choreography, which is itself a
   carefully timed hold-then-roll. The bottle should be the only thing moving.

   The FADE stays, so the panel still arrives rather than popping — it just
   arrives in place. Only the travel is removed, and only here. */
.bp-body.r,
.bp-body.r.in { transform: none; }

/* Backdrop: a very faded wash of the beer's own colour (--beer-color, e.g. tripel
   orange), so the 3D view sits on a soft tint that matches the type. A gentle
   radial keeps the centre lighter than the edges for depth. */
.bp-photo--3d {
  --beer-color: #F5A020;
  background:
    radial-gradient(120% 100% at 50% 38%,
      color-mix(in srgb, var(--beer-color) 42%, #1a1206) 0%,
      color-mix(in srgb, var(--beer-color) 58%, #1a1206) 100%);
}
/* branding image shown INSTANTLY on load, and stays the only visible layer
   until the roll starts. At that instant the canvas (already showing the
   same flat sheet, matched to this image's cover crop) fades in as this
   fades out, and the backdrop swaps to grey — all on the same 0.4s clock,
   so the handoff reads as one continuous motion, not three staggered ones. */
.bp-photo--3d .bp-3d-branding {
  /* Above the canvas, not below it — see THE MIDDLE RENDER below. The canvas is
     opaque and rendering underneath during the whole hold; this sheet is what the
     visitor actually sees, and it is dropped on the frame the motion starts. */
  position: absolute; left: 0; right: 0; z-index: 3;
  /* THE WHOLE SHEET, NEVER A CROP (15-09-2026).
     Reported: "the labels load in too far zoomed in, part of the face of the person is no longer
     visible". Measured on the dev server at 1440px: the panel is 530x632 and this image rendered
     1261px wide — a 2.4x zoom that threw away 58% of the artwork. Both ends of the name plate and
     the sides of the head were outside the panel.

     THE CAUSE WAS THE FIT, NOT THE OFFSET. A 2:1 sheet was cover-fitted into a portrait panel:
     `height: 100%` filled the tall side, so the wide side had to overflow. Every earlier attempt
     treated the SYMPTOM of that crop — first `object-position: 50% 15%`, then a focal-point
     translate of --label-focus-x — because the artwork puts its name plate at 47.8% to 48.1%
     instead of 50%, and a 2.4x crop turns that 2% into a visible 5%. Both moved the crop window
     around. Neither could show a part of the image that the crop had already discarded.

     A contain fit removes the crop, so it removes what the focal point was compensating for: the
     remaining 2% offset is 2% of the panel, about 10px, and the visitor sees the label the way it
     was drawn. bottle3d.js starts its sheet at the same contain scale (S0), so the single-frame
     handover below still lands on the same pixels.

     The band of backdrop above and below is deliberate: it is the beer's own colour wash, and the
     intro shrinks the sheet out of it onto the bottle. */
  top: 50%; transform: translateY(-50%);
  width: 100%; height: auto; max-height: 100%;
  object-fit: contain; object-position: center;
  display: block; transition: opacity 0.4s ease;
  /* This sheet stays in the layer stack at opacity 0 after the intro. An
     opacity-0 element still receives pointer events, so it covered the canvas
     (z-index 1) and swallowed every pointerdown — drag-to-spin was dead. The
     image is decorative and has no click target, so it never needs them. */
  pointer-events: none;
}
/* Once the label is wrapped (JS adds .is-rolled): drop the branding backdrop,
   switch to the plain UX grey with a beer-coloured border. */
.bp-photo--3d.is-rolled {
  background: var(--ink-2, #161410);
  border-color: var(--beer-color, var(--gold));
  box-shadow: 0 0 0 1px var(--beer-color, var(--gold));
  transition: background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
}
.bp-photo--3d.is-rolled .bp-3d-branding { opacity: 0; }
.bp-photo .b3d-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1;
  opacity: 0; transition: opacity 0.4s ease;
  background: transparent; /* branded backdrop shows through */
  cursor: grab; touch-action: pan-y; /* vertical page scroll keeps working on touch */
}
.bp-photo .b3d-canvas:active { cursor: grabbing; }

/* ── THE MIDDLE RENDER — one frame, not a 400ms dissolve ────────────────
   Reported twice: "the label is shown and right after that it zooms in/out a bit
   before morphing".

   MEASURED CAUSE. Freezing the hold and capturing each layer on its own shows
   the DOM image and the WebGL sheet are the same artwork at slightly DIFFERENT
   sizes — the sheet renders a few percent smaller. Both are "cover" fits, but one
   is a CSS crop of a 1200x600 image into a 530x670 box and the other is a 2:1
   sheet cover-fitted to a camera frustum (S0, computed once at mount). Two cover
   fits through different geometry do not land on the same pixels. Dissolving one
   into the other over 400ms, while nothing else on screen moves, is precisely a
   slow zoom — which is what was reported, twice.

   So the handover is a SINGLE FRAME: no transition on either layer. A few percent
   of scale difference across one frame reads as a redraw; across 400ms it reads
   as a defect.

   AND THAT FRAME IS THE FRAME THE MOTION STARTS. Keying the swap to .is-in
   instead put the redraw ~0.7s before the shrink, so the sequence read as label,
   a small blip, a beat of nothing, then the morph — reported as "it stops like
   0.5 seconds which makes it feel off". The fix is ordering, not timing: the
   canvas goes opaque at .is-in but stays UNDERNEATH this <img> (z-index 3 above
   the canvas's 1), so the visitor keeps seeing the flat sheet through the whole
   hold. bottle3d.js adds .is-intro on the first frame of the shrink, the image
   goes, and the redraw is absorbed by movement that is already underway.

   WHAT WAS TRIED AND REJECTED, so nobody re-treads it:
   1. Hold the canvas at opacity 0 until the roll, hiding the crossfade inside the
      motion. It delayed the roll from 7.1s to 14.5s — measured, twice each way.
      An invisible WebGL canvas gets deprioritised and the module's clock goes
      with it. Occluding an OPAQUE canvas with a sibling image (what we do now)
      does not: the roll still lands at ~7.1s, measured.
   2. Drop the image as soon as the renderer mounts (.is-3d-live). The renderer
      needs ~1.3s of shader compile and texture upload before it paints, so that
      left an empty coloured panel on screen for about a second.

   The <img> keeps its opacity when the renderer never arrives (no WebGL, module
   error) — that is its other job, and .is-intro only ever fires from inside the
   module's own animation loop, so it survives.

   NOT fixed here, because it is a design choice rather than a defect: the intro
   spends 1.27s shrinking the sheet from full-frame down to bottle-label size
   before the wrap begins (phase A in bottle3d.js). If that glide is also
   unwanted, it is that phase. */
.bp-photo .b3d-canvas.is-in { opacity: 1; transition: none; }
.bp-photo--3d.is-intro .bp-3d-branding { opacity: 0; transition: none; }

.bp-photo .b3d-chips { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.bp-photo .b3d-chip {
  position: absolute; left: 0; top: 0;
  padding: 6px 12px; border-radius: 999px; white-space: nowrap;
  font-size: 13px; font-weight: 600; letter-spacing: 0.01em;
  color: #f6ead8; background: rgba(24, 16, 9, 0.62);
  border: 1px solid var(--beer-color, var(--gold));
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  opacity: 0; transition: opacity 0.5s ease;
  will-change: transform;
}
.bp-photo .b3d-chips.is-live .b3d-chip { opacity: 1; }
.bp-photo .b3d-chips.is-live .b3d-chip.is-behind { opacity: 0.55; }

@media (max-width: 700px) {
  .bp-photo--3d { min-height: 420px; }
  .bp-photo .b3d-chip { font-size: 12px; padding: 5px 10px; }
}

/* ── TABLETS: THE PANEL HUGS THE BOTTLE, IT DOES NOT MATCH THE STORY ──────────────────────
   Reported on an iPad, 16-09-2026, both orientations: "looks like ass" — a tall, nearly empty
   frame with a small label adrift in the middle.
   WHY IT HAPPENS. `.bp-body` is a two-column grid with `align-items: stretch`, so the photo is
   as tall as the text beside it. On a desktop the two columns are about the same length and that
   reads as one spread. On a tablet the column is only ~40% of 810px — 298px wide — while the
   story, the tasting notes, the food pairing and two buttons stack up to 813px. Measured: a
   298 x 813 frame around a bottle that is drawn to fit its height, so the bottle turns into a
   sliver and the rest is empty backdrop. With no WebGL the flat 2:1 label sits in the middle of
   that emptiness, which is the screenshot.
   THE FIX IS TO STOP STRETCHING. The panel keeps a portrait aspect — a bottle is portrait, and a
   2:1 label still fits across it — and sticks to the top of the row. Desktop keeps the stretched
   spread it was designed for. */
@media (min-width: 701px) and (max-width: 1100px) {
  .bp-body { align-items: start; }
  .bp-photo { min-height: 0; }
  .bp-photo--3d { min-height: 0; aspect-ratio: 3 / 4; }
}

/* A CUT-OFF TAB MUST LOOK LIKE A SCROLL, NOT LIKE A BUG (same iPad report). The tab rail has
   always been `overflow-x: auto`, and on a tablet in portrait the nine beers do not fit — so the
   last one is sliced in half at the edge with nothing to say it can be dragged. The mask fades
   the final 36px, which is the one signal every touch guest already understands; snapping makes
   the drag land on a tab instead of between two. It only fades when there IS something under it:
   a rail that fits ends before the gradient starts. */
@media (max-width: 1100px) {
  .beer-tabs {
    scroll-snap-type: x proximity;
    padding-right: calc(var(--gutter) + 20px);
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 36px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 36px), transparent 100%);
  }
  .beer-tab { scroll-snap-align: start; }
}
