/* ==========================================================================
   Day Hair Habit, dayhairhabit.com
   Tokens are derived from .claude/data/brand.md. Do not hard-code colour
   values in pages; always use the custom properties below.
   ========================================================================== */

:root {
  /* ---- THE OFFICIAL PALETTE ----------------------------------------------
     Four colours, straight from "Brand Guideline - day.pdf" (Qoar Kreatif,
     2024), p.5. These are the brand's own values, do not re-sample them from
     artwork and do not add a fifth. */
  --red:   #944346;  /* corporate red   */
  --mocha: #B29684;  /* corporate mocha */
  --cream: #EDE4DC;  /* corporate cream */
  --blush: #F5DCD3;  /* corporate blush */

  /* ---- Derived working tones ---------------------------------------------
     Tints and shades of the four above, for states, rules and body copy.
     Nothing here introduces a new hue. */
  --red-deep:   #7A3639;  /* red, darkened, hover/pressed */
  --red-soft:   #B87275;  /* red, lightened, dividers, accents */
  --cream-lift: #F6F0EA;  /* cream, lifted, raised cards */
  --line:       #DFD3C9;  /* cream/mocha hairline */
  /* Mocha is too light for body text (2.6:1 on cream). These are mocha pulled
     down to pass WCAG AA, same hue, readable. */
  --ink:        #5E4441;  /* 7.1:1 on cream */
  --ink-soft:   #7C605C;  /* 4.6:1 on cream */

  /* ---- Typography, official, guideline p.6 ------------------------------
     PRIMARY: EB Garamond.  SECONDARY: Outfit (the PDF's "Outfir" is a typo).
     Both are the real specified families, available on Google Fonts. */
  --serif: "EB Garamond", Garamond, Georgia, serif;
  --sans:  "Outfit", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* ---- WhatsApp green, the one permitted non-brand colour ----------------
     This is WhatsApp's own mark colour, used ONLY on the floating chat button
     and the mobile book bar. A green chat bubble is a learned affordance, and
     recolouring it to brand red costs taps. Never use these two anywhere else,
     and never introduce a third off-palette colour on this reasoning. */
  --wa-green:      #25D366;
  --wa-green-deep: #1DA851;
  --wa-ink:        #FFFFFF;  /* WhatsApp's own glyph colour, for those two only */

  --wrap: 1180px;
  --gut: clamp(1.25rem, 5vw, 4rem);
  --arch: 50vw 50vw 0 0;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 300;
  font-size: 1.0625rem;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

/* --------------------------------------------------------------- type ---- */

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 500;
  line-height: 1.12;
  margin: 0;
  color: var(--red);
  letter-spacing: -0.01em;
}

h1 { font-size: clamp(2.75rem, 8vw, 5.25rem); }
h2 { font-size: clamp(2rem, 4.6vw, 3.25rem); }
h3 { font-size: clamp(1.35rem, 2.4vw, 1.75rem); }

/* The house move: italic display serif for the emotional line. */
.italic { font-style: italic; }

/* The homepage opener is a <p> so the h1 can carry the page's keyword further
   down. It still has to look exactly like the h1 it replaced, so it borrows the
   same display type and h1 scale. */
.hero__title {
  font-family: var(--serif);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--red);
  font-size: clamp(2.75rem, 8vw, 5.25rem);
  margin: 0;
}

/* An h1 that is not the loudest thing on the page. It sits above the section's
   h2 as a kicker, so it is set at h3 scale: the heading level is for search
   engines, the visual hierarchy stays with the h2 underneath. */
.section-h1 {
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  margin-bottom: 0.4rem;
  /* One line (owner, 2026-08-12). It is a short kicker above the h2 and it
     wrapped once the needs band put it in the narrower right-hand column.
     Below 380px it is allowed to wrap rather than overflow the screen. */
  white-space: nowrap;
}
@media (max-width: 380px) {
  .section-h1 { white-space: normal; }
}

.eyebrow {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--red);
  margin: 0 0 1.25rem;
}
.eyebrow--light { color: var(--blush); }

.lede { font-size: clamp(1.0625rem, 1.6vw, 1.25rem); color: var(--ink-soft); max-width: 58ch; }

.caps {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-weight: 400;
}

/* ------------------------------------------------------------- layout ---- */

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }

section { padding-block: clamp(4rem, 9vw, 7.5rem); }
/* The booking page's hero already introduces the cards; a full band of padding
   between the two just pushed them off the first screen. */
.pad-none { padding-block: 0 clamp(3rem, 6vw, 5rem); }
.hero-page:has(+ .pad-none) { padding-bottom: clamp(1.5rem, 3vw, 2.25rem); }

.section-head { max-width: 62ch; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.section-head .lede { margin-top: 1.25rem; }

.on-red { background: var(--red); color: var(--cream); }
.on-red h1, .on-red h2, .on-red h3 { color: var(--cream); }
.on-red .lede { color: rgba(238, 229, 220, 0.78); }

.on-blush { background: var(--blush); }

/* ----------------------------------------------------------- wordmark ---- */
/* FLAT ONLY, no gradient, shadow, glow or bevel may ever be added here. */

/* The real vector logo, extracted from the designer's editable PDF by
   tools/pdf-to-svg.mjs. It is NOT type, the "y" ends in a detached crescent
   swash that no web font reproduces.

   The SVG is inlined with fill="currentColor", so the colourway is just the
   `color` property. Only the four corporate colours are permitted (guideline
   p.7). Never add a filter, shadow, gradient or opacity to these. */

.mark,
.wordmark { display: inline-flex; text-decoration: none; color: var(--red); }
.mark svg { display: block; height: var(--mark-size, 2rem); width: auto; }
.wordmark svg { display: block; height: var(--wm-size, 4rem); width: auto; }

.mark--light,
.wordmark--light { color: var(--cream); }
.mark--mocha,
.wordmark--mocha { color: var(--mocha); }

/* --------------------------------------------------------------- nav ----- */

.nav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--cream) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--ease), background .3s var(--ease);
}
.nav.is-stuck { border-bottom-color: var(--line); }
.nav__inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; padding-block: 0.85rem; }
.nav__links { display: flex; align-items: center; gap: clamp(1rem, 2.2vw, 2rem); }

/* The full lockup, not the `d` mark alone. Kept small enough that the descender
   of the "y" and the nav's own bottom rule never crowd each other. FLAT ONLY -
   no shadow, glow or gradient may be added here or anywhere else. */
.nav__logo { --wm-size: clamp(2rem, 3.4vw, 2.5rem); }

/* :not(.btn) matters. Without it this rule outranks `.btn` on specificity and
   paints the Book pill's label dark ink on dark red. */
.nav__links > a:not(.btn), .nav__top {
  text-decoration: none; font-size: 0.9rem; font-weight: 400; color: var(--ink);
  position: relative; padding-block: 0.25rem;
}

/* The one CTA in the nav. Cream on red, tightened up so the longer label
   ("Book an appointment") still reads as a button and not a paragraph. */
.nav__book {
  color: var(--cream); font-size: 0.78rem; letter-spacing: 0.09em;
  padding: 0.72rem 1.3rem; white-space: nowrap;
}
.nav__book:hover, .nav__book:focus-visible { color: var(--cream); }
.nav__links .nav__book[aria-current="page"] { color: var(--cream); }
.nav__links > a:not(.btn)::after, .nav__top::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--red); transform: scaleX(0); transform-origin: left; transition: transform .3s var(--ease);
}
.nav__links > a:not(.btn):hover::after,
.nav__top:hover::after,
.nav__links > a:not(.btn)[aria-current="page"]::after,
.nav__top[aria-current="page"]::after { transform: scaleX(1); }
.nav__links > a:not(.btn)[aria-current="page"], .nav__top[aria-current="page"] { color: var(--red); }

/* ------------------------------------------------------- nav dropdowns --- */

.nav__item { position: relative; display: flex; align-items: center; gap: 0.35rem; }
.nav__caret {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0.35rem 0.15rem; color: var(--ink-soft);
  cursor: pointer; line-height: 0;
}
.nav__caret svg { width: 10px; height: 6px; transition: transform .3s var(--ease); }
.nav__item:hover .nav__caret svg,
.nav__caret[aria-expanded="true"] svg { transform: rotate(180deg); }

.nav__menu {
  position: absolute; top: calc(100% + 0.85rem); left: 50%; translate: -50% 0;
  width: max-content; max-width: min(92vw, 42rem);
  background: var(--cream-lift); border: 1px solid var(--line);
  border-radius: 1.25rem; padding: 1.6rem 1.75rem;
  opacity: 0; visibility: hidden; transform: translateY(-0.5rem);
  transition: opacity .25s var(--ease), transform .25s var(--ease), visibility .25s;
}
.nav__item:hover .nav__menu,
.nav__item:focus-within .nav__menu,
.nav__caret[aria-expanded="true"] + .nav__menu {
  opacity: 1; visibility: visible; transform: translateY(0);
}
/* A hover bridge, so the pointer can cross the gap without the menu closing. */
.nav__item.has-menu::after {
  content: ""; position: absolute; top: 100%; left: 0; right: 0; height: 0.85rem;
}
/* Three tidy columns of category groups rather than one long row, with six
   categories a flex row ran the panel off the screen and wrapped badly. */
.nav__menu-inner {
  display: grid; grid-template-columns: repeat(3, minmax(10.5rem, 1fr));
  gap: 1.5rem 2rem; align-items: start;
}
/* Most panels are one list, not a category grid. Three fixed tracks made a
   three-item menu three times wider than its contents. */
.nav__menu-inner[data-cols="1"] { grid-template-columns: minmax(12rem, max-content); }
.nav__menu-inner[data-cols="2"] { grid-template-columns: repeat(2, minmax(12rem, max-content)); }

/* A column title that is itself a page (Wash & Blow Dry). It keeps the small
   caps treatment of a plain title but has to be tappable. */
.nav__col-title a { text-decoration: none; color: inherit; }
.nav__col-title a:hover { text-decoration: underline; text-underline-offset: 0.25em; }

/* Top-level labels with no page of their own (Blonde Specialist, The Cut).
   They open the panel and nothing else, so they get no hover underline and no
   pointer, otherwise they read as a broken link. */
.nav__top--static { cursor: default; color: var(--ink-soft); }
.nav__top--static::after { content: none; }
.nav__col { display: flex; flex-direction: column; }
.nav__col--wide { grid-column: span 2; }
.nav__col-title {
  margin: 0 0 0.5rem; font-size: 0.66rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--red); font-weight: 400; line-height: 1.4;
}
.nav__col a {
  display: block; text-decoration: none; color: var(--ink);
  font-size: 0.86rem; line-height: 1.4; padding: 0.3rem 0;
  transition: color .2s var(--ease);
}
.nav__col a:hover { color: var(--red); }
.nav__col a strong { display: block; font-weight: 400; }
.nav__col a span { display: block; font-size: 0.74rem; color: var(--ink-soft); line-height: 1.35; }

.nav__toggle {
  display: none; background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: 0.5rem 0.9rem; font-family: var(--sans); font-size: 0.78rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink); cursor: pointer;
}

/* 1100, not 980: the nav carries six top-level items now (Home, Keratin,
   Coloring, Hair Treatment, Blonde Specialist, The Cut) plus the booking pill,
   and that row runs into the logo before 980px is reached. */
@media (max-width: 1100px) {
  .nav__toggle { display: block; }
  .nav__links {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: flex-start;
    gap: 0; background: var(--cream); border-bottom: 1px solid var(--line);
    padding: 0.5rem var(--gut) 2rem; transform: translateY(-120%);
    transition: transform .35s var(--ease);
    max-height: calc(100dvh - 100%); overflow-y: auto;
  }
  .nav__links.is-open { transform: translateY(0); }
  .nav__links > a { width: 100%; padding-block: 0.85rem; border-bottom: 1px solid var(--line); font-size: 1.05rem; }
  .nav__links .btn { margin-top: 1.25rem; width: 100%; justify-content: center; border-bottom: 0; }

  /* Dropdowns become accordions, hover is meaningless here, so only the
     caret button opens them and the top-level link stays tappable. */
  .nav__item { width: 100%; flex-wrap: wrap; border-bottom: 1px solid var(--line); }
  .nav__top { flex: 1; padding-block: 0.85rem; font-size: 1.05rem; }
  .nav__caret { padding: 0.85rem 0.5rem; }
  .nav__caret svg { width: 14px; height: 9px; }
  .nav__item.has-menu::after { content: none; }
  .nav__item:hover .nav__menu, .nav__item:focus-within .nav__menu { opacity: 0; visibility: hidden; }
  .nav__menu {
    position: static; translate: none; width: 100%; max-width: none; min-width: 0;
    background: none; border: 0; border-radius: 0; padding: 0;
    display: none; transform: none; transition: none;
  }
  .nav__caret[aria-expanded="true"] + .nav__menu { display: block; opacity: 1; visibility: visible; }
  .nav__menu-inner { grid-template-columns: 1fr; gap: 1.25rem; padding-bottom: 1.25rem; }
  .nav__col--wide { grid-column: auto; }
  .nav__col { width: 100%; }
  .nav__col a { padding: 0.5rem 0; font-size: 0.98rem; }
}

/* ------------------------------------------------------------ buttons ---- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.6rem;
  font-family: var(--sans); font-size: 0.88rem; font-weight: 400;
  letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none;
  padding: 0.95rem 1.85rem; border-radius: 999px; border: 1px solid var(--red);
  background: var(--red); color: var(--cream); cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease), transform .25s var(--ease);
}
/* Same trap as .ico: a bare WA_SVG inside a button had no size anywhere except
   .btn-dir, .chat-btn and .fab, so it filled the button. More specific button
   rules below still win. */
.btn svg { width: 1.15em; height: 1.15em; flex: none; fill: currentColor; }
.btn:hover { background: var(--red-deep); border-color: var(--red-deep); transform: translateY(-2px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }

.btn--ghost { background: transparent; color: var(--red); }
.btn--ghost:hover { background: var(--red); color: var(--cream); }

.btn--light { background: var(--cream); color: var(--red); border-color: var(--cream); }
.btn--light:hover { background: var(--blush); border-color: var(--blush); color: var(--red-deep); }

.btn--ghost-light { background: transparent; color: var(--cream); border-color: rgba(238,229,220,.45); }
.btn--ghost-light:hover { background: var(--cream); color: var(--red); border-color: var(--cream); }

.btn--sm { padding: 0.6rem 1.15rem; font-size: 0.72rem; }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: 2.25rem; }

.wa-icon { width: 1.05em; height: 1.05em; flex: none; fill: currentColor; }

/* --------------------------------------------------------------- hero ---- */

/* The booking buttons have to be on the first screen at every size, phone,
   tablet and desktop, so the hero is capped at one viewport minus the sticky
   nav and everything inside it scales on viewport HEIGHT as well as width.
   That is what the min(Xvw, Yvh) inside each clamp is doing: on a short laptop
   the vh term wins and the type comes down; on a tall phone the vw term wins
   and nothing looks oversized. dvh, not vh, so mobile browser chrome counts. */
.hero {
  --nav-h: clamp(4rem, 3rem + 2.4vw, 5.2rem);
  padding-block: clamp(1rem, 3.5vh, 3rem);
  text-align: center; position: relative; overflow: hidden;
  min-height: calc(100dvh - var(--nav-h));
  display: flex; flex-direction: column; justify-content: center;
}
.hero__mark { --wm-size: clamp(3rem, min(13vw, 12vh), 9rem); margin-bottom: clamp(0.75rem, 2.2vh, 2.75rem); }
.hero h1, .hero .hero__title { max-width: 16ch; margin-inline: auto; }
.hero .hero__title { font-size: clamp(2rem, min(8vw, 8.5vh), 5.25rem); }
.hero .lede { margin: clamp(0.6rem, 1.6vh, 1.5rem) auto 0; text-align: center; font-size: clamp(0.95rem, min(1.6vw, 2vh), 1.25rem); }
.hero .btn-row { justify-content: center; margin-top: clamp(1rem, 2.8vh, 2.25rem); }

/* Under 720px the fixed book-bar owns the bottom 84px of the screen, so the
   hero must stop short of it or the buttons hide behind the bar. */
@media (max-width: 720px) {
  .hero { min-height: calc(100dvh - var(--nav-h) - 84px); }
}

/* The signature arch, holding the hero image. */
.arch {
  position: relative; margin: clamp(3rem, 6vw, 4.5rem) auto 0;
  max-width: 760px; aspect-ratio: 3 / 3.6;
  border-radius: var(--arch);
  overflow: hidden; background: var(--blush);
}
.arch img { width: 100%; height: 100%; object-fit: cover; }
.arch--flat { border-radius: 18px; aspect-ratio: 4 / 3; }

/* Radiating "sun" motif, decorative, flat, low contrast, never behind body copy. */
.sunburst {
  position: absolute; inset-inline: -10%; bottom: 0; height: 55%; pointer-events: none;
  background: repeating-conic-gradient(from 180deg at 50% 100%,
    var(--blush) 0deg 6.5deg, transparent 6.5deg 13deg);
  -webkit-mask-image: linear-gradient(to top, #000 0%, transparent 78%);
  mask-image: linear-gradient(to top, #000 0%, transparent 78%);
  opacity: .55;
}
.sunburst--light {
  background: repeating-conic-gradient(from 180deg at 50% 100%,
    var(--cream) 0deg 6.5deg, transparent 6.5deg 13deg);
  opacity: .08;
}

/* Hero arch styled as a flat red panel with the sun motif behind the line. */
.arch--brand { background: var(--red); display: grid; place-items: center; text-align: center; padding: clamp(2rem, 6vw, 4rem); }
.arch--brand .arch__inner { position: relative; z-index: 1; max-width: 24ch; }
.arch--brand .arch__line { font-family: var(--serif); font-style: italic; color: var(--cream); font-size: clamp(1.5rem, 3.4vw, 2.4rem); line-height: 1.25; margin: 0; }
.arch--brand .caps { color: rgba(238,229,220,.72); margin-top: 1.5rem; }
/* The arch used to be a pure mood band with nothing to do in it. The CTA is
   what earns it its space, so give it room to read as an action. */
.arch__cta { margin-top: 2.25rem; }

/* -------------------------------------------------------------- strip ---- */

/* -------------------------------------------------------------- strip ---- */

.strip { text-align: center; }
/* An h2 as of 2026-08-12, owner's call. It was a <p>, and headings are reset to
   margin:0 here, so the gap to the caps line below has to be put back. */
.strip .quote { font-family: var(--serif); font-style: italic; font-size: clamp(1.75rem, 4vw, 2.75rem); color: var(--cream); line-height: 1.2; margin: 0 0 1.25rem; }
/* Nudged up from the 0.8rem .caps default: this is the brand's statement of how
   it works, not a footnote, and at caps-with-tracking it was reading as one. */
.strip .caps {
  margin: 1.5rem auto 0; max-width: 56ch; color: rgba(238,229,220,.8);
  font-size: 0.92rem; line-height: 1.85; letter-spacing: 0.11em;
}
/* The same band on blush instead of red (the About page, so it does not merge
   into the red booking band underneath it). Both colours above are hard-coded
   for cream-on-red, so both have to be restated, and the text goes left: a
   centred paragraph beside a photograph reads as neither centred nor aligned. */
.strip--blush { text-align: left; }
.strip--blush .quote { color: var(--red); }
.strip--blush .caps { color: var(--ink-soft); margin-inline: 0; }

/* ------------------------------------------------------------ pillars ---- */

.grid { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
/* Exactly four cards, so two rows of two. auto-fit would fit three across on a
   desktop and leave the fourth alone on the next line, which reads as a lesser
   option rather than as the fourth of four. */
.grid--quad { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 720px) { .grid--quad { grid-template-columns: 1fr; } }

.card {
  /* --card-pad is read by .card--proof, which bleeds its photo to the card edge
     by cancelling exactly this padding. Keep them the same value. */
  --card-pad: clamp(1.75rem, 3vw, 2.5rem);
  background: var(--cream-lift); border: 1px solid var(--line); border-radius: 18px;
  padding: var(--card-pad);
}
.card__num { font-family: var(--serif); font-style: italic; font-size: 2rem; color: var(--red-soft); line-height: 1; margin-bottom: 1rem; }
.card h3 { margin-bottom: 0.75rem; }
.card p { margin: 0; color: var(--ink-soft); }

/* ---------------------------------------------------- treatment pages ---- */

.pad-sm { padding-block: clamp(3rem, 6.5vw, 5.5rem); }

.hero-page { padding-block: clamp(2.5rem, 5vw, 4.5rem) clamp(2.5rem, 5vw, 4rem); }
.hero-page h1 { max-width: 18ch; }
.hero-page .lede { margin-top: 1.25rem; }

.crumbs { font-size: 0.8rem; color: var(--ink-soft); margin-bottom: 1.75rem; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.crumbs a { text-decoration: none; }
.crumbs a:hover { text-decoration: underline; text-underline-offset: 3px; }
.crumbs span[aria-current] { color: var(--red); }

.kicker { font-family: var(--serif); font-style: italic; font-size: clamp(1.15rem, 2.2vw, 1.5rem); color: var(--red-soft); margin: 0.85rem 0 0; }

/* .meta-row is gone from the treatment hero (owner, 2026-08-14). The rule is
   kept only so an old cached page still renders; nothing generated uses it now,
   and the duration it carried lives in .side-card__time. Delete this block once
   there is no chance of a stale page. */
.meta-row { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; margin: 2rem 0 0; }
.meta-row li {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.12em; color: var(--red);
  border: 1px solid var(--line); border-radius: 999px; padding: 0.45rem 1rem; background: var(--cream-lift);
}

.badge {
  display: inline-block; margin-top: 0.85rem; font-size: 0.68rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--red); border: 1px solid var(--red-soft);
  border-radius: 999px; padding: 0.35rem 0.85rem;
}

.prose-layout { display: grid; gap: clamp(2rem, 4vw, 3.5rem); grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); align-items: start; }
@media (max-width: 900px) { .prose-layout { grid-template-columns: 1fr; } }

.prose h2 { font-size: clamp(1.5rem, 2.6vw, 2rem); font-style: italic; margin-top: 2.75rem; }
.prose h2:first-child { margin-top: 0; }
.prose p { margin: 1rem 0 0; max-width: 66ch; color: var(--ink); }

/* Links inside copy. Owner's rule, 2026-08-21: the generator links every
   treatment the copy names, and until now those links inherited the ink colour
   from `a { color: inherit }`, so the only thing marking them was the browser's
   default underline. Red plus a real underline, because a link a guest does not
   notice is the same as no link. Offset clears the descenders in Outfit. */
.prose p a,
.faq p a,
.variant p a {
  color: var(--red);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
.prose p a:hover,
.faq p a:hover,
.variant p a:hover { text-decoration-thickness: 2px; }

.side-card { background: var(--cream-lift); border: 1px solid var(--line); border-radius: 18px; padding: clamp(1.5rem, 2.5vw, 2rem); position: sticky; top: 96px; }
@media (max-width: 900px) { .side-card { position: static; } }
.side-card h3 { font-size: 1.15rem; }
/* The duration, moved out of the hero pill row. Set at the size of a .ticks row
   so it reads as part of the card's list of facts rather than as body copy. */
.side-card__time { margin: 0.7rem 0 0; font-size: 0.95rem; color: var(--ink-soft); }

.ticks { list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: 0.7rem; font-size: 0.95rem; color: var(--ink-soft); }
.ticks li { position: relative; padding-left: 1.4rem; }
.ticks li::before { content: ""; position: absolute; left: 0; top: 0.62em; width: 6px; height: 6px; border-radius: 50%; background: var(--red-soft); }
/* Marks a reason that is true of one area and not the other, e.g. the beach in
   Uluwatu, a yoga class in Ubud. Set quietly: it is a qualifier on the line, not
   a second thing to read. Inline, so it wraps with the sentence rather than
   stranding itself on a line of its own in a narrow side card. */
.tick__where {
  display: inline-block; font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--red); background: var(--blush); border-radius: 999px;
  padding: 0.12em 0.6em; vertical-align: 0.1em; white-space: nowrap;
}

.plain { list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: 0.7rem; }
.plain li a { display: inline-flex; align-items: center; gap: 0.6rem; text-decoration: none; }
.plain li a:hover { color: var(--red); }

/* An inline SVG with a viewBox and no width/height expands to fill whatever
   box it lands in. Sizing .ico only inside .footer__grid meant these rendered
   full-card-width anywhere else. This is the base; contexts may override. */
/* Size only. NEVER set a colour here: these sit on cream cards and on the red
   footer, and pinning them to --red painted red on red and made them vanish.
   currentColor lets each context decide. */
.ico { width: 1.15em; height: 1.15em; flex: none; fill: currentColor; }
.plain .ico { color: var(--red); }

/* ---------------------------------------------------------------- faq ---- */

/* The FAQ heading is set small, owner's call 2026-08-14. The base h2 runs to
   3.25rem, which on a page whose h1 is deliberately tiny (see the inverted page
   heading further down) made the questions block the loudest thing above the
   booking band. Roughly h3 scale here: still a real h2 in the outline and in the
   FAQPage JSON-LD, just not shouting. The margin comes down with it, since a
   4rem gap under a small heading reads as a gap, not as breathing room. */
.section-head--faq { margin-bottom: clamp(1.25rem, 2.5vw, 1.85rem); }
.section-head--faq h2 { font-size: clamp(1.25rem, 2.2vw, 1.6rem); }

/* TWO columns and a tighter row, owner's call 2026-08-14. A treatment page runs
   six to eight questions, and one full-width column of them was a long stack of
   near-identical bars that pushed the booking band a screen further down. Two
   columns roughly halves that height.

   Each column is its OWN stack, owner's call 2026-08-21. align-items: start
   already stopped a neighbour's box stretching, but the grid ROW still grew to
   fit the open answer, so the next question in the other column was pushed down
   past a block of empty cream. Two flex columns share no rows at all: opening an
   answer moves only what sits under it. */
.faqs {
  display: grid; gap: 0.6rem 1.25rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
}
.faqs__col { display: flex; flex-direction: column; gap: 0.6rem; }
/* One column again below the point where a question would wrap to three lines
   in half the width, which is where two columns stop saving anything. */
@media (max-width: 820px) {
  .faqs { grid-template-columns: 1fr; }
}
.faq { background: var(--cream-lift); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.faq summary {
  cursor: pointer; list-style: none; padding: 0.85rem 2.5rem 0.85rem 1.1rem; position: relative;
  font-weight: 400; color: var(--red); font-size: 0.94rem; line-height: 1.45;
}
.faq summary::-webkit-details-marker { display: none; }
/* The question is an <h3> for the outline, not for the type scale: the base h3
   runs to 1.75rem and would have made every question bigger than the section
   heading above it. Inherit everything from the summary, and stay INLINE so the
   summary keeps its own padding and the caret keeps its vertical centring. */
.faq summary h3 {
  display: inline; font: inherit; color: inherit; letter-spacing: inherit; margin: 0;
}
.faq summary::after {
  content: ""; position: absolute; right: 1.1rem; top: 50%; width: 8px; height: 8px;
  border-right: 1.5px solid var(--red); border-bottom: 1.5px solid var(--red);
  translate: 0 -60%; rotate: 45deg; transition: rotate .25s var(--ease);
}
.faq[open] summary::after { rotate: -135deg; }
/* No max-width here any more: the column IS the measure now, and 62ch inside a
   half-width cell just left the answer ending short of its own box. */
.faq p { margin: 0; padding: 0 1.1rem 1.1rem; color: var(--ink-soft); font-size: 0.9rem; line-height: 1.65; }

/* ------------------------------------------------------- link cards ------ */

.card--link { text-decoration: none; display: flex; flex-direction: column; transition: border-color .25s var(--ease), transform .25s var(--ease); }
.card--link:hover { border-color: var(--red-soft); transform: translateY(-3px); }
.card__cat { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.16em; color: var(--red-soft); }
.card--link h3 { margin: 0.6rem 0 0.6rem; font-size: 1.3rem; }
.card--link p { flex: 1; }
.card__go { margin-top: 1.25rem; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.14em; color: var(--red); }
.card__go::after { content: " →"; }

/* ---- treatment card, with the page's own photograph ------------------------
   The "Other treatments" row. The card's padding moves off the <a> and onto an
   inner .card__body so the photo can run edge to edge across the top: padding
   on the link itself would inset the image and leave a cream border round it.
   Same trick .card--proof uses, from the opposite direction. */
.card--treat { padding: 0; overflow: hidden; }
/* No photo for this service: put the padding back so it does not collapse. */
.card--treat-flat { padding: var(--card-pad); }
.card__shot {
  margin: 0; aspect-ratio: 4 / 3; overflow: hidden; background: var(--blush);
  border-bottom: 1px solid var(--line);
}
.card__shot img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: scale .5s var(--ease);
}
/* A slow, small push in. Big enough to feel alive on hover, small enough that
   it never reads as the image jumping. */
.card--treat:hover .card__shot img { scale: 1.04; }
.card__body {
  display: flex; flex-direction: column; flex: 1;
  padding: clamp(1.25rem, 2vw, 1.6rem) clamp(1.25rem, 2vw, 1.75rem) clamp(1.35rem, 2.2vw, 1.75rem);
}
.card--treat h3 { margin: 0.5rem 0 0.5rem; font-size: 1.15rem; }
.card--treat p { flex: 1; margin: 0; font-size: 0.92rem; color: var(--ink-soft); line-height: 1.6; }
/* The arrow slides on hover. The label is a span inside a link, so it inherits
   the link's own hover underline unless it is told not to. */
.card--treat .card__go { margin-top: 1rem; text-decoration: none; }
.card--treat .card__go::after { display: inline-block; transition: translate .25s var(--ease); }
.card--treat:hover .card__go::after { translate: 4px 0; }
@media (prefers-reduced-motion: reduce) {
  .card--treat:hover .card__shot img { scale: 1; }
  .card--treat:hover .card__go::after { translate: 0; }
}

/* --------------------------------------------------------- catalogue ----- */
/* The whole menu on /hair-treatment. An INDEX, not a card grid: a service page
   argues in cards and photographs, this page's only job is to let someone find
   the thing they came for. 21 cards would be a scroll with no shape; 21 ruled
   rows under five headings can be read in one pass.

   Group name on the left, its treatments on the right. The label column is
   narrow and sticky on desktop, so while you read down a long group you can
   still see which family you are in. */
.catalogue { display: grid; gap: clamp(2.5rem, 5vw, 3.5rem); }
.catalogue__group {
  display: grid; gap: clamp(1rem, 2.5vw, 2.5rem);
  grid-template-columns: minmax(0, 0.32fr) minmax(0, 1fr);
  align-items: start;
}
.catalogue__cat {
  font-family: var(--serif); font-style: italic; font-size: clamp(1.35rem, 2.2vw, 1.75rem);
  color: var(--red); margin: 0; position: sticky; top: 96px;
}
.catalogue__cat a { color: inherit; text-decoration: none; }
.catalogue__cat a:hover { text-decoration: underline; text-underline-offset: 0.22em; }

.catalogue__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
/* One column now that the duration column is gone (owner: no timings on this
   page). Left as a grid rather than a block so the kicker keeps its own row
   under the name and a sub-row can slot in without re-laying anything out. */
.catalogue__row {
  display: grid; gap: 0.15rem;
  padding: 1rem 0; border-bottom: 1px solid var(--line);
  text-decoration: none; color: inherit;
  transition: padding-left .25s var(--ease), color .25s var(--ease);
}
/* The whole row shifts a few pixels on hover. Cheaper to read than a colour
   change on four separate spans, and it points the way the link goes. */
.catalogue__row:hover { padding-left: 0.5rem; color: var(--red); }
.catalogue__name { font-family: var(--serif); font-size: 1.15rem; color: var(--red); }
.catalogue__meta {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--ink-soft); align-self: center; text-align: right;
}
/* The kicker runs under the name across BOTH tracks, so a long one wraps under
   the duration rather than squeezing itself into the name column. */
.catalogue__kicker { grid-column: 1 / -1; font-size: 0.88rem; color: var(--ink-soft); line-height: 1.5; }
/* A variant: a named card on the row above's page, not a page of its own. Set
   as a quieter, indented run-on so the eye reads it as belonging to the line
   above rather than as another treatment at the same level. */
.catalogue__row--sub {
  padding: 0.5rem 0 0.5rem 1.25rem; position: relative;
  border-bottom-style: dashed;
}
.catalogue__row--sub::before {
  content: ""; position: absolute; left: 0.25rem; top: 1.05em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--red-soft);
}
.catalogue__row--sub .catalogue__name { font-family: var(--sans); font-size: 0.9rem; color: var(--ink-soft); }
.catalogue__row--sub:hover .catalogue__name { color: var(--red); }

@media (max-width: 720px) {
  /* Stack the label over its list. The sticky heading goes with it: sticky on a
     phone would park the group name over the rows you are trying to read. */
  .catalogue__group { grid-template-columns: 1fr; }
  .catalogue__cat { position: static; }
  .catalogue__meta { text-align: left; grid-column: 1 / -1; order: 3; margin-top: 0.15rem; }
}

/* --------------------------------------------------------- book block ---- */

/* Cap the PROSE at a readable measure, never the block itself. A max-width here
   squeezed the three-column salon grid into ~700px and left a dead column of
   space on the right. The grid must have the full wrap. */
.book-block > .eyebrow,
.book-block > h2,
.book-block > .book-block__sub,
.book-block > .lede { max-width: 68ch; }

/* The old "Find us" grid on the homepage said the same three things without
   maps. Its headline moved in here as a subhead rather than being thrown away. */
.book-block__sub {
  margin: 0.65rem 0 0; font-size: clamp(1.35rem, 2.4vw, 1.85rem);
  font-weight: 400; color: var(--red);
}
.book-block--light .book-block__sub { color: var(--blush); }
.book-block .lede { margin-top: 1rem; }
.book-block--light h2 { color: var(--cream); }
.book-block--light .lede { color: rgba(238, 229, 220, 0.78); }

/* ---------------------------------------------------- the chat button ----
   The site's single conversion target. Every one of these opens WhatsApp with
   the message already written and the ROOM named, one line serves both
   Uluwatu rooms, so the room name in the message is what tells reception which
   chair to hold. Never turn these into a form. See .claude/agents/conversion-designer.md */

/* This div carries id="book", so it is what every "Book an appointment" on the
   site scrolls to (see bookBlock() in tools/build.mjs). The nav is sticky, so
   without a scroll margin the first row of salon cards lands underneath it. The
   value is the nav height plus a little air: enough that the heading above stays
   just in frame and the guest can see WHICH section they landed in, without
   pushing the buttons back off the bottom. */
.chat-picker { margin-top: 1.75rem; scroll-margin-top: clamp(5rem, 4rem + 2.4vw, 6.5rem); }
.book-block--bare .chat-picker { margin-top: 0; }

/* One card per salon: map, address, then the green button. Map first because
   "where is it" is the question this grid answers, button last so all three
   line up however long the addresses run. */
.book-grid {
  display: grid; gap: 1.5rem;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: stretch;
}
@media (max-width: 980px) { .book-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .book-grid { grid-template-columns: 1fr; } }

.book-cell {
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--line); border-radius: 18px; overflow: hidden;
  background: var(--cream-lift);
  transition: border-color .22s var(--ease), box-shadow .22s var(--ease);
}
.book-cell:hover { border-color: var(--red-soft); box-shadow: 0 10px 28px rgba(58,47,48,.10); }

/* A generous map. It is the reason this card exists, so it gets the space.
   min-height keeps it readable when the column is narrow. */
.book-cell__map {
  aspect-ratio: 4 / 3; min-height: 220px;
  background: var(--blush); border-bottom: 1px solid var(--line);
}
.book-cell__map iframe { display: block; width: 100%; height: 100%; border: 0; }

.book-cell__body { display: flex; flex-direction: column; flex: 1; padding: 1.35rem 1.4rem 1.4rem; }
.book-cell__name {
  margin: 0 0 0.7rem; font-family: var(--sans); font-weight: 500;
  font-size: 0.94rem; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--red); line-height: 1.35;
}
.book-cell__addr { font-style: normal; font-size: 0.88rem; line-height: 1.55; color: var(--ink-soft); }
.book-cell__blurb {
  margin: 0.85rem 0 0; padding-top: 0.85rem; border-top: 1px solid var(--line);
  font-size: 0.88rem; line-height: 1.55; color: var(--ink);
}

/* Two equal actions, pinned to the bottom so all three cards line up. */
.book-cell__actions {
  margin-top: auto; padding-top: 1.25rem;
  display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem;
}
@media (max-width: 1080px) { .book-cell__actions { grid-template-columns: 1fr; } }

.btn-dir, .chat-btn {
  display: flex; align-items: center; justify-content: center; gap: 0.45rem;
  text-decoration: none; padding: 0.8rem 0.9rem; border-radius: 999px;
  font-size: 0.86rem; font-weight: 500; line-height: 1.2; white-space: nowrap;
  transition: background .22s var(--ease), color .22s var(--ease),
              border-color .22s var(--ease), transform .22s var(--ease);
}
.btn-dir svg, .chat-btn svg { width: 18px; height: 18px; fill: currentColor; flex: none; }

.btn-dir { background: transparent; color: var(--red); border: 1px solid var(--red); }
.btn-dir:hover, .btn-dir:focus-visible { background: var(--red); color: var(--cream); transform: translateY(-2px); }

/* Green, same as the floating button: people tap a green WhatsApp pill
   without having to read it. */
.chat-btn { background: var(--wa-green); color: var(--wa-ink); border: 1px solid var(--wa-green); }
.chat-btn:hover, .chat-btn:focus-visible {
  background: var(--wa-green-deep); border-color: var(--wa-green-deep); transform: translateY(-2px);
}

.chat-btn__icon {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; background: var(--red); color: var(--cream);
}
.chat-btn__icon svg { width: 21px; height: 21px; fill: currentColor; }

.chat-btn__text { display: flex; flex-direction: column; line-height: 1.32; min-width: 0; }
.chat-btn__name { font-weight: 500; font-size: 1.02rem; color: var(--red); }
.chat-btn__meta { font-size: 0.8rem; color: var(--ink-soft); }
.chat-btn__go { margin-left: auto; flex: none; font-size: 1.1rem; color: var(--red-soft); transition: transform .22s var(--ease); }
.chat-btn:hover .chat-btn__go { transform: translateX(3px); }

/* On the red band the cards invert to cream. The green button stays green. */
.book-block--light .book-cell { background: var(--cream); border-color: var(--cream); }
.book-block--light .book-cell:hover { border-color: var(--blush); }

/* Sticky mobile bar, on a phone the booking action must never scroll away. */
.book-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: none; padding: 0.7rem var(--gut) calc(0.7rem + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--cream) 94%, transparent);
  backdrop-filter: blur(12px); border-top: 1px solid var(--line);
}
.book-bar .btn, .book-bar > summary { width: 100%; }
@media (max-width: 720px) {
  .book-bar { display: block; }
  body { padding-bottom: 84px; }
  .wa-pick:not(.wa-pick--bar) { display: none; }  /* the bar replaces it, never show both */
}

/* ---------------------------------------------------------- locations ---- */

.loc-card { display: flex; flex-direction: column; }
.loc-card__area { font-family: var(--serif); font-style: italic; color: var(--red-soft); font-size: 1rem; }
.loc-card h3 { margin: 0.25rem 0 1rem; }
.loc-card address { font-style: normal; color: var(--ink-soft); margin-bottom: 1.25rem; }
.loc-card__blurb { flex: 1; }
.loc-card .btn-row { margin-top: 1.75rem; gap: 0.6rem; }

.hours { display: inline-flex; align-items: center; gap: 0.6rem; font-size: 0.85rem; color: var(--ink-soft); }
.hours::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--red-soft); }

/* A branch's own line, secondary to the booking button by design. */
.direct-line { margin: 0.5rem 0 0; font-size: 0.85rem; color: var(--ink-soft); }
.direct-line a { color: var(--red); text-decoration: none; border-bottom: 1px solid var(--red-soft); }
.direct-line a:hover { border-bottom-color: var(--red); }

/* ------------------------------------------------------------- forms ---- */

.form { display: grid; gap: 1.75rem; }
.field { display: grid; gap: 0.6rem; }
.field > label, .fieldset > legend {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.18em;
  color: var(--red); font-weight: 400; padding: 0;
}
.hint { font-size: 0.85rem; color: var(--ink-soft); margin: 0; }

input[type="text"], input[type="date"], input[type="time"], input[type="number"], select, textarea {
  font-family: var(--sans); font-size: 1rem; font-weight: 300; color: var(--ink);
  background: var(--cream-lift); border: 1px solid var(--line); border-radius: 12px;
  padding: 0.85rem 1rem; width: 100%; transition: border-color .2s var(--ease);
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--red-soft); outline-offset: 2px; border-color: var(--red-soft); }
textarea { resize: vertical; min-height: 96px; }
select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--red) 50%), linear-gradient(135deg, var(--red) 50%, transparent 50%); background-position: calc(100% - 20px) center, calc(100% - 14px) center; background-size: 6px 6px, 6px 6px; background-repeat: no-repeat; padding-right: 2.75rem; }

.fieldset { border: 0; padding: 0; margin: 0; display: grid; gap: 0.6rem; }

.choice-grid { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.choice {
  position: relative; display: block; border: 1px solid var(--line); border-radius: 14px;
  padding: 1rem 1.15rem; cursor: pointer; background: var(--cream-lift);
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice__title { display: block; font-weight: 400; color: var(--ink); }
.choice__meta { display: block; font-size: 0.82rem; color: var(--ink-soft); margin-top: 0.15rem; }
.choice:hover { border-color: var(--red-soft); }
.choice:has(input:checked) { border-color: var(--red); background: color-mix(in srgb, var(--red) 7%, var(--cream-lift)); }
.choice:has(input:checked) .choice__title { color: var(--red); }
.choice:has(input:focus-visible) { outline: 2px solid var(--red-soft); outline-offset: 2px; }

.checks { display: grid; gap: 0.5rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.check { display: flex; align-items: center; gap: 0.65rem; font-size: 0.95rem; cursor: pointer; }
.check input { width: 1.05rem; height: 1.05rem; accent-color: var(--red); flex: none; }

.two-up { display: grid; gap: 1.75rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .two-up { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------ summary ---- */

.booking-layout { display: grid; gap: clamp(2rem, 4vw, 3.5rem); grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); align-items: start; }
@media (max-width: 940px) { .booking-layout { grid-template-columns: 1fr; } }

.summary { position: sticky; top: 96px; background: var(--red); color: var(--cream); border-radius: 18px; padding: clamp(1.75rem, 3vw, 2.25rem); }
@media (max-width: 940px) { .summary { position: static; } }
.summary h3 { color: var(--cream); margin-bottom: 0.5rem; }
.summary__note { font-size: 0.8rem; color: rgba(238,229,220,.62); margin: 0; }
.summary .btn { width: 100%; margin-top: 1.5rem; }

.preview {
  margin-top: 1.25rem; background: rgba(0,0,0,.16); border-radius: 12px; padding: 1.15rem 1.25rem;
  font-size: 0.84rem; line-height: 1.65; white-space: pre-wrap; word-break: break-word;
  color: rgba(238,229,220,.9); font-family: var(--sans); font-weight: 300; margin-bottom: 0;
}

.notice { border: 1px solid var(--red-soft); background: color-mix(in srgb, var(--red) 8%, var(--cream-lift)); border-radius: 12px; padding: 1rem 1.15rem; font-size: 0.9rem; color: var(--ink); margin: 0; }

/* --------------------------------------------------------------- misc ---- */

.social-row { display: flex; flex-wrap: wrap; gap: 0.85rem; }

/* WhatsApp green, not brand red. People recognise the green bubble and tap it
   without reading, which is the entire job of this button. The ripple draws the
   eye once every few seconds without ever moving the button itself. */
/* The button is now the <summary> of a disclosure, so the fixed positioning
   moved up to .wa-pick and .fab keeps only the look of the thing. */
.fab {
  position: relative;
  width: 60px; height: 60px; border-radius: 50%; display: grid; place-items: center;
  background: var(--wa-green); color: var(--wa-ink); border: 0; cursor: pointer;
  box-shadow: 0 8px 24px rgba(37,211,102,.42), 0 2px 8px rgba(58,47,48,.18);
  transition: background .25s var(--ease), transform .25s var(--ease), box-shadow .25s var(--ease);
}
.fab::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%; z-index: -1;
  background: var(--wa-green);
  animation: fab-ripple 2.6s var(--ease) infinite;
}
@keyframes fab-ripple {
  0%   { transform: scale(1);   opacity: .55; }
  70%  { transform: scale(1.75); opacity: 0; }
  100% { transform: scale(1.75); opacity: 0; }
}
.fab:hover, .fab:focus-visible {
  background: var(--wa-green-deep); transform: translateY(-3px) scale(1.06);
  box-shadow: 0 12px 30px rgba(37,211,102,.5), 0 3px 10px rgba(58,47,48,.2);
}
.fab:hover::before { animation-play-state: paused; }
.fab svg { width: 30px; height: 30px; fill: currentColor; }

@media (prefers-reduced-motion: reduce) {
  .fab::before { animation: none; }
  .fab:hover, .fab:focus-visible { transform: none; }
}

/* ------------------------------------------------ the salon picker ------- */
/* Owner's call, 2026-08-21: the floating button asks WHICH ROOM before it opens
   WhatsApp, so a booking lands on that salon's own number instead of on the
   general line for reception to forward.

   It is a <details>, so it opens with no JavaScript and every option is a real
   link. main.js only adds click-outside and Escape. The panel is brand cream and
   red: the WhatsApp green stays on the button itself, which is the documented
   exception in CLAUDE.md §9 and not a licence to green the whole menu. */
.wa-pick:not(.wa-pick--bar) {
  position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem); z-index: 60;
}
.wa-pick > summary { list-style: none; cursor: pointer; }
.wa-pick > summary::-webkit-details-marker { display: none; }
/* The ripple is an invitation to tap. Once the panel is open it has done its
   job and would only pull the eye away from the three names. */
.wa-pick[open] .fab::before { animation: none; }

.wa-pick__menu {
  position: absolute; right: 0; bottom: calc(100% + 0.75rem);
  min-width: 15.5rem; display: flex; flex-direction: column; gap: 0.4rem;
  padding: 0.8rem; border-radius: 18px;
  background: var(--cream); border: 1px solid var(--line);
  box-shadow: 0 16px 44px rgba(58, 47, 48, 0.22);
}
.wa-pick__lead {
  margin: 0 0 0.15rem; font-size: 0.68rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--red-soft);
}
.wa-pick__item {
  display: block; padding: 0.62rem 0.8rem; border-radius: 11px;
  background: var(--cream-lift); border: 1px solid var(--line);
  color: var(--red); text-decoration: none; font-size: 0.95rem; white-space: nowrap;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.wa-pick__item:hover, .wa-pick__item:focus-visible {
  background: var(--blush); border-color: var(--blush);
}

/* On a phone the same picker hangs off the sticky bar instead, full width. */
.wa-pick--bar > summary { width: 100%; }
.wa-pick--bar .wa-pick__menu {
  left: var(--gut); right: var(--gut); bottom: calc(100% + 0.4rem); min-width: 0;
}

/* The mobile bar carries the same green, for the same reason. */
.btn--wa {
  background: var(--wa-green); border-color: var(--wa-green); color: var(--wa-ink);
}
.btn--wa:hover { background: var(--wa-green-deep); border-color: var(--wa-green-deep); color: var(--wa-ink); }
@media print { .wa-pick, .nav { display: none; } }

/* ------------------------------------------------------------- footer ---- */

.footer { background: var(--red); color: rgba(238,229,220,.8); padding-block: clamp(3.5rem, 6vw, 5rem) 2.5rem; position: relative; overflow: hidden; }
.footer a { color: var(--cream); text-decoration: none; }
.footer a:hover { text-decoration: underline; text-underline-offset: 4px; }
/* The brand sits above the grid rather than in it (owner's rule, 2026-08-14),
   which is what frees all four tracks for menu. See FOOTER_TREE in build.mjs. */
.footer__brand {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 1rem 3rem;
  margin-bottom: clamp(2.5rem, 5vw, 3.5rem);
  padding-bottom: clamp(2rem, 4vw, 2.75rem);
  border-bottom: 1px solid rgba(238,229,220,.2);
  position: relative; z-index: 1;
}
.footer__brand .footer__tag { margin-top: 0; }
.footer__brand > div { display: flex; flex-wrap: wrap; gap: 0.35rem 3rem; }

/* Exactly four tracks, owner's rule, 2026-08-08, restated 2026-08-14: one per
   nav category. auto-fit used to be in here, which quietly produced a fifth
   track at wide widths and threw the lists out of alignment. Every track is an
   equal 1fr now, no cell spans two, and each holds two stacked groups so the
   four columns come out six to eight rows each. */
.footer__grid { display: grid; gap: 2.5rem; grid-template-columns: repeat(4, minmax(0, 1fr)); position: relative; z-index: 1; }
.footer__col { display: flex; flex-direction: column; }
.footer__grid h4 { color: var(--blush); font-family: var(--sans); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.18em; font-weight: 400; margin-bottom: 1rem; }
/* The second group in a column needs clear air above it or the two lists read
   as one. Sized like the old social-list gap: about one and a half rows, which
   is plainly a new group without leaving a hole. */
.footer__grid h4 + ul + h4 { margin-top: 1.9rem; }
.footer__grid ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; font-size: 0.95rem; }
/* A group heading that is also its hub page (Keratin, Color, Treatments,
   Hairdressers, Wash) carries the underline. Scalp Treatment, Day Hair Habit
   and Find us have no single page, so they stay plain. */
.footer__grid h4.footer__h4--link a { color: var(--blush); text-decoration: underline; text-underline-offset: 6px; }
/* Tablet: 2x2, so the four-column rule degrades to a block rather than a
   ragged five. Phone: one column, groups in source order. */
@media (max-width: 980px) {
  .footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .footer__grid { grid-template-columns: 1fr; }
}
/* Glyphs sit on the text baseline row, never wrap away from their label, and
   dim slightly so the words stay the thing you read first. */
.footer__grid li a { display: inline-flex; align-items: center; gap: 0.55rem; }
.footer__grid .ico, .footer__grid .wa-icon {
  width: 15px; height: 15px; flex: none; fill: currentColor; opacity: .75;
}
.footer__grid li a:hover .ico, .footer__grid li a:hover .wa-icon { opacity: 1; }
/* Socials share the Day Hair Habit column rather than claiming a fifth one,
   which was wrapping them onto their own row under the logo. No divider: the
   list gap alone separates them, and a rule here cut the column in half. */
/* Must out-specify `.footer__grid ul { margin: 0 }` (0,1,1), or the margin is
   silently dropped and the two lists sit flush.

   Sizing this off the 0.6rem gap alone is misleading: at 0.95rem/1.7 each row
   also carries ~11px of leading, so a normal row already reads as roughly 20px
   of space. 1.6rem on top made it ~46px, more than twice a row. 0.7rem lands
   near 31px, about one and a half rows: clearly a new group, not a hole. */
.footer__grid ul.footer__social { margin-top: 0.7rem; }
.footer__tag { margin: 1.5rem 0 0; max-width: 26ch; font-size: 0.95rem; }
.footer__base { margin-top: 3rem; padding-top: 1.75rem; border-top: 1px solid rgba(238,229,220,.2); display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; font-size: 0.82rem; position: relative; z-index: 1; }

/* ------------------------------------------------------ needs finder ---- */
/* "What does your hair need today?", the problem in the guest's own words on
   the left, the treatment that fixes it on the right. */

.needs { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; border-top: 1px solid var(--line); }
.needs li { border-bottom: 1px solid var(--line); }
.needs a {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: 1rem;
  padding: 1.1rem 0.5rem; text-decoration: none; color: var(--ink);
  transition: background .25s var(--ease), padding-inline .25s var(--ease);
}
.needs a:hover { background: var(--blush); padding-inline: 1.25rem 1.25rem; }
.needs__need { font-family: var(--serif); font-size: clamp(1.1rem, 2vw, 1.4rem); }
.needs__arrow { color: var(--red-soft); }
.needs a:hover .needs__arrow { color: var(--red); }
.needs__fix { text-align: right; font-size: 0.9rem; color: var(--ink-soft); }
.needs a:hover .needs__fix { color: var(--red); }

@media (max-width: 640px) {
  .needs a { grid-template-columns: 1fr auto; gap: 0.5rem; }
  .needs__fix { grid-column: 1 / -1; text-align: left; }
}

/* --------------------------------------------------- treatment variants -- */
/* Several types of one treatment on one page, see CLAUDE.md §2. */

.variant { display: flex; flex-direction: column; }
.variant__tag { font-family: var(--serif); font-style: italic; color: var(--red); margin: 0.35rem 0 0.75rem; }

/* `margin-top: auto` pushes the button to the card floor so all three line up,
   but on the TALLEST card auto resolves to zero and the button ends up welded
   to the last line of copy. The bottom margin on whatever precedes it sets the
   floor, so the gap holds whether the card is short or full. */
.variant > p:last-of-type,
.variant > .ticks:last-of-type { margin-bottom: 1.75rem; }
.variant .btn { margin-top: auto; align-self: flex-start; }
.ticks--sm { font-size: 0.88rem; gap: 0.45rem; }

/* ---------------------------------------------------- contact page rows -- */
/* The booking band answers "pick a room and chat now" with three equal cards.
   This page answers "where is it and how do I get there", so it takes the
   opposite shape: one salon per row, a wide map, and the details set as a
   proper record. Alternating sides stop three rows reading as a table. */

.loc-rows { display: grid; gap: clamp(3rem, 7vw, 5.5rem); }

.loc-row {
  display: grid; gap: clamp(1.75rem, 4vw, 3.25rem);
  grid-template-columns: 1.15fr 1fr; align-items: center;
}
.loc-row:nth-child(even) .loc-row__map { order: 2; }
@media (max-width: 860px) {
  .loc-row { grid-template-columns: 1fr; }
  .loc-row:nth-child(even) .loc-row__map { order: 0; }
}

.loc-row__map {
  aspect-ratio: 3 / 2; border-radius: 20px; overflow: hidden;
  border: 1px solid var(--line); background: var(--blush);
}
.loc-row__map iframe { display: block; width: 100%; height: 100%; border: 0; }

.loc-row__body h2 { font-size: clamp(2rem, 4vw, 2.9rem); margin: 0.15rem 0 0; }
.loc-row__body .eyebrow { margin-bottom: 0; }
.loc-row__blurb {
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1.1rem, 1.8vw, 1.35rem); line-height: 1.45;
  color: var(--ink); margin: 1rem 0 0; max-width: 34ch;
}

/* A definition list, not a paragraph: address, hours and line are facts a guest
   scans for rather than reads. */
.loc-row__facts {
  margin: 1.75rem 0 0; display: grid; grid-template-columns: auto 1fr;
  gap: 0.5rem 1.5rem; font-size: 0.92rem; align-items: start;
}
.loc-row__facts dt {
  font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--red-soft); padding-top: 0.25rem;
}
.loc-row__facts dd { margin: 0; color: var(--ink-soft); }
.loc-row__facts address { font-style: normal; line-height: 1.55; }
.loc-row .btn-row { margin-top: 1.75rem; }

/* --------------------------------------------------------- hero photo --- */
/* Text left, photo right, in the brand arch. Every photo the salon shoots is
   portrait 2:3 and the arch was drawn for that ratio, so the two fit without
   cropping anything. Pages with no photo keep the single-column hero. */

/* minmax(0, ...) IS LOAD-BEARING. A bare "1fr" is minmax(auto, 1fr), and an
   auto minimum means a track cannot shrink below its content's min-content
   width. The photo track holds an <img width="1000">, so it refused to shrink,
   took width from the text track, and collapsed the text to ITS min-content:
   the longest single word. That is why the h1 was breaking one word per line
   ("keratin / smoothing / treatment") no matter what the wrapping rules said.
   Zero as the minimum lets both tracks share the row properly. */
.hero-split {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); align-items: center;
}
@media (max-width: 900px) {
  .hero-split { grid-template-columns: 1fr; }
  .hero-shot { max-width: 24rem; margin-inline: auto; }
}

.hero-shot {
  margin: 0; aspect-ratio: 2 / 3; overflow: hidden;
  background: var(--blush);
}
.hero-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- homepage hero ---------------------------------------------------------
   The homepage must NOT look like a service page (owner's call, 2026-08-12).
   A service page is text beside one arch. This is text beside a fan of three
   result photographs at three sizes, overlapping. Everything below still obeys
   the one-viewport cap on .hero, because the booking buttons have to stay on
   the first screen; the fan is sized in vh as well as vw so it shrinks on a
   short laptop instead of pushing them off. */
.hero--home { text-align: left; }
/* The photo column is now the WIDER of the two, owner's call 2026-08-14: the fan
   was the smaller half and read as an illustration beside the copy rather than
   the thing the hero is showing. The copy column still holds its measure, the
   lede is capped at 42ch below and the two headings are short, so giving the
   width to the photographs costs the text nothing. The one-viewport cap on
   .hero still governs the height: see the max-height on --a. */
.hero-home {
  display: grid;
  grid-template-columns: minmax(0, 0.98fr) minmax(0, 1.12fr);
  gap: clamp(1.25rem, 3.5vw, 3.25rem);
  align-items: center;
  width: 100%;
}
/* Two lines, broken on the comma by a <br> in the markup: owner's call, and it
   is the brand lockup's own line break rather than an accident of the column
   width. The title keeps the full display size because it no longer has to fit
   on one line. */
.hero--home .hero__title { margin-inline: 0; max-width: 11ch; }
/* The h1 sits under the brand line and stays small: the h2 above it is the
   thing a guest reads, this is the thing the page is about. Sans, so it does
   not compete with the two serif lines it sits between. */
.hero__h1 {
  font-family: var(--sans);
  font-size: clamp(0.95rem, 1.5vw, 1.15rem);
  font-weight: 400;
  letter-spacing: 0.01em;
  color: var(--ink);
  margin: clamp(0.9rem, 2vh, 1.4rem) 0 0;
}
/* `.hero h1` above sets `max-width: 16ch; margin-inline: auto`, and it BEATS
   .hero__h1 on specificity (0,1,1 against 0,1,0), so the h1 box was centring
   itself inside the left column while the h2 above it sat flush left. The two
   lines started at different x. Reset both here, scoped to the homepage so the
   centred service-page heroes keep their own behaviour. */
.hero--home .hero__h1 { margin-inline: 0; max-width: 30ch; }
.hero__h1 .italic { color: var(--red); }
.hero--home .lede { margin-inline: 0; text-align: left; max-width: 42ch; }
.hero--home .btn-row { justify-content: flex-start; }

/* NOTE ON THE RADIUS. --arch is a full four-value shorthand ("50vw 50vw 0 0"),
   not a single length, so `border-radius: var(--arch) var(--arch) 16px 16px`
   expands to ten values, is invalid, and the browser throws the whole
   declaration away. That is why these rendered as plain rectangles. Write the
   dome out literally instead of composing it from the token. */
/* NOTHING OVERLAPS. An earlier version pulled the right-hand shots over the tall
   one with negative margins and it read as a pile, not a composition (owner,
   twice). The rhythm now comes only from a shared gutter, two column widths and
   one vertical offset, which is calmer and cannot collide at any width. */
.hero-fan {
  position: relative;
  display: grid;
  /* minmax(0, ...) for the same reason as .hero-split below: these tracks hold
     images with intrinsic widths, and an auto minimum stops them shrinking. */
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(0.75rem, 1.6vw, 1.25rem);
  align-items: start;
}
.hero-fan__shot {
  margin: 0; overflow: hidden; background: var(--blush);
  border-radius: 50vw 50vw 18px 18px;
}
.hero-fan__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The tall dome fills the left column. The right column holds the other two,
   stacked, and the whole column starts lower so the tops do not line up. */
/* The vh term is what keeps the booking button on the first screen on a short
   laptop, so it stays. It is the rem cap that was holding the fan small on a
   big monitor, where there is height to spare. Raised together, vh first. */
.hero-fan__shot--a {
  grid-column: 1; grid-row: 1 / span 2;
  aspect-ratio: 2 / 3; max-height: min(70vh, 38rem);
}
.hero-fan__shot--b { grid-column: 2; aspect-ratio: 4 / 5; margin-top: clamp(1.5rem, 5vh, 3.5rem); }
.hero-fan__shot--c { grid-column: 2; aspect-ratio: 4 / 5; border-radius: 18px 18px 50vw 50vw; }

@media (max-width: 900px) {
  /* Stack, and drop the third shot: two photographs still carry the idea, and a
     third would push the booking buttons under the fold on a phone. */
  .hero--home { text-align: center; }
  .hero-home { grid-template-columns: 1fr; }
  .hero--home .hero__title, .hero--home .lede { margin-inline: auto; text-align: center; }
  .hero--home .btn-row { justify-content: center; }
  .hero-fan { grid-template-columns: 1fr 1fr; max-width: 26rem; margin-inline: auto; }
  .hero-fan__shot--a { grid-column: 1; grid-row: auto; aspect-ratio: 3 / 4; max-height: none; }
  .hero-fan__shot--b { grid-column: 2; margin-top: 1.5rem; }
  .hero-fan__shot--c { display: none; }
}

/* ---- placed photographs -----------------------------------------------------
   One picture answering the sentence beside it. NOT a gallery: the owner's
   brief is that photographs are scattered through the page with intent, so each
   of these is chosen to evidence a specific claim and lives next to it. */
.band { display: grid; gap: clamp(1.5rem, 4vw, 3.5rem); align-items: center; }
.band--figure-right { grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr); }
.band--figure-left { grid-template-columns: minmax(0, 0.65fr) minmax(0, 1.35fr); }
.band .section-head { margin-bottom: 0; }
.spot-shot {
  margin: 0; overflow: hidden; background: var(--blush);
  aspect-ratio: 3 / 4; border-radius: 50vw 50vw 16px 16px;
}
.spot-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ---- a band carrying the stacked pair (see spotStack in build.mjs) ---------
   The arch with a small cream-mounted print over its bottom-left corner, the
   same composition as the shower service hero. Two things this band has to do
   that a single-photo band does not:

   1. Give the figure a real share of the width. --figure-right alone leaves it
      0.65fr, and the overlap needs the arch to be big enough that a 52% print
      hanging off it is still small.
   2. Leave room UNDERNEATH. The print hangs 9% below the arch, so without extra
      bottom padding it crosses into whatever section follows. .hero-page--split
      solves this with :has() for the hero; same idea here. */
.band--stack {
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  align-items: center;
  /* WIDER GUTTER THAN A NORMAL BAND, and this is load-bearing, not taste. The
     small print is positioned at left: -12% of the arch, so it hangs OUT of the
     figure column by that much. If the gap is narrower than the hang, the print
     lands on the paragraph: that is exactly what happened at the default
     clamp(1.5rem, 4vw, 3.5rem), where the hang was ~80px and the gap ~56px.
     Keep this gap larger than the hang below if either is ever retuned. */
  gap: clamp(2rem, 6.5vw, 5.5rem);
}
.band--stack .hero-stack__second { left: -12%; }
/* The copy goes LEFT here. .strip centres its text, which is right for a
   full-width band but wrong beside a photograph: centred lines run out to both
   edges of their column, so the longest ones reached under the print no matter
   how wide the gutter got. Left-aligned, the ragged edge falls away from it. */
.band--stack > div:first-child { text-align: left; }
.band--stack .quote, .band--stack .caps { margin-inline: 0; }
.strip:has(.hero-stack) { padding-bottom: clamp(5rem, 9vw, 7.5rem); }
/* The arch itself. .spot-shot is a 3:4 dome by default; inside the stack it goes
   taller so the composition matches the hero it was taken from. */
.hero-stack > .spot-shot { aspect-ratio: 2 / 2.6; margin: 0; }

@media (max-width: 820px) {
  .band--figure-right, .band--figure-left, .band--stack { grid-template-columns: 1fr; }
  .band--figure-left .spot-shot { order: 2; }
  .band .spot-shot { max-width: 20rem; margin-inline: auto; }
  /* Cap the STACK, not the arch inside it: a max-width on .spot-shot here would
     shrink the arch while the print stayed a percentage of it, and the overlap
     would swallow the photo. */
  .band--stack .hero-stack { max-width: 20rem; margin-inline: auto; }
  .band--stack .hero-stack > .spot-shot { max-width: none; }
}

/* The hub card is not a link itself: the title links to the treatment and the
   button books it, so the two destinations stay separable. The title still has
   to look and behave like the primary link on the card. */
.variant__link { color: inherit; text-decoration: none; transition: color 0.3s var(--ease); }
.variant__link:hover { color: var(--red); text-decoration: underline; text-underline-offset: 5px; }
.card.variant .btn--sm { margin-top: 1.5rem; }

/* ---- menu spread ------------------------------------------------------------
   The homepage treatment list, set as a spread rather than a row of equal
   cards (owner, 2026-08-12: the card grid read as a template).

   Three things do the work, and all three are deliberate:
     1. no boxes. The entries sit straight on the blush ground, separated by a
        hairline that stops short of the column edge, so it reads as a printed
        menu rather than as UI.
     2. unequal widths. Twelve columns, and each of the six entries is placed by
        hand at a different span. Nothing lines up into a grid.
     3. different heights. Each entry is dropped a different distance, so the
        eye moves down the page diagonally instead of scanning rows.

   THE PLACEMENT IS PER-POSITION. Six entries, six nth-child rules. Change the
   length of the list in build.mjs and these have to change with it. */
.menu-spread {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(2rem, 5vw, 4.5rem);
  row-gap: 0;
  margin-bottom: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--line);
}
/* Every entry identical: same width, same type size, same rule above it,
   aligned on both axes. The one asymmetry left is the duration sitting out to
   the right of the name, which is how a printed menu sets a price column. */
.menu-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 1.25rem;
  text-decoration: none; color: inherit;
  border-bottom: 1px solid var(--line);
  padding: clamp(1.1rem, 2.2vw, 1.6rem) 0;
  transition: background 0.3s var(--ease), padding-inline 0.3s var(--ease);
}
.menu-item:hover { background: rgba(148, 67, 70, 0.045); padding-inline: 1rem; }
.menu-item__name {
  font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  line-height: 1.2; margin: 0;
  transition: color 0.3s var(--ease);
}
.menu-item:hover .menu-item__name { color: var(--red); }
.menu-item__meta {
  grid-column: 2; grid-row: 1;
  font-family: var(--sans); font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.16em;
  color: var(--ink-soft); white-space: nowrap;
}
.menu-item__kicker {
  grid-column: 1 / -1;
  color: var(--ink-soft); font-size: 0.92rem;
  margin: 0.4rem 0 0; max-width: 42ch;
}
@media (max-width: 700px) {
  .menu-spread { grid-template-columns: 1fr; }
}

/* ---- needs band -------------------------------------------------------------
   Owner's layout, 2026-08-12: two photographs stacked down the left, the
   question and the whole problem-to-treatment list down the right. The list
   used to run full width under the heading, which pushed the pictures away from
   the thing they illustrate and made the section very tall. */
.needs-band {
  display: grid;
  grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 4.5vw, 4rem);
  align-items: start;
}
.needs-band__art { display: grid; gap: clamp(0.9rem, 2vw, 1.5rem); }
/* The first shot takes the arch, the second is a plain rounded frame, so the
   pair reads as lead and support rather than two of the same thing. */
.needs-band__art .spot-shot:first-child { aspect-ratio: 4 / 5; border-radius: 50vw 50vw 18px 18px; }
.needs-band__art .spot-shot:last-child { aspect-ratio: 5 / 4; border-radius: 18px; }
.needs-band__copy .section-head { margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }

@media (max-width: 900px) {
  /* Stack, and put the copy first: on a phone the question has to arrive before
     the pictures or the section opens on an image with no context. */
  .needs-band { grid-template-columns: 1fr; }
  .needs-band__copy { order: 1; }
  .needs-band__art { order: 2; grid-template-columns: 1fr 1fr; }
}

/* A proof card carries its evidence at the top: photo bled to the card edge,
   then the number, then the claim. Equal heights across the row so the three
   read as one set, which is the whole point of numbering them. */
.grid--proof { align-items: stretch; }
.card--proof {
  padding-top: 0; overflow: hidden;
  display: flex; flex-direction: column;
  transition: border-color 0.35s var(--ease), transform 0.35s var(--ease);
}
.card--proof:hover { border-color: var(--red-soft); transform: translateY(-3px); }
.card--proof .spot-shot {
  aspect-ratio: 4 / 3; border-radius: 0;
  margin: 0 calc(var(--card-pad, 1.75rem) * -1) clamp(1.25rem, 2.5vw, 1.75rem);
  width: calc(100% + var(--card-pad, 1.75rem) * 2);
}
/* The number sits on a hairline that runs to the edge of the card: it reads as
   an index rather than as a heading competing with the red title under it.
   Deep ink brown, NOT the washed pink it used to be (owner, 2026-08-12), which
   sat too close to the title colour to look deliberate. */
.card--proof .card__num {
  margin: 0 0 0.65rem;
  color: var(--ink);
  font-size: 1.35rem;
  letter-spacing: 0.06em;
  display: flex; align-items: center; gap: 0.9rem;
}
.card--proof .card__num::after {
  content: ""; flex: 1 1 auto; height: 1px; background: var(--line);
}
.card--proof h3 { margin-bottom: 0.75rem; }
.card--proof p:last-child { margin-bottom: 0; }

/* ---- photo flow ------------------------------------------------------------
   Several photographs in a band, deliberately NOT a grid of squares (owner's
   brief 2026-08-12). Three things do the work: the arch silhouette, sizes that
   differ down the row, and every second shot dropped below its neighbour. The
   stagger is what makes four pictures read as a composition rather than a
   contact sheet, so do not "tidy" it into equal boxes. */
.photo-flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 44%), 1fr));
  gap: clamp(0.9rem, 2.4vw, 2rem);
  align-items: start;
}
.photo-flow__shot {
  margin: 0;
  position: relative;
  overflow: hidden;
  background: var(--blush);
  aspect-ratio: 2 / 3;
  border-radius: 50vw 50vw 16px 16px;
}
.photo-flow__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The stagger. Even children ride lower, and every third is a shallower crop, so
   the row has three different heights in it rather than one. */
.photo-flow__shot:nth-child(even) { margin-top: clamp(1.25rem, 4.5vw, 3.25rem); }
.photo-flow__shot:nth-child(3n) { aspect-ratio: 3 / 4; }
.photo-flow__shot:nth-child(4n) { border-radius: 16px 16px 50vw 50vw; }

/* BEFORE / AFTER chip, for a strip that is a set of pairs. It sits inside the
   crop rather than under it, because the frame is an arch and a caption hanging
   below would break the shape line. The 4n frame is the arch upside down, so its
   chip moves to the square end instead of the curved one. */
.photo-flow__cap {
  position: absolute; bottom: 0.9rem; left: 50%; translate: -50% 0;
  background: var(--cream); color: var(--ink);
  font-size: 0.65rem; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 0.3rem 0.75rem; border-radius: 999px; white-space: nowrap;
}
.photo-flow__shot:nth-child(4n) .photo-flow__cap { bottom: auto; top: 0.9rem; }
@media (max-width: 560px) {
  /* Two up on a phone, still staggered. One up would make a four-photo band a
     very long scroll for very little. */
  .photo-flow { grid-template-columns: repeat(2, 1fr); }
  .photo-flow__shot:nth-child(even) { margin-top: 1.25rem; }
}
.section-head--center { max-width: 46ch; margin-inline: auto; text-align: center; }

/* ---- inverted page heading ------------------------------------------------
   Owner's rule, 2026-08-12: the primary keyword is the h1 and it is SMALL; the
   menu name is the h2 below it and carries the display size. The usual
   hierarchy is upside down on purpose, so the type has to do the work the tag
   names normally do. The keyword reads as an eyebrow, the name reads as the
   headline, and nothing about the source order changes for a screen reader. */
.page-kw {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
  margin: 0 0 0.5rem;
  max-width: none;
  /* ONE LINE. The keyword is a single phrase and breaking it mid-phrase
     ("BALAYAGE HAIR / SALON") reads as a mistake. The longest keyword on the
     site, "keratin hair straightening treatment near me", is about 340px at
     this size, which clears the hero column on every desktop width. */
  white-space: nowrap;
}
@media (max-width: 700px) {
  /* Under this the longest keyword would run off the screen, so let it wrap
     rather than overflow. Most pages still hold one line. */
  .page-kw { white-space: normal; }
}
.page-name {
  font-family: var(--serif);
  font-size: clamp(2.4rem, 5.4vw, 4rem);
  line-height: 1.04;
  font-weight: 400;
  margin: 0 0 1rem;
  max-width: 18ch;
}
/* The h1 now opens the block (the category eyebrow above it is gone), so it
   needs a little air under the breadcrumb rather than a rule joining it to a
   line that no longer exists. */
.hero-page .crumbs + .page-kw { margin-top: 1.5rem; }

/* The same split used mid-page rather than in a hero, which is where the
   homepage photo sits. The section head inside it must drop its own bottom gap
   or it double-spaces against whatever the band sits above. */
.split-band { margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.split-band .section-head { margin-bottom: 0; }

/* The hero already carries its own top padding; the split version needs a
   little less of it so the arch does not push the fold down. */
.hero-page--split { padding-block: clamp(2.5rem, 5vw, 4rem) clamp(3rem, 6vw, 5rem); }

/* ---- style: stack -------------------------------------------------------
   A second, smaller photo layered over the corner of the arch. Currently only
   on Hair Wash & Blow Dry, as a template test. The cream ring is what makes it
   read as two photographs rather than one collage, so it must match the page
   background, not the card background. */

.hero-stack { position: relative; }
.hero-stack .hero-shot { margin: 0; }
.hero-stack__second {
  position: absolute; left: -14%; bottom: -9%; width: 52%;
  margin: 0; aspect-ratio: 4 / 5; overflow: hidden;
  border-radius: 14px; border: 8px solid var(--cream);
  box-shadow: 0 14px 34px rgba(58, 47, 48, .16);
  background: var(--blush);
}
.hero-stack__second img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The overlap hangs below the arch, so the section needs room or it collides
   with whatever follows. */
.hero-page--split:has(.hero-stack) { padding-bottom: clamp(5rem, 9vw, 8rem); }

@media (max-width: 900px) {
  .hero-stack { max-width: 24rem; margin-inline: auto; }
  .hero-stack__second { left: auto; right: -8%; bottom: -12%; width: 46%; }
}
@media (max-width: 460px) {
  /* Below this the overlap eats the photo it is sitting on. */
  .hero-stack__second { display: none; }
}

/* ---------------------------------------------------------- lead form --- */
/* The only form on the site, on the academy page. Sits on the red band, so the
   fields are cream and the labels blush. */

.lead-form {
  margin-top: 2.25rem; display: grid; gap: 1.1rem 1.5rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 720px) { .lead-form { grid-template-columns: 1fr; } }
.lead-form__row--wide, .lead-form__foot, .lead-form__status { grid-column: 1 / -1; }

.lead-form__row { display: flex; flex-direction: column; gap: 0.4rem; }
.lead-form label {
  font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--blush);
}
.lead-form input, .lead-form textarea {
  font-family: var(--sans); font-size: 1rem; font-weight: 300; color: var(--ink);
  background: var(--cream); border: 1px solid var(--cream); border-radius: 12px;
  padding: 0.8rem 1rem; width: 100%;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.lead-form textarea { resize: vertical; min-height: 5.5rem; }
.lead-form input::placeholder, .lead-form textarea::placeholder { color: var(--ink-soft); opacity: .7; }
.lead-form input:focus-visible, .lead-form textarea:focus-visible {
  outline: none; border-color: var(--mocha); box-shadow: 0 0 0 3px rgba(178,150,132,.45);
}
.lead-form input[aria-invalid="true"] { border-color: var(--blush); box-shadow: 0 0 0 3px rgba(245,220,211,.5); }

.lead-form__foot { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.5rem; margin-top: 0.35rem; }
.lead-form__foot .btn { background: var(--cream); color: var(--red); border-color: var(--cream); }
.lead-form__foot .btn:hover { background: var(--blush); border-color: var(--blush); }
.lead-form__note { margin: 0; font-size: 0.82rem; color: rgba(238,229,220,.72); max-width: 40ch; }

.lead-form__status { margin: 0; font-size: 0.95rem; color: var(--cream); }
.lead-form__status:empty { display: none; }
.lead-form.is-sent .lead-form__row,
.lead-form.is-sent .lead-form__foot { display: none; }
.lead-form.is-sent .lead-form__status {
  font-family: var(--serif); font-size: clamp(1.15rem, 2.2vw, 1.5rem); line-height: 1.45;
  margin-top: 1rem;
}

.lead-alt { margin: 1.5rem 0 0; font-size: 0.9rem; color: rgba(238,229,220,.78); }
.lead-alt a { color: var(--cream); }

/* ------------------------------------------------------------ reviews --- */
/* Real Google reviews, verbatim. Never restyled into a pull quote that implies
   more than the guest wrote, and never trimmed to make it fit. */

.review { display: flex; flex-direction: column; }
.review__stars { color: var(--red-soft); font-size: 1rem; letter-spacing: 0.18em; line-height: 1; }
.review blockquote { margin: 1rem 0 0; }
.review blockquote p {
  margin: 0; font-family: var(--serif); font-size: 1.12rem; line-height: 1.5; color: var(--ink);
}
.review figcaption {
  margin-top: auto; padding-top: 1.35rem;
  display: flex; flex-direction: column; gap: 0.1rem;
}
.review__who { font-size: 0.9rem; color: var(--red); }
.review__meta { font-size: 0.76rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-soft); }

/* -------------------------------------------------------------- a11y ---- */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 50%; translate: -50% -120%; z-index: 100;
  background: var(--red); color: var(--cream); padding: 0.75rem 1.5rem; border-radius: 0 0 12px 12px;
  text-decoration: none; font-size: 0.85rem; transition: translate .2s var(--ease);
}
.skip-link:focus { translate: -50% 0; }

:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; border-radius: 4px; }

/* ------------------------------------------------------------- reveal ---- */

.reveal { opacity: 0; transform: translateY(18px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; } }

/* ---- consent banner, EU visitors only -------------------------------------
   Shown only when tools/build.mjs has emitted the analytics script AND the
   visitor's country came back as EU, EEA, UK or Switzerland. Everyone else
   never sees it and never gets the markup unhidden. It is a <div hidden> in the
   HTML rather than DOM built by JS, so it inherits the palette here rather than
   carrying inline styles, the same pattern the nav dropdowns use. */
.consent {
  position: fixed; inset: auto 0 0 0; z-index: 60;
  background: var(--cream-lift);
  border-top: 1px solid var(--line);
  padding: 1rem var(--gut) calc(1rem + env(safe-area-inset-bottom));
  box-shadow: 0 -2px 24px rgba(94, 68, 65, .1);
}
.consent[hidden] { display: none; }
.consent__inner {
  max-width: var(--wrap); margin-inline: auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.5rem;
  justify-content: space-between;
}
.consent__text { margin: 0; color: var(--ink); font-size: .9375rem; max-width: 62ch; }
.consent__text a { color: var(--red); }
.consent__actions { display: flex; gap: .625rem; flex: none; }
@media (max-width: 560px) {
  .consent__actions { width: 100%; }
  .consent__actions .btn { flex: 1; justify-content: center; }
}
