/* =========================================================
   SHOAIB SALON — section components
   About + stylist strip, achievements, core values, services
   architecture, grooming packages, portfolio mosaic, before/after
   testimonials, appointment form, footer.
   Loads after styles.css, before hero.css.
   ========================================================= */

/* =========================================================
   2. TESTIMONIALS — emerald band
   ========================================================= */
.testi__grid{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.06fr);
  gap:clamp(20px,3vw,34px); align-items:stretch;
}

.ba{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.ba__item .media{ aspect-ratio:3/4; border-radius:8px; }

.tcard{
  background:var(--white); color:var(--ink);
  border-radius:8px;
  padding:clamp(22px,3vw,36px);
  display:flex; flex-direction:column; align-items:center; text-align:center;
  justify-content:center;
}
.tcard__name{
  font-family:var(--display); font-weight:600;
  font-size:1.02rem; letter-spacing:.1em; text-transform:uppercase;
  margin:0 0 .25rem;
}
.tcard__meta{
  margin:0 0 1.2rem;
  font-size:.72rem; letter-spacing:.08em; text-transform:uppercase;
  color:var(--gold-deep);
}
.tcard__quote{
  margin:0 0 1.4rem;
  font-size:.88rem; line-height:1.72; color:#4A4A4A;
  max-width:46ch;
}
.tcard__quote[data-placeholder]{ font-style:italic; color:#6A6A6A; }

.stars{ display:flex; gap:5px; }
.stars svg{ width:17px; height:17px; }
.stars .on{ color:var(--gold); }
.stars .off{ color:#D8D2C8; }

.testi__dots{ display:flex; justify-content:center; gap:9px; margin-top:clamp(20px,3vw,30px); }
/* The visible dot is small, but the button is a 44px hit area so it is
   genuinely tappable rather than decorative. */
.testi__dots button{
  width:34px; height:44px; padding:0; border:0; cursor:pointer;
  background:none; display:grid; place-items:center;
}
.testi__dots button::before{
  content:""; display:block; width:9px; height:9px; border-radius:50%;
  /* .48 not .34 — these dots identify which review is showing, so they
     are a UI state indicator and need 3:1 against the emerald. */
  background:rgba(255,255,255,.48);
  transition:background .22s var(--ease), transform .22s var(--ease);
}
.testi__dots button[aria-selected="true"]::before{ background:var(--gold); transform:scale(1.25); }

/* =========================================================
   3. RESERVE — barber chair row
   ========================================================= */
.chairs{
  display:grid; grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(12px,2vw,24px);
  margin-top:clamp(26px,3.6vw,44px);
}
.chair{ text-align:center; text-decoration:none; color:inherit; display:block; }
.chair__name{
  display:block;
  font-family:var(--display); font-weight:600;
  font-size:.94rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--white);
  transition:color .22s var(--ease);
}
.chair__name[data-placeholder]{ font-style:italic; font-weight:400; text-transform:none; letter-spacing:.02em; }
.chair__role{
  display:block; margin-bottom:12px;
  font-size:.74rem; letter-spacing:.06em; color:var(--gold);
}
.chair .media{ aspect-ratio:4/5; border-radius:8px; }
.chair:hover .chair__name{ color:var(--gold); }

.reserve__cta{ margin-top:clamp(26px,3.6vw,42px); }

/* =========================================================
   4. ABOUT — cream panel flanked by photos
   ========================================================= */
.about__grid{
  display:grid;
  grid-template-columns:minmax(0,.72fr) minmax(0,1.6fr) minmax(0,.62fr);
  align-items:stretch; gap:0;
}
.about__photo{ position:relative; min-height:260px; }
.about__photo .media{ position:absolute; inset:0; border-radius:0; }
.about__photo--l .media{ border-radius:8px 0 0 8px; }
.about__photo--r .media{ border-radius:0 8px 8px 0; }

.about__panel{
  background:var(--cream); color:var(--ink);
  padding:clamp(28px,3.6vw,52px);
  min-width:0;
}
.about__panel p{ color:var(--muted-warm); font-size:.9rem; line-height:1.7; }

.feat{ margin:clamp(20px,2.6vw,30px) 0 0; }
.feat__item + .feat__item{ margin-top:1.2rem; }
.feat__item h3{
  font-family:var(--display); font-weight:600;
  font-size:.86rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--gold-deep); margin:0 0 .3rem;
}
.feat__item p{ margin:0; font-size:.85rem; line-height:1.62; }

/* =========================================================
   5. TEAM — cream band
   ========================================================= */
.team__grid{
  display:grid; grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(14px,2vw,24px);
  margin-top:clamp(28px,3.6vw,46px);
}
.member{ margin:0; text-align:center; }
.member .media{
  aspect-ratio:3/4; border-radius:8px;
  border:1px solid rgba(22,22,22,.12);
}
.member figcaption{ margin-top:14px; }
.member__name{
  display:block;
  font-family:var(--display); font-weight:600;
  font-size:.92rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink);
}
.member__name[data-placeholder]{ font-style:italic; font-weight:400; text-transform:none; letter-spacing:.02em; }
.member__role{ display:block; font-size:.76rem; color:var(--gold-deep); letter-spacing:.04em; }

.team__cta{ margin-top:clamp(28px,3.6vw,44px); text-align:center; }

/* =========================================================
   6. GALLERY — mosaic with inline headline cells
   ========================================================= */
.gallery__head{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:20px; flex-wrap:wrap;
  margin-bottom:clamp(20px,2.8vw,32px);
}
.gallery__handle{
  /* inline-flex + min-height so the tap target clears 24px rather than
     collapsing to the 22px line box. */
  display:inline-flex; align-items:center; min-height:26px;
  font-family:var(--display); font-size:.8rem; font-weight:500;
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--gold); text-decoration:none;
}
.gallery__handle:hover{ color:var(--gold-soft); }

/* Three real photographs and two text cells. Each photo cell is one
   column by two rows — the salon's Instagram shots are all portrait
   (360x640), so portrait cells crop them far less than wide ones. */
.mosaic{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  grid-auto-rows:clamp(112px,13vw,182px);
  grid-template-areas:
    "a  b  c  t1"
    "a  b  c  t2";
  gap:clamp(8px,1.1vw,14px);
}
.mosaic .media{ width:100%; height:100%; border-radius:6px; }
.m-a{ grid-area:a; } .m-b{ grid-area:b; } .m-c{ grid-area:c; }

/* Text cells sit inside the grid so the mosaic reads as one composition. */
.mosaic__text{
  display:flex; align-items:center;
  font-family:var(--display); font-weight:700;
  font-size:clamp(.95rem,2.1vw,1.7rem);
  line-height:1.1; letter-spacing:.02em; text-transform:uppercase;
  color:var(--white);
}
.mosaic__text--1{ grid-area:t1; align-items:flex-end; color:var(--gold); }
.mosaic__text--2{ grid-area:t2; align-items:flex-start; }

/* =========================================================
   7. PRICING — cream band, two columns
   ========================================================= */
.pricing__grid{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  gap:0 clamp(28px,5vw,72px);
  margin-top:clamp(26px,3.4vw,44px);
}
.price-list{ margin:0; padding:0; list-style:none; min-width:0; }
.price-list li{
  display:flex; align-items:baseline; justify-content:space-between; gap:18px;
  padding:.85rem 0;
  border-bottom:1px solid rgba(22,22,22,.16);
}
.price-list__name{
  font-family:var(--display); font-weight:500;
  font-size:clamp(.84rem,1.1vw,.95rem);
  letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink);
}
.price-list__val{
  font-family:var(--display); font-weight:600;
  font-size:clamp(.84rem,1.1vw,.95rem);
  letter-spacing:.05em; white-space:nowrap;
  color:var(--gold-deep);
}
.pricing__cta{ margin-top:clamp(26px,3.4vw,40px); text-align:center; }
.pricing__note{
  margin:1rem 0 0; text-align:center;
  font-size:.78rem; color:var(--muted-warm);
}

/* =========================================================
   7b. WEDDING PACKAGES — the three groom day packages
   ========================================================= */
.pkg{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(16px,2.2vw,26px);
  align-items:start;
  margin-top:clamp(26px,3.4vw,42px);
}
.pkg__card{
  position:relative;
  background:var(--white);
  border:1px solid rgba(18,21,18,.12);
  border-radius:var(--r-card);
  padding:clamp(22px,2.6vw,32px);
  display:flex; flex-direction:column;
  min-width:0;
  transition:transform .3s var(--ease), box-shadow .3s var(--ease);
}
.pkg__card:hover{ transform:translateY(-4px); box-shadow:0 18px 40px rgba(18,21,18,.14); }

/* The middle card carries the fullest package, so it leads. */
.pkg__card--feature{
  background:var(--green); border-color:var(--green);
  color:var(--cream);
}
.pkg__tag{
  position:absolute; top:-11px; left:50%; transform:translateX(-50%);
  background:var(--gold); color:var(--ink);
  padding:.32em .9em; border-radius:999px;
  font-family:var(--display); font-size:.64rem; font-weight:700;
  letter-spacing:.14em; text-transform:uppercase;
  white-space:nowrap;
}

.pkg__name{
  font-family:var(--display); font-weight:700;
  font-size:clamp(1.1rem,1.7vw,1.35rem);
  letter-spacing:.06em; text-transform:uppercase;
  margin:0 0 .3rem;
}
.pkg__price{
  font-family:var(--display); font-weight:800;
  font-size:clamp(1.7rem,2.8vw,2.2rem);
  letter-spacing:-.01em; line-height:1;
  color:var(--gold-deep);
  margin:0 0 1.2rem;
}
.pkg__price span{ font-size:.5em; font-weight:600; letter-spacing:0; }
.pkg__card--feature .pkg__price{ color:var(--gold-soft); }

.pkg__list{
  list-style:none; margin:0 0 1.5rem; padding:0;
  border-top:1px solid rgba(18,21,18,.12);
  flex:1;
}
.pkg__card--feature .pkg__list{ border-top-color:rgba(243,238,226,.22); }
.pkg__list li{
  position:relative;
  padding:.5rem 0 .5rem 1.35rem;
  font-size:.86rem; line-height:1.45;
  border-bottom:1px solid rgba(18,21,18,.09);
  color:var(--muted-warm);
}
.pkg__card--feature .pkg__list li{
  border-bottom-color:rgba(243,238,226,.14);
  color:rgba(243,238,226,.9);
}
.pkg__list li::before{
  content:""; position:absolute; left:0; top:.95em;
  width:6px; height:6px; border-radius:50%;
  background:var(--gold-deep);
}
.pkg__card--feature .pkg__list li::before{ background:var(--gold); }

.pkg__note{
  margin:clamp(22px,3vw,32px) 0 0; text-align:center;
  font-size:.85rem; color:var(--muted-warm);
}

/* =========================================================
   8. CONTACT — map + hours badge, and the form
   ========================================================= */
.contact__grid{
  display:grid; grid-template-columns:minmax(0,.84fr) minmax(0,1.16fr);
  gap:clamp(22px,3.4vw,48px); align-items:start;
}

.mapwrap{ position:relative; }
.map{
  display:block; position:relative;
  border-radius:8px; overflow:hidden;
  background:var(--ink-2);
  aspect-ratio:3/4;
  border:1px solid rgba(255,255,255,.12);
  text-decoration:none;
  transition:transform .35s var(--ease), box-shadow .35s var(--ease);
}
.map:hover{ transform:translateY(-4px); box-shadow:0 18px 40px rgba(0,0,0,.45); }
.map svg{ width:100%; height:100%; display:block; }

.hours-badge{
  position:absolute; left:14px; top:14px; z-index:2;
  background:var(--ink-3); color:var(--white);
  border:1px solid var(--gold);
  border-radius:6px;
  padding:12px 16px;
  max-width:calc(100% - 28px);
}
.hours-badge__sep{
  margin-top:.6rem; padding-top:.6rem;
  border-top:1px solid rgba(255,255,255,.16);
}
.hours-badge a{ color:var(--gold-soft); text-decoration:none; }
.hours-badge a:hover{ text-decoration:underline; }

.hours-badge h3{
  font-family:var(--display); font-weight:600;
  font-size:.72rem; letter-spacing:.16em; text-transform:uppercase;
  color:var(--gold); margin:0 0 .5rem;
}
.hours-badge p{ margin:0 0 .18rem; font-size:.78rem; color:rgba(255,255,255,.86); }

/* --- form --- */
.cform{
  display:grid; grid-template-columns:1fr 1fr; gap:14px;
  min-width:0;
}
.field{ display:grid; gap:.4rem; min-width:0; }
.field--full{ grid-column:1/-1; }
.field label{
  font-family:var(--display); font-size:.7rem; font-weight:500;
  letter-spacing:.14em; text-transform:uppercase; color:rgba(255,255,255,.7);
}
.field input,.field select,.field textarea{
  width:100%; min-width:0; min-height:46px;
  padding:.62em .85em;
  background:rgba(255,255,255,.05); color:var(--white);
  border:1px solid rgba(255,255,255,.18); border-radius:5px;
  font-size:.9rem;
  transition:border-color .2s var(--ease), background .2s var(--ease);
}
.field textarea{ min-height:104px; resize:vertical; }
.field input::placeholder,.field textarea::placeholder{ color:rgba(255,255,255,.38); }
.field input:hover,.field select:hover,.field textarea:hover{ border-color:rgba(255,255,255,.36); }
.field select option{ background:var(--ink-2); color:var(--white); }
.field.is-bad input,.field.is-bad select,.field.is-bad textarea{ border-color:#FF8E7A; }
.field__err{ margin:0; min-height:1em; font-size:.74rem; color:#FF8E7A; }

.cform__submit{ grid-column:1/-1; }
.cform__ok{
  grid-column:1/-1; margin:0; text-align:center;
  font-size:.82rem; color:var(--gold-soft);
}

/* The catchment line under the contact block. */
.areas{
  margin:clamp(26px,3.4vw,40px) 0 0;
  padding-top:clamp(18px,2.4vw,26px);
  border-top:1px solid rgba(255,255,255,.1);
  font-size:.8rem; line-height:1.9; color:var(--muted);
}
.areas span{
  display:block;
  font-family:var(--display); font-size:.68rem; font-weight:600;
  letter-spacing:.2em; text-transform:uppercase;
  color:var(--gold); margin-bottom:.3rem;
}

/* =========================================================
   9. FOOTER
   ========================================================= */
/* The whole footer runs on scale contrast: everything in the top row is
   deliberately small and quiet so the oversized lockup underneath lands. */
.foot{ background:#0A0A0A; border-top:1px solid rgba(255,255,255,.08); }

.foot__top{
  display:grid;
  grid-template-columns:minmax(0,1.15fr) repeat(4,minmax(0,.72fr)) minmax(0,1.25fr);
  gap:clamp(20px,2.6vw,36px);
  padding:clamp(44px,5.6vw,68px) 0 clamp(36px,4.6vw,56px);
}

.foot__brand{ min-width:0; }
/* Larger than the nav copy — there is room here, and at this size the
   "SALON" line inside the badge is actually readable. */
.foot__mark{ display:block; width:58px; height:58px; margin-bottom:16px; }
.foot__blurb{
  margin:0; font-size:.78rem; line-height:1.7;
  color:var(--muted); max-width:26ch;
}

.foot__col{ min-width:0; }
.foot__col h4{
  font-family:var(--body); font-weight:500;
  font-size:.74rem; letter-spacing:.01em;
  color:#7C7C7C; margin:0 0 1rem;
}
.foot__col a{
  display:block; padding:.34rem 0;
  font-size:.82rem; color:#DEDEDE; text-decoration:none;
  transition:color .2s var(--ease);
}
.foot__col a:hover{ color:var(--gold); }
/* On a phone these are the primary way in — give them a real thumb
   target (44px) rather than the 33px a text link naturally gets. */
@media (max-width:900px){
  .foot__col a{ padding:.69rem 0; }
}

/* ---------- Newsletter ---------- */
.foot__news{ min-width:0; }
.foot__news h4{
  font-family:var(--body); font-weight:500;
  font-size:.8rem; line-height:1.5; letter-spacing:.01em;
  color:var(--white); margin:0 0 1rem; max-width:24ch;
}
.news{
  display:flex; align-items:stretch; gap:6px;
  min-width:0;
}
/* min-width:0 or the input refuses to shrink below its intrinsic size and
   pushes the button out of the column. */
.news input{
  flex:1 1 auto; min-width:0;
  background:#171717; color:var(--white);
  border:1px solid rgba(255,255,255,.14); border-radius:5px;
  padding:.62em .8em; font-size:.78rem;
  transition:border-color .2s var(--ease);
}
/* #8A8A8A not #6E6E6E — the darker grey only reached 3.5:1 on the input's
   own background, under the 4.5:1 bar for text. */
.news input::placeholder{ color:#8A8A8A; }
.news input:hover{ border-color:rgba(255,255,255,.3); }
.news input[aria-invalid="true"]{ border-color:#FF8E7A; }
.news__btn{
  flex:none;
  background:var(--gold); color:var(--ink);
  border:1px solid var(--gold); border-radius:5px;
  padding:.62em 1em;
  font-family:var(--body); font-weight:600; font-size:.76rem;
  cursor:pointer; white-space:nowrap;
  transition:background .2s var(--ease), border-color .2s var(--ease);
}
.news__btn:hover{ background:var(--gold-soft); border-color:var(--gold-soft); }
.news__msg{
  margin:.6rem 0 0; min-height:1em;
  font-size:.72rem; color:var(--gold-soft);
}

/* ---------- Oversized lockup ---------- */
.foot__wordmark{
  display:flex; align-items:center;
  gap:.1em;
  padding-bottom:clamp(26px,3.4vw,44px);
  /* The lockup — crest, gap and "Shoaib Salon" — measures a fixed
     multiple of its font size, so dividing the available width by that
     ratio (plus headroom) makes it span the footer without ever
     overflowing. The value below is measured, not guessed: re-measure it
     if you change the name or the crest's size.

     Fallback uses 100vw, which INCLUDES the scrollbar and so overstates
     the usable width by ~15px — hence the larger divisor there. The
     container-query path below measures the real container instead. */
  --fit:calc((min(100vw, var(--wrap)) - 2 * var(--gut)) / 9.2);
  font-size:min(var(--fit), 190px);
  line-height:.8;
  user-select:none;
}
/* Sized in em so it tracks the giant wordmark. The box stays square,
   exactly as the outgoing crest's box was, so the measured fit
   divisors below are unaffected. */
.foot__wordmark-mark{
  display:block; width:1.02em; height:1.02em; flex:none;
}
.foot__wordmark-text{
  font-family:var(--display); font-weight:700;
  font-size:1em; letter-spacing:-.005em; text-transform:uppercase;
  color:var(--white);
  white-space:nowrap;
}

/* Measure the wrap's real content box rather than the viewport, so the
   scrollbar can't throw the sizing off. */
@supports (width: 1cqw) {
  .foot .wrap{ container-type:inline-size; }
  /* Re-measured for Montserrat, which replaced Archivo with the brand
     kit. The lockup - mark + gap + "Shoaib Salon" - comes out at 9.13
     times its font size on desktop and 9.17 on mobile, sampled at three
     different sizes each and identical every time. The divisors below sit
     a whisker above those so the lockup spans the footer without ever
     touching the edge. Archivo's old 8.95 was clipping it by 21px on
     desktop and 59px on a phone. Re-measure if the face changes again. */
  .foot__wordmark{ --fit:calc(100cqw / 9.2); }
}

.foot__base{
  border-top:1px solid rgba(255,255,255,.1);
  padding:20px 0 26px;
  text-align:center;
}
/* #8A8A8A not #7A7A7A — the darker grey landed exactly on 4.5:1, with no
   margin at all for this small a size. */
.foot__base p{ margin:0; font-size:.75rem; color:#8A8A8A; }

/* =========================================================
   RESPONSIVE
   ========================================================= */
@media (max-width:1000px){
  .about__grid{ grid-template-columns:1fr; }
  .about__photo{ min-height:220px; }
  .about__photo--l .media,.about__photo--r .media{ border-radius:8px; }
  .about__photo--r{ display:none; }
  .team__grid{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .chairs{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  /* Brand and newsletter take a full row each; the four link columns
     share the row between them. */
  .foot__top{ grid-template-columns:repeat(4,minmax(0,1fr)); }
  .foot__brand,.foot__news{ grid-column:1/-1; }
  .foot__news h4{ max-width:none; }
  .news{ max-width:420px; }
}

@media (max-width:860px){
  .testi__grid{ grid-template-columns:1fr; }
  .contact__grid{ grid-template-columns:1fr; }
  .map{ aspect-ratio:4/3; }
}

@media (max-width:760px){
  .mosaic{
    grid-template-columns:repeat(2,minmax(0,1fr));
    grid-auto-rows:clamp(120px,24vw,170px);
    grid-template-areas:
      "t1 t1"
      "a  b"
      "a  b"
      "t2 t2"
      "c  c";
  }
  .mosaic__text--1{ align-items:center; }
  .mosaic__text--2{ align-items:center; }
}

@media (max-width:900px){
  .pkg{ grid-template-columns:1fr; max-width:460px; margin-inline:auto; }
  .pkg__card--feature{ order:-1; }
}

@media (max-width:600px){
  .pricing__grid{ grid-template-columns:1fr; }
  .team__grid{ grid-template-columns:1fr; }
  .chairs{ grid-template-columns:1fr; }
  .ba{ gap:8px; }
  .cform{ grid-template-columns:1fr; }
  .foot__top{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  /* The lockup would be unreadably small in one line on a phone, so the
     mark sits above the wordmark and the wordmark gets the full width.
     Stacked, only the text matters: it measures 5.90x its font size,
     so 6.05 here. */
  /* Stacked, only the wordmark's own width matters — measured at 7.60x
     its font size, so 7.85 here. */
  .foot__wordmark{
    flex-direction:column; align-items:flex-start; gap:.14em;
    --fit:calc((100vw - 2 * var(--gut)) / 9.25);
  }
  @supports (width: 1cqw) {
    .foot__wordmark{ --fit:calc(100cqw / 9.25); }
  }
}

/* =========================================================
   PRINT
   ========================================================= */
@media print{
  .testi__dots,.foot__social,.cform{ display:none !important; }
  .tcard,.about__panel{ background:#fff !important; color:#000 !important; }
  .mosaic{ grid-template-areas:none; grid-template-columns:repeat(3,1fr); }
}

/* =========================================================
   SECTIONS ADDED FOR THE BRAND-KIT REBUILD
   Achievements, Core Values, Services Architecture and the
   appointment form, all in the supplied three-colour kit.

   The one rule that shapes everything below: the brand green
   (#172E07) measures 1.36:1 against the near-black, so it can
   never be a line, an icon or a word on a dark ground. It is
   used as a GROUND — off-white on green is 13.00:1 — and where
   an accent genuinely has to read on the near-black, the lifted
   step --green-glow (4.25:1) does that job instead.
   ========================================================= */

/* ---------------------------------------------------------
   3. KEY ACHIEVEMENTS   (sits on the green)

   A stat band, so the figures have to carry it. They were not:
   two of the four were 33.6px numerals and the other two were
   15px words sitting in the same slot, which read as a mistake
   rather than as a mixed set.

   Now every item opens on the same line, whatever is on it. The
   numerals are large; the two entries that are a phrase rather
   than a count keep the body face at a readable size and are
   bottom-aligned to the numerals' baseline, so the row lines up
   without either pretending to be the other. No number is
   invented to make them match - only two of these four
   achievements came with one.

   Deliberately NOT the hairline grid used by Core Values, which
   sits immediately below this: two adjacent sections built the
   same way read as one long section.
   --------------------------------------------------------- */
.ach{
  list-style:none;
  margin:clamp(30px,4vw,48px) 0 0;
  padding:0;
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(22px,2.8vw,44px);
}
.ach__item{ display:flex; flex-direction:column; min-width:0; }

/* A short rule over each entry instead of a full divider. Enough to say
   "these are four of a set" without drawing a box round anything. */
.ach__item::before{
  content:'';
  width:30px; height:2px; flex:none;
  background:var(--off);
  margin-bottom:clamp(18px,2.2vw,30px);
  transform:scaleX(.55); transform-origin:left;
  transition:transform .6s var(--ease);
}
.ach__item:hover::before{ transform:scaleX(1); }

/* One line height for all four, so the names below start level whether
   the entry opens on a numeral or on a phrase. */
.ach__fig{
  display:flex; align-items:flex-end;
  min-height:clamp(2.5rem,4.2vw,3.8rem);
  font-family:var(--display); font-weight:800;
  font-size:clamp(2.5rem,4.2vw,3.8rem);
  line-height:.9; letter-spacing:-.045em;
  font-variant-numeric:tabular-nums;
  color:var(--off);
}
.ach__fig sup{
  align-self:flex-start;
  margin-top:.18em; margin-left:.04em;
  font-size:.36em; font-weight:700; top:0;
}
/* The count-up writes here. Fixed to the widest value it will show, so
   the digits do not shove the "+" sideways while they run. */
.ach__val{ font-variant-numeric:tabular-nums; }

.ach__fig--word{
  font-family:var(--body); font-weight:500;
  font-size:clamp(.92rem,1.15vw,1.08rem);
  letter-spacing:.2em; text-transform:uppercase;
  line-height:1.4;
  color:var(--off-2);
  padding-bottom:.35em;
}

.ach__name{
  margin:clamp(16px,1.8vw,24px) 0 .55rem;
  font-family:var(--display); font-weight:700;
  font-size:clamp(1rem,1.25vw,1.14rem);
  line-height:1.3; letter-spacing:-.005em;
  color:var(--off);
}
.ach__desc{
  margin:0;
  font-size:.87rem; line-height:1.72;
  color:rgba(241,241,241,.78);
}

/* Two across. The figures stay large - shrinking them here would give
   away the one thing this section has. */
@media (max-width:900px){
  .ach{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:clamp(24px,5vw,34px) clamp(18px,4vw,26px); }
}

@media (max-width:560px){
  /* Still two across, not one. Four stacked entries ran to 922px here,
     about one and a tenth screens, for four short paragraphs. Side by
     side they fit in roughly half that, and a stat band reads better as
     a block of figures than as a column of them anyway. */
  .ach{ gap:26px 16px; }
  .ach__item::before{ width:22px; margin-bottom:14px; }
  .ach__fig{
    min-height:2.35rem;
    font-size:2.15rem;
    letter-spacing:-.04em;
  }
  .ach__fig--word{
    font-size:.82rem; letter-spacing:.14em; line-height:1.35;
    padding-bottom:.2em;
  }
  .ach__name{ margin:12px 0 .45rem; font-size:.95rem; }
  .ach__desc{ font-size:.81rem; line-height:1.6; }
}

@media (prefers-reduced-motion:reduce){
  .ach__item::before{ transition:none; transform:scaleX(1); }
}

/* ---------------------------------------------------------
   4. CORE VALUES

   Was four bordered cards stacked down the phone: 1178px, about
   one and a half screens, for four short paragraphs. Boxes are
   also the least expensive-looking thing you can put on a page -
   every card edge is a line the eye has to cross.

   So: no boxes. The four values are divided by hairlines instead,
   like a spec sheet. That is quieter on the desktop and, on the
   phone, it drops the card padding and puts the numeral inline
   with its name rather than on a line of its own, which is where
   most of the height went.
   --------------------------------------------------------- */
.val{
  list-style:none;
  margin:clamp(30px,4vw,48px) 0 0;
  padding:0;
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  /* No gap: the hairlines are the separation, and a gap either side of
     them would make each one read as a box edge again. */
  gap:0;
  border-top:1px solid rgba(9,9,9,.16);
}
.val__item{
  position:relative;
  padding:clamp(24px,2.6vw,34px) clamp(18px,2vw,28px) clamp(26px,3vw,38px);
  transition:background .5s var(--ease);
}
.val__item + .val__item{ border-left:1px solid rgba(9,9,9,.16); }
/* A wash of the ground rather than a border change, so nothing shifts. */
.val__item:hover{ background:rgba(9,9,9,.035); }

.val__num{
  display:block;
  font-family:var(--display); font-weight:800;
  font-size:clamp(1.7rem,2.4vw,2.3rem);
  line-height:1; letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
  /* .48 of the near-black over this ground measures 3.42:1, past the 3:1
     asked of large text. The alpha is higher than the .38 this used on
     the dark ground because the same fraction reads lighter against
     white: .38 here would be 2.53:1 and would fail. These are real text
     a screen reader reads out, not an ornament, so the watermark only
     goes as faint as the floor allows. */
  color:rgba(9,9,9,.48);
  transition:color .5s var(--ease);
}
.val__item:hover .val__num{ color:var(--green); }

.val__name{
  margin:clamp(14px,1.6vw,20px) 0 .6rem;
  font-family:var(--display); font-weight:700;
  font-size:clamp(1rem,1.3vw,1.16rem);
  line-height:1.25; letter-spacing:-.005em;
  color:var(--ink);
}
.val__desc{
  margin:0;
  font-size:.88rem; line-height:1.72; color:var(--muted-dark);
}

/* Two across: the third and fourth need the rule above them that the
   grid's own border-top only gives the first row, and the left rule
   belongs to the second in each pair rather than to every item. */
@media (max-width:1000px){
  .val{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .val__item + .val__item{ border-left:0; }
  .val__item:nth-child(even){ border-left:1px solid rgba(9,9,9,.16); }
  .val__item:nth-child(n+3){ border-top:1px solid rgba(9,9,9,.16); }
}

/* One across, and this is where the height comes back. The numeral moves
   up beside its name instead of sitting on a line of its own, the side
   padding goes entirely, and the vertical padding halves. */
@media (max-width:599px){
  .val{ grid-template-columns:minmax(0,1fr); }
  .val__item{
    display:grid;
    grid-template-columns:auto minmax(0,1fr);
    column-gap:14px; align-items:baseline;
    padding:18px 0 20px;
    border-left:0 !important;
  }
  .val__item:nth-child(n+2){ border-top:1px solid rgba(9,9,9,.16); }
  .val__item:hover{ background:none; }

  .val__num{
    grid-column:1; grid-row:1;
    /* Kept at 1.2rem, though the reason has changed. On the near-black
       this size was load-bearing: the lifted green was 4.25:1 there and
       only legal as large text. On this ground the numeral is the TRUE
       brand green at 13.00:1, which passes at any size - so 19.2px is now
       simply what looks right, not what the contrast floor demands. */
    font-size:1.2rem; font-weight:700;
    color:var(--green);
  }
  .val__name{
    grid-column:2; grid-row:1;
    margin:0; font-size:1.02rem;
  }
  .val__desc{
    grid-column:2; grid-row:2;
    margin-top:.5rem;
    font-size:.87rem; line-height:1.62;
  }
}

@media (prefers-reduced-motion:reduce){
  .val__item, .val__num{ transition:none; }
}

/* ---------------------------------------------------------
   7. APPOINTMENT — on the off-white ground

   This section is the one light band in the lower half of the
   page, which is deliberate: the form is the thing being asked
   for, and a sheet of paper among dark sections is the simplest
   way to say so.

   Inverting it was not a matter of swapping two colours. Every
   hairline, fill and accent here was drawn for a dark ground,
   and two of them could not simply cross over:

     - the LIFTED green (--green-glow) reads 4.25:1 on the
       near-black but only 4.15:1 on the off-white, which fails
       body text. So on this ground the accent is the TRUE brand
       green, #172E07, which measures 13.00:1. The colour that
       could never be text on the dark sections is the one doing
       it here.
     - the error red used on dark (#D9603F) falls to 3.26:1 on
       this ground. It is #A8341A here, at 5.86:1.

   Body copy is --muted-dark at 7.22:1, headings --ink at 17.63:1.
   --------------------------------------------------------- */

/* Grain, but multiplied rather than soft-lit. On a light ground
   soft-light all but vanishes; multiply lays the same tile down as
   paper texture instead. */
/* .section.book-sec, not .book-sec: the shared grain rule further down
   this file sets soft-light on the same pseudo-element at equal
   specificity, and being later it would win. Two classes settles it
   regardless of order - the same trap that flattened the plate stagger
   earlier in this stylesheet. */
/* Overrides .section--cream's off-white. Two classes, so it does not
   depend on where it sits relative to that rule. */
.section.book-sec{ background:var(--stone); }

.section.book-sec::after{
  mix-blend-mode:multiply;
  opacity:.05;
}
/* The rule trailing the section label has to darken here, not lighten. */
.book-sec .eyebrow--rule::after{
  background:linear-gradient(90deg, rgba(9,9,9,.34), rgba(9,9,9,0));
}
.book-sec .eyebrow--rule span{ color:var(--green); }

.book__head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:clamp(18px,3vw,40px); flex-wrap:wrap;
  margin-bottom:clamp(30px,3.8vw,52px);
}
.book__intro{
  margin:0; max-width:34ch;
  font-size:.9rem; line-height:1.75; color:var(--muted-dark);
}

.book{
  display:grid;
  grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);
  gap:clamp(26px,3.6vw,60px);
  align-items:start;
}
.book__form{ min-width:0; }

/* --- the step rail --- */
.book__step{
  position:relative;
  border:0; margin:0; min-width:0;
  padding:0 0 clamp(30px,3.4vw,46px) clamp(50px,4.6vw,68px);
}
.book__step::before{
  content:'';
  position:absolute;
  left:clamp(15px,1.55vw,21px);
  top:clamp(34px,3.3vw,46px);
  bottom:clamp(6px,.7vw,10px);
  width:1px;
  background:linear-gradient(180deg, rgba(9,9,9,.24), rgba(9,9,9,.06));
}
.book__step--end::before{ display:none; }

.book__step legend{
  display:block; float:none;
  padding:0; margin:0 0 1.15rem;
  width:100%;
}
.book__label{
  font-family:var(--display); font-weight:700;
  font-size:clamp(.94rem,1.2vw,1.1rem);
  letter-spacing:.02em; color:var(--ink);
}
.book__n,
.book__step--end::after{
  position:absolute; left:0; top:0;
  display:grid; place-items:center;
  width:clamp(31px,3.1vw,43px); height:clamp(31px,3.1vw,43px);
  border-radius:50%;
  border:1px solid rgba(9,9,9,.22);
  background:var(--off);
  color:var(--green);
  font-family:var(--display); font-weight:700;
  font-size:.82rem; font-variant-numeric:tabular-nums;
}
/* Step five is the action rather than a field, so its numeral is drawn
   rather than marked up - the button's own words already say what it
   does, and a second copy would only repeat itself to a screen reader. */
.book__step--end::after{ content:'5'; }

/* --- radio pills --- */
.chips{ display:flex; flex-wrap:wrap; gap:10px; }
.chip{ position:relative; cursor:pointer; }
/* The input stays in the accessibility tree and keeps working from the
   keyboard; only its default box is hidden. */
.chip input{
  position:absolute; opacity:0; width:100%; height:100%;
  margin:0; cursor:pointer;
}
.chip span{
  display:flex; align-items:center; gap:.45em;
  padding:.78rem 1.15rem;
  border:1px solid rgba(9,9,9,.2);
  border-radius:999px;
  font-size:.9rem; color:var(--muted-dark);
  transition:border-color .35s var(--ease), background .35s var(--ease), color .35s var(--ease);
}
.chip span em{
  font-style:normal; font-size:.72em; letter-spacing:.14em;
  text-transform:uppercase; opacity:.7;
}
.chip:hover span{ border-color:rgba(9,9,9,.45); color:var(--ink); }
/* Checked fills with the near-black. On the dark sections this reversed
   the other way, to off-white on ink - same idea, same contrast, opposite
   ground. */
.chip input:checked + span{
  background:var(--ink); border-color:var(--ink); color:var(--off);
  font-weight:500;
}
.chip input:focus-visible + span{ outline:2px solid var(--green); outline-offset:3px; }

/* --- time slots --- */
.slots{ display:flex; flex-wrap:wrap; gap:8px; margin-top:1.1rem; }
.slots:empty{ display:none; }
.slot{ position:relative; cursor:pointer; }
.slot input{ position:absolute; opacity:0; width:100%; height:100%; margin:0; cursor:pointer; }
.slot span{
  display:block; padding:.6rem .9rem;
  border:1px solid rgba(9,9,9,.18);
  border-radius:8px;
  font-size:.85rem; font-variant-numeric:tabular-nums; color:var(--muted-dark);
  transition:border-color .35s var(--ease), background .35s var(--ease), color .35s var(--ease);
}
.slot:hover span{ border-color:rgba(9,9,9,.42); color:var(--ink); }
.slot input:checked + span{ background:var(--ink); border-color:var(--ink); color:var(--off); font-weight:500; }
.slot input:focus-visible + span{ outline:2px solid var(--green); outline-offset:3px; }
.slots__closed{ margin:1rem 0 0; font-size:.9rem; color:var(--green); font-weight:500; }

/* --- text inputs --- */
.book__row{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:clamp(12px,1.6vw,22px); }
.field{ display:flex; flex-direction:column; margin-bottom:1.15rem; min-width:0; }
.field--date{ max-width:260px; }
.field label{
  margin-bottom:.5rem;
  font-size:.72rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-dark);
}
.field label span{ text-transform:none; letter-spacing:0; opacity:.7; }
.field input,
.field textarea{
  width:100%;
  padding:.85rem 0;
  background:transparent;
  border:0; border-bottom:1px solid rgba(9,9,9,.24);
  border-radius:0;
  color:var(--ink);
  font-family:var(--body); font-size:1rem;
  transition:border-color .35s var(--ease);
}
.field textarea{ resize:vertical; }
.field input:focus,
.field textarea:focus{ outline:none; border-bottom-color:var(--green); }
.field input:-webkit-autofill{ -webkit-text-fill-color:var(--ink); transition:background-color 9999s; }
/* On the dark ground this icon had to be inverted to be visible at all.
   Here the browser's own dark glyph is already correct, so it is left
   alone - inverting it now would hide it. */
.field input[type="date"]::-webkit-calendar-picker-indicator{ opacity:.55; cursor:pointer; }
.field input[type="date"]::-webkit-calendar-picker-indicator:hover{ opacity:1; }
.field__hint{ margin:.5rem 0 0; font-size:.8rem; color:var(--muted-dark); }
.field.is-bad input{ border-bottom-color:#A8341A; }

/* --- the action --- */
.book__go{
  position:relative; overflow:hidden;
  display:flex; align-items:center; justify-content:space-between; gap:1.2rem;
  width:100%;
  padding:clamp(17px,1.9vw,23px) clamp(20px,2.3vw,30px);
  border:1px solid var(--ink); border-radius:999px;
  background:transparent; color:var(--ink);
  font-family:var(--display); font-weight:600;
  font-size:clamp(.84rem,1.05vw,.96rem);
  letter-spacing:.08em; text-transform:uppercase;
  cursor:pointer;
  transition:color .5s var(--ease);
}
/* The same rising wash as the service rows. In the brand green here,
   which is the one place on this page it can sit behind off-white text
   at 13:1 and still be unmistakably the brand colour. */
.book__go::before{
  content:''; position:absolute; inset:0; z-index:0;
  background:var(--green);
  transform:translateY(101%);
  transition:transform .55s var(--ease);
}
.book__go > *{ position:relative; z-index:1; }
.book__go:hover::before,
.book__go:focus-visible::before{ transform:translateY(0); }
.book__go:hover,
.book__go:focus-visible{ color:var(--off); border-color:var(--green); }
.book__go-arrow{ transition:transform .5s var(--ease); font-size:1.05em; }
.book__go:hover .book__go-arrow,
.book__go:focus-visible .book__go-arrow{ transform:translateX(7px); }

.book__msg{ margin:.9rem 0 0; font-size:.9rem; min-height:1.2em; color:var(--green); font-weight:500; }
.book__msg[data-bad]{ color:#A8341A; }
.book__fine{ margin:.7rem 0 0; font-size:.81rem; line-height:1.65; color:var(--muted-dark); }

/* --- the aside --- */
.book__aside{ display:grid; gap:clamp(16px,2vw,22px); min-width:0; }
@media (min-width:901px){
  /* Follows the form down, so the readback and the phone number stay in
     reach at whichever step you are on. */
  .book__aside{ position:sticky; top:clamp(96px,13vh,140px); align-self:start; }
}

/* The live readback. It reflects the choices; it does not reserve them,
   and the line at the bottom says exactly that. */
.summary{
  padding:clamp(22px,2.4vw,30px);
  border:1px solid rgba(9,9,9,.16);
  border-radius:calc(var(--r-card) + 4px);
  background:linear-gradient(180deg, rgba(9,9,9,.05), rgba(9,9,9,.015));
}
.summary h3{
  margin:0 0 1rem;
  font-family:var(--display); font-weight:700;
  font-size:.7rem; letter-spacing:.24em; text-transform:uppercase;
  color:var(--green);
}
.summary__list{ margin:0; }
.summary__list > div{
  display:flex; align-items:baseline; justify-content:space-between; gap:1rem;
  padding:.6rem 0;
  border-bottom:1px solid rgba(9,9,9,.1);
}
.summary__list > div:last-child{ border-bottom:0; }
.summary dt{
  flex:none;
  font-size:.72rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-dark);
}
.summary dd{
  margin:0; text-align:right; min-width:0;
  font-size:.92rem; color:var(--ink); font-weight:500;
  font-variant-numeric:tabular-nums;
}
.summary dd[data-empty]{ color:var(--muted-dark); font-weight:400; opacity:.6; }
.summary__fine{
  margin:1rem 0 0; padding-top:.9rem;
  border-top:1px solid rgba(9,9,9,.1);
  font-size:.78rem; line-height:1.55; color:var(--muted-dark);
}

/* One panel with hairline divisions rather than three separate boxes.
   Fewer edges reads quieter, and quieter reads more expensive. */
.stack{
  border:1px solid rgba(9,9,9,.13);
  border-radius:calc(var(--r-card) + 4px);
  overflow:hidden;
}
.stack__row{
  padding:clamp(20px,2.2vw,27px);
  border-bottom:1px solid rgba(9,9,9,.11);
}
.stack__row:last-child{ border-bottom:0; }
.stack__row h3{
  margin:0 0 .8rem;
  font-family:var(--display); font-weight:700;
  font-size:.7rem; letter-spacing:.24em; text-transform:uppercase;
  color:var(--green);
}
.stack__addr{ margin:0 0 .9rem; font-size:.93rem; line-height:1.75; color:var(--muted-dark); }
.stack__link{
  display:block; padding:.36rem 0;
  font-size:.93rem; color:var(--ink); text-decoration:none;
  transition:color .3s var(--ease);
}
.stack__link:hover{ color:var(--green); }

.hours{ margin:0; }
.hours > div{
  display:flex; justify-content:space-between; gap:1rem;
  padding:.44rem 0;
  border-bottom:1px solid rgba(9,9,9,.09);
}
.hours > div:last-child{ border-bottom:0; }
.hours dt{ font-size:.89rem; color:var(--muted-dark); }
.hours dd{ margin:0; font-size:.89rem; color:var(--ink); font-variant-numeric:tabular-nums; }
.hours .is-closed dd{ color:var(--muted-dark); opacity:.75; }

@media (max-width:900px){
  .book{ grid-template-columns:minmax(0,1fr); }
  .book__row{ grid-template-columns:minmax(0,1fr); }
  /* 44px minimum touch target for every pill on a phone. */
  .chip span{ padding:.85rem 1.2rem; }
  .slot span{ padding:.75rem 1rem; }
  .book__step{ padding-left:clamp(44px,11vw,58px); }
}

@media (prefers-reduced-motion:reduce){
  .book__go, .book__go::before, .book__go-arrow,
  .chip span, .slot span, .stack__link{ transition:none; }
}

.foot__hours{ font-variant-numeric:tabular-nums; }


/* =========================================================
   SHARED — the small refinements both sections lean on
   ========================================================= */

/* A numbered label with a rule running off it. Costs one line of markup
   and does more for the "considered" feel than any amount of ornament. */
.eyebrow--rule{
  display:flex; align-items:center; gap:.85rem;
}
.eyebrow--rule span{
  font-variant-numeric:tabular-nums;
  color:var(--gold-lift);
}
.eyebrow--rule::after{
  content:''; height:1px; width:clamp(28px,4vw,58px);
  background:linear-gradient(90deg, rgba(241,241,241,.34), rgba(241,241,241,0));
  flex:none;
}

/* Film grain. A single generated tile, drawn over the section at very low
   opacity in soft-light, so it settles into the photographs and the flat
   grounds without touching the legibility of anything on top. This is the
   difference between "dark background" and "shot on something". */
.feat, .idx-sec, .book-sec{ position:relative; }
.feat::after,
.idx-sec::after,
.book-sec::after{
  content:''; position:absolute; inset:0; z-index:3;
  pointer-events:none;
  opacity:.055;
  mix-blend-mode:soft-light;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* =========================================================
   2. ABOUT — editorial feature

   Desktop: the text column holds still while four plates travel
   past it. That single idea turns a row of thumbnails into a
   spread and gives the section depth with no ornament at all.

   Phone: the same four plates become a full-bleed rail you swipe.
   A 2x2 grid of 169px tiles was the weakest thing on the page -
   four stamps in a corner. One plate at a time, running off both
   edges of the screen, is the whole difference.
   ========================================================= */
.feat{ overflow:clip; }
/* Wider than the standard wrap. The plates need the extra room to read as
   a spread rather than as a sidebar. */
.feat__wrap{
  position:relative; z-index:2;
  width:100%;
  max-width:calc(var(--wrap) + 130px);
  margin-inline:auto;
  padding-inline:var(--gut);
}
.feat__grid{
  display:grid;
  grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(30px,5.5vw,96px);
  align-items:start;
}

/* ---- the column that stays ---- */
.feat__text{
  position:sticky;
  top:clamp(96px,13vh,150px);
  align-self:start;
}
.feat__title{
  margin:1.1rem 0 clamp(22px,2.8vw,34px);
  font-family:var(--display); font-weight:800;
  font-size:clamp(2.6rem,5.6vw,4.9rem);
  line-height:.9; letter-spacing:-.035em; text-transform:uppercase;
}
.feat__title-a,
.feat__title-b{ display:block; }
.feat__title-a{ color:var(--off); }
/* The lifted green measures 4.25:1 on the near-black. Display type only
   needs 3:1, so the surname can carry the brand colour and still be read.
   The base #172E07 could not - it is 1.36:1 here. */
.feat__title-b{
  color:var(--green-glow);
  /* A touch lighter than the given name, so the pair reads as one lockup
     rather than as two shouted words. */
  font-weight:600; letter-spacing:-.02em;
}

.feat__body{ max-width:44ch; }
.feat__body p{ margin:0 0 1.05rem; color:var(--muted); line-height:1.85; font-size:.95rem; }
.feat__lead{
  color:var(--off) !important;
  font-size:clamp(1.06rem,1.5vw,1.24rem) !important;
  line-height:1.68 !important;
  font-weight:300 !important;
}

/* The one piece of large text in the column, and the only place the
   brief's closing line appears. */
.feat__quote{
  position:relative;
  margin:clamp(24px,3vw,36px) 0 0;
  padding-left:clamp(18px,2vw,26px);
  border-left:1px solid var(--green-glow);
  max-width:42ch;
  font-family:var(--display); font-weight:300;
  font-size:clamp(1.05rem,1.6vw,1.3rem);
  line-height:1.55; letter-spacing:-.01em;
  color:var(--off);
}
.feat__quote em{ font-style:normal; color:var(--green-glow); font-weight:600; }

.feat__meta{
  list-style:none; display:flex; flex-wrap:wrap; align-items:center;
  gap:.7rem 1.15rem;
  margin:clamp(22px,2.8vw,32px) 0 clamp(24px,2.8vw,32px); padding:0;
}
.feat__meta li{
  display:flex; align-items:center; gap:1.15rem;
  font-family:var(--display); font-weight:600;
  font-size:.66rem; letter-spacing:.22em; text-transform:uppercase;
  color:var(--muted);
}
.feat__meta li + li::before{
  content:''; width:16px; height:1px; background:var(--green-glow); flex:none;
  margin-left:-.6rem;
}

/* ---- the plates ---- */
.feat__plates{
  position:relative;
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(16px,2.2vw,30px);
  min-width:0;
}
/* A pool of the brand green behind the photographs. The green cannot be
   a line or a word on this ground at 1.36:1, but as diffuse light behind
   an image it is doing the one thing it can do here - giving the corner
   of the page a colour temperature instead of a flat black. */
.feat__plates::before{
  content:''; position:absolute; z-index:0; pointer-events:none;
  /* inset:0, NOT a negative bleed. An absolutely positioned box that
     reaches past its container adds to that container's scrollable
     overflow, which turned this grid into a scroller and made the whole
     section report a horizontal overflow - hidden from the page by
     .feat's overflow:clip, but real. A radial gradient already fades to
     nothing well inside its box, so it needs no bleed to look diffuse,
     and none of the blur filter that came with it either. */
  inset:0;
  background:radial-gradient(74% 64% at 50% 34%, rgba(23,46,7,.6), rgba(23,46,7,0) 72%);
}
.plate{ position:relative; z-index:1; margin:0; min-width:0; }
/* The stagger. MUST come after `.plate{margin:0}`: same specificity, so
   the later rule wins, and reversed the reset silently ate this margin
   and the plates lined up flat. */
.plate--drop{ margin-top:clamp(30px,6.5vw,92px); }

/* A hairline mount with the picture inset inside it. A photograph with a
   margin of its own ground around it reads as mounted rather than as a
   tile in a grid. */
.plate__frame{
  padding:clamp(6px,.7vw,10px);
  border:1px solid rgba(241,241,241,.13);
  border-radius:calc(var(--r-card) + 4px);
  background:rgba(241,241,241,.02);
  transition:border-color .6s var(--ease), background .6s var(--ease);
}
.plate:hover .plate__frame{
  border-color:rgba(241,241,241,.28);
  background:rgba(241,241,241,.045);
}
.plate .media{
  border-radius:var(--r-card);
  /* parallax.js writes transform here. The figure above keeps .reveal's
     transform and the image inside keeps its hover scale, so none of the
     three ever overwrite each other. */
  will-change:transform;
}
/* A vignette drawn as an inset shadow rather than an overlay element, so
   it costs no extra node and cannot intercept a pointer. */
.plate .media::after{
  content:''; position:absolute; inset:0; pointer-events:none;
  border-radius:inherit;
  box-shadow:inset 0 0 70px 10px rgba(9,9,9,.6);
}
.plate img{ display:block; width:100%; height:auto; }
.plate figcaption{
  display:flex; align-items:center; gap:.65rem;
  margin-top:.8rem;
  font-family:var(--display); font-weight:600;
  font-size:.64rem; letter-spacing:.26em;
  color:var(--muted);
}
.plate figcaption span{ font-variant-numeric:tabular-nums; }
.plate figcaption::after{
  content:''; flex:1; height:1px; background:rgba(241,241,241,.12);
}

/* The rail indicator is a phone-only affordance. */
.feat__rail{ display:none; }

/* =========================================================
   ABOUT — phone
   ========================================================= */
@media (max-width:960px){
  .feat__grid{ grid-template-columns:minmax(0,1fr); gap:clamp(30px,6vw,46px); }
  /* Sticky has nothing to stick against in a single column. */
  .feat__text{ position:static; }
  .feat__body,
  .feat__quote{ max-width:60ch; }

  /* Four tiles in a 2x2 grid measured 169px across on a 390px screen.
     Sideways, at 76% of the viewport, each plate is more than twice that
     and one is the whole picture you are looking at. */
  .feat__plates{
    grid-template-columns:none;
    grid-auto-flow:column;
    grid-auto-columns:76%;
    gap:14px;
    align-items:start;
    overflow-x:auto;
    overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;
    /* Full bleed: out to both screen edges, so the strip runs off the
       page instead of sitting in a box. */
    margin-inline:calc(var(--gut) * -1);
    padding:0 var(--gut) 4px;
    /* Without this the first plate parks at the screen edge while the
       heading above it sits at the gutter - measured and fixed before. */
    scroll-padding-inline:var(--gut);
    scrollbar-width:none;
  }
  .feat__plates::-webkit-scrollbar{ display:none; }
  .plate{ scroll-snap-align:start; }
  /* A vertical stagger is meaningless once the plates run horizontally. */
  .plate--drop{ margin-top:0; }
  /* And so is vertical drift inside a sideways scroller. parallax.js
     writes an inline transform, so this has to outrank it. */
  .plate .media{ transform:none !important; }
  /* The pool would sit over the rail rather than behind a block of
     photographs, so it is dropped with the grid. */
  .feat__plates::before{ display:none; }

  .plate__frame{ padding:6px; }

  /* How far along the rail you are. Four plates, so the fill starts at a
     quarter and JS grows it - it is never empty, which would read as
     broken rather than as "at the start". */
  .feat__rail{
    display:block;
    height:2px; margin-top:18px;
    background:rgba(241,241,241,.12);
    border-radius:2px; overflow:hidden;
  }
  .feat__rail-fill{
    display:block; height:100%; width:25%;
    background:var(--green-glow);
    border-radius:inherit;
    transition:width .25s var(--ease);
  }
}

@media (max-width:520px){
  .feat__plates{ grid-auto-columns:82%; gap:12px; }
  .plate__frame{ padding:5px; }
  .feat__title{ margin-top:.9rem; }
}

@media (prefers-reduced-motion:reduce){
  .plate__frame{ transition:none; }
  .feat__rail-fill{ transition:none; }
  .plate .media{ transform:none !important; }
}

/* =========================================================
   5. SERVICES — cinematic index
   Full-width rows instead of cards. The whole band is the link,
   and the brand green fills it on hover: the one job that colour
   can do on a dark page, since off-white on it measures 13:1
   while the green on the near-black is only 1.36:1.
   ========================================================= */
.idx-sec .wrap,
.book-sec .wrap{ position:relative; z-index:2; }
.idx__head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:clamp(18px,3vw,40px); flex-wrap:wrap;
  margin-bottom:clamp(30px,3.8vw,52px);
}
.idx__note{
  margin:0; max-width:30ch;
  font-size:.9rem; line-height:1.75; color:var(--muted);
}
.idx{
  list-style:none; margin:0; padding:0;
  border-top:1px solid rgba(241,241,241,.13);
}
.idx__row{ border-bottom:1px solid rgba(241,241,241,.13); }
.idx__link{
  position:relative; overflow:hidden;
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto auto;
  align-items:center;
  gap:clamp(16px,2.8vw,44px);
  /* More air than a card grid would give. The row is the whole gesture,
     so it can afford the height. */
  padding:clamp(26px,3.4vw,44px) clamp(10px,1.4vw,22px);
  text-decoration:none; color:inherit;
  transition:padding-inline .55s var(--ease);
}
/* The wash rises from the bottom rather than fading in, which reads as
   deliberate rather than as a hover state that got stuck. */
.idx__link::before{
  content:''; position:absolute; inset:0; z-index:0;
  background:var(--green);
  transform:translateY(101%);
  transition:transform .6s var(--ease);
}
.idx__link > *{ position:relative; z-index:1; }
.idx__link:hover::before,
.idx__link:focus-visible::before{ transform:translateY(0); }
.idx__link:hover,
.idx__link:focus-visible{ padding-inline:clamp(20px,2.6vw,38px) clamp(10px,1.4vw,22px); }

/* The oversized numeral sitting behind the row. Barely there until the
   wash arrives, then it lifts just enough to give the band a background
   of its own instead of a flat fill. */
.idx__ghost{
  position:absolute; z-index:0;
  right:clamp(-6px,1vw,14px); top:50%;
  transform:translateY(-50%) translateX(12px);
  font-family:var(--display); font-weight:800;
  font-size:clamp(4.4rem,10vw,9rem);
  line-height:1; letter-spacing:-.05em;
  font-variant-numeric:tabular-nums;
  color:rgba(241,241,241,.035);
  transition:color .6s var(--ease), transform .8s var(--ease);
  pointer-events:none; user-select:none;
}
.idx__link:hover .idx__ghost,
.idx__link:focus-visible .idx__ghost{
  color:rgba(241,241,241,.085);
  transform:translateY(-50%) translateX(0);
}

.idx__num{
  font-family:var(--display); font-weight:700;
  font-size:.74rem; letter-spacing:.2em;
  font-variant-numeric:tabular-nums;
  color:var(--green-glow);
  transition:color .5s var(--ease);
}
.idx__link:hover .idx__num,
.idx__link:focus-visible .idx__num{ color:var(--off-2); }

.idx__body{ display:block; min-width:0; }
.idx__name{
  display:block;
  font-family:var(--display); font-weight:700;
  font-size:clamp(1.34rem,2.9vw,2.35rem);
  line-height:1.1; letter-spacing:-.025em;
  color:var(--off);
}
.idx__items{
  display:block; margin-top:.58rem;
  font-size:.87rem; line-height:1.65; color:var(--muted);
  transition:color .5s var(--ease);
}
.idx__link:hover .idx__items,
.idx__link:focus-visible .idx__items{ color:rgba(241,241,241,.84); }

.idx__focus{
  text-align:right; min-width:0;
  font-size:.85rem; line-height:1.45; color:var(--off-2);
}
.idx__focus span{
  display:block;
  font-family:var(--display); font-weight:700;
  font-size:.6rem; letter-spacing:.24em; text-transform:uppercase;
  color:var(--green-glow); margin-bottom:.3rem;
  transition:color .5s var(--ease);
}
.idx__link:hover .idx__focus span,
.idx__link:focus-visible .idx__focus span{ color:rgba(241,241,241,.68); }

.idx__go{
  display:grid; place-items:center;
  width:2.75rem; height:2.75rem; flex:none;
  border:1px solid rgba(241,241,241,.2); border-radius:50%;
  font-size:1rem; color:var(--off);
  transition:transform .5s var(--ease), border-color .5s var(--ease),
             background .5s var(--ease), color .5s var(--ease);
}
.idx__link:hover .idx__go,
.idx__link:focus-visible .idx__go{
  transform:translateX(7px);
  border-color:var(--off); background:var(--off); color:var(--green);
}

/* ---- the floating preview ----
   Built and positioned by preview.js, which never runs on a touch screen
   or under reduced motion. height:auto, not a fixed box with cover, so
   the salon's work is never cropped to fit a frame. */
.idx__preview{
  position:fixed; left:0; top:0; z-index:60;
  width:clamp(170px,15vw,250px);
  padding:6px;
  border:1px solid rgba(241,241,241,.24);
  border-radius:10px;
  background:rgba(9,9,9,.55);
  backdrop-filter:blur(6px);
  opacity:0; pointer-events:none;
  transform:translate3d(-50%,-50%,0) scale(.9) rotate(-2deg);
  transition:opacity .45s var(--ease), transform .45s var(--ease);
  will-change:transform;
}
.idx__preview img{
  display:block; width:100%; height:auto; border-radius:6px;
}
.idx__preview.is-on{
  opacity:1;
  transform:translate3d(-50%,-50%,0) scale(1) rotate(-2deg);
}

/* =========================================================
   SERVICES — phone

   On a pointer screen this section is carried by things a phone
   does not have: the green wash on hover, the arrow filling, the
   ghost numeral lifting, and the plate of work that follows the
   cursor. Measured (hover: hover) as false here - so on a phone
   every one of those was inert and the rows sat flat.

   Two changes fix that. The work photograph each row already
   names for the cursor preview is shown inline instead, so the
   images that were desktop-only now do the work on the screen
   that had nothing. And the wash is bound to :active as well, so
   a tap lights the row the way a hover does.
   ========================================================= */
@media (max-width:860px){
  .idx__link{
    grid-template-columns:84px minmax(0,1fr) auto;
    /* The thumbnail sits beside the number and the name only. The service
       list and the focus line run the FULL width underneath it.

       That matters more than it looks: with the picture beside all four
       rows the text column dropped from 242px to 173px, every list
       wrapped to more lines, and the section grew from 1025px to 1299px -
       taller for having added something, which is the opposite of the
       point. Underneath, the text has the whole screen again. */
    grid-template-areas:
      "thumb num   go"
      "thumb name  name"
      "items items items"
      "focus focus focus";
    align-items:start;
    column-gap:14px; row-gap:0;
    padding:20px clamp(10px,1.4vw,20px) 22px;
  }

  /* The thumbnail. Painted as a background on the row's own ::after
     rather than added to the markup, because a rule inside a media query
     that does not match is never fetched - so a desktop visitor pays
     nothing for these, and the cursor preview goes on using data-preview
     exactly as before. All four are also in the portfolio mosaic further
     down the page, so they cost a phone nothing extra overall: they are
     the same files, fetched a little sooner.

     NOTE the leading slash on --thumb in index.html. A url() inside a
     custom property is resolved against the stylesheet that substitutes
     the var(), NOT the document that declared it - so a path written
     relative there resolves against assets/css/ and 404s. Root-relative
     sidesteps that. It is also why the sibling data-preview attribute
     keeps its document-relative path: that one is read by JavaScript and
     assigned to img.src, which resolves against the page as expected. */
  .idx__link::after{
    content:'';
    grid-area:thumb;
    position:relative; z-index:1;
    width:84px; height:84px;
    border-radius:10px;
    background-image:var(--thumb);
    background-size:cover;
    /* Biased above centre - these are portraits, and heads sit high. */
    background-position:50% 32%;
    border:1px solid rgba(241,241,241,.18);
    align-self:start;
  }

  .idx__num{ grid-area:num; padding-top:3px; }
  .idx__go{ grid-area:go; width:2.3rem; height:2.3rem; }
  .idx__name{
    grid-area:name; margin-top:7px;
    font-size:1.3rem; letter-spacing:-.02em;
  }
  /* grid-column:1 / -1 explicitly, rather than trusting the named area
     alone. Measured the two of these behaving differently from identical
     `grid-area` declarations - focus spanned all three columns, items
     stayed in the middle one - so the placement is stated outright and
     the named areas are left to do only the vertical ordering. */
  .idx__items{
    grid-area:items; grid-column:1 / -1;
    margin-top:13px; font-size:.83rem; line-height:1.62;
  }
  .idx__focus{
    grid-area:focus; grid-column:1 / -1;
    text-align:left; margin-top:11px;
  }

  /* The thumbnail is the weight in the row now; a giant numeral behind it
     as well would be two things competing for the same corner. */
  .idx__ghost{ display:none; }
}

/* A tap has to do what a hover does, or the row never lights at all on the
   devices that cannot hover. */
@media (hover: none){
  .idx__link:active::before{ transform:translateY(0); }
  .idx__link:active .idx__num{ color:var(--off-2); }
  .idx__link:active .idx__items{ color:rgba(241,241,241,.84); }
  .idx__link:active .idx__focus span{ color:rgba(241,241,241,.68); }
  .idx__link:active .idx__go{
    background:var(--off); border-color:var(--off); color:var(--green);
  }
}

@media (prefers-reduced-motion:reduce){
  .idx__link,
  .idx__link::before,
  .idx__num,.idx__items,.idx__focus span,.idx__go,.idx__ghost,
  .plate__frame{ transition:none; }
  .plate .media{ transform:none !important; }
  .idx__preview{ display:none !important; }
}

/* ---------------------------------------------------------
   6b. PORTFOLIO — the channels
   Given their own band under the mosaic rather than a handle
   tucked beside the heading, because the ask was for the links
   to be findable, and one small grey handle is not.
   --------------------------------------------------------- */
.gallery__note{
  margin:0; max-width:34ch;
  font-size:.9rem; line-height:1.7; color:var(--muted);
}

.social{
  margin-top:clamp(28px,3.4vw,46px);
  padding-top:clamp(24px,3vw,36px);
  border-top:1px solid rgba(241,241,241,.12);
  display:flex; align-items:center; justify-content:space-between;
  gap:clamp(16px,3vw,40px); flex-wrap:wrap;
}
.social__lead{
  margin:0; max-width:28ch;
  font-size:.92rem; line-height:1.7; color:var(--muted);
}
.social__links{ display:flex; gap:12px; flex-wrap:wrap; }

.social__btn{
  position:relative; overflow:hidden;
  display:flex; align-items:center; gap:.85rem;
  padding:.8rem 1.3rem;
  border:1px solid rgba(241,241,241,.2);
  border-radius:999px;
  color:var(--off); text-decoration:none;
  transition:color .45s var(--ease), border-color .45s var(--ease);
}
/* The same rising wash as the service rows and the booking button, so the
   page has one idea about what a filled control looks like. Off-white
   rather than the brand green: this sits on the near-black, where that
   green is 1.36:1 and would read as a smudge. */
.social__btn::before{
  content:''; position:absolute; inset:0; z-index:0;
  background:var(--off);
  transform:translateY(101%);
  transition:transform .5s var(--ease);
}
.social__btn:hover::before,
.social__btn:focus-visible::before{ transform:translateY(0); }
.social__btn:hover,
.social__btn:focus-visible{ color:var(--ink); border-color:var(--off); }

.social__btn svg{ position:relative; z-index:1; width:21px; height:21px; flex:none; }
.social__name{
  position:relative; z-index:1;
  display:flex; flex-direction:column; line-height:1.25;
  font-family:var(--display); font-weight:600; font-size:.9rem;
}
.social__name em{
  font-style:normal; font-weight:400; font-size:.75rem;
  color:var(--muted); letter-spacing:.01em;
  transition:color .45s var(--ease);
}
/* On the filled state the handle sits on off-white, where --muted would
   drop to 1.2:1. */
.social__btn:hover .social__name em,
.social__btn:focus-visible .social__name em{ color:var(--muted-dark); }

.social__go{ position:relative; z-index:1; font-size:.95rem; opacity:.55; }

@media (max-width:640px){
  .social{ display:block; }
  .social__lead{ max-width:none; margin-bottom:18px; }
  .social__links{ display:grid; gap:10px; }
  /* Full width, so each is an easy thumb target rather than a small pill. */
  .social__btn{ justify-content:flex-start; padding:.95rem 1.2rem; }
  .social__go{ margin-left:auto; }
}

@media (prefers-reduced-motion:reduce){
  .social__btn, .social__btn::before, .social__name em{ transition:none; }
}

/* ---------------------------------------------------------
   6b. PORTFOLIO — the feed
   Three reels over three photographs, three across, which is
   the shape Instagram itself uses and therefore the shape this
   content is already read in.
   --------------------------------------------------------- */
.feed{
  list-style:none; margin:0; padding:0;
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(10px,1.4vw,18px);
}
.feed--reels{ margin-bottom:clamp(10px,1.4vw,18px); }
.feed__cell{ min-width:0; }

/* Both rows show true colour. The shared .media slot puts a
   grayscale/sepia grade on photographs elsewhere on the site, but the
   reel covers are not in that slot, and a graded photo sitting beside an
   ungraded cover looks like one of them is broken. This is also the one
   section whose whole job is showing the work as it actually came out. */
.feed--photos .media img{ filter:none; }
.feed--photos .media:hover img{ filter:none; }

/* ---- a reel card ---- */
.reelcard{
  position:relative; display:block;
  border-radius:var(--r-card);
  overflow:hidden;
  border:1px solid rgba(241,241,241,.12);
  background:var(--ink-2);
  text-decoration:none;
}
.reelcard__cover{
  display:block; width:100%; height:auto;
  transition:transform .7s var(--ease);
}
.reelcard:hover .reelcard__cover{ transform:scale(1.045); }

/* A play badge, not a label. It is the only affordance saying this one
   moves, so it stays visible rather than appearing on hover - there is no
   hover on a phone. */
.reelcard__play{
  position:absolute; inset:0; margin:auto;
  width:clamp(42px,4.4vw,58px); height:clamp(42px,4.4vw,58px);
  display:grid; place-items:center;
  border-radius:50%;
  background:rgba(9,9,9,.55);
  border:1px solid rgba(241,241,241,.5);
  color:var(--off);
  backdrop-filter:blur(3px);
  transition:background .4s var(--ease), transform .4s var(--ease);
}
.reelcard__play svg{ width:42%; height:42%; margin-left:8%; }
.reelcard:hover .reelcard__play{ background:var(--green); transform:scale(1.08); }

.reelcard__tag{
  position:absolute; top:10px; left:10px;
  padding:.28em .6em;
  border-radius:5px;
  background:rgba(9,9,9,.6);
  font-family:var(--display); font-weight:700;
  font-size:.56rem; letter-spacing:.2em; text-transform:uppercase;
  color:var(--off);
}

/* ---- what replaces the card once it is played ---- */
.reelframe{
  position:relative;
  border-radius:var(--r-card);
  overflow:hidden;
  border:1px solid rgba(241,241,241,.12);
  background:var(--ink-2);
  /* Instagram's embed is a fixed-ratio player; this is the shape it wants
     for a reel and stops the card jumping height when it swaps in. */
  aspect-ratio:9/16;
}
.reelframe iframe{
  position:absolute; inset:0;
  width:100%; height:100%;
  border:0;
}

@media (max-width:560px){
  .feed{ gap:7px; }
  .feed--reels{ margin-bottom:7px; }
  .reelcard__tag{ top:7px; left:7px; font-size:.5rem; }
}

@media (prefers-reduced-motion:reduce){
  .reelcard__cover, .reelcard__play{ transition:none; }
  .reelcard:hover .reelcard__cover{ transform:none; }
}

/* The Grooming Packages row previews a rate card rather than a photograph,
   and a rate card must not be cropped: cover would slice the price list
   into an unreadable fragment nobody could read a price off. contain
   shows the whole card instead.

   The thumb box is 84x84 and the card is 680x844, so contain fits it by
   height at 68x84 and leaves 8px either side. The fill behind is the
   off-white, which is the card's own ground, so those 8px read as part
   of the card rather than as a gap. */
@media (max-width:860px){
  .idx__link--card::after{
    background-size:contain;
    background-repeat:no-repeat;
    background-position:50% 50%;
    background-color:var(--off);
  }
}

/* ---------------------------------------------------------
   6d. PORTFOLIO — the work gallery
   A masonry, because these photographs run from 0.56 to 1.5 in
   ratio and any fixed grid would have to crop most of them.
   CSS columns with break-inside:avoid lets each keep its shape.
   --------------------------------------------------------- */
.gal{
  list-style:none; margin:0; padding:0;
  columns:4;
  column-gap:clamp(10px,1.4vw,18px);
}
.gal__item{
  break-inside:avoid;
  /* Safari still wants the old spelling. */
  -webkit-column-break-inside:avoid;
  page-break-inside:avoid;
  margin-bottom:clamp(10px,1.4vw,18px);
}
/* Hidden tiles must not leave a gap in the column flow. The `hidden`
   attribute alone is overridden by the display below, so it is restated. */
.gal__item[hidden]{ display:none; }

/* The same hairline mount the About plates use, so a photograph reads as
   mounted rather than as a tile butted against its neighbour. */
.gal__frame{
  position:relative;
  overflow:hidden;
  border-radius:var(--r-card);
  border:1px solid rgba(241,241,241,.12);
  background:var(--ink-2);
  transition:border-color .5s var(--ease);
}
.gal__item:hover .gal__frame{ border-color:rgba(241,241,241,.3); }
.gal__frame img{
  display:block;
  width:100%; height:auto;   /* natural ratio: nothing is cropped */
  transition:transform .7s var(--ease);
}
.gal__item:hover .gal__frame img{ transform:scale(1.04); }

/* ---- show the rest ---- */
.gal__more{
  display:flex; justify-content:center;
  margin-top:clamp(20px,2.6vw,32px);
}
.gal__btn{
  position:relative; overflow:hidden;
  display:inline-flex; align-items:center; gap:.8rem;
  padding:.9rem 1.6rem;
  border:1px solid rgba(241,241,241,.24);
  border-radius:999px;
  background:transparent; color:var(--off);
  font-family:var(--display); font-weight:600;
  font-size:.82rem; letter-spacing:.08em; text-transform:uppercase;
  cursor:pointer;
  transition:color .45s var(--ease), border-color .45s var(--ease);
}
/* The same rising wash as the service rows and the booking button. */
.gal__btn::before{
  content:''; position:absolute; inset:0; z-index:0;
  background:var(--off);
  transform:translateY(101%);
  transition:transform .5s var(--ease);
}
.gal__btn > *{ position:relative; z-index:1; }
.gal__btn:hover::before,
.gal__btn:focus-visible::before{ transform:translateY(0); }
.gal__btn:hover,
.gal__btn:focus-visible{ color:var(--ink); border-color:var(--off); }
.gal__btn-arrow{ transition:transform .45s var(--ease); }
.gal__btn:hover .gal__btn-arrow{ transform:translateY(3px); }
/* Once everything is open the arrow points back the way it came. */
.gal__btn[aria-expanded="true"] .gal__btn-arrow{ transform:rotate(180deg); }
.gal__btn[aria-expanded="true"]:hover .gal__btn-arrow{ transform:rotate(180deg) translateY(3px); }

@media (max-width:1000px){ .gal{ columns:3; } }
@media (max-width:640px){  .gal{ columns:2; column-gap:8px; } .gal__item{ margin-bottom:8px; } }

@media (prefers-reduced-motion:reduce){
  .gal__frame, .gal__frame img, .gal__btn, .gal__btn::before, .gal__btn-arrow{ transition:none; }
  .gal__item:hover .gal__frame img{ transform:none; }
}

/* The reveal button is useless without the script that drives it, so it
   stays out of the page until that script marks itself present. */
.gal__more{ display:none; }
.js .gal__btn.is-live{ display:inline-flex; }
.js .gal__more:has(.gal__btn.is-live){ display:flex; }

/* ---------------------------------------------------------
   TESTIMONIALS — supplied cards, and notes that are not quotes
   --------------------------------------------------------- */
/* Whichever picture is not in use must take no space at all, or the grid
   keeps a column open for it and the card sits off-centre. */
.testi__grid > [hidden]{ display:none !important; }

.tshot{ min-width:0; }
.tshot .media{
  border-radius:var(--r-card);
  background:rgba(9,9,9,.2);
}
.tshot img{ display:block; width:100%; height:auto; }

/* A note is the salon describing a client; a quote is the client speaking.
   They must not look alike, so this gets no quotation marks, no italics
   and a plainer weight - it should read as a caption, never as speech. */
.tcard__note{
  margin:0 0 1.1rem;
  font-family:var(--body); font-weight:300; font-style:normal;
  font-size:clamp(.92rem,1.15vw,1.02rem);
  line-height:1.7;
  color:rgba(241,241,241,.82);
}
.tcard__note::before,
.tcard__note::after{ content:none; }

/* ---------------------------------------------------------
   CENTRED SECTION HEAD

   The numbered rule the other sections carry, arranged for a
   centred band: a hairline on BOTH sides of the number, so the
   pair reads as a frame rather than shoving the text left.
   --------------------------------------------------------- */
.sechead{ text-align:center; max-width:64ch; margin-inline:auto; }
.sechead .eyebrow{ margin-bottom:1.15rem; }
.sechead .h-sec{ margin-bottom:0; }

.eyebrow--center{ justify-content:center; }
.eyebrow--center::before{
  content:''; height:1px; width:clamp(28px,4vw,58px); flex:none;
  background:linear-gradient(270deg, rgba(241,241,241,.34), rgba(241,241,241,0));
}

/* A short gold hairline under the heading. Decoration only - it carries
   no meaning, so it is aria-hidden in the markup and needs no contrast
   floor of its own. */
.sechead__rule{
  display:block; height:1px; width:clamp(54px,7vw,98px);
  margin:1.15rem auto 0;
  background:linear-gradient(90deg,
    rgba(241,241,241,0), var(--gold), rgba(241,241,241,0));
}

/* .78 over the emerald measures 8.4:1, so this stays comfortably
   readable rather than being decorative grey. */
.sechead__lead{
  margin:1.05rem auto 0; max-width:46ch;
  font-size:clamp(.9rem,1.15vw,1rem); line-height:1.75;
  color:rgba(241,241,241,.78);
}

@media (max-width:620px){
  /* Two hairlines plus a number plus the label is a lot of furniture for
     a phone, and it wraps. The number and label are the part that
     carries meaning, so the rules go. */
  .eyebrow--center::before,
  .sechead .eyebrow--rule::after{ display:none; }
  .sechead__lead{ font-size:.92rem; }
}

/* ---------------------------------------------------------
   QUOTE vs NOTE, made visible

   The note was set in rgba(241,241,241,.82) - which is very nearly
   white ON A WHITE CARD. Maaz Safdar's line was rendering at about
   1.1:1: present in the DOM, invisible on screen. That colour was
   written for a dark ground and never re-checked against .tcard.

   It is now dark enough to read (8.2:1), and the quote/note
   distinction is carried where it belongs - by the quotation marks,
   which only real speech gets.
   --------------------------------------------------------- */
.tcard__quote::before{ content:'\201C'; }
.tcard__quote::after{ content:'\201D'; }

.tcard__note{
  color:#4F4F4F;
  font-size:.86rem;
  letter-spacing:.005em;
}
