﻿/* Hair by Kristen — Salon 5 Sixteen
 *
 * Strictly black and white — no colour anywhere. The page ground is flat
 * black; "accent" means brighter-than-body-text, not a hue.
 *
 * Type is TWO faces, split by job — the Prada register (trial, 2026-08-03).
 *
 *   Bodoni Moda  wordmark, headings, prices. A didone: hard thick-to-thin
 *     contrast, the same family of letterform as Prada's custom wordmark.
 *     LARGE ONLY — its hairlines disappear at small sizes on black.
 *   Tenor Sans   descriptions, quotes, labels, buttons, nav. Everything that
 *     is actually read.
 *
 * That split is the whole reason the pairing works. Set Bodoni at body size
 * and it breaks up; the fix is always to move the rule to --body, never to
 * add weight.
 *
 * Caps and letter-spacing carry the wordmark. No cursive anywhere — Kristen
 * asked for it gone. Signatures are italic; italic is what says "signed" now
 * there's no handwriting.
 */

:root {
  /* ground */
  --black: #0a0a0a;
  --panel: #111111;
  --panel-2: #171717;

  /* type */
  --white: #ffffff;
  --text: rgba(255,255,255,.92);
  --text-soft: rgba(255,255,255,.64);
  --text-faint: rgba(255,255,255,.40);
  --line: rgba(255,255,255,.13);
  --line-soft: rgba(255,255,255,.07);

  /* accent — white. The palette is black and white only; "accent" now means
     brighter than body text, not a different hue. */
  --accent: #ffffff;
  --accent-dim: rgba(255,255,255,.42);

  /* ONE face: Fahkwang, a high-contrast sans — the free stand-in for the
     commercial Prestiggio (see site.config.js for why). Montserrat sits behind
     it in the stack as the fallback, so a font failure degrades to the
     previous look rather than to Times New Roman. */
  /* TWO faces, split by job — the Prada register.
       --brand / --display  Bodoni Moda. Wordmark, headings, prices. LARGE only.
       --body               Tenor Sans. Everything anyone actually reads.
     A didone's hairlines vanish at small sizes on black, so every rule that
     sets running text points at --body. If something in Bodoni looks fragile,
     it's too small — move it, don't add weight. */
  --brand: 'Bodoni Moda', Didot, 'Times New Roman', Georgia, serif;
  --display: var(--brand);
  --body: 'Tenor Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* Light is the page's voice. --w-ui exists because 300 stops being legible
     once type gets small and sits white-on-black — nav, buttons and captions
     use it so they don't turn to grey mush. Not a compromise on the look; the
     large type is where "light" actually reads. */
  /* Tenor Sans ships 400 only, so body weight is 400 — nothing to choose.
     --w-display is Bodoni's: 500 keeps the hairlines from disappearing at
     heading sizes without the letterforms going heavy. */
  --w-light: 400;
  --w-ui: 400;
  --w-display: 500;

  --wrap: 1120px;
  --gut: clamp(1.25rem, 5vw, 3rem);
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--black);
  position: relative;
  color: var(--text);
  font-family: var(--body);
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 { font-family: var(--display); font-weight: var(--w-display); letter-spacing: .06em; line-height: 1.15; margin: 0; color: var(--white); }
p { margin: 0 0 1rem; }
a { color: inherit; }
img { max-width: 100%; display: block; }

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); position: relative; z-index: 1; }
.center { text-align: center; }

/* Page ground is flat black. The leopard print — generated tile, then Jay's own
   background.jpg — was removed 2026-07-30 at his instruction, ahead of a full
   layout rework. Panels below are still translucent from when they sat over the
   print; over flat black they read as near-black, which is harmless. Flatten
   them to solid if the new layout wants real surface separation. */

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-block;
  font-family: var(--body);
  font-size: .74rem;
  font-weight: var(--w-ui);
  letter-spacing: .18em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 1rem 2.1rem;
  border: 1px solid transparent;
  border-radius: 2px;
  cursor: pointer;
  transition: background .25s ease, color .25s ease, border-color .25s ease;
}
.btn-primary { background: var(--accent); color: #0a0a0a; }
.btn-primary:hover { background: var(--white); color: #0a0a0a; }
.btn-ghost { border-color: var(--line); color: var(--white); }
.btn-ghost:hover { background: var(--white); color: #0a0a0a; border-color: var(--white); }
.btn-light { border-color: rgba(255,255,255,.45); color: var(--white); }
.btn-light:hover { background: var(--white); color: #0a0a0a; }
.btn-sm { padding: .7rem 1.4rem; font-size: .66rem; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ----------------------------------------------------------------- header */

.hdr {
  position: sticky; top: 0; z-index: 50;
  background: rgba(10,10,10,.88);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line-soft);
  /* Header gutter, deliberately much tighter than the page gutter (--gut, up
     to 3rem). One value drives both the burger's distance from the edge and
     the dropdown's left edge, so they can never drift apart. */
  --hgut: clamp(.75rem, 2.5vw, 1.5rem);
}
/* The header bar spans the full viewport instead of the 1120px content column.
   On a wide screen the centred column put the burger ~400px in from the edge,
   which read as floating in the middle of the photo. Content below is
   unaffected — this only widens the header. */
.hdr > .wrap { max-width: none; padding-inline: var(--hgut); }
/* Home only: the header floats over the full-bleed hero photo instead of
   sitting on a bar above it, per the reference. */
.hdr.is-over {
  position: absolute; top: 0; left: 0; right: 0;
  background: transparent;
  backdrop-filter: none;
  border-bottom: 0;
}
/* Three columns so the wordmark is centred in the VIEWPORT, not just centred
   in the leftover space — with a flex row it drifts off-centre as the burger
   and nav change width. */
/* The dropdown is anchored with top:100%, so it follows this height
   automatically if it ever changes again. */
.hdr-in { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; height: 88px; }
.logo {
  font-family: var(--brand);
  font-size: 2.15rem;
  line-height: 1.15;
  letter-spacing: .01em;
  text-decoration: none;
  white-space: nowrap;
  grid-column: 2;
  justify-self: center;
  color: var(--white);
  padding-top: .25rem;
  /* Containing block for the doubled echo on the home page. */
  position: relative;
}
.logo span { color: var(--accent); }
/* Sits the wordmark a touch below the burger's centre line rather than level
   with it — matches the reference, and stops the name reading as pinned to the
   very top of the photo. transform, not margin, so it's a purely visual nudge
   that can't change the header's height or shift the burger. Scoped to .hdr so
   the footer logo is untouched. */
.hdr .logo {
  transform: translateY(.2rem);
  /* Small and very widely tracked. A light geometric sans becomes a wordmark
     through spacing, not size — set large and tight it just looks like a
     heading. .3em is doing more work here than any font-size would. */
  font-size: clamp(1.3rem, 2.8vw, 1.75rem);
  font-weight: var(--w-display);
  letter-spacing: .22em;
  text-transform: uppercase;
  /* On the home page this sits directly on the photo with no bar behind it.
     The shadow is what makes it legible where the blurred surround goes pale —
     without it the name disappears against the light half of the image. */
  text-shadow: 0 2px 18px rgba(0,0,0,.65), 0 1px 3px rgba(0,0,0,.4);
}

.nav { display: flex; align-items: center; gap: 1.6rem; }
.nav a {
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text-soft);
  padding: .35rem 0;
  border-bottom: 1px solid transparent;
  transition: color .2s, border-color .2s;
}
.nav a:hover { color: var(--white); }
.nav a[aria-current='page'] { color: var(--white); border-bottom-color: var(--accent); }

/* The burger is now shown at EVERY width, not just mobile — the reference has
   no visible desktop nav, just the burger and the centred wordmark. The links
   are still in the HTML on every page, so search engines and screen readers
   see the whole site; only the visual chrome changed.
   To bring desktop links back: wrap the .nav drawer rules below in
   @media (max-width: 1000px) again and hide .menu-btn above it. */
.menu-btn {
  display: block; background: none; border: 0; padding: .5rem;
  font: inherit; color: var(--white); cursor: pointer;
  grid-column: 1; justify-self: start;
  /* Cancel the button's own padding on the left so the ICON — not the
     invisible tap target around it — sits at --hgut, flush with the left edge
     of the dropdown below. The padding stays for touch size. */
  margin-left: -.5rem;
}
.menu-btn svg { display: block; }

/* Drops directly under the burger and aligned to its left edge, rather than
   the full-width bar it was before. `top: 100%` rides the header's own height
   (.hdr-in is the positioned parent) so it stays attached if that ever
   changes; `left: var(--hgut)` is the same value that positions the burger. */
.nav {
  position: absolute;
  top: 100%;
  left: var(--hgut);
  right: auto;
  width: max-content;
  min-width: 268px;
  max-width: calc(100vw - var(--hgut) * 2);
  flex-direction: column; align-items: stretch; gap: 0;
  /* Transparent, with a blur doing the legibility work. Fully clear was not an
     option: on the home page this panel opens over the pale wall in her photo,
     and nav links run at 64% white — that lands around 2:1 against the wall.
     At .5 black plus an 18px blur the backdrop settles near #696969, which
     clears AA for the brightened link colour below. */
  background: rgba(10,10,10,.5);
  backdrop-filter: blur(18px) saturate(.85);
  -webkit-backdrop-filter: blur(18px) saturate(.85);
  border: 1px solid var(--line);
  padding: 1.1rem 1.9rem 1.5rem;
  display: none;
}
.nav.open { display: flex; }

/* Four categories, each with its pages listed under it.
   Flex column, specifically: a flex container discards whitespace-only text
   nodes, where a block container renders them as an empty line box. Services
   has no sub-links, and the newline between its <a> and </div> was drawing a
   blank row underneath it — the group read as broken rather than short. This
   fixes it structurally, so it can't come back with a markup edit. */
.nav-group {
  display: flex;
  flex-direction: column;
  padding: .85rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.nav-group:last-of-type { border-bottom: 0; }
/* Selector is `.nav a.nav-head`, not `.nav-head`. `.nav a` further up is more
   specific than a bare class, so it was silently winning: the headings were
   rendering at .72rem instead of 1.15rem and inheriting .9rem of vertical
   padding meant for drawer links. That padding was the mystery gap under
   Services. Specificity, not a spacing tweak. */
.nav a.nav-head {
  display: block;
  font-family: var(--body);
  font-size: 1.15rem;
  letter-spacing: .04em;
  text-transform: none;
  color: var(--white);
  padding: 0 0 .3rem;
  border-bottom: 0;
}
.nav-group a:not(.nav-head) {
  display: block;
  font-size: .68rem;
  letter-spacing: .15em;
  padding: .3rem 0;
  border-bottom: 0;
  color: var(--text-soft);
}
.nav-group a:not(.nav-head):hover { color: var(--white); }
.nav-group a[aria-current='page'] { color: var(--white); }

/* Same specificity trap as .nav-head: `.nav a` (0,2,0) beats `.btn-primary`
   (0,1,0), so the Book button inside the menu was drawing WHITE text on its
   white fill — invisible. It only looked passable on the home page because a
   text-shadow was faintly outlining letters that had no colour of their own. */
.nav a.btn-primary { color: #0a0a0a; }
.nav a.btn-primary:hover { color: #0a0a0a; }
/* Brighter than the .64 default, because the ground behind them is no longer
   solid black. Shadow catches any bright patch the blur doesn't flatten. */
.nav a { color: var(--text); text-shadow: 0 1px 10px rgba(0,0,0,.55); }
.nav a:hover { color: var(--white); }
.nav a { padding: .9rem 0; border-bottom: 1px solid var(--line-soft); }
.nav a[aria-current='page'] { border-bottom-color: var(--accent); }
.nav .btn { margin-top: 1rem; text-align: center; }

/* Gift card, under Book Now at the foot of the menu. Selector is
   `.nav a.nav-gift`, not `.nav-gift` — `.nav a` is (0,2,0) and beats a bare
   class, the same trap that made .nav-head render at the wrong size and the
   Book button draw white-on-white.

   Deliberately not a button. Two buttons stacked read as two equal choices,
   and booking is the one that matters; this is the quieter second option. */
.nav a.nav-gift {
  margin-top: .75rem;
  padding: .35rem 0 0;
  border-bottom: 0;
  text-align: center;
  /* .62rem/--text-faint measured 9.9px at 3.8:1 against the panel — under the
     4.5:1 needed for text this size, and small enough to read as a disclaimer
     rather than a link. These numbers measure 11.2px at 8.2:1 on both the
     solid inner-page panel and the blurred one on home, and still sit clearly
     below Book Now. */
  font-size: .7rem;
  letter-spacing: .18em;
  color: var(--text-soft);
}
.nav a.nav-gift:hover { color: var(--white); }

/* ------------------------------------------------------------------- hero
   Full-bleed portrait, one outlined BOOK NOW centred over it, nothing else —
   built to the lashblissxx.glossgenius.com reference Jay sent 2026-08-03.
   The h1 is visually hidden rather than deleted: the reference screen carries
   no heading, but the page still needs one for search and screen readers. */

.hero {
  position: relative;
  /* vh first as the fallback, then svh — on mobile 100vh is the viewport
     WITHOUT browser chrome, so the button lands under the address bar and the
     photo overflows. No min-height: the hero is exactly one screen on every
     device, including short landscape windows where a floor would force a
     scrollbar on a page that should never have one. */
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  display: grid;
  background: #0a0a0a;
}
/* Always `cover`, never `contain` — Kristen wants the photo edge to edge with
   no bars at any size, and is fine with her body being cropped to get it.
   `contain` was letterboxing on landscape screens; that's gone.

   The crop anchor lives in site.config.js (SITE.heroImage.focus) and is
   emitted inline on the element, because it belongs to whichever photo is
   currently in the hero — swap the image and the right anchor comes with it.
   The value below is only the fallback if none is set. */
.hero-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Whole photo, uncropped. A 3:4 image in a 16:9 window can only be cropped,
   letterboxed or stretched — so it's letterboxed, and .hero-blur fills the
   gap with the same image blown up and blurred. Reads as an extension of the
   photo rather than a black bar. */
.hero-img.is-contain { object-fit: contain; }

.hero-blur {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* Scaled up because blur samples past the element's edge and would otherwise
     leave a visibly soft, transparent margin around the frame. */
  transform: scale(1.18);
  filter: blur(42px) brightness(.42);
  pointer-events: none;
  z-index: 0;
}
.hero-img { z-index: 1; }
.hero-veil { z-index: 1; }
/* Her wall is near-white, so a white button over it would fail contrast in
   places. Just enough veil to guarantee the type reads, not enough to grey
   the photo out. Heavier at top and bottom for the header and the edges. */
.hero-veil {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(10,10,10,.42) 0%, rgba(10,10,10,0) 22%,
                    rgba(10,10,10,0) 68%, rgba(10,10,10,.38) 100%),
    rgba(10,10,10,.16);
  pointer-events: none;
}
.hero-mid { position: relative; z-index: 2; display: grid; place-items: center; }
.hero-book {
  font-family: var(--body);
  font-size: .95rem;
  letter-spacing: .32em;
  text-transform: uppercase;
  text-decoration: none;
  color: #fff;
  background: transparent;
  border: 1px solid rgba(255,255,255,.92);
  /* Left padding carries an extra .3rem. Letter-spacing appends a gap after
     the final letter, so equal padding leaves the label sitting visibly left
     of centre — the extra matches the tracking and optically centres it. */
  padding: 1.35rem 3.4rem 1.35rem 3.7rem;
  transition: background .25s ease, color .25s ease;
  text-shadow: 0 1px 12px rgba(0,0,0,.45);
}
.hero-book:hover,
.hero-book:focus-visible { background: #fff; color: #0a0a0a; text-shadow: none; }

@media (max-width: 560px) {
  .hero-book { font-size: .82rem; padding: 1.15rem 2.6rem 1.15rem 2.86rem; }
}

.hero-cta { display: flex; gap: .75rem; justify-content: center; flex-wrap: wrap; margin-top: 2.25rem; }

/* page banner */
.banner {
  position: relative; overflow: hidden;
  background: transparent;
  padding: clamp(3rem, 8vw, 5rem) 0;
}
.banner h1 { font-size: clamp(2.4rem, 6vw, 3.75rem); margin-bottom: .5rem; }
.banner .blurb { font-family: var(--body); color: var(--accent); letter-spacing: .2em; text-transform: uppercase; font-size: .7rem; margin-bottom: 1.25rem; }
.banner .intro { max-width: 58ch; color: var(--text-soft); margin: 0; }

/* --------------------------------------------------------------- sections */

.sec { position: relative; overflow: hidden; padding: clamp(3.5rem, 9vw, 6rem) 0; }
/* A banner already carries its own generous bottom padding, so a full section
   pad straight after it stacked two gaps into one dead band — most visible on
   the reviews page, where the heading floated far above the cards. */
.banner + .sec { padding-top: clamp(.5rem, 1.5vw, 1.25rem); }
.sec-alt { background: transparent; }
.sec-ink { background: transparent; }

.eyebrow { font-size: .68rem; letter-spacing: .24em; text-transform: uppercase; color: var(--accent); margin-bottom: .9rem; }
.sec h2 { font-size: clamp(2rem, 5vw, 3rem); margin-bottom: 1rem; }
.lede { max-width: 58ch; color: var(--text-soft); font-size: 1.02rem; }
.center .lede { margin-inline: auto; }

/* A short line sitting directly under an h2, above the .lede that explains it.
   Brand serif and near-white so it reads as a phrase of hers rather than a
   second subheading arguing with the h2 above it; the .lede below stays in the
   body face and grey, so the three lines descend in weight instead of
   competing. Pulled up tight under the heading — the h2's own 1rem bottom
   margin left it floating between the two. */
.h2-sub {
  font-family: var(--brand);
  font-size: clamp(1.05rem, 2.4vw, 1.4rem);
  letter-spacing: .06em;
  color: rgba(255,255,255,.88);
  margin: -.35rem 0 .85rem;
}
/* When nothing follows it but the buttons. The .85rem above was sized to sit
   against a paragraph, and the button row needs more air than that or the line
   reads as a label attached to the buttons rather than to the heading. */
.h2-sub.is-last { margin-bottom: 0; }

/* SERVICE MENU — all caps in Cormorant. Kept in the text serif rather than the
   brand face so it reads as structure, not as another brand moment. (It was
   originally here because cursive set in capitals loses its joins entirely —
   no longer the reason, but still the right call.) */
.h-caps {
  font-family: var(--display);
  text-transform: uppercase;
  letter-spacing: .16em;
  font-size: clamp(1.5rem, 3.6vw, 2.4rem) !important;
  display: inline-flex; align-items: center; gap: .7rem;
}
.ico-martini { width: .85em; height: .85em; color: var(--accent); flex: none; }

/* Brand-face headings — the display serif, tracked out. Letter-spacing does
   for a display serif what the flourishes did for the script: it's what
   separates a brand moment from an ordinary heading. */
.h-brand { font-family: var(--brand); font-weight: var(--w-display); letter-spacing: .14em; }

/* ------------------------------------------------------------ service list */

.menu-hint { margin: 0; font-size: .7rem; letter-spacing: .16em; text-transform: uppercase; color: var(--text-faint); }
.menu {
  margin-top: 1.25rem;
  border-top: 1px solid var(--line);
  background: rgba(8,8,8,.78);
  backdrop-filter: blur(3px);
  border-radius: 4px;
  padding-inline: 1.25rem;
  margin-inline: -1.25rem;
}

.item {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .25rem 2rem;
  padding: 1.75rem 1.25rem;
  margin-inline: -1.25rem;
  border-bottom: 1px solid var(--line-soft);
  align-items: baseline;
  border-radius: 3px;
  transition: background .2s ease;
}
.item:hover { background: rgba(255,255,255,.035); }
.item h3 { font-size: 1.5rem; font-weight: 400; transition: color .2s ease; }
.item:hover h3 { color: var(--accent); }
.item .price { font-family: var(--display); font-size: 1.4rem; white-space: nowrap; color: var(--white); }
.item .price.soft { font-family: var(--body); font-size: .76rem; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); }
.item .meta { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; }
.item .dur { font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--text-faint); }
.item .desc { grid-column: 1 / -1; color: var(--text-soft); max-width: 62ch; margin: .55rem 0 0; font-size: .94rem; }

.item-book {
  grid-column: 1 / -1; justify-self: start; margin-top: 1rem;
  font-size: .68rem; letter-spacing: .18em; text-transform: uppercase;
  text-decoration: none; color: var(--white);
  border-bottom: 1px solid var(--accent-dim);
  padding-bottom: .2rem;
  transition: color .2s ease, border-color .2s ease;
}
.item-book::after { content: '→'; margin-left: .5rem; display: inline-block; transition: transform .2s ease; }
.item-book::before { content: ''; position: absolute; inset: 0; }
.item:hover .item-book { color: var(--accent); border-color: var(--accent); }
.item:hover .item-book::after { transform: translateX(4px); }

.tag {
  font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
  border: 1px solid var(--line); color: var(--text-soft);
  padding: .28rem .6rem; border-radius: 2px; white-space: nowrap;
}
.tag-free { border-color: var(--accent); color: var(--accent); }

.vh { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* minmax(0, 1fr), not 1fr. A `1fr` track has an automatic minimum of
   min-content, so one unshrinkable child — here a nowrap .tag reading
   "AVAILABLE ONLY WITH A COLOR SERVICE", 289px of uppercase and letter-spacing
   — widened the whole track past the row's padding and dragged every
   full-width child out under the photo with it. The 0 minimum caps the track
   at the space actually available, whatever Kristen types into Square later. */
@media (max-width: 560px) {
  .item { grid-template-columns: minmax(0, 1fr); }
  .item .price { grid-column: 1; }
  /* nowrap keeps a short pill like "20 min" from breaking across lines, which
     is right on a desktop where there is room. On a phone next to a thumbnail
     there isn't, and a pill that overflows is worse than one that wraps. */
  .item .tag { white-space: normal; }
}

/* ------------------------------------------------------------------ cards */

.grid { display: grid; gap: 1.25rem; margin-top: 2.75rem; }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); }

.card {
  display: flex; flex-direction: column;
  background: rgba(8,8,8,.82);
  backdrop-filter: blur(3px);
  border: 1px solid var(--line-soft);
  border-radius: 3px;
  padding: 2rem 1.75rem;
  text-decoration: none;
  transition: transform .3s ease, border-color .3s ease, background .3s ease;
}
.card:hover { transform: translateY(-4px); border-color: var(--accent-dim); background: rgba(26,26,26,.9); }
.card .n { font-family: var(--body); font-size: .9rem; color: var(--accent); margin-bottom: .9rem; letter-spacing: .1em; }
.card h3 { font-size: 1.75rem; margin-bottom: .6rem; }
.card p { color: var(--text-soft); font-size: .92rem; margin-bottom: 1.25rem; }
.card .go { margin-top: auto; font-size: .68rem; letter-spacing: .18em; text-transform: uppercase; color: var(--white); display: inline-flex; align-items: center; gap: .5rem; }
.card:hover .go { color: var(--accent); }
.card .go::after { content: '→'; transition: transform .25s ease; }
.card:hover .go::after { transform: translateX(4px); }
.card .count { font-size: .7rem; color: var(--text-faint); letter-spacing: .1em; }

.card-feature { background: linear-gradient(160deg, #1a1a1a, var(--panel)); border-color: var(--accent-dim); }

/* --------------------------------------------------------------- split/img */

.split { display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center; grid-template-columns: 1fr; }
@media (min-width: 820px) { .split { grid-template-columns: 1fr 1fr; } }

.ph { border-radius: 3px; }
img.ph { width: 100%; height: auto; max-height: 620px; object-fit: cover; object-position: 50% 18%; }
div.ph { aspect-ratio: 4 / 5; background: linear-gradient(150deg, #212121, #0a0a0a); border: 1px solid var(--line); position: relative; }
.ph-wide { aspect-ratio: 3 / 2; }
div.ph::after {
  content: attr(data-label); position: absolute; inset: auto 0 0; padding: .8rem;
  font-size: .64rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-faint); text-align: center;
}

/* --------------------------------------------------------------- about me */

.about { position: relative; overflow: hidden; padding: clamp(2.5rem, 6vw, 4.5rem) 0 clamp(3.5rem, 9vw, 6rem); }

/* The page's brand moment. Sized well down from the script version — a display
   serif at 9.5rem is enormous where a calligraphic face at the same size read
   as decorative. The swash allowance (padding-bottom) is gone with it. */
.about-hl {
  font-family: var(--brand);
  font-weight: var(--w-light);
  font-size: clamp(2.2rem, 6.4vw, 4.2rem);
  letter-spacing: .13em;
  line-height: 1.18;
  margin: 0 0 .35rem;
  color: var(--white);
}
.about-label { font-family: var(--body); font-size: .72rem; letter-spacing: .24em; text-transform: uppercase; color: var(--accent); margin-bottom: 2.5rem; }

.about-grid { display: grid; gap: clamp(1rem, 2.5vw, 1.75rem); grid-template-columns: 1fr; }
@media (min-width: 780px) { .about-grid { grid-template-columns: minmax(0, .32fr) minmax(0, 1fr); align-items: start; } }

.about-accents { display: grid; gap: clamp(1rem, 2.5vw, 1.75rem); }
@media (max-width: 779px) { .about-accents { grid-template-columns: repeat(3, 1fr); } }

/* ⚠️ The image is set with a plain inline `background-image`, NOT through a
   --img custom property. This is the same bug that silently 404'd the page
   background in July: a relative url() inside a custom property is resolved
   against the stylesheet that CONSUMES it, not the one that declares it — so
   `assets/img/about/x.jpg` became `assets/assets/img/about/x.jpg` and every
   box rendered empty with no error anywhere.
   Rule, twice learned: never put url() in a custom property read from another
   file. Set background-image directly. */
.accent {
  aspect-ratio: 1 / 1;
  background-color: #131313;
  background-image: linear-gradient(150deg, #1f1f1f, #0d0d0d);
  background-size: cover; background-position: center; background-repeat: no-repeat;
  filter: grayscale(1) contrast(1.05);
  border: 1px solid var(--line);
  /* Softened from 2px to relate to the bubbles elsewhere — but nowhere near
     the 1.75rem the big panels use. On a small square, that much curve reads
     as a sticker rather than a photograph. */
  border-radius: .85rem;
  position: relative;
  overflow: hidden;
}
/* The label only shows on a slot with no photo. Keyed off background-image
   now that --img is gone. */
.accent:not([style*='background-image'])::after {
  content: attr(data-label); position: absolute; inset: 0;
  display: grid; place-items: center; padding: .75rem; text-align: center;
  font-size: .56rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-faint);
}

.portrait { border-radius: 2px; }
img.portrait { width: 100%; height: auto; }
div.portrait { aspect-ratio: 4 / 5; background: linear-gradient(150deg, #212121, #0a0a0a); border: 1px solid var(--line); position: relative; }
div.portrait::after {
  content: attr(data-label); position: absolute; inset: auto 0 0; padding: 1rem;
  font-size: .64rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-faint); text-align: center;
}

.about-bio { margin-top: clamp(1.5rem, 3vw, 2.25rem); max-width: 62ch; }
.about-bio p { font-family: var(--body); font-size: clamp(1.15rem, 1.9vw, 1.4rem); line-height: 1.55; color: var(--text); margin-bottom: 1.1rem; }
/* Signatures stay ITALIC SERIF rather than the display face. A sign-off set
   in the same font as the headings just reads as another heading; italic is
   what still says "signed" now that the handwriting is gone. */
.about-sig { font-family: var(--display); font-style: italic; font-weight: var(--w-light); letter-spacing: .06em; font-size: 1.6rem; line-height: 1.3; color: var(--accent); margin-top: 1.75rem; }

/* ---------------------------------------------------------------- reviews */

.sec-reviews { background: transparent; }
.reviews-hl { font-family: var(--brand); font-weight: 400; letter-spacing: .13em; font-weight: var(--w-light); font-size: clamp(1.9rem, 4.8vw, 3rem); line-height: 1.2; padding-bottom: 0; margin-bottom: .35rem; color: var(--white); }

.stars { display: flex; gap: .4rem; }
.center > .stars, .center .stars { justify-content: center; }
.star { width: 15px; height: 15px; fill: var(--accent); flex: none; }
.review .stars { justify-content: flex-start; }

.rec {
  align-self: flex-start;
  font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--accent-dim);
  border-radius: 2px; padding: .3rem .6rem;
}

.reviews { margin-top: 3rem; text-align: left; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); }
@media (min-width: 900px) { .reviews { grid-template-columns: repeat(var(--cols, 3), 1fr); } }

/* Same rounded, translucent shell as the contact cards and the service
   bubbles — one surface treatment across the site rather than three. */
/* Outline bubbles, same treatment as the service categories — transparent, the
   border carries the shape. */
.review {
  margin: 0;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 1.75rem;
  padding: 2.15rem 2rem;
  display: flex; flex-direction: column; gap: 1rem;
  transition: border-color .25s ease;
}
.review:hover { border-color: var(--accent-dim); }
.review blockquote { margin: 0; font-family: var(--body); font-style: italic; font-weight: 300; font-size: 1.25rem; line-height: 1.5; color: var(--white); }
.review figcaption { margin-top: auto; display: grid; gap: .2rem; }
.review .who { font-size: .74rem; letter-spacing: .14em; text-transform: uppercase; }
.review .what { font-size: .8rem; color: var(--accent); }
.review .src { font-size: .68rem; color: var(--text-faint); letter-spacing: .08em; }

.reviews-page { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); margin-top: 0; }
@media (min-width: 900px) { .reviews-page { grid-template-columns: 1fr 1fr; } }
.review-lg { padding: 2.75rem 2.5rem; gap: 1.25rem; border-radius: 2rem; }
.review-lg blockquote { font-size: clamp(1.3rem, 2.1vw, 1.6rem); }

.review.is-empty { border-style: dashed; border-color: var(--accent-dim); background: rgba(255,255,255,.04); }
.review.is-empty blockquote { color: var(--text-faint); }
.review.is-empty .star { fill: var(--accent); opacity: .4; }
.review.is-empty .who { color: var(--accent); }

/* -------------------------------------------------------------- statement */

.statement { position: relative; overflow: hidden; padding: clamp(3.5rem, 9vw, 6rem) 0; }
/* Deliberately quiet. The lines still solve to the same width — that's the
   idea worth keeping — but at 44% of the container, softened off pure white,
   and with the crushed .86 leading opened up. Tight leading on huge caps is
   what made it read as a poster rather than part of the page.
   letter-spacing is NOT set here: it's emitted inline by statementSection(),
   because the width solver has to know the exact value to keep the lines
   flush. Setting it in both places is how they drift. */
.statement-type {
  display: flex; flex-direction: column; align-items: center;
  line-height: 1.04; text-transform: uppercase;
  /* Nothing follows the type any more — the sub-line and the button were both
     removed. That 1.75rem was the gap down to the sub; left in, it stacked on
     top of the section's own padding and pushed the block off centre in its
     own space. The section padding does the spacing now. */
  margin-bottom: 0; color: var(--text);
  font-family: var(--display); font-weight: var(--w-light);
}
.statement-line { display: block; white-space: nowrap; }

/* ------------------------------------------------------------------- band */

.band { position: relative; overflow: hidden; background: var(--accent); color: #0a0a0a; padding: clamp(3rem, 7vw, 4.5rem) 0; }
.band h2 { font-family: var(--brand); font-weight: 400; letter-spacing: .13em; font-weight: var(--w-light); font-size: clamp(1.6rem, 3.6vw, 2.4rem); line-height: 1.25; padding-bottom: 0; margin-bottom: .35rem; color: #0a0a0a; }
.band p { max-width: 52ch; margin-inline: auto; color: rgba(0,0,0,.7); }
.band .btn-primary { background: #0a0a0a; color: var(--accent); }
.band .btn-primary:hover { background: #fff; color: #0a0a0a; }
.band .btn-ghost { border-color: rgba(0,0,0,.45); color: #0a0a0a; }
.band .btn-ghost:hover { background: #0a0a0a; color: var(--accent); border-color: #0a0a0a; }

/* ----------------------------------------------------------------- footer */

.ftr { position: relative; z-index: 1; background: rgba(8,8,8,.86); backdrop-filter: blur(3px); border-top: 1px solid var(--line); color: var(--text-soft); padding: clamp(3rem, 7vw, 4.5rem) 0 2rem; font-size: .9rem; }
.ftr-grid { display: grid; gap: 2.5rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.ftr h4 { font-family: var(--body); font-size: .66rem; letter-spacing: .22em; text-transform: uppercase; color: var(--accent); margin: 0 0 1rem; font-weight: 400; }
.ftr .logo { color: var(--white); font-size: 1.05rem; font-weight: var(--w-light); letter-spacing: .26em; line-height: 1.35; display: block; margin-bottom: .75rem; }
.ftr a { color: var(--text-soft); text-decoration: none; }
.ftr a:hover { color: var(--accent); }
.ftr ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.ftr .todo { color: var(--accent); opacity: .8; font-style: italic; }
.ftr-btm {
  margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--line-soft);
  display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; justify-content: space-between;
  font-size: .74rem; color: var(--text-faint); letter-spacing: .04em;
}

/* ----------------------------------------------------------------- policy */
/* Terms copy: readability over decoration. The three figures sit above the
   prose because almost nobody reads three paragraphs of policy, but everyone
   should leave knowing what 24 hours, 50% and 100% mean. */

.pol-figs {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  margin-bottom: 3rem;
}
/* Outline bubbles, matching the service categories, review cards and contact
   cards. One surface treatment across the whole site. */
.pol-fig {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 1.75rem;
  padding: 2rem 1.5rem;
  text-align: center;
  transition: border-color .25s ease;
}
.pol-fig:hover { border-color: var(--accent-dim); }
.pol-fig .fig {
  font-family: var(--display);
  font-size: clamp(2rem, 5vw, 2.9rem);
  line-height: 1;
  color: var(--accent);
  margin-bottom: .6rem;
}
.pol-fig .lab {
  font-size: .7rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-soft);
}

.pol-body {
  max-width: 68ch;
  background: transparent;
  border: 1px solid var(--line);
  /* Larger radius than the figure cards above — a tall block of text needs
     more curve to read as the same shape at a different size. */
  border-radius: 2rem;
  padding: clamp(2rem, 4vw, 2.75rem);
}
.pol-body p {
  font-family: var(--body);
  font-size: clamp(1.05rem, 1.8vw, 1.28rem);
  line-height: 1.62;
  color: var(--text);
}
.pol-body p:last-child { margin-bottom: 0; }

.pol-sign { font-family: var(--display); font-style: italic; font-weight: var(--w-light); letter-spacing: .06em; font-size: 1.5rem; line-height: 1.3; color: var(--accent); margin-top: 1.75rem; }

/* Quiet line under the booking buttons — present on every page. */
.band-note { margin: 1.5rem 0 0; font-size: .78rem; color: rgba(0,0,0,.62); }
.band-note a { color: #0a0a0a; text-underline-offset: 3px; }

/* ------------------------------------------------- contact page (bubbly)
   Soft rounded shapes, deliberately scoped to .contact-page so the hard
   editorial edges everywhere else on the site stay intact. */

.contact-page .lede { max-width: 52ch; margin-inline: auto; }

.contact-grid {
  display: grid;
  grid-template-columns: 1.35fr .85fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
  margin-top: clamp(2.5rem, 6vw, 4rem);
}
@media (max-width: 860px) { .contact-grid { grid-template-columns: 1fr; } }

.cform { display: flex; flex-direction: column; gap: 1.15rem; }
.field { display: flex; flex-direction: column; gap: .5rem; }
.field label {
  font-family: var(--body);
  font-size: .68rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--text-soft);
  padding-left: 1.4rem;
}

.cform input,
.cform textarea {
  font-family: var(--body);
  font-size: 1rem;
  color: var(--white);
  background: rgba(255,255,255,.05);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1.05rem 1.5rem;
  width: 100%;
  transition: border-color .2s ease, background .2s ease;
}
/* A pill is wrong for multi-line text — it clips the corners of the first and
   last rows. Big soft radius instead, same family of shape. */
.cform textarea { border-radius: 1.75rem; resize: vertical; min-height: 9rem; line-height: 1.55; }

.cform input::placeholder,
.cform textarea::placeholder { color: var(--text-faint); }
.cform input:focus,
.cform textarea:focus {
  outline: none;
  border-color: var(--white);
  background: rgba(255,255,255,.09);
}

.cbtn {
  align-self: flex-start;
  font-family: var(--body);
  font-size: .74rem; letter-spacing: .3em; text-transform: uppercase;
  color: #0a0a0a; background: var(--white);
  border: 1px solid var(--white); border-radius: 999px;
  padding: 1.05rem 2.9rem 1.05rem 3.2rem;
  cursor: pointer;
  transition: background .25s ease, color .25s ease;
}
.cbtn:hover { background: transparent; color: var(--white); }

.cform-note { margin: 0; font-size: .85rem; color: var(--text-soft); min-height: 1.2em; }
.cform-note.is-warn { color: var(--white); }

.cdetails { display: flex; flex-direction: column; gap: 1rem; }
.cdetail {
  background: rgba(255,255,255,.05);
  border: 1px solid var(--line);
  border-radius: 1.75rem;
  padding: 1.5rem 1.75rem;
}
.cdetail h3 {
  font-family: var(--body); font-weight: 400;
  font-size: .66rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--text-soft); margin: 0 0 .6rem;
}
.cdetail p { margin: 0; font-family: var(--body); font-size: 1.15rem; line-height: 1.5; color: var(--white); }
.cdetail a { text-decoration: none; }
.cdetail a:hover { text-decoration: underline; }

/* ------------------------------------------------- per-service photo
   Optional thumbnail beside each service, matching the layout of her Square
   page. Placed explicitly at grid-row 1/5 because the row structure of .item
   is fixed and known — title+price, meta, description, book link — so the
   image spans the full row rather than relying on auto-placement. */
/* The thumbnail is POSITIONED, not placed in the grid.
   As a grid item spanning four rows it fed its own height back into those
   rows: at 104px that was invisible, at 150px it forced a dead gap under the
   service name. Absolute inside the row reserves nothing and stretches
   nothing, so the text sets its own rhythm and the image just sits beside it.
   The row keeps padding-right so nothing can run underneath. */
.item.has-thumb {
  --thumb-w: 150px;
  padding-right: calc(var(--thumb-w) + 2.25rem);
}
.item-thumb {
  position: absolute;
  top: 1.75rem;
  right: 1.25rem;
  width: var(--thumb-w);
  height: var(--thumb-w);
  object-fit: cover;
  border-radius: .9rem;
  border: 1px solid var(--line);
  background: #111;
}
/* Mobile: smaller thumbnail, same idea — photo top-right, text beside it.
   NO second grid column. The thumbnail is position:absolute, so it is out of
   flow and a reserved track would sit permanently empty — except that .meta,
   .desc and .item-book are all `grid-column: 1 / -1`, so they expanded into
   that empty track and ran straight underneath the photo. The row's
   padding-right couldn't stop them: padding bounds the content box, but a
   grid track whose min-content is wider than the space left simply overflows
   it, and the spanning children go with it.
   One column means `1 / -1` is that column, which respects padding-right. */
@media (max-width: 560px) {
  .item.has-thumb {
    --thumb-w: 104px;
    /* Tighter than the desktop 2.25rem: on a 390px screen every pixel the
       gutter takes is a pixel off the description. Leaves a 16px channel
       between the longest line and the photo. */
    padding-right: calc(var(--thumb-w) + 1.75rem);
  }
  .item-thumb { right: .75rem; }
  /* The accordion pairing lives further down, next to the desktop rule it has
     to override — same specificity there, and later wins. */
}

/* "and up" rides with the price but shouldn't compete with it — a qualifier,
   not part of the number. */
.item .price .from {
  font-family: var(--body);
  font-size: .62rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-faint);
  white-space: nowrap;
}

/* ------------------------------------------- services page: category bubbles
   Native <details> accordions in the rounded style of the contact page. No
   JavaScript: they open and close on their own, they're keyboard operable, and
   the collapsed services stay in the HTML so search engines still read all 26. */

.acc-list { display: flex; flex-direction: column; gap: 1rem; margin-top: clamp(2rem, 5vw, 3rem); }

/* Transparent — the outline alone carries the bubble shape. The fill is gone
   so the page ground reads straight through; the border brightens on open so
   there's still a clear signal about which category you're in. */
.acc {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 1.75rem;
  overflow: hidden;
  transition: border-color .25s ease;
}
.acc[open] { border-color: var(--accent-dim); }

.acc-head {
  display: flex; align-items: center; gap: 1rem;
  padding: 1.45rem 1.9rem;
  cursor: pointer;
  list-style: none;                 /* kills the default disclosure triangle */
  user-select: none;
}
.acc-head::-webkit-details-marker { display: none; }
.acc-head:hover .acc-title { color: var(--accent); }

.acc-title {
  font-family: var(--display);
  font-size: clamp(1.25rem, 2.6vw, 1.65rem);
  font-weight: var(--w-light);
  letter-spacing: .06em;
  color: var(--white);
  transition: color .2s ease;
}
.acc-count {
  margin-left: auto;
  font-size: .64rem; font-weight: var(--w-ui);
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--text-faint);
  white-space: nowrap;
}

/* Drawn rather than a character, so it can rotate cleanly and never depends on
   a glyph being present in the font. */
.acc-mark { position: relative; width: 13px; height: 13px; flex: none; }
.acc-mark::before,
.acc-mark::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  background: var(--white);
  transition: transform .28s ease, opacity .28s ease;
}
.acc-mark::before { width: 13px; height: 1px; }
.acc-mark::after  { width: 1px; height: 13px; }
.acc[open] .acc-mark::after { transform: rotate(90deg); opacity: 0; }

.acc-body { padding: 0 1.9rem 1.6rem; }
.acc-blurb {
  margin: 0 0 .5rem;
  font-size: .84rem; letter-spacing: .04em;
  color: var(--text-soft);
}
/* The shared .menu block carries its own panel styling for the category pages;
   inside a bubble that would be a panel within a panel. */
.acc-body .menu {
  background: transparent;
  backdrop-filter: none;
  border-radius: 0;
  margin-inline: 0;
  padding-inline: 0;
}
.acc-body .item { margin-inline: 0; padding-inline: 0; }
/* ...but a row WITH a thumbnail still needs its right gutter. The image is
   absolutely positioned, so that padding is the only thing keeping the price
   from running underneath it — zeroing it put "$135" behind the photo. */
.acc-body .item.has-thumb { padding-right: calc(var(--thumb-w) + 1.5rem); }
.acc-body .item-thumb { right: 0; }
/* Must sit AFTER the rule above, not up in the mobile block with its sibling:
   both are (0,3,0), a media query adds no specificity, and later wins. */
@media (max-width: 560px) {
  .acc-body .item.has-thumb { padding-right: calc(var(--thumb-w) + 1rem); }
}
.acc-body .item:last-of-type { border-bottom: 0; }

.acc-more {
  display: inline-block; margin-top: 1.1rem;
  font-size: .66rem; font-weight: var(--w-ui);
  letter-spacing: .18em; text-transform: uppercase;
  text-decoration: none; color: var(--text-soft);
  border-bottom: 1px solid var(--accent-dim);
  padding-bottom: .25rem;
  transition: color .2s ease, border-color .2s ease;
}
.acc-more:hover { color: var(--white); border-color: var(--white); }

@media (max-width: 560px) {
  .acc-head { padding: 1.2rem 1.35rem; gap: .7rem; }
  .acc-body { padding: 0 1.35rem 1.3rem; }
  .acc-count { font-size: .58rem; letter-spacing: .14em; }
}

/* ------------------------------------------------------ booking widget page
   Square renders the widget itself and its interior can't be restyled, so this
   only frames it: rounded shell to match the rest of the site, and a minimum
   height so the page doesn't collapse then jump when the script loads. */
/* Square's widget builds its own iframe here and sets it to height:100%, so
   the height has to come from this container. Too short and the booking flow
   scrolls inside a box while the page also scrolls — two scrollbars, which is
   miserable on a phone. Generous by design: the service list is long. */
.book-embed {
  margin: clamp(2rem, 5vw, 3rem) auto 0;
  max-width: 860px;
  height: clamp(760px, 128vh, 1500px);
  background: #fff;               /* Square's flow is light; avoids a flash */
  border: 1px solid var(--line);
  border-radius: 1.75rem;
  padding: 0;
  overflow: hidden;
}
.book-embed iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
@media (max-width: 560px) {
  .book-embed { height: clamp(720px, 140vh, 1400px); border-radius: 1.25rem; }
}

.book-fallback, .book-alt, .book-policy {
  text-align: center; margin: 1.25rem auto 0; max-width: 60ch;
  font-size: .82rem; color: var(--text-soft);
}
.book-policy { font-size: .74rem; color: var(--text-faint); margin-top: .5rem; }

/* Framed booking flow. Tall by design: Square's flow is a long scrolling list
   of services and time slots, and a short frame turns it into a scroll box
   inside a scroll box, which is miserable on a phone. */
.book-embed.is-frame { padding: 0; min-height: 0; }
.book-embed.is-frame iframe {
  width: 100%;
  height: min(1400px, 165vh);
  border: 0;
  border-radius: 1.75rem;
  display: block;
  background: #fff;   /* Square's flow is light; stops a black flash on load */
}
@media (max-width: 560px) {
  .book-embed.is-frame iframe { height: min(1500px, 180vh); border-radius: 1.25rem; }
}

/* Decorative five-star row under the "Client Reviews" headings. Gold is the
   only colour on an otherwise strictly black-and-white site — deliberate, so
   it reads as a review mark rather than a brand colour. aria-hidden in the
   markup: it is not a rating anything should announce or index. */
/* Heading and stars on ONE line — the stars belong to the words, not floating
   underneath them. Only wraps when there genuinely isn't room. */
.rev-title {
  display: flex;
  align-items: center;
  gap: clamp(.9rem, 2vw, 1.5rem);
  flex-wrap: wrap;
}
.rev-title.is-center { justify-content: center; }
.rev-title h1, .rev-title h2 { margin: 0; }

.stars-gold { gap: .34rem; margin: 0; align-items: center; }
.stars-gold .star {
  fill: #d9b25e;
  width: clamp(22px, 2.4vw, 30px);
  height: clamp(22px, 2.4vw, 30px);
}

/* Add-on-only services get a plain note where the Book link would be. A button
   that leads nowhere bookable is worse than no button. */
.item-note {
  grid-column: 1 / -1; justify-self: start; margin-top: 1rem;
  font-size: .66rem; letter-spacing: .18em; text-transform: uppercase;
  font-weight: var(--w-ui);
  color: var(--text-faint);
  border-bottom: 1px dashed var(--line);
  padding-bottom: .25rem;
}

/* ------------------------------------------------------------- instagram
   Drawn glyph, inherits currentColor so it matches whatever text it sits with. */
.ico-ig { width: 1em; height: 1em; flex: none; display: block; }

/* Top-right of the dropdown panel. Absolute rather than in the flow so it
   can't push the category groups around or change the panel's width. */
.nav-ig {
  position: absolute;
  top: 1rem;
  right: 1.4rem;
  display: grid; place-items: center;
  width: 34px; height: 34px;
  color: var(--text-soft);
  border-bottom: 0 !important;
  padding: 0 !important;
  transition: color .2s ease, transform .2s ease;
}
.nav-ig .ico-ig { width: 21px; height: 21px; }
.nav-ig:hover { color: var(--white); transform: translateY(-1px); }

/* Footer + contact card: glyph and handle on one line, aligned on the text
   baseline rather than the box, so the icon doesn't ride high. */
.ftr-ig, .cdetail-ig {
  display: inline-flex; align-items: center; gap: .5rem;
  text-decoration: none;
}
.ftr-ig .ico-ig { width: 15px; height: 15px; }
.cdetail-ig .ico-ig { width: 19px; height: 19px; }
.ftr-ig:hover span, .cdetail-ig:hover span { text-decoration: underline; }

/* ------------------------------------------------ doubled wordmark (every page)
   The reference Jay sent: each letter repeated and offset so the two copies
   overlap. Built as a pseudo-element carrying the same string from
   data-text, offset a fraction of an em and drawn as an OUTLINE — solid over
   solid would just look blurred, where outline-over-solid reads as deliberate.

   This was home-only at first, on the reasoning that a doubled mark in a
   compact 15px header would read as a rendering fault rather than a choice.
   The answer to that was to stop making it 15px: the inner pages now carry the
   same size and tracking as home, so the mark is identical everywhere and the
   site reads as one brand rather than a landing page plus a set of documents.

   Offsets are in em, not px, so the echo scales with the type instead of
   drifting as the clamp resizes it. */
.hdr .logo {
  font-size: clamp(1.5rem, 5.5vw, 3.6rem);
  /* Tighter than the small header's .22em — at this size the letters have
     their own presence and wide tracking just pulls the word apart, which
     also breaks the overlap the doubling depends on. */
  letter-spacing: .12em;
  transform: translateY(.1em);
}
.hdr .logo::after {
  content: attr(data-text);
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
  text-transform: uppercase;
  letter-spacing: inherit;
  transform: translate(.045em, .055em);
  color: transparent;
  -webkit-text-stroke: 1px rgba(255,255,255,.62);
  text-stroke: 1px rgba(255,255,255,.62);
  text-shadow: none;
  pointer-events: none;
}
@media (max-width: 560px) {
  /* The stroke is a fixed 1px, so at small sizes it stops reading as a second
     letterform and starts reading as a smudge. Thinner and fainter. */
  .hdr .logo::after {
    -webkit-text-stroke-width: .6px;
    -webkit-text-stroke-color: rgba(255,255,255,.5);
  }
}

/* The doubled mark needs room the old compact 88px bar didn't have. */
.hdr .hdr-in { height: clamp(96px, 13vh, 150px); }

/* "HAIR BY KRISTEN" is 15 characters. In Bodoni caps that's roughly
   15 x (0.60 + tracking) em wide, so at the desktop size and .12em tracking it
   needs ~11em — far more than a 390px phone has. Left alone it overflowed and
   dragged the whole page sideways.
   Smaller and tighter below 560px: ~207px at a 390px viewport, which clears
   the burger with room to spare. */
@media (max-width: 560px) {
  .hdr .logo {
    font-size: clamp(1.05rem, 5.2vw, 1.6rem);
    letter-spacing: .08em;
  }
  .hdr .hdr-in { height: clamp(80px, 11vh, 104px); }
}

/* ------------------------------------------------- dropdown menu on mobile
   The desktop panel is a small card hanging off the burger. On a phone it
   ends up nearly full width, so it needs its own rhythm rather than the
   desktop one stretched: tighter rows, smaller type, and a Book button that
   doesn't dominate the sheet. */
@media (max-width: 560px) {
  .nav {
    left: var(--hgut);
    right: var(--hgut);
    width: auto;
    min-width: 0;
    max-width: none;
    padding: .9rem 1.35rem 1.1rem;
    border-radius: 1.1rem;
    max-height: calc(100svh - 140px);
    overflow-y: auto;               /* long menus scroll instead of overflowing */
    -webkit-overflow-scrolling: touch;
  }

  .nav-group { padding: .6rem 0; }
  .nav a.nav-head { font-size: 1.02rem; padding: 0 0 .15rem; }
  .nav-group a:not(.nav-head) { font-size: .62rem; letter-spacing: .13em; padding: .26rem 0; }

  /* Sat level with the first heading instead of floating alone above it. */
  .nav-ig { top: .75rem; right: 1rem; width: 30px; height: 30px; }
  .nav-ig .ico-ig { width: 19px; height: 19px; }

  /* Was a full-width slab that read as the loudest thing on the screen. */
  .nav .btn {
    margin-top: .85rem;
    padding: .72rem 1.5rem;
    font-size: .62rem;
    letter-spacing: .16em;
    justify-self: center;
    align-self: center;
    width: auto;
  }

  /* The address is the only line long enough to wrap; let it sit tighter. */
  .nav-group a:not(.nav-head) { line-height: 1.45; }
}

/* Mobile menu on INNER pages — solid, not translucent.
   On the home page the panel opens over the hero photo: a smooth dark surface,
   so 50% black plus a blur reads beautifully. On every other page it opens
   over headings and body copy, and that same treatment let the text behind
   collide with the menu items — genuinely unreadable.
   It also stops depending on backdrop-filter, which some phones drop in
   low-power mode and some browsers don't support at all. A menu has to be
   readable on the worst device, not the best one.
   Scoped to :not(.is-over) so the home page keeps the look Jay signed off. */
@media (max-width: 560px) {
  .hdr:not(.is-over) .nav {
    background: #0d0d0d;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-color: var(--line);
    /* Lifts the panel off the page now that it no longer blurs what's behind. */
    box-shadow: 0 20px 44px rgba(0,0,0,.6);
  }
  /* The links were brightened to survive a translucent panel; on solid they can
     go back to the normal hierarchy. */
  .hdr:not(.is-over) .nav a { text-shadow: none; }
}

/* ----------------------------------------- header stays put (mobile)
   On a phone the bar does not travel with the page at all. It sits at the top
   of the document and scrolls off like any other content, exactly the way the
   home page behaves.

   This replaced a hide-on-scroll-down / reveal-on-scroll-up header. That still
   moved — it slid back into view every time you scrolled up, which is the part
   Jay was reacting to. Any sticky treatment, however clever, is still a black
   strip with her name in it sliding around over her photos.

   The cost is real and deliberate: once you scroll down, the burger is gone
   until you return to the top. Every page carries the full navigation in the
   footer, so there is always a way onward without scrolling back.

   Desktop keeps the sticky bar — there it costs nothing and pinning is normal.
   Home is unaffected either way: .is-over is already position:absolute. */
@media (max-width: 560px) {
  /* relative, NOT static. Both leave the header in normal flow so it scrolls
     away, but z-index only applies to POSITIONED elements — under `static` the
     header's z-index: 50 is ignored, the dropdown drops to DOM order, and the
     page content that follows it in the markup paints straight over the open
     menu. That is the unreadable menu Jay reported once already.
     `relative` with no offsets moves nothing and keeps the stacking. */
  .hdr:not(.is-over) { position: relative; }
}

/* ------------------------------------------------ category hero photo
   The large image directly under a category title. Fixed aspect ratio rather
   than natural height: the five source photos are 1:1, 3:4 and 9:16, and left
   alone each page would open at a different height — which is exactly what
   stops a set of pages feeling like one site. */
.cat-hero {
  margin-top: clamp(1.75rem, 4vw, 2.75rem);
  border-radius: 1.75rem;
  overflow: hidden;
  border: 1px solid var(--line);
  background: #0d0d0d;
}
.cat-hero img {
  width: 100%;
  /* height:auto is REQUIRED, not tidiness. The width/height HTML attributes on
     the <img> map to CSS presentational hints, so height was resolving to the
     photo's real 2133px and aspect-ratio had nothing left to compute — the
     hero rendered at full natural height, taller than the screen. The
     attributes still earn their keep by reserving space before load. */
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center 35%;   /* hair sits high in these frames */
  display: block;
}
@media (max-width: 560px) {
  .cat-hero { border-radius: 1.1rem; margin-top: 1.4rem; }
  /* Taller crop on a phone — 16:9 across 360px is a letterbox strip. */
  .cat-hero img { aspect-ratio: 4 / 3; }
}

/* Service thumbnails, tidied up now they're bigger. Square, consistent radius,
   aligned to the top of the row so a long description doesn't drag the image
   down the page away from its own title. */
.item-thumb {
  border-radius: .9rem;
  align-self: start;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: #111;
}

/* Mood photo at the top of a services-menu bubble. Fixed ratio for the same
   reason as the category heroes: these come in whatever shape Jay sends, and
   five different heights inside five bubbles reads as accidental. height:auto
   is required — the width/height attributes otherwise pin the real pixel
   height and aspect-ratio never gets used. */
.acc-photo {
  margin: 0 0 1.1rem;
  border-radius: 1.1rem;
  overflow: hidden;
  border: 1px solid var(--line);
  background: #111;
}
.acc-photo img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center;
  display: block;
}
@media (max-width: 560px) {
  .acc-photo { border-radius: .85rem; margin-bottom: .9rem; }
  .acc-photo img { aspect-ratio: 3 / 2; }
}

/* These photos sit on a near-black page, so the frame has to do some of the
   separating — a dark image inside a barely-visible border reads as nothing at
   all. Stronger border than the other bubbles use, on purpose. */
.acc-photo { border-color: rgba(255,255,255,.2); }

/* Two photos side by side in one menu slot.
   Each half is 4:5 rather than the single photo's 16:9 — two landscape frames
   next to each other are letterbox strips, where two portraits read as a
   deliberate pair. They stack on a phone, where side-by-side would leave each
   about 150px wide. */
.acc-photo-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  border: 0;
  background: none;
  border-radius: 0;
  overflow: visible;
}
.acc-photo-pair img {
  aspect-ratio: 4 / 5;
  border-radius: 1.1rem;
  border: 1px solid rgba(255,255,255,.2);
  background: #111;
}
@media (max-width: 560px) {
  .acc-photo-pair { grid-template-columns: 1fr; gap: 8px; }
  .acc-photo-pair img { aspect-ratio: 3 / 2; border-radius: .85rem; }
}
