.elementor-kit-11{--e-global-color-primary:#14301F;--e-global-color-secondary:#1C3E28;--e-global-color-text:#1C1A17;--e-global-color-accent:#E8BF70;--e-global-color-cf58033:#F7F4E9;--e-global-color-9ece698:#FFFFFF;--e-global-typography-primary-font-family:"IBM Plex Sans";--e-global-typography-primary-font-weight:600;--e-global-typography-secondary-font-family:"Cormorant Garamond";--e-global-typography-secondary-font-weight:400;--e-global-typography-text-font-family:"IBM Plex Sans";--e-global-typography-text-font-weight:400;--e-global-typography-accent-font-family:"IBM Plex Sans";--e-global-typography-accent-font-weight:500;}.elementor-kit-11 e-page-transition{background-color:#FFBC7D;}.elementor-section.elementor-section-boxed > .elementor-container{max-width:1240px;}.e-con{--container-max-width:1240px;}.elementor-widget:not(:last-child){--kit-widget-spacing:20px;}.elementor-element{--widgets-spacing:20px 20px;--widgets-spacing-row:20px;--widgets-spacing-column:20px;}{}h1.entry-title{display:var(--page-title-display);}.site-header .site-branding{flex-direction:column;align-items:stretch;}.site-header{padding-inline-end:0px;padding-inline-start:0px;}.site-footer .site-branding{flex-direction:column;align-items:stretch;}@media(max-width:1024px){.elementor-section.elementor-section-boxed > .elementor-container{max-width:1024px;}.e-con{--container-max-width:1024px;}}@media(max-width:767px){.elementor-section.elementor-section-boxed > .elementor-container{max-width:767px;}.e-con{--container-max-width:767px;}}
/* Start custom CSS *//* ==========================================================================
   iMagnet - GLOBAL CSS for Elementor  (Site Settings -> Custom CSS)
   Generated from the FINAL preview: site-preview/css/tokens.css + site.css
   Do NOT hand-edit here. Edit the source CSS and regenerate.
   --------------------------------------------------------------------------
   BEFORE YOU PASTE - two 30-second actions:

   1) FONTS: the @import on the first line loads the brand fonts. Keep it first.

   2) IMAGES: 5 background images below use the token  https://imagnet.co.il/wp-content/uploads/2026/07  as their folder.
      After you upload the images to Media (Part 4.1), Find-and-Replace every
      https://imagnet.co.il/wp-content/uploads/2026/07  in this block with your uploads folder URL, no trailing slash,
      e.g.   https://YOURDOMAIN.co.il/wp-content/uploads/2026/07
      Images referenced:  scene-shop-frame.webp, scene-story.webp,
      foliage-rail-left.webp, foliage-rail-right.webp
      (the two foliage rails are the transparent webp files delivered with this
       block - upload them to Media with the product/scene images.)

   --------------------------------------------------------------------------
   ADAPTATION NOTES (things that behaved differently in the static preview):

   * HEADER SPACING:  body has  padding-block-start: var(--chrome-h, 76px)  so
     content clears a FIXED header. In Elementor, set your Header template to
     "Sticky/overlap" for this to line up. If your header is NOT fixed, delete
     that one line (search "padding-block-start: var(--chrome-h") or the page
     opens with a ~76px gap at the top.

   * OPENING ANIMATION:  the scroll-driven home "journey" intro needs a small JS
     that the preview ran. Without it the CSS falls back to the finished hero
     automatically (the html:not(.has-journey) rules) - the correct, accessible
     static state. We handle the home hero in Part 6.1.

   * PREVIEW FLAG:  a few rules mention .preview-flag / --flag-h. That strip does
     not exist in WordPress; the rules are inert (--flag-h falls back to 0). Safe
     to leave as-is.

   * GLOBAL RESET:  this block styles bare tags (body, a, ul, h1-h3, button, img)
     so pasted HTML widgets inherit the brand automatically - the point of the
     hybrid method. Side effect: a plain Elementor bullet list loses its bullets
     (ul{list-style:none}). If you ever need a normal bulleted list in a Text
     widget, re-enable it locally on that widget.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=IBM+Plex+Sans+Hebrew:wght@300;400;500&display=swap');
/* Arabic launch is Part 11. When you add it, extend the import with:
   &family=IBM+Plex+Sans+Arabic:wght@300;400;500  */

/* ============================ tokens.css ============================ */
/* ==========================================================================
   iMagnet design tokens
   SINGLE SOURCE OF TRUTH. Every value here maps 1:1 into the WordPress
   theme.json when this preview becomes the block theme.
   Locked 2026-07-20 in clients/besamim-turkiye/store/brand-system.md
   ========================================================================== */

:root {
  /* --- Color ------------------------------------------------------------
     Aligned 2026-07-21 to the FINAL logo files, not to brand-direction.html.
     Those two disagreed: the doc said green #15281C / gold #CBA45A, while
     the approved logo art is built on #14301F / #E8BF70. Same hues, but the
     logo runs ~10 points more saturated on the green and ~20 on the gold,
     which is the difference the owner could see.
     The logo wins for a concrete reason: og-share.png and
     logo-full-gold-on-green.svg carry a baked-in green background, so any
     other site green shows a visible rectangle seam behind the logo.
     It also measures better everywhere (gold on green 6.65 -> 8.23).
     ---------------------------------------------------------------------- */
  --c-primary:        #14301F;  /* deep green: header, hero, punctuation blocks */
  --c-primary-soft:   #1C3E28;  /* deep green, one step up: hover, flag bar     */
  --c-accent:         #E8BF70;  /* gold: hairlines, the arch, price, CTA fill   */
  --c-accent-soft:    #F2D6A0;  /* gold, lighter: hover                         */
  --c-bg:             #F7F4E9;  /* ivory page background                        */

  /* Channel values, so a tint is never a second place to change the brand. */
  --rgb-primary:      20, 48, 31;
  --rgb-accent:       232, 191, 112;
  --c-surface:        #FFFFFF;  /* cards, panels, form fields                   */
  --c-text:           #1C1A17;  /* primary text on light            15.8:1      */
  --c-muted:          #8A8178;  /* NON-TEXT ONLY: dividers, icons. 3.5:1 fails  */
  --c-muted-text:     #6B6259;  /* the brand muted, darkened for real text 5.4:1 */
  --c-hairline:       #E7E1D4;  /* 1px rules on light                           */
  --c-on-dark:        #F4EDE1;  /* text on deep green              12.3:1      */
  --c-on-dark-muted:  rgba(244, 237, 225, 0.62);  /* on FLAT green only  5.6:1 */
  /* Secondary text over a photograph, not over a flat colour. 62% is tuned for
     a known background; on the hero plate it measured 4.26:1 at 390px and
     4.53:1 at 1440, against a 4.5:1 legal floor, so it failed on a phone and
     passed by 0.03 on a desktop. The honest fix is weight, not a darker
     photograph: pushing the scrim until 62% works would have buried the image
     the hero exists to show. At 78% the same pixels measure 5.7:1 and 6.1:1.
     Measured by check-hero-contrast.mjs on the rendered page.               */
  --c-on-scene-muted: rgba(244, 237, 225, 0.78);
  --c-line-on-dark:   rgba(var(--rgb-accent), 0.30);  /* gold hairline on green */
  --c-arch-on-dark:   rgba(var(--rgb-accent), 0.50);  /* the arch reads as a form */

  /* --- Type ------------------------------------------------------------- */
  /* Preview loads these from Google Fonts. The WP theme self-hosts the
     same woff2 cuts, so the stack stays identical.                        */
  --f-he: "IBM Plex Sans Hebrew", "IBM Plex Sans", system-ui, sans-serif;
  --f-ar: "IBM Plex Sans Arabic", "IBM Plex Sans Hebrew", system-ui, sans-serif;
  --f-la: "Cormorant Garamond", "IBM Plex Sans", system-ui, serif;

  --fw-light:  300;
  --fw-book:   400;
  --fw-medium: 500;

  /* Fluid scale. Display sits far above body on purpose: the typeface is a
     humanist sans, so the luxury has to come from scale and air, not weight. */
  --t-display:  clamp(2.15rem, 4.5vw, 3.6rem);
  --t-title:    clamp(1.8rem, 3.6vw, 2.8rem);
  --t-subtitle: clamp(1.15rem, 1.9vw, 1.45rem);
  --t-body:     1.0625rem;
  --t-small:    0.9375rem;
  --t-meta:     0.8125rem;
  --t-eyebrow:  0.75rem;

  /* Latin product names are always tracked uppercase. That bilingual
     contrast is the page's texture: quiet Hebrew, spaced Latin.           */
  --tr-eyebrow: 0.24em;
  --tr-latin:   0.18em;

  /* --- Space ------------------------------------------------------------ */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 1rem;
  --s-4: 1.5rem;
  --s-5: 2.5rem;
  --s-6: 4rem;
  --s-7: 6rem;
  --s-8: 8rem;

  /* --- Structure -------------------------------------------------------- */
  --container: 1240px;
  --gutter: clamp(1.25rem, 5vw, 4rem);

  /* The arch. Derived from the logo mark: an upright, a semicircular
     crown, an upright. 999px clamps to half the width = a true semicircle. */
  --arch-crown: 999px;

  /* Corner radius. One softening step across the whole system, so buttons,
     inputs, cards and framed panels read a touch friendlier without turning
     into pills. --radius-sm is for controls and small frames, --radius for
     larger cards and panels. Maps 1:1 to the theme.json radius scale in WP. */
  --radius-sm: 8px;
  --radius:    13px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* --- Contrast rules, measured not guessed --------------------------------
   Checked against the ivory surface #F7F4E9 (WCAG AA needs 4.5:1 for text).
   Israeli standard 5568 makes this a legal requirement, and the store ships
   an accessibility statement, so these are not preferences.

     text        #1C1A17  15.8:1   ok anywhere
     green       #15281C  14.1:1   ok anywhere
     muted-text  #6B6259   5.4:1   ok for captions and meta
     muted       #8A8178   3.5:1   FAILS as text. dividers and icons only
     gold        #CBA45A   2.1:1   FAILS as text on light. decoration only

   So gold carries text only on the deep green (6.7:1 there), and every
   eyebrow flips to green on light surfaces. Gold on ivory is reserved for
   hairlines, the arch, and button fills, where contrast rules do not apply.
   -------------------------------------------------------------------- */

/* ============================ site.css ============================= */
/* ==========================================================================
   iMagnet · layout and components
   Every color and size comes from tokens.css. Nothing hardcoded.
   Written with CSS logical properties throughout so the same rules serve
   Hebrew RTL, Arabic RTL and English LTR without a second stylesheet.
   ========================================================================== */

/* --- Reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--f-he);
  font-size: var(--t-body);
  font-weight: var(--fw-light);
  line-height: 1.85;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, p, figure { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
button { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 3px;
}

/* --- Primitives ----------------------------------------------------------- */
.container {
  max-inline-size: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--s-7); }
.section--tight { padding-block: var(--s-6); }
/* Pulls a section up toward the one above it. Used on the collection so it sits
   closer to the value strip. */
.section--flush-top { padding-block-start: var(--s-5); }

.band-dark {
  background: var(--c-primary);
  color: var(--c-on-dark);
}

/* A green band that carries a jungle-and-gold scene instead of a flat fill, so
   the section reads as part of the same world as the hero rather than a slab of
   colour. The foliage is a real photograph; a flat near-black-green scrim rides
   over it so the ivory copy keeps its contrast (the scrim is darker than the
   base green, so text is at least as legible as it was, verified on rendered
   pixels). The scene stays subordinate: present as depth and light, never
   loud enough to fight the words. */
.band-dark--scene {
  /* The same transparent foliage frame and the same green as the top banner, so
     every green scene band across the site reads as one identical treatment. */
  background:
    url(https://imagnet.co.il/wp-content/uploads/2026/07/scene-shop-frame.webp) center / cover no-repeat,
    #0b2013;
}

/* --- Scene banners on interior pages (visual audit 2026-07-23) -------------
   A generated jungle-and-gold scene behind a page's opening title, so no
   interior page opens on bare ivory. scene-shop / scene-story each carry a
   dark ramp baked into the RIGHT of the frame (the RTL copy side), so only a
   light overall wash is added here and contrast is measured on real pixels. */
.page-hero {
  display: grid;
  align-content: center;
  min-block-size: 340px;
  /* Run full-bleed up under the fixed header (cancelling the body's top pad),
     then pad the copy back down so it clears the header. Same move the home
     hero makes, so the header floats on the banner and its logo/pills flip to
     the on-dark treatment via the is-dark marker the script reads. */
  margin-block-start: calc(-1 * var(--chrome-h, 76px));
  padding-block: calc(var(--chrome-h, 76px) + var(--s-6)) var(--s-7);
  background: center / cover no-repeat var(--c-primary);
  color: var(--c-on-dark);
  border-block-end: 1px solid var(--c-line-on-dark);
}
/* The banner opens over the header, so the header starts in on-dark mode; the
   script then flips --np back to 1 as the ivory grid scrolls under the bar. */
html:has(.page-hero) { --np: 0; }
.page-hero .eyebrow { color: var(--c-accent); }
.page-hero .title { margin-block-end: var(--s-3); }
.page-hero .lede { color: var(--c-on-scene-muted); max-inline-size: 54ch; }
/* The shop banner is a transparent foliage frame laid over the solid brand
   green (the green is the CSS colour, not baked into the art), with the copy
   centred in the clean middle the leaves frame. */
.page-hero--shop {
  background-image: url(https://imagnet.co.il/wp-content/uploads/2026/07/scene-shop-frame.webp);
  /* Matched to the green the lower scene band actually renders (side-1 under
     its 0.18 scrim), so the two green bands on the page read as one colour. */
  background-color: #0b2013;
  text-align: center;
}
.page-hero--shop .lede { margin-inline: auto; }
.page-hero--story { background-image: linear-gradient(to left, rgba(10, 22, 14, 0.62), rgba(10, 22, 14, 0.16) 55%, rgba(10, 22, 14, 0.04)), url(https://imagnet.co.il/wp-content/uploads/2026/07/scene-story.webp); background-position: center 40%; }
[dir="ltr"] .band-dark--scene { background-position: center; }

/* The muted copy on a scene band is over a photograph, not a flat fill, so it
   takes the on-scene muted value (0.78) the hero already uses, never the flat
   0.62 which is calibrated for solid green and drops below the 4.5:1 floor on
   the lit gold of the scene. Measured worst case on the band: 5.15:1. */
.band-dark--scene .lede,
.band-dark--scene .story__body { color: var(--c-on-scene-muted); }

/* Gold fails contrast on ivory, so the eyebrow is green on light and gold
   only on the deep green, where it measures 8.2:1. */
.eyebrow {
  font-size: var(--t-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--c-primary);
  margin-block-end: var(--s-4);
}
/* Every deep-green surface, not only .band-dark. The hero paints the same
   green from its own rule, so an eyebrow there was #14301F on #14301F: a
   1:1 ratio and literally invisible. Gold on the green measures 8.2:1. */
.band-dark .eyebrow,
.hero .eyebrow { color: var(--c-accent); }

/* The brand wordmark. Always the Latin display face, mixed case (never the
   eyebrow's uppercase), so MaxVolmer reads as a name, not a shout. */
.brand {
  font-family: var(--f-la);
  text-transform: none;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
/* When the brand IS the eyebrow (hero, collection), it keeps the eyebrow colour
   but drops the uppercase and heavy tracking, which suit the sans, not a serif. */
.eyebrow--brand {
  font-family: var(--f-la);
  text-transform: none;
  letter-spacing: 0.05em;
  font-size: 1.05rem;
}

/* Skip to content. Standard 5568 asks that a keyboard user be able to pass the
   navigation, and the store ships an accessibility statement, so this is a
   requirement rather than a nicety. Off screen until it takes focus. */
.skip-link {
  position: fixed;
  inset-block-start: var(--s-2);
  inset-inline-start: var(--s-2);
  z-index: 50;
  padding: 0.7rem 1.3rem;
  background: var(--c-accent);
  color: var(--c-primary);
  font-size: var(--t-small);
  font-weight: var(--fw-medium);
  border-radius: var(--radius-sm);
  translate: 0 -400%;
}
.skip-link:focus { translate: 0 0; }

/* Visually hidden, still read aloud. Carries state that color alone implies. */
.vh {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}

/* Latin scent names. Never uppercase the brand name with this class. */
.latin {
  font-family: var(--f-la);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-latin);
  text-transform: uppercase;
}

.title {
  font-size: var(--t-title);
  font-weight: var(--fw-light);
  line-height: 1.25;
}

.lede {
  font-size: var(--t-subtitle);
  font-weight: var(--fw-light);
  line-height: 1.7;
  color: var(--c-muted-text);
}
.band-dark .lede { color: var(--c-on-dark-muted); }

.rule {
  border: 0;
  border-block-start: 1px solid var(--c-hairline);
  margin: 0;
}
.band-dark .rule { border-block-start-color: var(--c-line-on-dark); }

/* --- Buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0.95rem 2.1rem;
  font-size: var(--t-small);
  font-weight: var(--fw-medium);
  letter-spacing: 0.04em;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color 0.3s var(--ease), color 0.3s var(--ease),
              border-color 0.3s var(--ease);
}
.btn--gold {
  background: var(--c-accent);
  color: var(--c-primary);
}
.btn--gold:hover { background: var(--c-accent-soft); }

.btn--ghost {
  background: transparent;
  color: var(--c-accent);
  border-color: var(--c-line-on-dark);
}
.btn--ghost:hover {
  border-color: var(--c-accent);
  background: rgba(var(--rgb-accent), 0.08);
}

.btn--ink {
  background: var(--c-primary);
  color: var(--c-on-dark);
}
.btn--ink:hover { background: var(--c-primary-soft); }

/* Secondary action on ivory. Gold text on ivory fails contrast, so the
   light-background outline button uses the green ink instead. */
.btn--outline {
  background: transparent;
  color: var(--c-primary);
  border-color: var(--c-primary);
}
.btn--outline:hover { background: var(--c-primary); color: var(--c-on-dark); }

/* --- The arch ------------------------------------------------------------- */
/* The signature form. Taken from the logo mark: two uprights joined by a
   semicircular crown. Used in exactly two places, hairline only, never
   filled: the hero frame and the scent card. */
.arch {
  position: relative;
  border: 1px solid var(--c-arch-on-dark);
  background: rgba(244, 237, 225, 0.035);
  border-radius: var(--arch-crown) var(--arch-crown) var(--radius) var(--radius);
  display: grid;
  place-items: end center;
  overflow: hidden;
  transition: border-color 0.5s var(--ease);
}
.arch::before {
  /* light held inside the arch. The brand promise is a presence that stays
     in the air, so the bottle sits in a glow rather than on a gradient. */
  content: "";
  position: absolute;
  inset-block-end: 12%;
  inset-inline-start: 50%;
  translate: -50% 0;
  inline-size: 82%;
  aspect-ratio: 1;
  background: radial-gradient(circle, rgba(var(--rgb-accent), 0.20) 0%, transparent 68%);
  pointer-events: none;
}
/* The interior deepens toward its floor. Enki is near-white glass, and on the
   flat ivory it had almost nothing to sit against: the card read as a hole
   rather than as a bottle. A vertical wash gives every bottle a floor to stand
   on, costs no request, and applies to all six cards equally so the grid still
   reads as one set.
   Measured on rendered screenshots rather than judged by eye: the interior was
   flat to within 1.2 luminance points before this and runs 8.3 points deeper
   at the floor after it. 0.16 was tried and rejected. It separates the bottle
   better still, but it turns the arch into a grey-green panel and this arch is
   specified as hairline only, never filled. */
.arch--light {
  border-color: rgba(var(--rgb-accent), 0.45);
  background: linear-gradient(180deg,
    rgba(var(--rgb-primary), 0.012) 0%,
    rgba(var(--rgb-primary), 0.10) 100%);
}
.arch--light::before {
  background: radial-gradient(circle, rgba(var(--rgb-accent), 0.18) 0%, transparent 68%);
}

.arch__bottle {
  position: relative;
  inline-size: 76%;
  margin-block-end: 8%;
  filter: drop-shadow(0 22px 34px rgba(var(--rgb-primary), 0.30));
}


/* A boxed shot is a 1000 square while the arch is portrait, so it cannot use
   the bottle's sizing: at 76% of the arch width a square composition renders
   the bottle inside it tiny. It sits wider and lower instead. */
.arch__pack {
  position: relative;
  inline-size: 94%;
  margin-block-end: 12%;
  filter: drop-shadow(0 18px 28px rgba(var(--rgb-primary), 0.26));
}

/* Placeholder for packshots still in transit. Every product now has real
   photography, so this survives only for a future product added before its
   shoot lands. */
.arch__pending {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--s-3);
  margin-block-end: 26%;
  text-align: center;
}
.arch__pending img { inline-size: 96px; opacity: 0.26; }
.arch__pending span {
  font-size: var(--t-meta);
  letter-spacing: 0.08em;
  color: var(--c-muted-text);
}

/* --- Header --------------------------------------------------------------- */
/* Deep green, and on the home page it runs straight into the hero so the
   two read as one field. */
/* The header is fixed now, so the document has to make room for it or every
   page but the home page starts underneath it. --chrome-h is measured at
   runtime rather than written down, because the preview flag counts toward it
   and will not exist in the theme. The fallback is the header's own height at
   its usual size, so a page with the script blocked is merely a few pixels off
   rather than broken. The opening cancels this padding on purpose: running
   under the header is the whole point of a transparent one. */
body { padding-block-start: var(--chrome-h, 76px); }

/* Fixed and transparent, so the opening frame is a full screen of photograph
   with the brand floating on it rather than a photograph with a green bar
   stapled above it. It earns a background only once there is content behind it
   to separate from.
   backdrop-filter is the part doing the work: over foliage a flat translucent
   panel turns leaf detail into noise behind the nav, and the blur turns it back
   into a surface. -webkit- prefix included because Safari still wants it. */
.site-header {
  position: fixed;
  /* Below the preview flag, which is also fixed and paints above this. At 0 the
     two overlapped and the flag covered the top half of the header: on desktop
     they cleared each other by luck, and at 390px the flag wraps to two lines
     and swallowed the nav entirely. --flag-h is 0 in the theme, where the flag
     does not exist, so this line costs production nothing. */
  inset-block-start: var(--flag-h, 0px);
  inset-inline: 0;
  z-index: 100;
  color: var(--c-on-dark);
  /* The bar itself is never a surface on desktop: no fill, no hairline, no
     frame. The only surfaces in the header are the pills, so the photograph
     runs unbroken behind it.
     That moves a real problem onto the pills rather than removing it. A bar
     used to be solid here because a transparent one measured 1.53:1 over the
     ivory sections, and ivory links on light glass over ivory is the same
     failure. --np is the answer: see the nav pill below. */
  background: transparent;
  border-block-end: 0;
  transition: background 0.35s var(--ease), border-color 0.35s var(--ease);
}

/* "What is under the header right now."
   0 means a dark region is behind it, so the mark can be the light one and the
   pills can stay a thin smoked glass that lets the scene through. 1 means it is
   over something light, so the mark flips to the dark one and the pills bring
   their own darkness, because ivory links on light glass over ivory is ivory on
   ivory. Everything in the bar reads this one number.
   The script keeps it current as you scroll past each section. These two are
   the value before it runs and the value it falls back to: light is the safe
   default because pills that carry their own darkness are legible on anything,
   and a page that opens on the photograph says so up front. */
   Both of these sit on the root, which is also where the script writes, so the
   live value simply overrides them as an inline style. On body they would win
   against it for everything inside body, which is the whole page. */
:root { --np: 1; }
html:has(.journey) { --np: 0; }
/* Opt in only where a full-bleed dark scene runs underneath. :has() is load
   bearing and also chosen for how it fails: a browser without it keeps the
   solid header, which is legible on every page. */
/* The preview flag is position:static above a fixed header, so the page needs
   to be pushed down by the header's own height or the first section hides
   under it. Only pages that are not the journey need this: the journey is
   deliberately full-bleed under the header. */
/* Three columns rather than space-between, because the nav has to be centred on
   the bar and not merely placed between two groups: with space-between it drifts
   whenever the cart label or the language segment changes width, and it would
   move again the day a third link is added. The outer columns are equal and
   flexible, the nav is its own width, so the centre is the centre.
   In RTL the first column is the right one, which is why the toggle sits first
   in the markup: on a phone it wants the far side from the cart, and on desktop
   it is not there at all and the logo takes the column. */
.site-header__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s-4);
  padding-block: var(--s-3);
}
.site-header__logo {
  justify-self: start;
  position: relative;
  display: inline-flex;
}
.site-header__logo img { block-size: 52px; inline-size: auto; display: block; }
/* The second mark stacks on the first and the two cross fade on --np, the same
   number that darkens the pills. The first one is what sizes the link, so it
   stays in flow and only the second is lifted out of it. */
.site-header__logo img + img {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
}
.logo--on-dark { opacity: calc(1 - var(--np, 1)); }
.logo--on-light { opacity: var(--np, 1); }
.site-header__logo img { transition: opacity 0.3s var(--ease); }

/* The links sit on their own surface rather than directly on the photograph.
   Glass and not a solid fill: the bar is over foliage that is meant to be seen,
   so the pill lifts the text off it without cutting a black hole in the scene.
   It reads as almost nothing over a dark plate, which is the intent, and it is
   what carries the links' contrast once the bar behind it went transparent. */
.nav {
  position: relative;
  display: flex;
  align-items: center;
  justify-self: center;
  gap: var(--s-5);
  font-size: var(--t-small);
  padding: 0.7rem var(--s-5);
  border-radius: 999px;
  /* The brand green, not a smoked black. Glass here means tinted and blurred,
     and the tint is the brand's own colour so the bar belongs to the palette
     rather than reading as a grey panel dropped on the photograph.
     It is a tint and not a veil of ivory: a light veil brightened the leaf
     behind the links and pushed the gold of the active one to 4.48:1, under
     the floor. */
  background-color: rgba(20, 48, 31, 0.62);
  border: 1px solid rgba(244, 237, 225, 0.12);
  backdrop-filter: blur(16px) saturate(115%);
  -webkit-backdrop-filter: blur(16px) saturate(115%);
}
/* The rest of the darkness, for when there is nothing dark behind the pill. It
   is a layer rather than a second colour on the element because a background
   cannot cross fade and this has to: the tone changes while you scroll past a
   section edge, and a step there is visible. */
.nav::before,
.lang-switch::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(20, 48, 31, 0.92);
  opacity: var(--np, 1);
  transition: opacity 0.3s var(--ease);
  pointer-events: none;
}
/* Positioned, so they paint above the layer rather than under it. */
.nav > *,
.lang-switch > * { position: relative; }
.nav a {
  padding-block: var(--s-1);
  border-block-end: 1px solid transparent;
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
}
.nav a:hover, .nav a[aria-current="page"] {
  color: var(--c-accent);
  border-block-end-color: var(--c-accent);
}

.site-header__end {
  display: flex;
  align-items: center;
  justify-self: end;
  gap: var(--s-3);
  font-size: var(--t-small);
}

/* A switch, so it has to look like one thing with two halves rather than like
   two links that happen to be adjacent. The filled half is where you are and
   the empty half is the one tap that changes it. */
.lang-switch {
  position: relative;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: 999px;
  /* Same two layers as the nav pill, and for the same reason. */
  background-color: rgba(10, 22, 14, 0.34);
  background-image: linear-gradient(
    rgba(10, 22, 14, calc(var(--np, 1) * 0.62)),
    rgba(10, 22, 14, calc(var(--np, 1) * 0.62)));
  border: 1px solid rgba(244, 237, 225, 0.12);
  backdrop-filter: blur(16px) saturate(115%);
  -webkit-backdrop-filter: blur(16px) saturate(115%);
}
.lang-switch__seg {
  display: grid;
  place-items: center;
  min-inline-size: 34px;
  block-size: 28px;
  padding-inline: 0.5rem;
  border-radius: 999px;
  font-size: var(--t-meta);
  letter-spacing: 0.06em;
  /* The on-scene 78%, never the flat 62%. This bar is over a photograph and the
     flat value measured 4.30:1 on a lit leaf, under the 4.5:1 floor. */
  color: var(--c-on-scene-muted);
  transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}
.lang-switch__seg.is-on {
  background: var(--c-accent);
  color: var(--c-primary);
  font-weight: var(--fw-medium);
}
.lang-switch__seg:not(.is-on):hover { color: var(--c-on-dark); }

/* The one filled control on the bar. Gold, pill, and the only thing here that
   is not glass, because it is the only thing here that is an action. */
.cart-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0.62rem 1.3rem;
  border-radius: 999px;
  background: var(--c-accent);
  color: var(--c-primary);
  font-size: var(--t-small);
  font-weight: var(--fw-medium);
  letter-spacing: 0.04em;
  white-space: nowrap;
  transition: background-color 0.3s var(--ease);
}
.cart-btn:hover { background: var(--c-accent-soft); }
/* The cart sits at the far side of the button, which in RTL means last in the
   markup. It is the object the button is about, so it is the larger of the two
   things in there and the word is the caption. */
.cart-btn__cart {
  position: relative;
  display: block;
  flex: none;
  inline-size: 24px;
  block-size: 24px;
}
.cart-btn__icon {
  inline-size: 24px;
  block-size: 24px;
  display: block;
}
/* On the cart, not next to it. Pulled up and out so it reads as a badge stuck
   on the corner rather than as part of the drawing. */
.cart-btn__count {
  position: absolute;
  inset-block-start: -7px;
  inset-inline-end: -8px;
  display: grid;
  place-items: center;
  min-inline-size: 17px;
  block-size: 17px;
  padding-inline: 3px;
  border-radius: 999px;
  background: var(--c-primary);
  color: var(--c-accent);
  font-size: 11px;
  line-height: 1;
  font-weight: var(--fw-medium);
}

/* Below 900px the nav was simply display:none, which left a phone with no
   navigation at all: six links unreachable, and the only way to any of them
   was the one CTA in the hero. This is the missing half of that rule. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  inline-size: 44px;                /* a real 44px target, not a 24px icon */
  block-size: 44px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  inline-size: 24px;
  block-size: 2px;
  margin-inline: auto;
  border-radius: var(--radius-sm);
  background: var(--c-on-dark);
  transition: transform 0.3s var(--ease), opacity 0.2s var(--ease);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* --- Hero ----------------------------------------------------------------- */
/* One full-bleed photograph that the copy and the bottle stand inside. It was
   a 360px framed arch first, and that was wrong: a whole generated scene was
   showing through a 48px strip down each side of the bottle, which reads as a
   texture rather than as a place. The plates are now generated with a
   deliberate empty half and the copy sits in it. */
.hero {
  position: relative;
  isolation: isolate;          /* keeps the plate and scrim out of the page's stacking order */
  display: grid;
  align-items: center;
  min-block-size: min(80vh, 780px);
  background: var(--c-primary);
  color: var(--c-on-dark);
  padding-block: var(--s-6) var(--s-7);
  overflow: hidden;
}
/* The wrapper exists only to carry the art direction, so it must not become a
   box between .hero and the image it positions. */
.hero picture { display: contents; }
.hero__plate {
  position: absolute;
  inset: 0;
  z-index: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* Anchored to the empty half, not centred. When the viewport is proportionally
     narrower than the plate, cover crops horizontally, and centring would walk
     the foliage into the copy at exactly the widths nobody tests. Anchoring
     here keeps the empty half whole and takes the crop out of the foliage,
     where it costs a narrower band behind the bottle and nothing else. */
  object-position: 100% 50%;
}
/* Hebrew and Arabic both set the copy on the right, which is where the plate's
   empty half already is. English arrives in early August and flips the layout,
   and one mirror handles it: object-position resolves before the transform, so
   the crop still keeps the empty half and the mirror simply moves it to the
   left, under the copy. Foliage has no handedness, so nothing looks reversed. */
[dir="ltr"] .hero__plate { transform: scaleX(-1); }

/* Contrast here is a legal requirement, not a preference, so it is enforced in
   CSS rather than left to whatever the generator produced. Measured on the
   shipped plate by check-plate-deadzone.mjs: the wide plate's copy zone is
   already 15.87:1 for solid ivory and 6.63:1 for the 62% muted colour with no
   scrim at all, so on desktop this is insurance for odd viewport ratios. On
   the phone plate it is load bearing: the muted colour measures 3.01:1 bare
   and 4.80:1 under the scrim. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to left,
    rgba(10, 22, 14, 0.55) 0%,
    rgba(10, 22, 14, 0.34) 34%,
    rgba(10, 22, 14, 0) 66%);
}
[dir="ltr"] .hero::after {
  background: linear-gradient(to right,
    rgba(10, 22, 14, 0.55) 0%,
    rgba(10, 22, 14, 0.34) 34%,
    rgba(10, 22, 14, 0) 66%);
}

.hero__grid {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1.35fr 0.65fr;
  align-items: center;
  gap: clamp(2rem, 6vw, 5rem);
}
.hero__title {
  font-size: var(--t-display);
  font-weight: var(--fw-light);
  line-height: 1.14;
  margin-block-end: var(--s-4);
  text-wrap: balance;
}
.hero__sub {
  font-size: var(--t-subtitle);
  /* Not --c-on-dark-muted. This line sits on a photograph now, and the flat
     background value fails the contrast floor on a phone. See tokens.css. */
  color: var(--c-on-scene-muted);
  max-inline-size: 40ch;
  margin-block-end: var(--s-5);
}
/* Two hero actions: the featured bottle (gold) and the full collection
   (ghost, which is built for the dark scene). They wrap on a narrow phone. */
.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}
.hero__figure { justify-self: center; margin: 0; }
/* Free standing in the scene rather than inside a frame. The shadow is black
   and not the green used on ivory cards, because here it falls on foliage:
   its job is separating the glass from the leaves behind it, not implying a
   floor the photograph does not contain. */
.hero__bottle {
  display: block;
  /* 300 rather than 340. The opening rests the bottle at 0.92 of this, so what
     is actually on screen goes from 313px to 276px: present, and no longer
     competing with the foliage for the picture.
     The vw arm is untouched. It only takes over below about 480px, which is a
     width where the phone rules have replaced this sizing anyway. */
  inline-size: min(300px, 62vw);
  block-size: auto;
  filter: drop-shadow(0 26px 46px rgba(0, 0, 0, 0.60));
}

/* --- The opening ---------------------------------------------------------- */
/* A full screen of foliage with the mark standing in it, which resolves into
   the hero as you scroll: the leaves recede, the mark grows and dissolves, the
   photograph underneath takes over, and the copy and the bottle rise into it.
   Built by wrapping the hero rather than rebuilding it, deliberately. The hero
   is the finished state, its contrast is measured on rendered pixels, and this
   only decides when it becomes visible. That also means the reduced-motion
   path is not a second design to maintain: it is the hero, unwrapped.
   One number crosses the JS/CSS seam, --p, the scroll progress from 0 to 1.
   Everything else is a band expressed here, so the timing is tunable in the
   stylesheet by whoever is looking at it rather than buried in a script. */
.journey {
  position: relative;
  background: var(--c-primary);
  /* Scrub distance is the part beyond one screen. 130vh means the opening
     takes a little over one full flick on a phone. */
  block-size: calc(100vh + 130vh);
  block-size: calc(100svh + 130vh);
  margin-block-start: calc(-1 * var(--chrome-h, 76px));   /* sit under the fixed header */
}
.journey__stage {
  position: sticky;
  inset-block-start: 0;
  block-size: 100vh;
  block-size: 100svh;                 /* svh so mobile browser chrome cannot jump the stage */
  overflow: hidden;
}
/* The header floats over the photograph, so the photograph has to give it
   something to sit on. This plate's upper left is its brightest area, which is
   exactly where the cart and language links sit, and a translucent bar plus a
   blur was not enough: the nav measured below the 4.5:1 floor there.
   Sits above the intro rather than under it so it holds at every point of the
   opening, not only once the plate has dissolved. It stops short of the header's
   own height plus a soft tail, so it reads as light falling off rather than as
   a band. */
.journey__stage::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 8;
  block-size: calc(var(--chrome-h, 76px) + 72px);
  background: linear-gradient(to bottom,
    rgba(10, 22, 14, 0.78) 0%,
    rgba(10, 22, 14, 0.62) 46%,
    rgba(10, 22, 14, 0) 100%);
  pointer-events: none;
}
.journey .hero {
  block-size: 100%;
  min-block-size: 0;
  /* Centre the copy in the screen the visitor sees, not in the whole stage. The
     stage is a full 100svh and runs up under the fixed header, but the base
     .hero centres its content in that whole height, so the header's 132px counts
     as empty space above the copy and the copy is pulled up into it: measured,
     the eyebrow sat 76px below the header with 240px of void under the button,
     top heavy enough that the eyebrow lined up with the logo.
     Offsetting the top padding by the chrome height moves the centre of the
     padding box down to the middle of [chrome, stage bottom], which is the
     middle of the visible area. The symmetric s-5 is breathing room on each side
     of that region. --chrome-h is measured and includes the preview flag, so
     this follows the flag out in production and re-centres on the header alone,
     with the 92px fallback covering the first paint before the measure lands. */
  padding-block: calc(var(--chrome-h, 92px) + var(--s-5)) var(--s-5);
}

/* The site opens as one photograph with the mark in it and nothing else. The
   header, and the preview strip above it, arrive on the same gesture that takes
   the mark away: the chrome assembles itself as the brand dissolves, instead of
   sitting over the picture from the first frame.
   The band starts where the mark starts leaving, 0.24, and completes before the
   mark is fully gone at 0.46, so the two read as one handover rather than as a
   gap with nothing on screen.
   Opacity only, never display or visibility: --chrome-h is measured from these
   two elements, and the negative pull that lets the photograph run under the
   header is derived from it. Taking them out of flow would move the thing they
   sit on and the opening would jump at the moment it is meant to settle.
   Scoped to .has-journey, so reduced motion and a failed script get the header
   they always had, fully visible, immediately. */
html.has-journey .site-header,
html.has-journey .preview-flag {
  opacity: clamp(0, calc((var(--p, 0) - 0.24) / 0.22), 1);
}
/* A keyboard reaches the nav before a scroll does. Following focus into a
   header that cannot be seen is worse than showing it early, so focus brings it
   back whatever the scroll position. */
html.has-journey .site-header:focus-within { opacity: 1; }

.journey__intro {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  overflow: hidden;
  pointer-events: none;
  /* Holds full until 0.24, gone by 0.54, so the mark has the screen to itself
     before anything starts resolving. */
  opacity: clamp(0, calc((0.54 - var(--p, 0)) / 0.30), 1);
}
/* With the film running the opening is one continuous camera move that ends on
   the hero, so the intro must NOT dissolve halfway: it plays the whole scrub and
   hands over at the very end. The last frame matches the hero plate to a mean
   of 5.2/255, so that handover is a crossfade between two near-identical
   pictures and reads as nothing at all. Ending on the real plate rather than on
   the canvas is deliberate: the resting hero is the one whose contrast is
   measured, and it stays that way. */
html.has-film .journey__intro {
  opacity: clamp(0, calc((0.90 - var(--p, 0)) / 0.12), 1);
}
.journey__intro picture { display: contents; }
/* display:contents hands the picture's children to the parent's layout, and a
   <source> is one of those children. In a block box that costs nothing, so this
   went unnoticed. In this grid it became a second row: two auto rows, the free
   space split between them by the default stretch, and the mark pushed 115px
   below the centre place-items asked for. It was landing below its own pocket,
   the ring of shadow that exists to keep gold legible on lit leaves, which is
   the part that made this worth chasing rather than a matter of taste.
   Found by the press target, which is centred by the box model and therefore
   did not inherit the same mistake: 230 against 345 in one measurement. */
.journey__intro picture > source,
.hero picture > source { display: none; }
.journey__intro img.journey__plate {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* A slow push in. The leaves come toward you as they dissolve, which is what
     makes the hero underneath feel like it was always further back. */
  transform: scale(calc(1 + var(--p, 0) * 0.14));
}
/* The plate keeps its CSS push even once the film is running. Cancelling it
   looked tidier and was worse: .has-film lands mid scroll, so the plate would
   snap back from its zoom while the canvas was still fading in over it. It is
   fully covered by then anyway, so there is nothing to gain and a visible jump
   to lose. */

/* The camera move. Covers the stage exactly as the plate does, and is painted
   only once the script has frames to paint, so it never flashes empty over the
   plate it is standing on. */
.journey__film {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  display: block;
  opacity: 0;
  transition: opacity 240ms linear;
}
html.has-film .journey__film { opacity: 1; }
/* Gold on foliage is the one contrast case here that no plate solves, because
   every plate has lit leaf edges somewhere. So the mark carries its own pocket
   of shadow rather than depending on where the leaves happened to fall. */
.journey__pocket {
  position: absolute;
  inset: 0;
  background: radial-gradient(46% 46% at 50% 50%,
    rgba(10, 22, 14, 0.82) 0%,
    rgba(10, 22, 14, 0.58) 52%,
    rgba(10, 22, 14, 0) 100%);
}
/* The pocket exists to hold the mark, so it has to leave with the mark. It used
   to get that for free from the intro dissolving at 0.54; now that the intro
   stays for the whole move, a shadow with nothing in it would ride the camera
   all the way down. Same band as the mark, deliberately. */
html.has-film .journey__pocket {
  opacity: clamp(0, calc((0.42 - var(--p, 0)) / 0.24), 1);
}
.journey__mark {
  position: relative;
  z-index: 2;
  /* Sized for a full screen rather than for a slot. At 340px it read as a logo
     dropped on a photograph; the opening is the one moment the brand gets the
     whole screen, and the mark should take it. */
  inline-size: min(36vw, 440px);
  block-size: auto;
  /* The mark gets the screen to itself first. It holds at full opacity while it
     grows, and only once it has grown does it start to go, which is also when
     the camera starts moving and the chrome arrives. Three things on one beat
     rather than a logo that is already dissolving before it has been read.
     Leaves a touch before the plate does, so the screen is foliage alone for a
     moment rather than cutting from logo straight to copy. */
  opacity: clamp(0, calc((0.46 - var(--p, 0)) / 0.22), 1);
  transform: scale(calc(1 + var(--p, 0) * 0.95));
}
/* The press target, sitting exactly on the mark and growing with it. It carries
   no look of its own: the mark is the button as far as anyone can see, and a
   ring or a fill around it would put a control on top of the one moment the
   brand has the screen to itself. Only a keyboard gets a visible outline,
   because a keyboard cannot see where the pointer is.
   inset plus auto margins rather than a centring translate: the page is RTL and
   transforms are physical, so a translate would need a different sign per
   direction while this is centred by the box model in both.
   z-index 6 puts it over the intro at 5. The scrim at 8 stays above it, which is
   correct: that is a gradient with pointer-events none and it must keep sitting
   over the picture. */
.journey__play {
  position: absolute;
  inset: 0;
  margin: auto;
  z-index: 6;
  inline-size: min(36vw, 440px);
  block-size: min(36vw, 440px);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  transform: scale(calc(1 + var(--p, 0) * 0.95));
}
/* No focus rule of its own: the sheet's :focus-visible already draws the ring
   in the accent, which is the one this would have asked for by name. */
/* Once the mark has gone the button is a 440px invisible disc in the middle of
   the screen, and the hero's own title and bottle end up underneath it. Display
   rather than opacity, so it leaves the tab order too: a control nobody can see
   is not one a keyboard should still find. The class is driven from the same
   progress the mark's own fade uses. */
.journey__play.is-spent { display: none; }

/* The whole opening is a click target on a desktop, so it shows a pointer until
   the hero has settled, at which point the stage hands the cursor back and its
   own copy and call to action behave normally. On a phone the opening plays
   itself and the cursor never comes up. */
html.has-journey .journey__stage { cursor: pointer; }
html.has-journey .journey.is-done .journey__stage { cursor: default; }

/* The copy and the bottle arrive last, and share one band so they move as one
   thing rather than as two elements that happen to animate. */
.journey .hero__copy,
.journey .hero__figure {
  --r: clamp(0, calc((var(--p, 0) - 0.46) / 0.30), 1);
  opacity: var(--r);
  transform: translateY(calc((1 - var(--r)) * 26px));
}
/* Later with the film, because the camera is still travelling at 0.46 and copy
   riding a moving frame reads as a caption on someone else's footage.
   It starts where the handover above ends rather than under it. The intro is a
   layer above the hero, so copy that rises while the film is still opaque rises
   invisibly: the first cut of this had the band at 0.74 and the whole hero
   appeared in one jump in the last few percent, which is the pop this timing
   exists to avoid.
   Finishes at 0.90, not 1.0, and that is the whole point of this change. The
   opening used to assemble on the very last pixel of the scrub: measured frame
   by frame, both clips are dead still from about 0.88 on, diff from the last
   frame under 1/255, yet the bottle only reached full size at 1.0. So the one
   moment the hero was whole was the moment the section released, and on a phone,
   which is flicked rather than nudged, you went straight past it. Ending the
   rise at 0.90 leaves 0.90 to 1.0 as a hold: the finished hero, pinned, while
   there is still scroll left to rest on it. */
html.has-film .journey .hero__copy,
html.has-film .journey .hero__figure {
  --r: clamp(0, calc((var(--p, 0) - 0.78) / 0.12), 1);
}
/* The bottle is not a third line of copy, so it stops sharing the copy's move.
   It comes up from below the frame, slightly small, and reaches full size as it
   arrives, which reads as the object being set down in the pocket the camera
   opened rather than being switched on in place.
   It starts a little before the copy and travels further, so it leads and the
   words settle around it.
   The origin is the base of the bottle, not its centre: growing from the centre
   makes glass inflate, growing from the foot makes it stand up. */
html.has-film .journey .hero__figure {
  /* Rises 0.76 to 0.90, a touch ahead of the copy, and comes to rest where the
     camera does. It is the element the owner could not see on a phone, because
     it finished at 1.0 with no scroll left to hold it. It now finishes at 0.90
     and is held, at full size, through the last tenth of the scrub. It still
     starts behind the intro, which is opaque until 0.78 and gone by 0.90, so it
     spends its first sliver hidden and then emerges rising as the film clears
     rather than popping in once it has. */
  --r: clamp(0, calc((var(--p, 0) - 0.76) / 0.14), 1);
  transform-origin: 50% 100%;
  /* Starts smaller and comes to rest below full size, so the arrival is a real
     change of scale and the bottle sits in the scene rather than filling it.
     0.70 to 0.92, not 0.86 to 1: the travel is more than twice what it was and
     the resting size leaves the foliage room to be the picture. */
  transform:
    translateY(calc((1 - var(--r)) * 96px))
    scale(calc(0.70 + var(--r) * 0.22));
}
/* The load-time rise animation would fight the scroll reveal for the same
   properties, and it has nothing to add here: the journey is the entrance. */
.journey .rise { animation: none; opacity: 1; transform: none; }

/* Without JS there is no --p, so every band would sit at its zero and the page
   would open on foliage with the copy invisible and no way to reveal it. The
   journey therefore only exists once a script has confirmed it can drive it.
   Everything above is scoped to that class; this is the state before it. */
/* The negative pull is deliberately NOT reset here. It was at first, and that
   left the transparent header floating over the bare strip of body background
   that the padding had opened, with nothing behind its links: measured at
   1.53:1 for every pixel of the bar. A transparent header needs the dark
   photograph underneath it in every state, not only when the opening runs. */
html:not(.has-journey) .journey { block-size: auto; }
html:not(.has-journey) .journey__stage { position: static; block-size: auto; }
html:not(.has-journey) .journey__intro { display: none; }
html:not(.has-journey) .journey .hero { block-size: auto; min-block-size: min(80vh, 780px); }
html:not(.has-journey) .journey .hero__copy,
html:not(.has-journey) .journey .hero__figure { --r: 1; }

/* Reduced motion lands on exactly the same place as no JS: the finished hero,
   with its measured contrast, and no scroll hijack. The script does not add
   .has-journey when the query matches, so this needs no rules of its own. */

/* --- Value strip ---------------------------------------------------------- */
.values {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-block: 1px solid var(--c-hairline);
}
.values__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--s-5) var(--s-4);
  text-align: center;
  font-size: var(--t-small);
  color: var(--c-text);
}
.values__item + .values__item { border-inline-start: 1px solid var(--c-hairline); }
/* The four store reassurances carry the same line-icon language as the product
   page trust bar: check-circle, delivery, padlock, gift box. */
.values__icon {
  inline-size: 26px;
  block-size: 26px;
  fill: none;
  stroke: var(--c-primary);
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  margin-block-end: var(--s-3);
}
.values__item strong {
  display: block;
  font-weight: var(--fw-medium);
  margin-block-end: var(--s-1);
}
.values__item span { color: var(--c-muted-text); }
/* Four marks wrap to a 2x2 on a phone. The single-row inline dividers would
   then land in the wrong places, so the odd cells drop their start border and
   the second row gains a top border. */
@media (max-width: 720px) {
  .values { grid-template-columns: 1fr 1fr; }
  .values__item:nth-child(odd) { border-inline-start: 0; }
  .values__item:nth-child(n+3) { border-block-start: 1px solid var(--c-hairline); }
}

/* --- Section head --------------------------------------------------------- */
.section-head {
  max-inline-size: 62ch;
  margin-block-end: var(--s-6);
}
.section-head--center {
  margin-inline: auto;
  text-align: center;
}
.section-head .title { margin-block-end: var(--s-3); }

/* --- Scent grid ----------------------------------------------------------- */
.scents {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 3vw, 2.75rem);
}
/* A column, so the price rule can be pinned to the bottom. Hook lines differ
   in length between scents, and with plain blocks a two-line hook pushed that
   card's price 25px below its neighbours' and broke the row's baseline. */
.scent {
  display: flex;
  flex-direction: column;
  block-size: 100%;
}
.scent .arch {
  aspect-ratio: 3 / 3.9;
  margin-block-end: var(--s-4);
}

/* The signature interaction: the bottle is pulled up toward the crown of
   the arch. The brand is called iMagnet. */
@media (prefers-reduced-motion: no-preference) {
  .scent .arch__bottle { transition: transform 0.55s var(--ease), opacity 0.45s var(--ease); }
  .scent .arch__box { transition: opacity 0.45s var(--ease); }
  .scent:hover .arch__bottle,
  .scent:focus-visible .arch__bottle { transform: translateY(-14px); }
}
.scent:hover .arch--light { border-color: var(--c-accent); }

/* Hovering reveals the box the bottle comes in. Savour and Pour Homme are the
   same olive at card size, and the box is what separates them: a different
   colour each, and five of six carry a readable name. It is also the gifting
   proof, which is this store's commercial angle.
   This rides the gesture that already lifts the bottle instead of adding a
   second signature: the bottle is pulled away and what is under it is the box.
   Two deliberate choices. It is a background rather than an <img>, so a screen
   reader hears each product once instead of twice. And it lives inside a hover
   query, so a phone never downloads 221KB of boxes it has no way to reveal.
   Which is also the limit: on a phone this does nothing at all. */
/* display:none on a touch device, so the box is never even fetched there.
   Paired with loading="lazy" this keeps 221KB of boxes off a phone that has no
   way to reveal them. It was first built as a background-image driven by a
   custom property, which was wrong: a relative url() inside a custom property
   resolves against the stylesheet consuming the var(), not the document
   declaring it, so ../assets/ resolved from css/ and pointed at
   site-preview/assets, which does not exist. An <img> src has no such trap. */
.arch__box { display: none; }

@media (hover: hover) and (pointer: fine) {
  .scent .arch__box {
    display: block;
    position: absolute;
    inset-block-end: 10%;
    /* Centred with inset-inline + auto margins, never with
       inset-inline-start:50% plus translate:-50%. That idiom shifts twice in
       the same direction under RTL and threw the box almost entirely outside
       the arch, where overflow:hidden ate it. The same trap is documented for
       .info__bubble further down this file. */
    inset-inline: 0;
    margin-inline: auto;
    inline-size: 94%;
    opacity: 0;
    pointer-events: none;
  }
  .scent:hover .arch__box,
  .scent:focus-visible .arch__box { opacity: 1; }
  .scent:hover .arch__bottle,
  .scent:focus-visible .arch__bottle { opacity: 0; }
}

.scent__cat {
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  color: var(--c-muted-text);
  margin-block-end: var(--s-1);
}
/* The brand line sits above each scent name, in the brand green rather than the
   muted gender line, so the card reads brand then product. It is a dedicated
   line because more brands than MaxVolmer may join the shelf later. */
.scent__brand {
  font-size: var(--t-meta);
  font-weight: var(--fw-medium);
  letter-spacing: 0.14em;
  color: var(--c-primary);
  margin-block-end: var(--s-2);
}
.scent__name {
  font-size: 1.05rem;
  color: var(--c-primary);
  margin-block-end: var(--s-2);
}
.scent__hook {
  font-size: var(--t-small);
  line-height: 1.7;
  margin-block-end: var(--s-3);
  text-wrap: pretty;
}
.scent__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-block-start: auto;   /* pins the price rule to the card's bottom edge */
  padding-block-start: var(--s-3);
  border-block-start: 1px solid var(--c-hairline);
}
.scent__price {
  font-size: 1.15rem;
  font-weight: var(--fw-book);
  color: var(--c-primary);
}
.scent__family { font-size: var(--t-meta); color: var(--c-muted-text); }

/* --- Story ---------------------------------------------------------------- */
.story__inner {
  max-inline-size: 60ch;
  margin-inline: auto;
  text-align: center;
}
.story__inner .title { margin-block-end: var(--s-4); }
.story__body {
  color: var(--c-muted-text);
  font-size: var(--t-subtitle);
  margin-block-end: var(--s-5);
  text-wrap: pretty;
}
/* On the deep green the body flips to the on-dark muted ink. The home page's
   #story teaser is a dark band and reads the same as before; the story page
   adds light sections, where the ivory-on-ivory default would be invisible. */
.band-dark .story__body { color: var(--c-on-dark-muted); }
.story__inner .btn { display: inline-flex; }

/* Story page: tropical foliage rails down both outer edges.
   iMagnet jungle foliage, colour-matched to the site leaves and rebuilt as a
   vertically seamless mirror loop (transparent PNGs in ../assets/story-page/).
   The rails sit ABOVE the flat section colours and BELOW the text, so the leaves
   spill from the green bands onto the ivory sections as one continuous motif
   down the scroll. They reach UP behind the transparent fixed header so the
   foliage begins at the very top of the page, not below the menu. */
.page-story main { position: relative; }
.foliage-rail {
  position: absolute;
  inset-block-start: calc(-1 * var(--chrome-h, 76px));  /* start at the page top, behind the header */
  inset-block-end: 0;
  inline-size: clamp(72px, 15vw, 280px);   /* full opacity at every size, incl. mobile */
  z-index: 0;
  pointer-events: none;
  background-repeat: repeat-y;
  background-size: 100% auto;
}
.foliage-rail--left  { left: 0;  background-image: url(https://imagnet.co.il/wp-content/uploads/2026/07/foliage-rail-left.webp);  background-position: left top; }
.foliage-rail--right { right: 0; background-image: url(https://imagnet.co.il/wp-content/uploads/2026/07/foliage-rail-right.webp); background-position: right top; }

/* The copy stays centred and its width tracks the viewport, so it always sits
   in the clear centre the two rails frame and never touches a leaf, on mobile
   or desktop. 58vw leaves ~21vw of clear gutter each side; the leaves reach at
   most ~12vw in, so there is always daylight between the words and the plants. */
.page-story .container { position: relative; z-index: 1; }
.page-story .page-head,
.page-story .story__inner {
  max-inline-size: min(56ch, 58vw);
  margin-inline: auto;
  text-align: center;
}
.page-story .page-head .lede { max-inline-size: min(52ch, 56vw); margin-inline: auto; }

/* Bold words inside the running copy carry the full ink (not the muted body
   colour) so the emphasis actually pops and guides the eye. */
.story__body strong,
.lede strong { font-weight: var(--fw-medium); color: var(--c-text); }
.band-dark .story__body strong,
.band-dark .lede strong { color: var(--c-on-dark); font-weight: var(--fw-medium); }

/* On phones the leaves stay at full strength but slim, and the copy narrows to
   a centred column with clear daylight on both sides. */
@media (max-width: 720px) {
  .foliage-rail { inline-size: 56px; }
  .page-story .page-head,
  .page-story .story__inner,
  .page-story .page-head .lede { max-inline-size: 56vw; }
}

/* The scene-frame bands (home story teaser, shop hero + closing CTA) carry the
   leaves baked into the background at the section edges. On phones the cover
   crop pulls those leaves inward, so the copy takes extra side room to keep
   clear daylight between the words and the plants. */
@media (max-width: 720px) {
  .band-dark--scene .container,
  .page-hero .container { padding-inline: 18vw; }
}

/* On the story page the side rails run up behind the header, so the logo (at the
   container's right edge) would otherwise sit under the right rail's leaves.
   Nudge it inward, tied to the rail's own reach (~15vw), capped so it never
   drifts too far on wide screens where the leaves are already clear. */
.page-story .site-header__logo { margin-inline-start: clamp(0px, 11vw, 130px); }

/* --- Gifting -------------------------------------------------------------- */
.gift {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(2rem, 6vw, 5rem);
  background: var(--c-surface);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius);
  padding: clamp(2rem, 5vw, 4rem);
}
.gift__title { margin-block-end: var(--s-3); }
.gift__body { color: var(--c-muted-text); margin-block-end: var(--s-5); }
.gift__figure .arch {
  inline-size: min(280px, 70vw);
  aspect-ratio: 3 / 3.6;
  margin-inline: auto;
}

/* --- Closing + footer ----------------------------------------------------- */
.closing {
  text-align: center;
  padding-block: var(--s-7);
}
.closing .title { margin-block-end: var(--s-5); }

/* Every page ends with a dark band directly above this dark footer, and two
   green blocks in a row read as one undifferentiated field: the closing call
   to action was being swallowed. A gold hairline is the only separator this
   palette needs. */
.site-footer {
  padding-block: var(--s-5) var(--s-4);
  border-block-start: 1px solid var(--c-line-on-dark);
}
.site-footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--s-5);
  padding-block-end: var(--s-4);
}
.site-footer__logo img { block-size: 56px; inline-size: auto; margin-block-end: var(--s-2); }
.site-footer__tag {
  font-size: var(--t-small);
  color: var(--c-on-dark-muted);
  max-inline-size: 32ch;
}
/* The social row sits under the brand: line icons that pick up the muted value
   and warm to gold on hover, the same affordance as the link lists. */
.site-footer__social {
  display: flex;
  gap: var(--s-3);
  list-style: none;
  padding: 0;
  margin-block-start: var(--s-3);
}
.site-footer__social a {
  display: inline-flex;
  color: var(--c-on-dark-muted);
  transition: color .15s ease;
}
.site-footer__social a:hover { color: var(--c-accent); }
.site-footer__social svg {
  inline-size: 20px;
  block-size: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* WhatsApp reads clearest as its solid glyph, so it opts out of the stroke
   treatment the monoline icons share. */
.site-footer__social svg.is-solid { fill: currentColor; stroke: none; }
.site-footer h3 {
  font-size: var(--t-meta);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--c-accent);
  margin: 0 0 var(--s-3);
}
.site-footer li { margin-block-end: var(--s-2); font-size: var(--t-small); }
.site-footer li a:hover { color: var(--c-accent); }
.site-footer__base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding-block-start: var(--s-3);
  border-block-start: 1px solid var(--c-line-on-dark);
  font-size: var(--t-meta);
  color: var(--c-on-dark-muted);
}
.site-footer__by { display: inline-flex; align-items: center; gap: 0.6ch; }
.site-footer__webside {
  block-size: 15px;
  inline-size: auto;
  display: inline-block;
  vertical-align: middle;
  opacity: 0.85;
}

/* --- Shop page ------------------------------------------------------------ */
.page-head {
  padding-block: var(--s-6) var(--s-5);
  border-block-end: 1px solid var(--c-hairline);
}
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  padding-block: var(--s-5);
}
.filter {
  padding: 0.5rem 1.25rem;
  font-size: var(--t-small);
  border: 1px solid var(--c-hairline);
  border-radius: 999px;
  background: transparent;
  cursor: pointer;
  transition: all 0.3s var(--ease);
}
.filter:hover { border-color: var(--c-accent); }
.filter[aria-pressed="true"] {
  background: var(--c-primary);
  border-color: var(--c-primary);
  color: var(--c-on-dark);
}

/* --- Product page --------------------------------------------------------- */
.crumbs {
  padding-block: var(--s-4);
  font-size: var(--t-meta);
  color: var(--c-muted-text);
}
.crumbs a:hover { color: var(--c-primary); }

.product {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 6vw, 5rem);
  padding-block: var(--s-5) var(--s-6);
  align-items: start;
}

/* Related products. A green band closes the page, and the cards run in a
   compact variant so the strip supports the product rather than competing
   with it: shorter arch, no hook line, smaller type. */
.related-band { padding-block: var(--s-6) var(--s-4); }
.scents--compact {
  gap: var(--s-4);
  /* Narrower than the container as well as shorter, so the strip reads as a
     footnote to the product rather than a second shop page. */
  max-inline-size: 860px;
  margin-inline: auto;
}
.scents--compact .arch {
  aspect-ratio: 3 / 2.5;
  margin-block-end: var(--s-3);
}
.scents--compact .scent__name { font-size: 0.95rem; }
.scents--compact .scent__price { font-size: 1.05rem; }
.scents--compact .arch__pending img { inline-size: 60px; }
.scents--compact .arch__pending { margin-block-end: 16%; gap: var(--s-2); }

/* Scent cards sitting on the deep green need their own ink. */
.band-dark .scent__name { color: var(--c-on-dark); }
.band-dark .scent__cat,
.band-dark .scent__family,
.band-dark .arch__pending span { color: var(--c-on-dark-muted); }
.band-dark .scent__price { color: var(--c-accent); }
.band-dark .scent__foot { border-block-start-color: var(--c-line-on-dark); }
/* The info column is much taller than the media column, so on desktop the
   media sticks and the right side stops being empty on the way down.
   .product uses align-items:start, which is what lets sticky work here.

   The catch is laptop screens. A stuck element taller than the viewport can
   never be scrolled to the bottom of, so the image gets clipped and stays
   clipped. So the whole media column is capped against the viewport height
   and shrinks to fit rather than being cut. The factor 0.66 is derived from
   what the column actually stacks: the arch at its 3/3.8 ratio, its 16px
   margin, and the thumbnail strip. On a tall desktop 100% wins and nothing
   changes. */
@media (min-width: 901px) {
  .product__media {
    position: sticky;
    inset-block-start: var(--s-5);
    inline-size: min(100%, calc((100vh - 5rem) * 0.66));
    inline-size: min(100%, calc((100dvh - 5rem) * 0.66));
    margin-inline: auto;
  }
}
.product__media .arch {
  aspect-ratio: 3 / 3.8;
  margin-block-end: var(--s-3);
}

/* --- Product gallery ------------------------------------------------------
   The arch becomes the viewport for a horizontal scroll-snap track. The swipe
   on a phone is then the browser's own, which is what makes it smooth on iOS
   Safari, and the arrows and thumbnails are enhancements layered on top. The
   track is laid over the arch's glow rather than replacing the arch, so the
   frame, its hairline and its interior wash are all unchanged. */
.gallery__track {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;
  border-radius: inherit;
}
.gallery__track::-webkit-scrollbar { display: none; }
.gallery__slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  display: grid;
  place-items: end center;   /* the bottle stands on the arch floor, as before */
  min-inline-size: 0;
}
/* The close-up is landscape, so it centres rather than standing. */
.gallery__slide--center { place-items: center; }
.gallery__detail {
  position: relative;
  inline-size: 90%;
  max-block-size: 72%;
  object-fit: contain;
  filter: drop-shadow(0 16px 26px rgba(var(--rgb-primary), 0.22));
}

/* The lifestyle slide: the real bottle inside a generated jungle-and-gold
   scene. Unlike the packshot slides it is a full photograph, so it fills the
   arch edge to edge and inherits its rounded crown, rather than floating on the
   light interior. The scene carries its own light, so it needs none of the
   arch's glow. */
.gallery__slide--scene { place-items: stretch; }
.gallery__scene {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  border-radius: inherit;
}

/* Circular controls pinned to the arch's vertical centre, where its sides are
   straight and full width so the round radius never clips them. Green fill with
   an ivory chevron reads on the light interior at any slide. Hidden until the
   script marks the gallery ready, and desktop only: a phone swipes. */
/* Relative wrapper around the arch only, so the arrows can be positioned
   against the image's own box and sit just outside it without the arch's
   overflow:hidden clipping them. */
.gallery-frame { position: relative; }
.gallery__arrow {
  position: absolute;
  inset-block-start: 50%;
  translate: 0 -50%;
  z-index: 2;
  display: none;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--c-primary);
  color: var(--c-on-dark);
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(var(--rgb-primary), 0.30);
  transition: background-color 0.25s var(--ease);
}
.gallery__arrow:hover { background: var(--c-primary-soft); }
.gallery__arrow svg { inline-size: 42%; block-size: 42%; }
/* Outside the image, not on top of it. The owner asked for this directly: an
   arrow sitting on the photograph hides a good part of it. The arrows now live
   in .gallery-frame, a relative wrapper around the arch that does NOT clip, so
   they can sit just beyond the arch's edges in the column's own breathing room.
   2.75rem is the arrow's own size, plus a small gap. */
.gallery__arrow--prev { inset-inline-start: calc(-1 * (2.75rem + 0.6rem)); }   /* just past the right edge in RTL */
.gallery__arrow--next { inset-inline-end: calc(-1 * (2.75rem + 0.6rem)); }     /* just past the left edge in RTL  */
@media (min-width: 901px) {
  /* Sibling of the arch now, revealed once the gallery marks itself ready. */
  .gallery.is-ready ~ .gallery__arrow { display: grid; }
}

/* Thumbnails, now .elementor-kit-11s rather than decoration. One per slide, the active
   one carries a green border, and the image inside is decorative because the
   button names itself. */
.product__thumbs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-2);
}
.product__thumb {
  aspect-ratio: 4 / 3;   /* square thumbs cost too much of the capped height */
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius-sm);
  background: rgba(var(--rgb-primary), 0.035);
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: border-color 0.25s var(--ease);
}
.product__thumb img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
}
.product__thumb:hover { border-color: var(--c-accent); }
.product__thumb.is-active { border-color: var(--c-primary); }

/* The H1 holds the display name AND the qualifying terms, so the heading
   Google reads carries "בושם", the concentration and the size, while the
   page still leads with the wordmark. */
.product__name {
  margin-block-end: var(--s-2);
  font-weight: var(--fw-light);
}
.product__name-latin {
  display: block;
  font-size: clamp(2rem, 4vw, 2.9rem);
  color: var(--c-primary);
  line-height: 1.1;
}
.product__name-sub {
  display: block;
  margin-block-start: var(--s-2);
  font-family: var(--f-he);
  font-size: var(--t-small);
  font-weight: var(--fw-light);
  letter-spacing: normal;
  text-transform: none;
  color: var(--c-muted-text);
}
.product__cat { font-size: var(--t-meta); letter-spacing: 0.1em; color: var(--c-muted-text); }
.product__hook {
  font-size: var(--t-subtitle);
  line-height: 1.7;
  margin-block: var(--s-4);
  text-wrap: pretty;
}
.product__price {
  font-size: 1.9rem;
  font-weight: var(--fw-book);
  color: var(--c-primary);
  margin-block-end: var(--s-1);
}
.product__vat { font-size: var(--t-meta); color: var(--c-muted-text); margin-block-end: var(--s-4); }

.product__buy {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-block-end: var(--s-4);
}
.product__buy .btn { flex: 1 1 12rem; justify-content: center; }

/* Quantity stepper. Sized so the two buttons clear a 24px touch target and
   the whole control matches the add-to-cart height beside it. */
.qty {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--c-primary);
  border-radius: var(--radius-sm);
  flex: none;
}
.qty__btn {
  inline-size: 2.75rem;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--c-primary);
  font-size: 1.15rem;
  line-height: 1;
  transition: background-color 0.25s var(--ease);
}
.qty__btn:hover { background: rgba(var(--rgb-primary), 0.07); }
.qty__input {
  inline-size: 3rem;
  border: 0;
  border-inline: 1px solid var(--c-hairline);
  background: transparent;
  text-align: center;
  font: inherit;
  font-size: var(--t-small);
  color: var(--c-text);
  -moz-appearance: textfield;
  appearance: textfield;
}
.qty__input::-webkit-outer-spin-button,
.qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* The three reassurances. The owner asked for these to carry weight, so
   they are a bordered three-up with icons rather than a thin caption row. */
.trust {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border: 1px solid var(--c-hairline);
  background: var(--c-surface);
  border-radius: var(--radius);
  overflow: hidden;                 /* so the inner dividers stop at the rounded corners */
  margin-block-end: var(--s-5);
}
.trust__item {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-3);
  font-size: var(--t-meta);
  line-height: 1.45;
  color: var(--c-text);
}
.trust__item + .trust__item { border-inline-start: 1px solid var(--c-hairline); }
.trust__item svg {
  inline-size: 20px;
  block-size: 20px;
  flex: none;
  stroke: var(--c-primary);
  fill: none;
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.product__desc { margin-block-end: var(--s-5); text-wrap: pretty; }

/* Fragrance pyramid. Head, heart, base genuinely are a sequence in
   perfumery, so the ordered structure carries real information. */
.pyramid__intro {
  font-size: var(--t-small);
  color: var(--c-muted-text);
  margin-block-end: var(--s-4);
}

/* Drawn as an actual pyramid. Each row owns its own band, so the bands can
   never drift out of line with their text the way a single background
   shape would. The three tints ascend with depth, which is real: the base
   is what is still on the skin hours later. A 2px gap does the separating,
   never a stroke. */
.pyramid { margin-block-end: var(--s-5); display: grid; gap: 2px; }
.pyramid__row {
  display: grid;
  grid-template-columns: 104px 3.5rem 1fr;
  gap: var(--s-3);
  align-items: center;
}
.pyramid__band {
  block-size: 34px;
  background: var(--c-primary);
}
.pyramid__row:nth-child(1) .pyramid__band {
  clip-path: polygon(50% 0, 66% 100%, 34% 100%);
  opacity: 0.22;
}
.pyramid__row:nth-child(2) .pyramid__band {
  clip-path: polygon(34% 0, 66% 0, 83% 100%, 17% 100%);
  opacity: 0.45;
}
.pyramid__row:nth-child(3) .pyramid__band {
  clip-path: polygon(17% 0, 83% 0, 100% 100%, 0 100%);
  opacity: 0.78;
}
.pyramid__label {
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  color: var(--c-primary);   /* was gold: 2.1:1 on ivory, fails as text */
  font-weight: var(--fw-medium);
  display: inline-flex;
  align-items: center;
}
.pyramid__notes { font-size: var(--t-small); }

.spec {
  font-size: var(--t-meta);
  color: var(--c-muted-text);
  letter-spacing: 0.04em;
  margin-block-end: var(--s-5);
}

details.faq {
  border-block-end: 1px solid var(--c-hairline);
}
details.faq summary {
  cursor: pointer;
  padding-block: var(--s-3);
  font-size: var(--t-small);
  font-weight: var(--fw-medium);
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-3);
}
details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::after {
  content: "+";
  color: var(--c-primary);   /* a control affordance needs 3:1; gold gives 2.1 */
  font-size: 1.2rem;
  line-height: 1;
}
details.faq[open] summary::after { content: "\2212"; }
details.faq p {
  padding-block-end: var(--s-4);
  font-size: var(--t-small);
  color: var(--c-muted-text);
  max-inline-size: 66ch;
}

/* --- Info bubble ---------------------------------------------------------- */
/* Opens on hover for mouse and on focus for keyboard and touch, so one
   component covers all three without JavaScript. */
.info {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
  margin-inline-start: var(--s-2);
}
.info__btn {
  inline-size: 18px;
  block-size: 18px;
  border: 1px solid var(--c-muted);
  border-radius: 50%;
  background: transparent;
  color: var(--c-muted-text);
  font-family: var(--f-la);
  font-size: 11px;
  font-weight: var(--fw-medium);
  line-height: 1;
  display: grid;
  place-items: center;
  cursor: help;
  padding: 0;
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.info__btn:hover { border-color: var(--c-primary); color: var(--c-primary); }

/* Anchored by its inline-start edge, not centred. Centring with
   inset-inline-start:50% plus a physical translate double-shifts in RTL and
   pushes the bubble off the left edge. Anchoring makes it open along the
   reading direction and stay inside the text column in both directions. */
.info__bubble {
  position: absolute;
  inset-block-start: calc(100% + 10px);
  inset-inline-start: 0;
  inline-size: min(288px, 74vw);
  padding: var(--s-3) var(--s-3);
  background: var(--c-primary);
  color: var(--c-on-dark);
  border-radius: 3px;
  font-size: var(--t-meta);
  font-weight: var(--fw-light);
  line-height: 1.65;
  text-align: start;
  letter-spacing: normal;
  text-transform: none;
  opacity: 0;
  visibility: hidden;
  translate: 0 4px;
  transition: opacity 0.22s var(--ease), translate 0.22s var(--ease),
              visibility 0.22s;
  z-index: 5;
}
.info__bubble::before {
  content: "";
  position: absolute;
  inset-block-end: 100%;
  inset-inline-start: 4px;
  border: 5px solid transparent;
  border-block-end-color: var(--c-primary);
}
.info:hover .info__bubble,
.info:focus-within .info__bubble {
  opacity: 1;
  visibility: visible;
  translate: 0 0;
}

/* --- Performance metrics -------------------------------------------------- */
/* Longevity and sillage are single ratios against a limit, so each is a
   meter, not a chart. One hue: the fill is the brand green, the track a
   lighter step of the same green. The number is always printed as text, so
   no value is reachable only by colour. */
.metrics {
  border-block: 1px solid var(--c-hairline);
  padding-block: var(--s-4);
  margin-block-end: var(--s-5);
  display: grid;
  gap: var(--s-4);
}

.metric__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-block-end: var(--s-2);
}
.metric__label {
  font-size: var(--t-small);
  font-weight: var(--fw-medium);
  display: inline-flex;
  align-items: center;
}
.metric__value {
  font-size: var(--t-meta);
  color: var(--c-muted-text);
  font-variant-numeric: tabular-nums;
}
.metric__value b {
  font-weight: var(--fw-medium);
  color: var(--c-text);
  font-size: var(--t-small);
}

.meter {
  block-size: 6px;
  border-radius: 3px;
  background: rgba(var(--rgb-primary), 0.12);
  overflow: hidden;
}
.meter__fill {
  display: block;
  block-size: 100%;
  background: var(--c-primary);
  border-start-end-radius: 4px;
  border-end-end-radius: 4px;
}
.metric__note {
  font-size: var(--t-meta);
  color: var(--c-muted-text);
  margin-block-start: var(--s-2);
}

/* Categorical rows: season, time of day, gender. Nominal categories, so
   every active chip shares one colour rather than a ramp, and the label
   text is always present so state never rests on colour alone. */
.metric--chips .metric__head { margin-block-end: var(--s-3); }
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.34rem 0.85rem;
  border: 1px solid var(--c-hairline);
  border-radius: 999px;
  font-size: var(--t-meta);
  color: var(--c-muted-text);
  background: transparent;
}
.chip.is-on {
  background: var(--c-primary);
  border-color: var(--c-primary);
  color: var(--c-on-dark);
}
/* Icons inherit currentColor, so they invert with the chip automatically.
   Hairline strokes only, no fills: at 14px a filled glyph turns to mud. */
.chip svg {
  inline-size: 14px;
  block-size: 14px;
  flex: none;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}

/* --- Section head, secondary ---------------------------------------------
   A left-anchored eyebrow + title used above a form column or a story band,
   lighter than .page-head so it groups a subsection rather than opening a page. */
.subhead { margin-block-end: var(--s-5); }
.subhead .title { font-size: var(--t-subtitle); margin-block-start: var(--s-2); }

/* --- FAQ page ------------------------------------------------------------- */
/* Reuses the product page's details.faq accordion, grouped into headed sets. */
.faq-groups { max-inline-size: 760px; margin-inline: auto; }
.faq-group + .faq-group { margin-block-start: var(--s-6); }
.faq-group > .eyebrow { margin-block-end: var(--s-3); }
/* A hairline over the first item so each set reads as a bounded list, matching
   the bottom border every row already carries. */
.faq-group .faq:first-of-type { border-block-start: 1px solid var(--c-hairline); }
/* The homepage FAQ teaser: a short set of questions that ends in a link to the
   full knowledge centre, centred under the accordion. */
.faq-home__cta { text-align: center; margin-block-start: var(--s-5); }

/* --- Forms (contact, checkout) -------------------------------------------
   Fields sit on white so they read as an input against the ivory page. The
   focus ring is a legal requirement under 5568, not decoration, so it is a
   green border plus a soft halo that shows on any field. Every control clears
   a 48px touch target. All spacing logical, so the grid holds in He, Ar and
   later En with no second rule. */
.form { display: grid; gap: var(--s-4); }
.field { display: grid; gap: var(--s-2); }
.field > label {
  font-size: var(--t-small);
  font-weight: var(--fw-medium);
  color: var(--c-text);
}
.field input,
.field textarea,
.field select {
  font: inherit;
  font-size: var(--t-body);
  color: var(--c-text);
  background: var(--c-surface);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius-sm);
  padding: 0.8rem 1rem;
  min-block-size: 3rem;
  inline-size: 100%;
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.field textarea { min-block-size: 8.5rem; resize: vertical; line-height: 1.6; }
/* The label carries the meaning, so the placeholder is a hint and can be the
   muted-text value, which still measures 5.4:1 rather than the 3.5:1 muted. */
.field input::placeholder,
.field textarea::placeholder { color: var(--c-muted-text); opacity: 1; }
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--c-primary);
  box-shadow: 0 0 0 3px rgba(var(--rgb-primary), 0.12);
}
.field--wide { grid-column: 1 / -1; }
.field__hint { font-size: var(--t-meta); color: var(--c-muted-text); }

/* --- Contact page --------------------------------------------------------- */
.contact {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: clamp(2rem, 6vw, 5rem);
  padding-block: var(--s-5) var(--s-6);
  align-items: start;
}
.contact-methods { display: grid; gap: var(--s-3); }
/* A method is a card; when its value exists later (a wa.me link, a tel:) the
   whole card becomes the anchor and lights its border on hover. Inert now,
   because there is no real number to point at yet. */
.contact-method {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius-sm);
}
a.contact-method { transition: border-color 0.25s var(--ease); }
a.contact-method:hover { border-color: var(--c-accent); }
.contact-method__icon {
  flex: none;
  inline-size: 46px; block-size: 46px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(var(--rgb-primary), 0.06);
}
.contact-method__icon svg {
  inline-size: 22px; block-size: 22px;
  stroke: var(--c-primary); fill: none;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.contact-method__body { display: grid; gap: 2px; min-inline-size: 0; }
.contact-method__label {
  font-size: var(--t-meta);
  letter-spacing: 0.04em;
  color: var(--c-muted-text);
}
.contact-method__value { font-size: var(--t-body); color: var(--c-text); font-weight: var(--fw-medium); }
/* The value that has not been filled yet reads plainly as pending, not as a
   real detail a visitor could act on. */
.contact-method__value--pending { color: var(--c-muted-text); font-weight: var(--fw-book); }

/* --- Content page (legal shells: terms, privacy, shipping, accessibility) -
   The frame only. The four pages carry no real copy: a plugin injects the
   legal text into .prose later. .prose therefore styles arbitrary injected
   headings, paragraphs and lists for a comfortable RTL read, and the preview
   shows a marked-empty placeholder so the shell does not read as broken. */
.content-page { padding-block: var(--s-5) var(--s-7); }
.content-page .container { max-inline-size: 820px; }
.content-page__title {
  font-size: var(--t-title);
  font-weight: var(--fw-light);
  line-height: 1.2;
  margin-block-end: var(--s-3);
}
.content-page__meta { font-size: var(--t-meta); color: var(--c-muted-text); margin-block-end: var(--s-5); }
.prose { color: var(--c-text); }
.prose > * + * { margin-block-start: var(--s-4); }
.prose h2 {
  font-size: var(--t-subtitle);
  font-weight: var(--fw-medium);
  line-height: 1.3;
  color: var(--c-primary);
  margin-block-start: var(--s-5);
}
.prose h3 { font-size: var(--t-body); font-weight: var(--fw-medium); margin-block-start: var(--s-4); }
.prose p, .prose li { font-size: var(--t-body); line-height: 1.8; max-inline-size: 68ch; }
.prose ul, .prose ol { padding-inline-start: var(--s-4); display: grid; gap: var(--s-2); }
.prose a { color: var(--c-primary); text-decoration: underline; text-underline-offset: 3px; }
.prose__placeholder {
  border: 1px dashed var(--c-hairline);
  border-radius: var(--radius-sm);
  padding: var(--s-5);
  color: var(--c-muted-text);
  font-size: var(--t-small);
  line-height: 1.7;
  text-align: center;
}

/* --- Cart + checkout (static mockups) ------------------------------------
   Non-functional on purpose: their job is to prove WooCommerce's own Cart and
   Checkout blocks can be styled to this design, and to be the CSS target at
   migration. So every surface is a real brand surface, nothing is invented. */
.cart {
  display: grid;
  grid-template-columns: 1.5fr 0.7fr;
  gap: clamp(2rem, 5vw, 4rem);
  padding-block: var(--s-5) var(--s-6);
  align-items: start;
}
.cart__lines { border-block-start: 1px solid var(--c-hairline); }
.cart-line {
  display: grid;
  grid-template-columns: 84px 1fr auto;
  gap: var(--s-4);
  align-items: center;
  padding-block: var(--s-4);
  border-block-end: 1px solid var(--c-hairline);
}
.cart-line__media {
  inline-size: 84px;
  aspect-ratio: 3 / 4;
  background: rgba(var(--rgb-primary), 0.035);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius-sm);
  display: grid; place-items: center; overflow: hidden;
}
.cart-line__media img { inline-size: 78%; block-size: 78%; object-fit: contain; }
.cart-line__body { display: grid; gap: var(--s-2); min-inline-size: 0; }
/* The stepper is inline-flex, but as a grid item it would stretch to the whole
   column under the default justify-self:stretch. Anchor it to its intrinsic
   width at the inline start (the right, in RTL). */
.cart-line__body .qty { justify-self: start; }
.cart-line__title { font-size: var(--t-body); }
.cart-line__meta { font-size: var(--t-meta); color: var(--c-muted-text); }
.cart-line__end { display: grid; justify-items: end; gap: var(--s-2); text-align: end; }
.cart-line__price { font-size: var(--t-body); font-weight: var(--fw-medium); white-space: nowrap; }
.cart-line__remove {
  background: none; border: 0; padding: 0;
  color: var(--c-muted-text);
  font-size: var(--t-meta);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cart-line__remove:hover { color: var(--c-primary); }

/* The empty state. Shipped in the markup as a hidden variant so the owner can
   see it and the WordPress build has the target, without a second page. */
.cart-empty {
  text-align: center;
  padding-block: var(--s-7);
  border-block: 1px solid var(--c-hairline);
}
.cart-empty p { color: var(--c-muted-text); margin-block: var(--s-3) var(--s-5); }
.is-hidden { display: none; }

/* Order summary, shared by cart and checkout. Sticks beside a tall column on
   desktop and drops under it on a phone. */
.summary {
  background: var(--c-surface);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius-sm);
  padding: var(--s-5);
}
@media (min-width: 901px) {
  .summary { position: sticky; inset-block-start: var(--s-5); }
}
.summary__title { font-size: var(--t-subtitle); font-weight: var(--fw-light); margin-block-end: var(--s-4); }
.summary__row {
  display: flex;
  justify-content: space-between;
  gap: var(--s-3);
  padding-block: var(--s-2);
  font-size: var(--t-small);
  color: var(--c-muted-text);
}
.summary__row span:last-child { color: var(--c-text); }
.summary__row--total {
  border-block-start: 1px solid var(--c-hairline);
  margin-block-start: var(--s-2);
  padding-block-start: var(--s-4);
  font-size: var(--t-body);
  color: var(--c-text);
  font-weight: var(--fw-medium);
}
.summary .btn { inline-size: 100%; justify-content: center; margin-block-start: var(--s-4); }
.summary__note { font-size: var(--t-meta); color: var(--c-muted-text); text-align: center; margin-block-start: var(--s-3); }

/* Checkout: a form of stacked sections beside the order summary. */
.checkout {
  display: grid;
  grid-template-columns: 1.25fr 0.75fr;
  gap: clamp(2rem, 5vw, 4rem);
  padding-block: var(--s-5) var(--s-6);
  align-items: start;
}
.checkout__form { display: grid; gap: var(--s-6); }
.checkout__section > h2 { font-size: var(--t-subtitle); font-weight: var(--fw-light); margin-block-end: var(--s-4); }
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }

/* A chosen option in a mockup (a shipping method, the payment gateway). Reads
   as selectable without being a live control. */
.option {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  font-size: var(--t-small);
}
.option + .option { margin-block-start: var(--s-2); }
.option.is-on { border-color: var(--c-primary); box-shadow: inset 0 0 0 1px var(--c-primary); }
.option__dot {
  flex: none;
  inline-size: 18px; block-size: 18px;
  border-radius: 50%;
  border: 1px solid var(--c-muted);
  display: grid; place-items: center;
}
.option.is-on .option__dot { border-color: var(--c-primary); }
.option.is-on .option__dot::after {
  content: "";
  inline-size: 9px; block-size: 9px;
  border-radius: 50%;
  background: var(--c-primary);
}
.option__body { display: grid; gap: 2px; min-inline-size: 0; }
.option__meta { font-size: var(--t-meta); color: var(--c-muted-text); }
.option__price { margin-inline-start: auto; font-weight: var(--fw-medium); white-space: nowrap; }

/* Gift wrap add-on (checkout, after the shipping choice). A static mockup shown
   in its chosen state: three wrap slots and a handwritten-greeting field. The
   slots are image placeholders, real wrap photos swap into .wrap__img later. */
.giftwrap__panel { margin-block-start: var(--s-4); }
.giftwrap__label { font-size: var(--t-small); font-weight: var(--fw-medium); margin-block-end: var(--s-3); }
.giftwrap__wraps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-3);
  margin-block-end: var(--s-4);
}
.wrap {
  padding: 0;
  border: 1px solid var(--c-hairline);
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  cursor: pointer;
  overflow: hidden;
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.wrap__img {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  background: rgba(var(--rgb-primary), 0.05);
}
.wrap__img svg {
  inline-size: 34px; block-size: 34px;
  stroke: var(--c-primary); fill: none;
  stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.7;
}
.wrap__label { display: block; padding-block: var(--s-2); font-size: var(--t-meta); color: var(--c-muted-text); }
.wrap:hover { border-color: var(--c-accent); }
.wrap.is-on { border-color: var(--c-primary); box-shadow: inset 0 0 0 1px var(--c-primary); }
.wrap.is-on .wrap__label { color: var(--c-text); font-weight: var(--fw-medium); }

/* --- Forms + mockups, responsive ----------------------------------------- */
@media (max-width: 900px) {
  .contact,
  .cart,
  .checkout { grid-template-columns: 1fr; }
  .field-grid { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  /* The price and remove control drop under the title rather than fighting the
     name for width at 390. media | body across two rows. */
  .cart-line {
    grid-template-columns: 68px 1fr;
    grid-template-areas: "media body" "media end";
    column-gap: var(--s-3);
    row-gap: var(--s-3);
  }
  .cart-line__media { grid-area: media; inline-size: 68px; align-self: start; }
  .cart-line__body { grid-area: body; }
  .cart-line__end {
    grid-area: end;
    justify-items: start;
    text-align: start;
    flex-direction: row;
  }
}

/* --- Preview-only note ---------------------------------------------------- */
/* Fixed, because the header above it is fixed and a static flag would scroll
   out from under it and leave a gap. Preview-only scaffolding: it does not
   exist in the theme, which is exactly why the space it takes is measured at
   runtime into --chrome-h rather than written into a rule as a number. */
.preview-flag {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 101;
  /* The brand green itself, not the step up from it. The strip is meant to read
     as the brand's own colour above the bar rather than as a lighter band. */
  background: var(--c-primary);
  color: var(--c-on-dark-muted);
  font-size: var(--t-meta);
  text-align: center;
  padding-block: var(--s-2);
  letter-spacing: 0.04em;
}
.preview-flag strong { color: var(--c-accent); font-weight: var(--fw-medium); }

/* --- Load-in -------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .rise {
    opacity: 0;
    transform: translateY(18px);
    animation: rise 0.9s var(--ease) forwards;
  }
  .rise--2 { animation-delay: 0.12s; }
  .rise--3 { animation-delay: 0.24s; }
  @keyframes rise {
    to { opacity: 1; transform: none; }
  }
}

/* --- Responsive ----------------------------------------------------------- */
@media (max-width: 900px) {
  .nav-toggle { display: flex; }

  /* The whole bar becomes one pill on a phone, with the toggle on one side, the
     logo centred and the cart on the other. The toggle is first in the markup,
     so in RTL it takes the right hand column with no reordering.
     The centre column is the flexible one here, the opposite of desktop, so the
     logo stays centred while the two ends size to their content. */
  .site-header__inner {
    grid-template-columns: auto 1fr auto;
    gap: var(--s-2);
    border-radius: 999px;
    padding: var(--s-2) var(--s-3);
    margin-block: var(--s-2);
    background-color: rgba(244, 237, 225, 0.07);
    /* The brand green, the same tint the desktop nav pill carries, not the
       near-black it used to be: over an ivory section the bar reads as green
       rather than as a black bar dropped on white. */
    background-image: linear-gradient(
      rgba(20, 48, 31, calc(var(--np, 1) * 0.88)),
      rgba(20, 48, 31, calc(var(--np, 1) * 0.88)));
    border: 1px solid rgba(244, 237, 225, 0.12);
    backdrop-filter: blur(16px) saturate(115%);
    -webkit-backdrop-filter: blur(16px) saturate(115%);
  }
  .site-header__logo { justify-self: center; }
  .site-header__logo img { block-size: 40px; }
  /* The mark stays the light cut on a phone, always, and does not cross fade the
     way it does on desktop. The bar here is a pill with a dark fill of its own:
     the photograph shows through it over the hero, and the dark green covers it
     over every ivory section, so the mark is on dark in all of them. The desktop
     switch exists because there the logo sits on the bare bar and meets ivory
     directly; here it would swap in the dark cut over an ivory section and land
     it on the pill's own dark green, dark on dark, which is the one pairing the
     whole tone system is there to avoid. So it is pinned, not switched. */
  .logo--on-dark { opacity: 1; }
  .logo--on-light { opacity: 0; }
  /* Four controls do not fit across 390px without the logo losing the middle,
     so the cart keeps its icon and its count and drops the word. The accessible
     name is on the anchor, so nothing is lost to a screen reader. */
  .cart-btn { padding: 0.45rem 0.7rem; gap: var(--s-1); }
  .cart-btn__label { display: none; }
  .lang-switch__seg {
    min-inline-size: 27px;
    block-size: 24px;
    padding-inline: 0.3rem;
  }
  .nav-toggle { inline-size: 40px; block-size: 40px; }
  /* The drawer hangs off the fixed header, so it is fixed too and starts where
     the header ends. The inline offset is logical, so it opens correctly under
     Hebrew and Arabic now and under English in August without a second rule. */
  .nav.is-open {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    position: fixed;
    inset-block-start: var(--header-h, 76px);
    inset-inline: 0;
    padding: var(--s-4) var(--s-5) var(--s-5);
    /* The brand green, like the desktop nav pill, not a near-black panel: the
       drawer belongs to the palette rather than reading as black on the white
       page behind it. Near-opaque so the content under it stays covered, and
       the cream links measure 12.3:1 on this green. */
    background: rgba(20, 48, 31, 0.98);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-block-end: 1px solid var(--c-line-on-dark);
    font-size: var(--t-body);
  }
  .nav.is-open a {
    padding-block: var(--s-3);
    border-block-end: 1px solid rgba(var(--rgb-accent), 0.12);
  }
  .nav.is-open a:last-child { border-block-end: 0; }

  .hero__grid,
  .gift,
  .product { grid-template-columns: 1fr; }
  .hero__figure { order: -1; }

  /* On a phone the opening's stage is exactly one screen and it clips, so the
     hero has to FIT that screen rather than merely start in it. It did not:
     the bottle ran to the bottom edge of the plate and the call to action was
     cut off below the fold entirely, which is not a small crop, it is the one
     thing the hero is for.
     The bottle is therefore capped in svh rather than in px or vw. Its height
     is what competes with the copy for the screen, so height is what has to be
     bounded, and bounding it in the same unit as the stage means it holds on a
     short phone in landscape and on a tall one without a second guess. */
  /* The hero's own vertical padding plus the grid's came to about 317px inside
     an 844px screen, and the bottle's row is what paid for it: it collapsed to
     nothing while the copy took everything. Inside the opening the grid owns
     the spacing, because it is the thing that has to clear the fixed header. */
  /* A longer scrub than the desktop's 130vh. The reveal now finishes at 0.90
     and holds to 1.0, and that hold is a tenth of the scrub: a tenth of 130vh
     is about a finger flick, gone in one gesture. A tenth of this is a real
     stretch of screen with the finished hero pinned in it, which on a phone is
     the difference between resting on the bottle and never landing on it.
     It also slows the camera itself, which reads as a heavier, more deliberate
     push on a small screen. */
  .journey {
    block-size: calc(100vh + 190vh);
    block-size: calc(100svh + 190vh);
  }
  html.has-journey .journey .hero { padding-block: 0; }
  html.has-journey .journey .hero__grid {
    /* The screen, stated outright, in the same unit the stage is built from.
       100% and stretch were both tried first and both left this at its content
       height, 597 inside an 844 screen, because the hero is itself a grid whose
       row is auto: the row sized to this and this asked for the row. Naming the
       height rather than deriving it ends that, and the inner 1fr row below can
       finally resolve, which is what gives the bottle its space back. */
    block-size: 100svh;
    /* The copy takes the height it needs and the bottle takes what is left,
       rather than the bottle taking a share and the copy taking its chances.
       Capping the bottle in svh instead was measured on four phones and the
       button was still cut off on all four, by 12px to 118px: any fixed share
       is a guess about a copy block whose height depends on how the Hebrew
       wraps at that width. This asks the question the other way round and there
       is nothing left to guess.
       If the copy alone ever exceeds the screen the bottle collapses to nothing
       and the words survive, which is the right way for this to fail. */
    grid-template-rows: minmax(0, 1fr) auto;
    /* The hero grid centres its items on a wide screen, which sizes each one to
       its own content. Here the figure's only content is the bottle and the
       bottle is out of flow, so centring gave a row 267px tall holding a box
       0px tall. Stretch is what hands the row's height to the figure. */
    align-items: stretch;
    gap: var(--s-3);
    /* Clears the fixed header at the top, and lifts the group off the bottom
       edge so the bottle stands in the scene the way it does on a wide screen
       instead of sitting on the frame. */
    padding-block: calc(var(--chrome-h, 76px) + var(--s-2)) var(--s-5);
  }
  html.has-journey .journey .hero__figure {
    position: relative;
    min-block-size: 0;          /* or the row refuses to shrink under its image */
    /* Stretch, because the bottle inside it is out of flow: the figure has no
       content left to be as wide as, and centring a box with no width gives a
       box with no width. That rendered the hero with the button finally in
       place and no bottle at all. */
    justify-self: stretch;
  }
  /* Taken out of flow on purpose. A percentage height on an image inside a 1fr
     row is circular, the row sizes to the image and the image to the row, and
     the browser resolves it by falling back to the file's intrinsic 1400px:
     the measured need went UP from 834 to 911 doing it the obvious way.
     Absolute against the figure resolves against a box the grid has already
     given a definite height, so the picture fits the space instead of defining
     it. contain keeps the glass in proportion. */
  html.has-journey .journey .hero__bottle {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    max-inline-size: none;
    object-fit: contain;
  }

  /* The scrim runs bottom up on a phone, because that is where the copy is.
     It was load bearing under the old 4:5 plate: 3.01:1 bare, 4.80:1 under
     this, against a 4.5:1 legal floor. The plate is now the frame the phone's
     camera move comes to rest on, whose lower half is genuinely dark rather
     than merely darker, and the same copy measures 16.22:1 worst pixel. So this
     is margin now rather than the thing carrying the floor. It stays: it costs
     nothing visible on an already dark plate, and it is what keeps the floor
     met if the plate is ever recut. */
  .hero::after,
  [dir="ltr"] .hero::after {
    background: linear-gradient(to top,
      rgba(10, 22, 14, 0.62) 0%,
      rgba(10, 22, 14, 0.55) 42%,
      rgba(10, 22, 14, 0) 74%);
  }
  .scents { grid-template-columns: repeat(2, 1fr); }
  .site-footer__grid { grid-template-columns: 1.2fr 1fr 1fr; gap: var(--s-4); }
  .nav { display: none; }

  /* Below desktop the related strip becomes a one-row carousel instead of
     wrapping onto a second row. Cards are 62% wide so the next one always
     peeks in, which is what tells the reader it scrolls. It bleeds to the
     screen edges: the negative margin only cancels the container's own
     padding, so this never widens the page. */
  /* Doubled class on purpose: the narrower breakpoint later in this file
     also matches .scents, and at equal specificity it would win and put the
     first card in a 1fr column while the rest went to auto columns. */
  .scents.scents--compact {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 62%;
    gap: var(--s-3);
    max-inline-size: none;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline-start: var(--gutter);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    padding-block-end: var(--s-2);
    scrollbar-width: none;
  }
  .scents--compact::-webkit-scrollbar { display: none; }
  .scents--compact .scent { scroll-snap-align: start; }
  .scents--compact .arch { aspect-ratio: 3 / 3.1; }
}

@media (max-width: 560px) {
  :root { --s-7: 4rem; --s-8: 5rem; }

  /* Two up, not one. Stacked single cards made the home page 7,962px on a
     390 screen, about twenty screenfuls, and four of the six scents sat below
     any reasonable scroll. The grid stays at two columns from the wider
     breakpoint; here the gap tightens and the hook drops to meta size so the
     narrower column still holds a readable line. */
  /* Scoped away from the compact strip on purpose. This breakpoint sits after
     the 900 one, so an unscoped .scent rule wins over .scents--compact at
     equal specificity and would resize the product page's carousel too. */
  .scents:not(.scents--compact) { gap: var(--s-3); }
  .scents:not(.scents--compact) .arch {
    aspect-ratio: 3 / 3.6;
    margin-block-end: var(--s-3);
  }
  .scents:not(.scents--compact) .scent__hook {
    font-size: var(--t-meta);
    line-height: 1.6;
  }
  .scents:not(.scents--compact) .scent__name { font-size: 0.95rem; }
  .scents:not(.scents--compact) .scent__price { font-size: 1.05rem; }
  /* Pins the carousel to what it already renders here, which the old
     single-column rule was setting from outside. */
  .scents--compact .arch { aspect-ratio: 3 / 3.4; }
  .site-footer__grid { grid-template-columns: 1fr 1fr 1fr; gap: var(--s-3); }
  .site-footer__logo img { block-size: 34px; }
  .site-footer__tag { font-size: var(--t-meta); }
  .site-footer__social { flex-wrap: wrap; gap: var(--s-2); margin-block-start: var(--s-2); }
  .site-footer__social svg { inline-size: 17px; block-size: 17px; }
  .site-footer__base { justify-content: flex-start; }
  .pyramid__row { grid-template-columns: 76px 3rem 1fr; gap: var(--s-2); }
  .pyramid__notes { font-size: var(--t-meta); }
  .trust { grid-template-columns: 1fr; }
  .trust__item + .trust__item {
    border-inline-start: 0;
    border-block-start: 1px solid var(--c-hairline);
  }
  .product__buy .btn { flex: 1 1 100%; }

  /* An 18px button is too small an anchor at this width. The bubble is 288px
     wide and hung off the page edge, which made the whole document scroll
     sideways by 58px at 390 even with every bubble shut, because a
     visibility:hidden box still counts toward scrollable overflow. Anchoring
     to the row instead and letting the bubble span it keeps it inside the
     viewport in both reading directions, and an open bubble is no longer cut
     off on a phone. */
  .metric__head, .pyramid__row { position: relative; }
  .metric__head .info, .pyramid__row .info { position: static; }
  .metric__head .info__bubble, .pyramid__row .info__bubble {
    inset-inline: 0;
    inline-size: auto;
  }
}/* End custom CSS */