/* == fonts: generated by `npm run fonts` (sites/tools/fonts.mjs), do not edit by hand == */
/* Self-hosted from Google Fonts. To change the faces, edit the URL below and
   re-run `npm run fonts`; it reads the URL back from here, and the preload
   list too (PAGE.md, "Preload what paints first").
   source: https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,400&family=Hanken+Grotesk:wght@400;500;600&display=swap
   preload: Crimson Pro:400,Hanken Grotesk:500
*/
@font-face {
  font-family: 'Crimson Pro';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/crimson-pro-400-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Crimson Pro';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/crimson-pro-400-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/hanken-grotesk-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/hanken-grotesk-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/hanken-grotesk-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/hanken-grotesk-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/hanken-grotesk-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/hanken-grotesk-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* == end fonts == */

/* ===========================================================================
   Born2Glow (LUV2B)
   Built to the blended direction Jacob approved on 25 Sep 2026: concept 3's
   ivory ground with concept 2's colour, drawn by ChatGPT as page mockups.
   Mockup: design/mockup/home-941.png (941px native).

   Every value below traces to a measurement in design/SPEC.md. If a number
   here is not in SPEC.md, it is a bug - not a preference.

   1200 MAX, NO BLEED (sites/.claude/rules/BUILD.md, "The defaults"). Sizes
   are written in 941px design pixels as `N * var(--u)`; --u renders
   them at 1200/941 at 1200 and above and shrinks below it, and the
   header, page and footer are one 1200 column. Floor a size for legibility
   with max() on the same line: max(16px, calc(17 * var(--u))).
   =========================================================================== */

:root {
  --u: min(calc(1200px / 941), 100vw / 941);

  /* --- Colour: sampled off home-941.png (SPEC.md, "Colour") ------------- */
  --paper: #FCF8F4;        /* page ground, x450-520 y1080 */
  --paper-2: #F8F4EF;      /* header strip, x300-400 y5-20 */
  --paper-3: #FDF9F3;      /* footer ground, x300-500 y1620-1640 */
  --blush: #F7D3CC;        /* ritual panel, x5-40 y500-530 */
  --ink: #1B1A12;          /* headline ink, darkest of x75-370 y85-120 (#111601) lifted to the body ink */
  --ink-2: #55514B;        /* eyebrow ink, darkest of x74-180 y66-74 */
  --olive: #4B522F;        /* buttons, x80-88 y205-225 */
  --olive-ink: #F8F4EF;    /* button label = header paper */
  --peach: #FADFD0;        /* card 1 ground, x40-70 y850-860 */
  --lavender: #EDDFED;     /* card 2 ground, x270-300 y850-860 */
  /* The client's own two (their comments, 1 Oct 2026: "Incorporate some green
     & light lavender"), sampled off the palette strip they sent: green at
     x110 #A7ECCF and lavender at x230 #CCC1D9, each laid on --paper as a
     ground. --coral, card 3's drawn #FBDCD4, gave its place to the green. */
  --mint: #DDF6E8;         /* #A7ECCF at 35% over --paper: card 3 */
  --lilac: #E7E0EC;        /* #CCC1D9 at 45% over --paper: the ingredients band */
  --plum: #EADCEC;         /* card 4 ground, x697-727 y850-860 */
  --dot-peach: #E0B58F;    /* routine step circles, x278-290 y978-988 */
  --dot-lavender: #D0B3D3; /* routine step circles, x508-520 y978-988 */

  /* Three ground levels (Jacob's second design review, 27 Sep 2026): the
     ivory page is the air, two tints repeat as feature bands, and inset
     content sits on white. Each tint is one the page already draws. */
  --band-blush: #F8E6DC;   /* routine + CTA bands: the hero gradient's warm end */
  --band-lavender: var(--lilac);  /* ingredients band: the client's light lavender (was the drawn tile tint) */
  --panel-stone: #F2DDD2;  /* ritual copy panel: "a slightly richer blush/stone", between --blush and --band-blush */
  --inset: #FFFFFF;        /* cards on a band: the routine steps, the ingredients */

  /* --- Type ---------------------------------------------------------------
     Crimson Pro for the body serif, Hanken Grotesk for the tracked caps and
     buttons (SPEC.md, "Typefaces", with the scores).
     The wordmark and the headings are in the sans since the client's comments
     of 10 Oct 2026 ("change the font on all these descriptors to a 'STXihei'
     font or similar"; on the header name, "STXihei, Aptos or like ?"): Hanken
     Grotesk, already loaded, rather than a third face. To undo, set --display
     back to var(--serif) and divide the four sizes below by 0.829.
     The sizes keep each heading's measured cap height and baseline pitch:
     Hanken's cap is 0.70em to Crimson Pro's 0.58 (measured, canvas, 10 Oct),
     so every size is the serif's x 0.829 and every leading / 0.829. */
  --serif: "Crimson Pro", Georgia, serif;
  --sans: "Hanken Grotesk", system-ui, sans-serif;
  --display: var(--sans);
  --f-lg: max(26px, calc(39.8 * var(--u)));   /* ritual h2: cap 28 (48 in the serif) */
  --lh-lg: 0.893;                /* pitch 388 -> 423 = 35 */
  --f-md: max(25px, calc(36.5 * var(--u)));   /* collection h2: cap 25-26 (44 in the serif) */
  --lh-md: 0.893;                /* pitch 596 -> 628 = 32 */
  --f-sm: max(23px, calc(30.7 * var(--u)));   /* ingredients + cta h2: cap 21-22 (37 in the serif) */
  --lh-sm: 0.917;                /* pitch 1444 -> 1471 = 27 */
  --f-sub: max(17px, calc(19 * var(--u)));   /* hero subline: ink 13 */
  --f-body: max(16px, calc(14.5 * var(--u))); /* ritual body: pitch 16 */
  --lh-body: 1.15;
  --f-small: max(15px, calc(12 * var(--u)));  /* intros, card copy: pitch 13-14 */
  --f-caps: max(11px, calc(9.5 * var(--u)));  /* nav, card titles: cap 7-8 */
  --f-eyebrow: max(10px, calc(8.5 * var(--u))); /* eyebrows: cap 7 */
  --track: 0.14em;               /* eyebrow "THE COLLECTION" 113px over 14 glyphs */

  /* --- Geometry ----------------------------------------------------------- */
  --gut: calc(73 * var(--u));    /* hero copy left edge x73-76 */
  --gut-2: calc(57 * var(--u));  /* lower bands' copy left edge x56-60 */
  --radius: calc(8 * var(--u));  /* card corners */
  --btn-h: calc(30 * var(--u));  /* hero button y201-231 */
  /* Vertical room between the bands from the collection down (Jacob's design
     review, 27 Sep 2026: "about 15-25% more vertical space from Four
     Essentials downward"). Pinned to a measured value, the 41px right inset
     the header and the lower bands share, so the ladder stays closed. */
  --band: calc(41 * var(--u));
}

/* No bleed: one 1200 column, bands and photographs included. */
.site-header, main, .site-footer { max-width: 1200px; margin-inline: auto; }

/* A minimal reset. Everything else belongs to the design. */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; background: var(--paper); color: var(--ink); font-family: var(--serif); font-size: var(--f-body); line-height: var(--lh-body); }
img, picture, svg, video { display: block; max-width: 100%; height: auto; }
h1, h2, h3, p, figure, blockquote, ul, ol { margin: 0; padding: 0; }
ul, ol { list-style: none; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--olive); outline-offset: 3px; }

/* --- Shared type ----------------------------------------------------------- */
.display { font-family: var(--display); font-weight: 400; text-transform: uppercase; letter-spacing: -0.015em; white-space: nowrap; color: var(--ink);
  --sweep-allow: "leading-too-tight: capitals only, at the mockup's baseline pitch (0.89-0.92 in the sans)"; }
.display--lg { font-size: var(--f-lg); line-height: var(--lh-lg); }
.display--md { font-size: var(--f-md); line-height: var(--lh-md); }
.display--sm { font-size: var(--f-sm); line-height: var(--lh-sm); }
/* The quieter heading (review, 27 Sep 2026: four headlines "shouting at
   roughly the same volume"). Sentence case at --f-sm, so two of the lower
   bands speak rather than announce. Mixed case wants real leading. */
.display--editorial { font-size: var(--f-sm); line-height: 1.27; text-transform: none; letter-spacing: -0.01em; }   /* 1.27: the serif's 1.05 pitch at the sans' size (1.05 / 0.829) */
/* The client's own headings keep the capitals they typed (10 Oct 2026):
   "SKINCARE.... An Act Of SELF-LOVE !", "I LUV2B. You LUV2B. We ALL LUV2B." */
.display--as-written { text-transform: none; }
/* A product name is set by the data file, not by hand, so it wraps rather
   than carrying a written <br>. */
.display--wrap { white-space: normal; text-wrap: balance; }
.eyebrow { font-family: var(--sans); font-size: var(--f-eyebrow); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-2); }
.eyebrow--rule::before { content: ""; display: block; width: calc(34 * var(--u)); height: 1px; background: var(--dot-peach); margin-bottom: calc(10 * var(--u)); }

/* Buttons: olive, square-ish, tracked caps. Hero button 179x30 at x73. */
.btn { display: inline-flex; align-items: center; gap: 0.7em; min-height: max(44px, var(--btn-h)); padding-inline: calc(19 * var(--u));
  background: var(--olive); color: var(--olive-ink); border-radius: calc(3 * var(--u)); text-decoration: none;
  font-family: var(--sans); font-size: var(--f-caps); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase; white-space: nowrap; }
.btn:hover { background: #3C4225; }
.btn--sm { min-height: max(40px, calc(24 * var(--u))); padding-inline: calc(15 * var(--u)); }
.link-arrow { font-family: var(--sans); font-size: var(--f-caps); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase; text-decoration: none;
  display: inline-flex; align-items: center; min-height: 44px; gap: 0.5em; }
.link-arrow:hover { text-decoration: underline; text-underline-offset: 0.3em; }

/* Placeholders and TODOs (PAGE.md, "Placeholders that are obviously placeholders"). */
.skip { position: absolute; left: -9999px; }
.skip:focus { left: 12px; top: 12px; z-index: 10; background: var(--paper); padding: 8px 12px; }

/* --- Florals -----------------------------------------------------------------
   One transparent spray from ChatGPT (assets/MANIFEST.md), placed where the
   mockup draws flowers growing in from an edge, and mirrored on the right. */
.decor { position: absolute; z-index: 1; width: auto; max-width: none; pointer-events: none; }
.decor--hero { left: calc(-18 * var(--u)); top: calc(24 * var(--u)); height: calc(210 * var(--u)); }   /* x0-55 y70-280 */
.decor--collection { right: 0; top: calc(-6 * var(--u)); height: calc(120 * var(--u)); transform: scaleX(-1); }   /* x890-941 y534-650 */
.decor--care { right: 0; top: 0; height: calc(90 * var(--u)); transform: scaleX(-1); }   /* x875-941 y1244-1330 */

/* --- Handwritten word art, cut from one ChatGPT sheet (assets/MANIFEST.md). */
.script { position: absolute; z-index: 3; width: auto; max-width: none; pointer-events: none; }
.script--hero { right: calc(14 * var(--u)); top: calc(20 * var(--u)); height: calc(112 * var(--u)); }        /* x860-930 y62-180 */
.script--routine-right { right: calc(6 * var(--u)); top: calc(30 * var(--u)); height: calc(66 * var(--u)); }   /* re-cut 11 Oct without the stray heart tip from the sheet above (desktop review); 66 keeps the letters at their old size */ /* x895-935 y946-1022 */
.script--cta { right: calc(24 * var(--u)); top: calc(10 * var(--u)); height: calc(72 * var(--u)); }           /* x840-910 y1427-1500 */

/* === HEADER. home-941.png y 0-45 ========================================= */
.site-header { position: relative; display: flex; align-items: center; gap: calc(40 * var(--u)); min-height: calc(45 * var(--u));
  padding: calc(8 * var(--u)) calc(41 * var(--u)) calc(8 * var(--u)) var(--gut); background: var(--paper-2); }
.brand { display: flex; align-items: center; gap: calc(8 * var(--u)); text-decoration: none; margin-right: auto; }
.brand__words { display: flex; flex-direction: column; }
/* The client's B2G mark beside the set name: the header's 29 inner height, less a hair. */
.brand__mark { width: auto; height: max(40px, calc(37 * var(--u))); margin-block: calc(-4 * var(--u)); }   /* its hairline hearts need the size; the negative margin keeps the header at its drawn 45 */
.brand__name { font-family: var(--display); font-size: max(18px, calc(21.5 * var(--u))); line-height: 1; letter-spacing: -0.01em; }  /* ink 9-30: the serif's 26 x 0.829 */
.brand__tag { font-family: var(--sans); font-size: max(8px, calc(6.5 * var(--u))); letter-spacing: 0.24em; text-transform: uppercase; color: var(--ink-2); margin-top: calc(4 * var(--u)); }
.nav__list { display: flex; gap: calc(38 * var(--u)); }   /* x334, 432, 514, 607 */
.nav__list a { white-space: nowrap; font-family: var(--sans); font-size: var(--f-caps); font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none; }
.nav__list a:hover, .nav__list a[aria-current="page"] { text-decoration: underline; text-underline-offset: 0.35em; }
.nav__toggle { display: none; }

/* === HERO. y 45-338 ======================================================== */
.hero { position: relative; display: grid; grid-template-columns: calc(430 * var(--u)) 1fr; min-height: calc(293 * var(--u));
  background: linear-gradient(90deg, var(--paper) 30%, #F8E6DC 100%); overflow: hidden; }
.hero__copy { position: relative; z-index: 2; padding: calc(21 * var(--u)) 0 calc(96 * var(--u)) var(--gut); }
/* The LUV2B wordmark is the h1 (client's comments, 10 Oct 2026), drawn over
   the old headline at about 305 of the page's 941. Capped at the file's own
   304px, so it never upscales (MOBILE.md 7): a larger file is still wanted. */
.hero__brand { line-height: 0; margin-top: calc(6 * var(--u)); }
.hero__brand img { width: min(304px, calc(305 * var(--u))); min-width: 200px; height: auto; }
.hero__sub { font-size: var(--f-sub); line-height: 1.2; margin: calc(12 * var(--u)) 0 calc(14 * var(--u)); }
/* The scene is 16:9 and the band is drawn at 3.2:1, so a cover crop loses the
   products' feet and the model's head. Instead it takes the band's full height
   on the right, whole, and its quiet left edge fades into the ground. */
.hero__art { position: absolute; inset: 0 0 0 auto; z-index: 1; height: 100%; }
.hero__art img, .cta__art { height: 100%; width: auto; max-width: none;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 18%); mask-image: linear-gradient(90deg, transparent 0, #000 18%); }
/* The four carton claims, on a pale panel under the copy: y 258-332. */
.badges { position: absolute; z-index: 2; left: 0; bottom: calc(8 * var(--u)); width: calc(370 * var(--u)); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  padding: calc(12 * var(--u)) calc(14 * var(--u)) calc(10 * var(--u)) calc(38 * var(--u)); background: rgba(253, 249, 243, 0.94); border-radius: 0 calc(40 * var(--u)) calc(40 * var(--u)) 0; }   /* 0.94: the labels' 10px ink needs 4.5:1 over the hero scene (check:derived) */
.badges li { display: flex; flex-direction: column; align-items: center; text-align: center; gap: calc(5 * var(--u));
  font-family: var(--sans); font-size: max(10px, calc(7.5 * var(--u))); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink); }   /* --ink, not --ink-2: 10px caps measured 4.39:1 in --ink-2 (check:derived) */
.badges img { width: calc(26 * var(--u)); min-width: 22px; height: auto; }

/* === LESS ROUTINE. y 346-539 ============================================== */
/* One soft band: blush ground (x2-14 y440-470 reads #CEB8A7 under the leaves,
   #F7D3CC in the open), a cream oval behind the copy (#FAF3E9, x250-330
   y500-512), and the swatches fading in from the left rather than cut. */
/* One band, y346-539, with an ivory gap above and below: the cream panel
   from the left edge across the band's full height to a shallow curve at
   x~400, and the swatch panorama fading in under that curve. */
.ritual { position: relative; display: grid; grid-template-columns: calc(400 * var(--u)) 1fr; margin-block: calc(6 * var(--u)); min-height: calc(193 * var(--u));
  background: var(--blush); overflow: hidden; }
.ritual__copy { position: relative; z-index: 2; background: var(--panel-stone);   /* drawn #FAF3E9; richer by the review */
  border-radius: 0 calc(70 * var(--u)) calc(70 * var(--u)) 0 / 0 50% 50% 0;   /* a shallow arc, not a round tip (Jacob, 25 Sep 2026) */
  padding: calc(14 * var(--u)) calc(40 * var(--u)) calc(14 * var(--u)) var(--gut); }   /* flush left; copy at x73 */

.ritual__body { font-size: max(16px, calc(13.5 * var(--u))); margin: calc(10 * var(--u)) 0 calc(12 * var(--u)); max-width: calc(310 * var(--u)); }   /* four lines, x74-376 */
.ritual__art { position: relative; }
.ritual__img { position: absolute; inset: 0 0 0 calc(-170 * var(--u)); width: calc(100% + 170 * var(--u));   /* 170 under the opaque panel: the client's longer copy (10 Oct 2026) made the band taller, and a 3.5:1 panorama needs a frame of 2.28:1 or wider to keep 65% (check:layout) */ max-width: none; height: 100%; object-fit: cover; object-position: 0 50%;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 10%); mask-image: linear-gradient(90deg, transparent 0, #000 10%); }   /* 3.5:1 panorama, tucked under the curve */
/* A pale backing so the words hold 4.5:1 whatever part of the panorama
   ends up under them (check:derived, 11 Oct 2026: 2.5:1 at 1200 once the
   client's longer copy moved the image). */
.ritual__words { padding: calc(8 * var(--u)) calc(12 * var(--u)); background: rgba(253, 249, 243, 0.86); border-radius: calc(4 * var(--u)); position: absolute; right: calc(60 * var(--u)); top: 50%;   /* on the clear stone the panorama leaves at its right, x812 */ transform: translateY(-50%); display: grid; gap: calc(6 * var(--u));
  font-family: var(--sans); font-size: var(--f-caps); letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink);
 }

/* === THE COLLECTION. y 540-886 ============================================= */
.collection { position: relative; padding: var(--band) calc(33 * var(--u)); }   /* drawn 14 / 24; opened up by the review */
.collection__head { position: relative; z-index: 2; display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; padding-left: calc(42 * var(--u)); margin-bottom: calc(24 * var(--u)); }
.collection .eyebrow { margin-bottom: calc(8 * var(--u)); }
.collection__intro { font-size: var(--f-small); line-height: 1.2; max-width: calc(305 * var(--u)); margin-top: calc(28 * var(--u)); }
/* Home and The Collection: the band's floral sprig where its line was (the
   client, 3 Nov 2026), laid along the slot the line filled: 305 wide, the
   sprig's 280/825 giving 104 tall, turned on its side. */
.collection__flower { position: relative; flex: 0 0 calc(305 * var(--u)); height: calc(104 * var(--u)); pointer-events: none; }
.collection__flower img { position: absolute; left: 50%; top: 50%; width: auto; max-width: none; height: calc(305 * var(--u)); transform: translate(-50%, -50%) rotate(-90deg) scaleX(-1); }
.cards { position: relative; z-index: 2; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: calc(10 * var(--u)); }   /* x33-908, gaps 10 */
.card { display: flex; flex-direction: column; align-items: center; text-align: center; border-radius: var(--radius); padding: 0 calc(14 * var(--u)) calc(12 * var(--u)); overflow: hidden; }   /* taller than drawn: the carton names run to three lines (review, 27 Sep) */
.card--peach { background: var(--peach); }
.card--lavender { background: var(--lavender); }
.card--mint { background: var(--mint); }
.card--plum { background: var(--plum); }
/* The product scene runs edge to edge across the top of the card: y 638-785,
   205 wide (1.39:1). */
.card__img { width: calc(100% + 24 * var(--u)); max-width: none; margin-inline: calc(-12 * var(--u)); height: auto; aspect-ratio: 205 / 147; object-fit: cover; }
.card__title { font-family: var(--sans); font-size: var(--f-caps); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; line-height: 1.3; margin-top: calc(14 * var(--u)); min-height: 2.6em; }
.card__desc { font-size: var(--f-small); line-height: 1.3; margin-top: calc(8 * var(--u)); flex: 1; max-width: 22em; }
.card .link-arrow { margin-top: calc(10 * var(--u)); }

/* === MORNING TO NIGHT. y 887-1087 ========================================== */
.routine { background: var(--band-blush); }   /* one field behind heading, photographs and steps, so it reads as one module */
.routine__head { display: flex; justify-content: space-between; align-items: flex-end; gap: 24px; padding: var(--band) calc(41 * var(--u)) calc(28 * var(--u)) var(--gut-2); }
.routine__body { position: relative; display: grid; grid-template-columns: calc(215 * var(--u)) minmax(0, 1fr) minmax(0, 1fr) calc(215 * var(--u));   /* drawn 245 / 241; narrowed so the taller photos do not crowd the steps */ align-items: stretch; min-height: calc(215 * var(--u)); }   /* drawn 164; "substantially taller" (review, 27 Sep) */
.routine__photo { width: 100%; height: 100%; object-fit: cover; }
/* Straight edges and no fade (Jacob, 27 Sep 2026): the script lines sit on
   the photographs themselves. Her face sits at 45-80% of the left frame. */
.routine__photo--left { object-position: 50% 30%; }
.routine__photo--right { object-position: 30% 30%; }
.steps { align-self: center; margin: 0 calc(10 * var(--u)); padding: calc(16 * var(--u)) calc(20 * var(--u)) calc(18 * var(--u)) calc(22 * var(--u));
  background: var(--inset); border-radius: var(--radius); }   /* 10: the cards' gap */
.steps__title { font-family: var(--sans); font-size: max(12px, calc(11 * var(--u))); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; }
.steps__sub { font-size: var(--f-small); color: var(--ink-2); margin: 4px 0 calc(18 * var(--u)); }
.steps ol { display: grid; gap: calc(12 * var(--u)); counter-reset: step; }
.steps li { position: relative; padding-left: calc(max(26px, 26 * var(--u)) + max(8px, 10 * var(--u))); min-height: calc(26 * var(--u)); counter-increment: step; }
.steps li::before { content: counter(step); position: absolute; left: 0; top: 0; width: max(26px, calc(26 * var(--u))); height: max(26px, calc(26 * var(--u)));
  border-radius: 50%; display: grid; place-items: center; font-family: var(--serif); font-size: max(15px, calc(14 * var(--u))); color: #fff; }
.steps--peach li::before { background: var(--dot-peach); }
.steps--lavender li::before { background: var(--dot-lavender); }
.steps b { display: block; font-family: var(--sans); font-size: max(11px, calc(8.5 * var(--u))); font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; }
.steps span { display: block; margin-top: 2px; font-size: var(--f-small); line-height: 1.25; }

/* === THOUGHTFUL IN EVERY DROP. y 1088-1243 ================================ */
/* Reworked by the review (27 Sep 2026): it read as "another product grid
   immediately after another dense grid". A wider copy column, the band on
   the tile tint, and the four ingredients as a quiet 2x2 list, photograph
   beside words, with no card behind them. */
.ingredients { display: grid; grid-template-columns: calc(290 * var(--u)) minmax(0, 1fr); gap: calc(41 * var(--u)); align-items: center;
  padding: var(--band) calc(41 * var(--u)) var(--band) var(--gut-2); background: var(--band-lavender); }
.ingredients__copy p { font-size: var(--f-body); line-height: 1.3; margin: calc(14 * var(--u)) 0 calc(20 * var(--u)); max-width: calc(260 * var(--u)); }
.tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(14 * var(--u)); }
.tile { display: grid; grid-template-columns: calc(84 * var(--u)) minmax(0, 1fr); grid-template-rows: 1fr auto auto 1fr; column-gap: calc(12 * var(--u));
  padding: calc(10 * var(--u)); background: var(--inset); border-radius: var(--radius); }   /* 84: inside the white inset, leaves the words 18+ characters a line (check:layout) */
.tile__img { grid-row: 1 / -1; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: calc(4 * var(--u)); }
.tile__title { grid-row: 2; font-family: var(--sans); font-size: max(11px, calc(8.5 * var(--u))); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: calc(5 * var(--u)); }
.tile p { grid-row: 3; font-size: var(--f-small); line-height: 1.25; }

/* === CARE. y 1244-1416 ===================================================== */
.care { position: relative; display: grid; grid-template-columns: calc(470 * var(--u)) minmax(0, 1fr); align-items: center; min-height: calc(172 * var(--u)); margin-top: var(--band); }
.care__photo { align-self: stretch; width: 100%; height: auto; aspect-ratio: 2.6 / 1;   /* drawn 500x172 (2.91:1); 2.6 keeps 68% of the 1.78:1 portrait, over check:layout's 65% floor */ object-fit: cover; object-position: 50% 30%; border-radius: 0 calc(90 * var(--u)) calc(90 * var(--u)) 0; }
.care__copy { position: relative; z-index: 2; padding: calc(8 * var(--u)) calc(60 * var(--u)) calc(8 * var(--u)) calc(48 * var(--u)); }
.care__copy p:not(.eyebrow) { font-size: var(--f-body); line-height: 1.35; margin-top: calc(16 * var(--u)); max-width: calc(340 * var(--u)); }   /* a larger copy block beside the photograph (review, 27 Sep) */

/* === A CLICK AWAY. y 1417-1598 ============================================ */
/* The scene fills the band behind the copy; its left third is quiet ivory. */
/* One split panel (review, 27 Sep 2026: the lineup "feels disconnected from
   the headline"), and then a band behind the whole of it to anchor the page's
   end (second review, same day): copy and scene share the blush ground edge
   to edge, the scene fading into it rather than into the page. */
.cta { position: relative; display: grid; grid-template-columns: calc(360 * var(--u)) 1fr; align-items: center;
  margin-top: var(--band); padding: calc(40 * var(--u)) calc(20 * var(--u)) calc(40 * var(--u)) var(--gut-2);
  min-height: calc(240 * var(--u)); overflow: hidden; background: var(--band-blush); }
.cta__copy { position: relative; z-index: 2; }
.cta__copy p { font-size: var(--f-small); line-height: 1.2; margin: calc(9 * var(--u)) 0 calc(10 * var(--u)); }
.facts { display: flex; flex-wrap: wrap; gap: 4px calc(18 * var(--u)); margin-top: calc(12 * var(--u)); font-family: var(--sans); font-size: max(10px, calc(7.5 * var(--u))); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.cta__art { position: absolute; top: 0; right: 0; z-index: 1; }

/* === FOOTER. y 1599-1672 =================================================== */
.site-footer { background: var(--paper-3); border-top: 1px solid #E3D6CC; padding: calc(28 * var(--u)) calc(41 * var(--u)) calc(14 * var(--u)) calc(45 * var(--u)); font-family: var(--sans); }   /* a rule and room: it was "swallowed" by the band above (review, 27 Sep) */
.site-footer__top { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px calc(40 * var(--u)); padding-bottom: calc(14 * var(--u)); }
.brand--footer .brand__name { font-size: max(17px, calc(18.2 * var(--u))); }   /* the serif's 22 x 0.829 */
.brand--footer .brand__mark { height: max(38px, calc(34 * var(--u))); margin-block: 0; }
.site-footer__mid { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px calc(40 * var(--u)); padding-bottom: max(14px, calc(14 * var(--u))); }
.site-footer__social { display: flex; gap: calc(20 * var(--u)); }
.site-footer__social a { display: inline-flex; align-items: center; min-height: 44px; font-size: max(11px, calc(7.5 * var(--u))); letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none; }
.site-footer__social a:hover { text-decoration: underline; text-underline-offset: 0.35em; }
/* The product line the client asked for on regulatory grounds: "a small
   product compliance dialog box". On the footer's own ground, outlined in the
   signup field's border colour. It sat on --mint for a day; Jacob had the
   green taken off, 1 Oct 2026, since they asked for a box and not a colour. */
.site-footer__compliance { padding: 8px 12px; border: 1px solid #D8CFC4; border-radius: 3px;
  font-size: max(12px, calc(8.5 * var(--u))); color: var(--ink); }
.site-footer__compliance a { white-space: nowrap; }
.site-footer__nav { display: flex; gap: calc(28 * var(--u)); margin: calc(2 * var(--u)) auto 0 calc(80 * var(--u)); white-space: nowrap; }
.site-footer__nav a, .site-footer__legal a { font-size: max(11px, calc(7.5 * var(--u))); letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none; }
.signup__label { display: block; font-size: max(10px, calc(7.5 * var(--u))); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 4px; }
.signup__trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.signup input[aria-invalid="true"] { border-color: #9A3B2F; }
.signup__status { margin-top: 6px; max-width: calc(190 * var(--u)); min-width: 204px; font-size: 13px; line-height: 1.3; color: var(--ink-2); }
.signup__status.is-bad { color: #9A3B2F; }
.signup__status.is-good { color: var(--olive); }
.signup__row { display: flex; }
.signup input { width: calc(145 * var(--u)); min-width: 160px; min-height: 44px; border: 1px solid #D8CFC4; border-right: 0; background: #fff; padding: 0 10px; font: inherit; font-size: 14px; }
.signup__go { cursor: pointer; min-width: 44px; min-height: 44px; border: 0; background: var(--olive); color: var(--olive-ink); font-size: 16px; }
.site-footer__bottom { display: flex; justify-content: space-between; gap: 16px; font-size: max(11px, calc(7.5 * var(--u))); color: var(--ink-2); }
.site-footer__legal { display: flex; gap: calc(26 * var(--u)); }

/* === PRODUCT PAGES (/products/<slug>, written by `npm run products`) ========
   No mockup of these four products exists: b2g-page-3-product-detail.png
   draws an invented "Moisturizer 01" with generated photography. So the band
   order is that mockup's (hero, highlights, ingredients, pairing) and every
   value is the home page's ladder (HANDOFF.md 7): --band, --gut-2, the 41
   right inset, the 10 and 14 card gaps, the card tints and white insets. */
/* Where the Shop on Amazon button goes once the listing is live: until then
   the client's purchasing line (3 Nov 2026), in the slot the placeholder
   chip held, solid now that it is real copy. */
.pdp__buy { display: inline-block; padding: 8px 12px; border: 1px solid var(--ink-2); border-radius: calc(3 * var(--u));
  font-family: var(--sans); font-size: var(--f-caps); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink); }
.pdp__buy a { color: inherit; text-decoration: underline; text-underline-offset: 0.3em; }
/* The hero is the shared one on this product's card tint, the badges in a
   row under the copy rather than over the scene (the copy runs long).
   products--radiance-firming-hydration-serum-875.png y45-445, x1.075 to 941. */
.hero--product { grid-template-columns: calc(470 * var(--u)) 1fr; grid-template-rows: auto auto; }
.hero--product .hero__copy { padding-bottom: calc(18 * var(--u)); }
.hero--product .badges { position: relative; grid-column: 1; grid-row: 2; bottom: auto; margin-bottom: calc(10 * var(--u)); }
/* The scene keeps to the right column: the description runs long, so the
   photograph cannot sit under it the way the home scene does. */
.hero--product .hero__art { position: relative; inset: auto; grid-column: 2; grid-row: 1 / span 2; height: auto; align-self: center; padding-right: calc(41 * var(--u)); }   /* its own 4:3, not stretched to the copy's height (check:layout kept 59% when it was) */
.hero--product .hero__art img { width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%); mask-image: linear-gradient(90deg, transparent 0, #000 12%); }
.pdp__copy .eyebrow { margin-bottom: calc(8 * var(--u)); }
.pdp__copy .eyebrow a { text-decoration: none; }
.pdp__copy .eyebrow a:hover { text-decoration: underline; text-underline-offset: 0.3em; }
.pdp__tagline { font-size: var(--f-sub); line-height: 1.2; margin: calc(12 * var(--u)) 0 calc(14 * var(--u)); }   /* the hero subline's */
.pdp__desc { font-size: max(16px, calc(13.5 * var(--u))); line-height: 1.35; max-width: 34em; margin-bottom: calc(16 * var(--u)); }   /* the ritual body's size; 34em keeps ~75 characters a line */
/* Highlights: the cards' row, white with the footer rule, a 46 round icon
   on top (y450-710: five across, 150 wide, 8 apart). */
.points { padding: var(--band) calc(33 * var(--u)); }
.points__head { padding-left: calc(24 * var(--u)); }
.points__list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); gap: calc(8 * var(--u)); }   /* 170: five across at 1200, wrapping to fewer below */
.point { display: grid; align-content: start; gap: calc(6 * var(--u)); padding: calc(14 * var(--u)) calc(14 * var(--u)) calc(16 * var(--u)); background: var(--inset); border: 1px solid #E3D6CC; border-radius: var(--radius); }
.point__title { font-family: var(--sans); font-size: max(11px, calc(8.5 * var(--u))); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; line-height: 1.3; }   /* the tile title's */
.point p { font-size: var(--f-small); line-height: 1.3; }
/* Ingredients on a product: the tiles this product carries, then its full
   list on a blush inset across the band (y950-1060). */
.ingredients.ingredients--product { grid-template-columns: calc(310 * var(--u)) minmax(0, 1fr); background: var(--band-lavender); }
.ingredients--product .tiles--row { grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); }
.ingredients--product .inci { grid-column: 1 / -1; padding: calc(16 * var(--u)) calc(22 * var(--u)); background: var(--band-blush); border-radius: var(--radius); }
.inci__list { font-size: var(--f-small); line-height: 1.45; margin-top: calc(8 * var(--u)); }   /* a long comma list reads at a looser leading */
/* Pair it with: copy left, the partner as one wide white card (y1295-1450). */
.pair { display: grid; grid-template-columns: calc(280 * var(--u)) minmax(0, 1fr); gap: calc(41 * var(--u)); align-items: center;
  padding: var(--band) calc(41 * var(--u)) var(--band) var(--gut-2); }
.pair__sub { font-size: var(--f-sub); line-height: 1.2; margin: calc(8 * var(--u)) 0; }
.pair__copy > p:last-child { font-size: var(--f-small); line-height: 1.3; }
.pair-card { display: grid; grid-template-columns: calc(230 * var(--u)) minmax(0, 1fr); align-items: center; background: var(--inset); border: 1px solid #E3D6CC; border-radius: var(--radius); overflow: hidden; text-decoration: none; }
.pair-card__img { width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.pair-card__words { display: grid; gap: calc(6 * var(--u)); padding: calc(14 * var(--u)) calc(22 * var(--u)); }
.pair-card .card__title { margin: 0; min-height: 0; }
.pair-card .card__desc { margin: 0; }
.pair-card:hover .link-arrow { text-decoration: underline; text-underline-offset: 0.3em; }

/* === INNER PAGES (the redraw of 11 Oct 2026, design/mockup/<page>-r2) =======
   Drawn by ChatGPT from the home page as built, each at its own width
   (collection 1024, your-ritual 971, our-story 878...). Each redraws the
   home page's bands at a constant ratio, so it is drawing scale, not design
   (BUILD.md, "A page drawn at a different scale"): the shared bands stay the
   home page's components, and what is new is measured, divided by the page's
   ratio onto the same 941 ladder (SPEC.md, "Inner pages"). */

/* Page hero: the home hero with a heading where the wordmark is.
   collection-1024 y42-330, copy x84; heading cap 27 -> 25 at 941. */
.display--hero { font-size: var(--f-md); line-height: 1.04; text-transform: none; letter-spacing: -0.015em; }   /* mixed case at --f-md; pitch 30/1024 = 27.6 at 941 */
.hero--page .eyebrow { margin-bottom: calc(8 * var(--u)); }
.hero__lede { font-size: max(16px, calc(13.5 * var(--u))); line-height: 1.2; max-width: calc(300 * var(--u)); margin: calc(12 * var(--u)) 0 calc(16 * var(--u)); }   /* the ritual body's size and measure: four lines, x84-349/1024 */
.hero__caption { position: absolute; z-index: 2; right: 0; bottom: calc(8 * var(--u)); padding: calc(12 * var(--u)) calc(41 * var(--u)) calc(12 * var(--u)) calc(38 * var(--u)); background: rgba(253, 249, 243, 0.94); border-radius: calc(40 * var(--u)) 0 0 calc(40 * var(--u));   /* the badge panel, mirrored: the scene's foot is too busy to read over */
  font-family: var(--sans); font-size: var(--f-eyebrow); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase; color: var(--ink); line-height: 1.6; }   /* x777-915 y300-320; --ink over the scene's pale foot */

/* Moisturizers / serums: two tinted panels. collection-1024 y690-893,
   x0-506 and x518-1024: 187 tall at 941, the cards' 10 between them. */
.lines { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(10 * var(--u)); }   /* the collection band above already ends on --band */
.line { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) calc(260 * var(--u)); align-items: end; min-height: calc(187 * var(--u)); overflow: hidden; }
.line--peach { background: var(--peach); }
.line--plum { background: var(--plum); }
.line__copy { position: relative; z-index: 2; align-self: center; padding: calc(28 * var(--u)) 0 calc(28 * var(--u)) var(--gut-2); }   /* copy x36/1024 = 33; the lower bands' 57 */
.line__copy .eyebrow { margin-bottom: calc(8 * var(--u)); }
/* The panel heading is drawn smaller than any band heading: cap 16/1024 = 14.7 at 941, 21 in the sans. */
.line__copy .display { font-size: max(22px, calc(21 * var(--u))); line-height: 1.2; white-space: normal; }
.line__copy p:not(.eyebrow) { font-size: var(--f-small); line-height: 1.25; margin: calc(10 * var(--u)) 0 calc(14 * var(--u)); max-width: calc(250 * var(--u)); }
.line__img { width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; object-position: 50% 70%; }   /* 230: the products drawn x262-506/1024 */
.line__seal { position: absolute; z-index: 3; right: calc(40 * var(--u)); top: calc(22 * var(--u)); width: calc(82 * var(--u)); min-width: 74px; aspect-ratio: 1; border-radius: 50%;
  display: grid; place-content: center; text-align: center; background: rgba(253, 249, 243, 0.9);
  font-family: var(--sans); font-size: max(10px, calc(6.5 * var(--u))); font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; line-height: 1.5; color: var(--ink); }   /* 90/1024 round, x388-476 */

/* Build your glow ritual: copy beside three steps on a white inset.
   collection-1024 y900-1018; inset x492-962 = 430 at 941. Shared by The
   Collection and Your Ritual (partials/bands/flow.html). */
.flow { display: grid; grid-template-columns: minmax(0, 1fr) calc(430 * var(--u)); gap: calc(41 * var(--u)); align-items: center;
  margin-top: calc(10 * var(--u)); padding: var(--band) calc(41 * var(--u)) var(--band) var(--gut-2); background: var(--band-blush); }   /* 10: drawn hard under the hero and the panels, the cards' gap */
.flow__copy p { font-size: var(--f-small); line-height: 1.25; margin: calc(10 * var(--u)) 0 calc(14 * var(--u)); max-width: calc(330 * var(--u)); }
.flow__steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: start; gap: calc(24 * var(--u));
  padding: calc(18 * var(--u)) calc(16 * var(--u)); background: var(--inset); border-radius: var(--radius); text-align: center; }
.flow__step { position: relative; display: grid; justify-items: center; gap: calc(5 * var(--u)); }
/* The arrow between two steps: centred in the 24 gap, level with the numbers. */
.flow__step + .flow__step::before { content: "\2192"; position: absolute; left: calc(-12 * var(--u)); top: max(15px, calc(17 * var(--u))); transform: translate(-50%, -50%); color: var(--ink-2); font-family: var(--sans); }   /* level with the icons' middle (34 tall); the numbers came off, Jacob 11 Oct 2026 */
.flow__icon { width: calc(34 * var(--u)); min-width: 30px; height: auto; }   /* 34: the icons drawn x583-611/1024 */
.flow__step b { font-family: var(--sans); font-size: max(11px, calc(8.5 * var(--u))); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; }
.flow__step span:last-child { font-size: var(--f-small); line-height: 1.2; }

/* Your Ritual's hero ground: the home hero's gradient, toward the lilac
   the redraw puts behind it (your-ritual-971 x900 y60, #EDE2F0). */
.hero--lavender { background: linear-gradient(90deg, var(--paper) 30%, var(--lilac) 100%); }

/* The two pairings. your-ritual-971 y565-785, x33-478 and x492-938: at
   0.969 to 941, 213 tall, the photograph 194 wide, the cards' 10 apart. */
.pairings { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(10 * var(--u)); padding: var(--band) calc(33 * var(--u)); }
.pairing { display: grid; grid-template-columns: calc(240 * var(--u)) minmax(0, 1fr);   /* drawn 194; widened so both products in the 4:3 photograph stay in frame */
   min-height: calc(213 * var(--u)); border-radius: var(--radius); overflow: hidden; }
.pairing--mint { background: var(--mint); }
.pairing--plum { background: var(--plum); }
.pairing__img { width: 100%; height: 100%; object-fit: cover; }
/* The routine's step list, on the panel's tint rather than a white inset. */
.pairing__steps.steps { align-self: center; margin: 0; background: none; padding: calc(18 * var(--u)) calc(20 * var(--u)); }
.pairing__title { font-family: var(--display); font-size: max(18px, calc(17.5 * var(--u))); font-weight: 400; line-height: 1.1; text-transform: uppercase; letter-spacing: -0.005em; }   /* cap 13/971 */
.steps b a { text-decoration: none; }
.steps b a:hover { text-decoration: underline; text-underline-offset: 0.25em; }

/* Our Approach in four parts. our-approach-941 y668-868: the home cards,
   with a 74 round photograph where the product scene is. */
.part { padding-top: calc(14 * var(--u)); }
.part__img { width: calc(74 * var(--u)); min-width: 64px; aspect-ratio: 1; height: auto; border-radius: 50%; object-fit: cover; }
.part .card__title { min-height: 0; }

/* Thoughtful ingredients, in a row. our-approach-941 y885-1080: the copy
   x60-260, four tiles x263-905, 8 apart, photograph over words. */
.ingredients--row { grid-template-columns: calc(200 * var(--u)) minmax(0, 1fr); background: var(--band-blush); }
.tiles--row { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: calc(8 * var(--u)); }
.ingredients--row .display { white-space: normal; }   /* a narrow copy column: the heading wraps where it must */
.tiles--row .tile { grid-template-columns: 1fr; grid-template-rows: auto; row-gap: calc(8 * var(--u)); align-content: start; text-align: left; }
.tiles--row .tile__img { grid-row: auto; aspect-ratio: 145 / 85; }
.tiles--row .tile__title, .tiles--row .tile p { grid-row: auto; }

/* Powered by thoughtful formulation. our-approach-941 y1085-1250: the
   ingredients band's 2x2, each tile led by a 40 round line icon. */
.tile--icon { grid-template-columns: calc(40 * var(--u)) minmax(0, 1fr); align-items: center; }
.tile__icon { grid-row: 1 / -1; width: 100%; min-width: 36px; height: auto; aspect-ratio: 1; }

/* Our Approach's textures band starts its copy on the lower bands' line, not
   the hero's: our-approach-941 draws it at x63, under the hero's x73, and at
   the hero indent it sat right of everything below it (Jacob, 11 Oct 2026). */
.ritual--aligned .ritual__copy { padding-left: var(--gut-2); }

/* Our Story (our-story-878: x1.072 to 941). */
/* The hero's photograph is the group, cropped to their faces. */
.hero--story .hero__art img { width: calc(600 * var(--u)); object-fit: cover; object-position: 50% 30%; }
/* Our why: the textures band on lilac (y308-565). */
.ritual--lilac { background: var(--band-lavender); }
.ritual--lilac .ritual__copy { background: var(--lilac); }
.ritual--lilac .ritual__img { inset: 0; width: 100%; object-position: 0 55%; }   /* bundle 5 draws the products on the left and leaves the right third for the words, so none of it goes under the panel */
/* Our journey / a kinder tomorrow: photograph to one side with a curved
   inner edge, copy to the other (y568-885 = 340 tall; photo x0-430 = 461). */
.feature { display: grid; grid-template-columns: calc(461 * var(--u)) minmax(0, 1fr); align-items: center; min-height: calc(340 * var(--u)); margin-top: calc(10 * var(--u)); }
.feature__photo { width: 100%; height: 100%; object-fit: cover; border-radius: 0 calc(150 * var(--u)) calc(150 * var(--u)) 0 / 0 50% 50% 0; }
.feature__copy { padding: calc(28 * var(--u)) calc(41 * var(--u)) calc(28 * var(--u)) calc(30 * var(--u)); }
.feature__copy .eyebrow { margin-bottom: calc(8 * var(--u)); }
.feature__copy > p:not(.eyebrow) { font-size: var(--f-body); line-height: 1.3; margin-top: calc(14 * var(--u)); max-width: calc(400 * var(--u)); }
.feature--flip { grid-template-columns: calc(400 * var(--u)) minmax(0, 1fr); }
/* Icons in a row under the copy: a 56 outlined round each (60 at 941). */
.icons { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: calc(14 * var(--u)); margin-top: calc(22 * var(--u)); max-width: calc(400 * var(--u)); }
.icons--four { grid-template-columns: repeat(4, minmax(0, 1fr)); max-width: calc(440 * var(--u)); }
.icons li { display: grid; justify-items: center; gap: calc(8 * var(--u)); text-align: center;
  font-family: var(--sans); font-size: max(10px, calc(7.5 * var(--u))); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; }
.icons img { width: calc(60 * var(--u)); min-width: 48px; height: auto; aspect-ratio: 1; padding: calc(12 * var(--u)); border: 1px solid var(--dot-peach); border-radius: 50%; }
/* Our name: the LUV2B wordmark band (y888-1150 = 281 tall), flower right. */
.name { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) calc(430 * var(--u)); align-items: center; min-height: calc(281 * var(--u)); margin-top: calc(10 * var(--u)); background: var(--band-blush); overflow: hidden; }
.name__copy { padding: calc(28 * var(--u)) calc(30 * var(--u)) calc(28 * var(--u)) var(--gut-2); }
.name__copy > p:not(.eyebrow):not(.hero__sub) { font-size: var(--f-small); line-height: 1.3; margin-bottom: calc(16 * var(--u)); max-width: calc(380 * var(--u)); }
.name__img { width: 100%; height: 100%; object-fit: cover; }
/* The "Crafted by LocalForge" credit, rightmost on the copyright row. */
.credit { font-size: inherit; }
.credit a { text-decoration: none; color: var(--ink-2); }
.credit a:hover { color: var(--ink); }

/* Contact (contact-1024: x0.919 to 941). */
.display--xl { font-size: max(40px, calc(50 * var(--u))); line-height: 0.98; }   /* "Let's Connect." cap 38/1024 = 35 */
.hero--contact .hero__copy > .eyebrow:last-child { margin-top: calc(14 * var(--u)); }
/* The form on a lilac panel with the textures band's curve, the four topics
   beside it. y352-830 = 439 tall; panel x0-530 = 487. */
.reach { display: grid; grid-template-columns: calc(487 * var(--u)) minmax(0, 1fr); gap: calc(41 * var(--u)); align-items: center; margin-top: calc(10 * var(--u)); }
.reach__form { background: var(--lilac); border-radius: 0 calc(110 * var(--u)) calc(110 * var(--u)) 0 / 0 50% 50% 0;
  padding: calc(28 * var(--u)) calc(60 * var(--u)) calc(28 * var(--u)) var(--gut-2); }
.reach__form .eyebrow { margin-bottom: calc(8 * var(--u)); }
.reach__intro { font-size: var(--f-small); line-height: 1.3; margin: calc(10 * var(--u)) 0 calc(16 * var(--u)); }
.form { display: grid; gap: calc(10 * var(--u)); }
.form__row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(10 * var(--u)); }
.field { display: grid; gap: 4px; }
.field label { font-family: var(--sans); font-size: max(11px, calc(8.5 * var(--u))); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; }
.field input, .field select, .field textarea { width: 100%; min-height: 44px; padding: 8px 12px; border: 1px solid #D8CFC4; border-radius: calc(3 * var(--u)); background: #fff; font: inherit; font-size: 16px; color: var(--ink); }
.field textarea { resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--olive); }
/* Red is drawn from the attribute, never a class, and never alone: the
   message under the field carries the meaning (CONTACT-FORMS.md). The red is
   the signup's, so there is one. */
.field [aria-invalid='true'] { border-color: #9A3B2F; }
.field__error { font-size: 14px; color: #9A3B2F; }
.field__count { justify-self: end; font-family: var(--sans); font-size: 12px; color: var(--ink-2); }
.form__send { justify-content: center; width: 100%; border: 0; cursor: pointer; line-height: var(--lh-body); }   /* a <button> resets to line-height: normal, which sets the label and its arrow on different tops */
.form__send:disabled { opacity: 0.6; cursor: wait; }
.form__status { font-size: 15px; line-height: 1.3; color: var(--ink-2); }
.form__status--bad { color: #9A3B2F; }
.form__status--ok { color: var(--olive); }
/* The topics: four rows parted by the footer's rule, a 64 round icon each. */
.topics { display: grid; padding-right: calc(41 * var(--u)); }
.topic { display: grid; grid-template-columns: calc(59 * var(--u)) minmax(0, 1fr); column-gap: calc(22 * var(--u)); align-items: center; padding: calc(18 * var(--u)) 0; border-bottom: 1px solid #E3D6CC; }
.topic:last-child { border-bottom: 0; }
.topic img { grid-row: 1 / 3; width: 100%; min-width: 48px; height: auto; aspect-ratio: 1; padding: calc(12 * var(--u)); border: 1px solid var(--dot-peach); border-radius: 50%; }
.topic h3 { font-family: var(--display); font-size: max(17px, calc(13.5 * var(--u))); font-weight: 400; }
.topic p { font-size: var(--f-small); line-height: 1.25; color: var(--ink-2); margin-top: 4px; }
/* Our promise: the care band mirrored, copy first. */
.care--flip { grid-template-columns: minmax(0, 1fr) calc(470 * var(--u)); }
.care--flip .care__copy { padding-left: var(--gut-2); }
.care--flip .care__copy .eyebrow { margin-bottom: calc(8 * var(--u)); }
.care--flip .care__photo { border-radius: calc(90 * var(--u)) 0 0 calc(90 * var(--u)); }

/* Privacy and 404: no mockup; the page hero's type on the home ladder
   (HANDOFF.md 3, default 7). The legal measure is the care copy's 34em. */
.legal, .lost { padding: var(--band) calc(41 * var(--u)) var(--band) var(--gut); }
.legal .eyebrow, .lost .eyebrow { margin-bottom: calc(8 * var(--u)); }
.legal__date { font-size: var(--f-small); color: var(--ink-2); margin-top: calc(10 * var(--u)); }
.legal h2 { font-family: var(--display); font-size: max(19px, calc(17.5 * var(--u))); font-weight: 400; margin: calc(28 * var(--u)) 0 calc(8 * var(--u)); }
.legal strong { font-family: var(--sans); font-weight: 600; }   /* 600 is loaded; a bold serif is not */
.legal p { font-size: var(--f-body); line-height: 1.45; max-width: 34em; margin-bottom: calc(10 * var(--u)); }
.lost { background: linear-gradient(90deg, var(--paper) 30%, var(--band-blush) 100%); }
.lost .hero__lede { max-width: calc(380 * var(--u)); }

/* ChatGPT desktop review, 11 Oct 2026 (design/chatgpt-desktop-feedback/):
   the inner pages' bands tightened, applied where it did not contradict the
   client or a rule. Each value is a measured one on the ladder: 32 is the
   mockups' inset band padding (collection y900-1018 = 118, 32 top and bottom
   at 941). */
.flow, .ingredients--row, .ingredients.ingredients--product, .pair, .points { padding-top: calc(32 * var(--u)); padding-bottom: calc(32 * var(--u)); }
.hero__caption { padding-block: calc(8 * var(--u)); }   /* "too large and visually heavy" */
.pdp__desc { max-width: 30em; }   /* a narrower measure so the photograph holds its own against the copy */
.point { padding-top: calc(12 * var(--u)); padding-bottom: calc(12 * var(--u)); }
.ingredients--product .inci .display { font-size: max(19px, calc(17.5 * var(--u))); }   /* the list's heading was larger than the list wanted */
.pairing__steps.steps ol { gap: calc(8 * var(--u)); }
.icons { margin-top: calc(14 * var(--u)); }
.name .hero__brand img { width: min(260px, calc(260 * var(--u))); }   /* "oversized ... reduce it about 10-15%" */
.ritual--lilac .ritual__words { right: calc(90 * var(--u)); }   /* in from the edge */
.hero--contact .hero__copy { padding-bottom: calc(28 * var(--u)); }   /* no badges here, so none of the room kept for them */
.reach__form { padding-right: calc(40 * var(--u)); border-radius: 0 calc(70 * var(--u)) calc(70 * var(--u)) 0 / 0 50% 50% 0; }   /* a tighter curve, the fields wider */
.field label { font-size: max(12px, calc(9.5 * var(--u))); }
.topic { padding: calc(12 * var(--u)) 0; }
.care--flip .icons { max-width: calc(340 * var(--u)); }

/* === BETWEEN THE DRAWN WIDTH AND THE PHONE =================================
   Structure only (PAGE.md, "Breakpoints for structure only"). Below 1180 the
   ingredient list sets its copy too narrow beside
   the heading (check:layout), so the heading moves above them. Below 1000
   the nav folds (the routine's own break is at 1140, above). */
@media (max-width: 1180px) {
  .ingredients { grid-template-columns: 1fr; gap: 20px; }
  .ingredients__copy p { max-width: none; }
}
/* Below 1140 the steps wrap enough to stretch the routine's photographs to
   under two thirds of their frame (check:layout), so the photographs move
   to their own row above the two step lists. */
@media (max-width: 1140px) {
  .routine__body { grid-template-columns: 1fr 1fr; }
  .routine__photo { height: auto; aspect-ratio: 4 / 3; }
  .routine__photo--left { grid-row: 1; grid-column: 1; }
  .routine__photo--right { grid-row: 1; grid-column: 2; }
  .steps { padding: 16px; margin-bottom: 16px; }
}
@media (max-width: 1000px) {
  /* Story rows keep the split at tablet (ChatGPT tablet review), but the
     photograph takes its own 4:3 rather than the row's height: stretched, it
     turned square and kept 63% of the leaf at 901 (check:layout). */
  .feature, .feature--flip, .name { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .feature__photo, .name__img { height: auto; aspect-ratio: 4 / 3; }
  /* The two pairings go one above the other: side by side here each photograph turns portrait (check:layout at 901). */
  .pairings { grid-template-columns: 1fr; }
  .icons--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }   /* four in a half column set the labels 7 characters a line */
  /* A product hero's description runs long, so its photograph keeps its own 4:3 beside it rather than stretching to the copy's height (check:layout: 46% kept). */
  .hero.hero--product .hero__art { height: auto; align-self: center; padding-right: 16px; }
  .hero.hero--product .hero__art img { height: auto; aspect-ratio: 4 / 3; border-radius: var(--radius); }
  .pairing { grid-template-columns: 38% minmax(0, 1fr); }   /* tablet review: the photograph was 27% of a stacked row */
  /* Inner-page heroes stack here: past this width the scene sits under the
     copy and the lede drops below 4.5:1 (check:derived at 768). The caption
     comes off the scene with the badges. */
  /* Inner heroes stay split at tablet (ChatGPT tablet review), the scene in
     a column of its own beside the copy, never under it: under it, the lede
     fell to 1.4:1 at 768 (check:derived). Badges and the caption share the
     row beneath, as one band. */
  .hero--page, .hero--product { grid-template-columns: minmax(0, 46fr) minmax(0, 54fr); grid-template-rows: auto auto; }
  .hero--page .hero__art, .hero--product .hero__art { position: relative; inset: auto; grid-column: 2; grid-row: 1; height: 100%; padding: 0; align-self: stretch; }
  .hero--page .hero__art img, .hero--product .hero__art img { width: 100%; height: 100%; aspect-ratio: auto; object-fit: cover; border-radius: 0; -webkit-mask-image: none; mask-image: none; }
  .hero--page .badges, .hero--product .badges { grid-column: 1; grid-row: 2; }
  .hero__caption { position: static; grid-column: 2; grid-row: 2; align-self: center; border-radius: 0; background: none; padding: 12px 16px; }
  /* Four nav labels and the button stop fitting on one line: the nav folds into the menu. */
  /* One row at tablet (ChatGPT tablet review: the menu on a row of its own
     made a 114px header): brand, menu, button; the open list drops below. */
  .site-header { flex-wrap: nowrap; padding: 10px 16px; gap: 12px; }
  .nav { order: 2; }
  .nav__list.is-open { position: absolute; z-index: 20; top: 100%; left: 0; right: 0; padding: 6px 16px 12px; background: var(--paper-2); border-bottom: 1px solid #E3D6CC; }
  .nav__toggle { display: inline-flex; min-height: 44px; align-items: center; background: none; border: 1px solid #D8CFC4; padding: 0 14px; font-family: var(--sans); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; }
  .nav__list { display: none; flex-direction: column; gap: 0; padding-top: 6px; }
  .nav__list.is-open { display: flex; }
  .nav__list a { display: flex; align-items: center; min-height: 44px; }
  /* The badge panel's four labels drop under 8 characters a line beside the scene. */
  .badges { position: relative; grid-column: 1 / -1; width: auto; border-radius: 0; padding: 14px 16px; }
  .hero__copy { padding-bottom: calc(24 * var(--u)); }
  /* The word list stops fitting beside the swatches: it moves under them. */
  .ritual__art { display: flex; flex-direction: column; }
  .ritual__img { position: static; width: 100%; height: auto; aspect-ratio: 16 / 7; -webkit-mask-image: none; mask-image: none; }
  .ritual__words { position: static; transform: none; display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 18px; padding: 12px 16px; background: none; }
}
/* The two category panels stack below 1080: side by side, the photograph
   leaves the heading 9.7 characters a line up to 1061 (check:layout). */
@media (max-width: 1080px) {
  .lines { grid-template-columns: 1fr; }
}
/* The inner pages' two-up rows stop fitting their words beside each other
   below 900: each stacks, in reading order. */
@media (max-width: 900px) {
  .lines { grid-template-columns: 1fr; }
  .topics { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 24px; }   /* tablet review: 2 x 2 rather than four full-width rows */
  .topic { border-bottom: 0; }
  .line { grid-template-columns: minmax(0, 1fr) 45%; }   /* tablet review: the photograph was a quarter of the panel */
  .reach { grid-template-columns: 1fr; }
  .reach__form { border-radius: 0; }
  .topics { padding-inline: 16px; }
  .tiles--row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ingredients--row, .ingredients.ingredients--product { grid-template-columns: 1fr; }
}
/* Below 900 four product cards set their copy at 16 characters a line. */
@media (max-width: 900px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* === PHONE AND TABLET ======================================================
   No phone mockup exists; the layout is derived (HANDOFF.md, section 3):
   same reading order, same content, columns stacked. */
@media (max-width: 760px) {
  .display { white-space: normal; }
  .decor, .script, .collection__flower { display: none; }   /* no edge to grow in from once the columns stack */
  .site-header__cta { display: none; }
  .hero { grid-template-columns: 1fr; }
  .hero__copy { padding: 28px 16px 20px; }
  .hero__art { position: static; }
  .hero__art { height: auto; }
  .hero__art img { width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; object-position: 100% 50%; -webkit-mask-image: none; mask-image: none; }   /* the scene's quiet left edge is cropped away on a phone */
  .badges { position: static; width: auto; border-radius: 0; padding: 16px; }
  .ritual { grid-template-columns: 1fr; }
  .ritual__copy, .ritual--aligned .ritual__copy { border-radius: 0; margin: 0; padding: 28px 16px; }
  .ritual__body { max-width: none; }
  .collection { padding: 28px 16px; }
  .collection__head { flex-direction: column; padding-left: 0; }
  .collection__intro { max-width: none; margin-top: 0; }
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .routine__head { flex-direction: column; align-items: flex-start; padding: 28px 16px 16px; }
  .steps { grid-column: 1 / -1; margin-inline: 16px; }
  .ingredients { grid-template-columns: 1fr; padding: 28px 16px; gap: 20px; }
  .ingredients__copy p { max-width: none; }
  .tiles { grid-template-columns: 1fr; gap: 16px; }
  .tile { grid-template-columns: 96px minmax(0, 1fr); column-gap: 14px; }
  .care { grid-template-columns: 1fr; }
  .care__photo { aspect-ratio: 16 / 9; border-radius: 0; }
  .care__copy { padding: 24px 16px; }
  .care__copy p:not(.eyebrow) { max-width: none; }   /* the 340 measure was for a column beside the photograph */
  .cta { grid-template-columns: 1fr; margin-top: 28px; padding: 28px 16px 0; }
  .cta__art { position: static; width: 100%; height: auto; -webkit-mask-image: none; mask-image: none; }
  /* Inner pages, stacked (HANDOFF.md 3: the phone layout is derived). */
  .hero__lede, .line__copy p:not(.eyebrow) { max-width: none; }
  .lines .line { grid-template-columns: 1fr; }
  .line__copy { padding: 28px 16px 12px; }
  .line__seal { right: 16px; top: 16px; }
  .flow { grid-template-columns: 1fr; padding: 28px 16px; }
  /* The tablet splits (hero, topics) undone for the phone, at the tablet rules' own weight. */
  .hero--page .hero__art, .hero--product .hero__art { grid-column: auto; grid-row: auto; height: auto; }
  .hero--page .hero__art img, .hero--product .hero__art img { height: auto; aspect-ratio: 16 / 9; }
  .hero--page .badges, .hero--product .badges, .hero__caption { grid-column: auto; grid-row: auto; }
  .topics { grid-template-columns: 1fr; }
  /* ChatGPT mobile review, 11 Oct 2026. */
  .topic { grid-template-columns: 56px minmax(0, 1fr); column-gap: 14px; align-items: start; padding: 14px 0; }   /* the 59u icon column fell under its 48px floor and the icon ran into the words */
  .topic img { min-width: 0; padding: 12px; }
  .line__seal { top: auto; bottom: 16px; right: 16px; }   /* onto the photograph, off the copy */
  .badges { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 14px; }   /* four across set the labels three lines deep */
  .icons { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 8px; }
  .icons li { letter-spacing: 0.03em; }   /* "BRIGHTER" at 0.08em overran its third of 320 (sweep) */
  .icons--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .facts { gap: 6px 16px; }
  .line { grid-template-columns: 1fr; }
  .flow__steps { gap: 16px; padding: 16px 8px; }
  .pairings { padding: 28px 16px; }
  .pairing { grid-template-columns: 1fr; }
  .pairing__img { height: auto; aspect-ratio: 4 / 3; }
  .feature, .feature--flip, .name { grid-template-columns: 1fr; }
  .feature__photo { height: auto; aspect-ratio: 4 / 3; border-radius: 0; }
  .feature__copy, .name__copy { padding: 28px 16px; }
  .name__img { height: auto; aspect-ratio: 4 / 3; }
  .icons, .icons--four { max-width: none; }
  .reach__form { padding: 28px 16px; }
  .form__row { grid-template-columns: 1fr; }
  .care--flip { grid-template-columns: 1fr; }
  .care--flip .care__copy { padding-left: 16px; }
  .care--flip .care__photo { border-radius: 0; }
  .tiles--row { grid-template-columns: 1fr; }
  .legal, .lost { padding: 28px 16px; }
  .site-footer { padding: 24px 16px; }
  .site-footer__top { flex-direction: column; gap: 20px; }
  .site-footer__nav { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 16px; margin: 0; }   /* four links in two columns (mobile review: the list was most of a phone screen) */
  .site-footer__nav a { display: flex; align-items: center; min-height: 44px; }
  .site-footer__bottom { flex-direction: column; }
  .site-footer__legal a { display: inline-flex; align-items: center; min-height: 44px; }
  /* A product hero on a phone: name, photograph, then the words and the
     action (MOBILE.md 11), so the first screen shows the product. */
  .points, .pair { padding: 28px 16px; }
  .points__head { padding-left: 0; }
  .ingredients--product { grid-template-columns: 1fr; }
  .pair { grid-template-columns: 1fr; gap: 20px; }
  .pair-card { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
}
@media (max-width: 480px) {
  /* Three icons to a 320 row overran their labels (sweep): two up, the odd one centred beneath. */
  .icons { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .icons li:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .cards { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; transition: none !important; } }
