/* ==========================================================================
   MR STYLE — main stylesheet
   Mobile first. Every breakpoint adds, never subtracts.
   Colour is never the only signal: state is also carried by weight, border,
   icon or underline, so the site reads correctly with any colour vision.
   ========================================================================== */

/* --- tokens --------------------------------------------------------------

   Lightened in 4.4. The ground was #0A0B0F — a relative luminance of 0.0034,
   which is within rounding distance of pure black. It looked expensive in a
   screenshot and it was hard work to read for any length of time, and this is
   a site people arrive at anxious and stay on for a while: reading four base
   types, a price list, a FAQ and a booking form.

   The new ground is 3.9x lighter and every text colour was re-measured against
   it rather than assumed. The quietest text improved as well, from 5.04:1 to
   6.05:1, because a lighter ground let the greys come up with it:

     bone        #F6F3EF   15.03:1 on ground
     bone-dim    #C0BAB2    8.64:1
     bone-quiet  #A29B93    6.05:1   <- the floor, well over the 4.5 minimum
     bronze      #E9CCBA   10.93:1
     bronze-dim  #CBA792    7.51:1
     flame       #F0913F    6.99:1
     #14100C on flame (button labels)  7.96:1

   These are also the defaults in the Design tab, so the two cannot drift. A
   site that has saved custom colours keeps them — changing a colour somebody
   chose is not an upgrade.
   -------------------------------------------------------------------------- */
:root{
  --ground:#1C1E26;
  --panel:#262A35;
  --line:rgba(242,238,233,.14);
  --line-soft:rgba(242,238,233,.08);
  --flame:#F0913F;
  --bronze:#E9CCBA;
  --bronze-dim:#CBA792;
  --bone:#F6F3EF;
  --bone-dim:#C0BAB2;
  --bone-quiet:#A29B93;      /* 6.05:1 on --ground — the quietest text allowed */
  --img-fit:contain;

  --display:"Barlow Condensed","Oswald","Arial Narrow",system-ui,sans-serif;
  --text:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;

  --shell:1240px;
  --gut:20px;
  --nav-h:60px;
  --tap:44px;                /* WCAG 2.5.5 minimum */
  --r:4px;
  --ease:cubic-bezier(.22,1,.36,1);
}

/* --- reset --------------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + env(safe-area-inset-top, 0px) + 12px)}
body{
  margin:0;
  background:var(--ground);
  color:var(--bone);
  font-family:var(--text);
  font-size:16px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img,video,svg,iframe{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
h1,h2,h3,h4,p,figure,address{margin:0}
ul{margin:0;padding:0;list-style:none}
address{font-style:normal}
:focus-visible{outline:3px solid var(--flame);outline-offset:3px;border-radius:2px}

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

.skip{position:absolute;left:-9999px;top:0;z-index:999;background:var(--flame);color:#0A0B0F;padding:12px 18px;font-weight:700}
.skip:focus{left:8px;top:8px}

/* --- layout -------------------------------------------------------------- */
.shell{width:100%;max-width:var(--shell);margin-inline:auto;padding-inline:var(--gut)}
.shell--narrow{max-width:760px}

.sec{padding:clamp(56px,11vw,120px) 0;position:relative}
.sec--panel{background:var(--panel);border-block:1px solid var(--line-soft)}
.sec__head{margin-bottom:clamp(28px,5vw,52px);max-width:640px}
.sec__head--c{margin-inline:auto;text-align:center}
.sec__cta{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-top:clamp(28px,5vw,44px)}

/* --- type ---------------------------------------------------------------- */
.d1,.d2{
  font-family:var(--display);
  font-weight:800;
  text-transform:uppercase;
  line-height:.92;
  letter-spacing:-.01em;
  overflow-wrap:break-word;
}
.d1{font-size:clamp(2.6rem,12vw,7rem)}
.d2{font-size:clamp(2rem,7vw,4.2rem)}

.eyebrow{
  font-family:var(--display);
  font-size:.82rem;
  font-weight:700;
  letter-spacing:.24em;
  text-transform:uppercase;
  color:var(--bronze);
  margin-bottom:.75rem;
}
.eyebrow::before{content:"";display:inline-block;width:22px;height:2px;background:var(--flame);vertical-align:.28em;margin-right:.7em}

.body{font-size:clamp(1rem,2.4vw,1.12rem);color:var(--bone-dim);max-width:56ch}
.sec__head--c .body{margin-inline:auto}
.small{font-size:.9rem;color:var(--bone-dim)}

.prose p{margin-bottom:1.1em}
.prose h2,.prose h3{font-family:var(--display);text-transform:uppercase;color:var(--bone);margin:1.6em 0 .5em}
.prose a{color:var(--flame);text-decoration:underline;text-underline-offset:3px}

/* --- buttons ------------------------------------------------------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;
  min-height:var(--tap);padding:0 26px;
  font-family:var(--display);font-size:1rem;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;
  border:2px solid transparent;border-radius:var(--r);
  transition:transform .2s var(--ease),background-color .2s,color .2s,border-color .2s;
}
.btn:active{transform:translateY(1px)}
.btn--flame{background:var(--flame);color:#14100C;border-color:var(--flame)}
.btn--flame:hover{background:#F19B55;border-color:#F19B55}
.btn--ghost{border-color:var(--line);color:var(--bone)}
.btn--ghost:hover{border-color:var(--bone);background:rgba(242,238,233,.06)}
.btn--sm{min-height:var(--tap);padding:0 16px;font-size:.86rem}

/* --- header -------------------------------------------------------------- */
/*
 * The header was a fixed --nav-h tall AND had the notch inset added as inner
 * padding, so on every iPhone with a notch the padding ate into that fixed
 * height and sliced the top off the logo and the menu. The inset has to grow
 * the box, not be pushed inside it.
 */
.nav{
  position:fixed;inset:0 0 auto;z-index:80;
  height:calc(var(--nav-h) + env(safe-area-inset-top, 0px));
  padding-top:env(safe-area-inset-top, 0px);
  display:flex;align-items:center;
  background:rgba(28,30,38,.72);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border-bottom:1px solid transparent;
  transition:background-color .3s,border-color .3s;
}
.nav[data-solid="true"]{background:rgba(28,30,38,.96);border-bottom-color:var(--line-soft)}
.nav__row{display:flex;align-items:center;gap:12px;width:100%}

.nav__brand{display:flex;align-items:center;gap:10px;min-height:var(--tap);margin-right:auto}
.nav__brand .custom-logo{height:30px;width:auto}
.nav__id{display:flex;flex-direction:column;line-height:1}
.nav__id b{font-family:var(--display);font-size:1.22rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase}
.nav__id i{font-style:normal;font-size:.56rem;letter-spacing:.2em;text-transform:uppercase;color:var(--bronze-dim);margin-top:3px}

/*
 * Drawer on phones, inline row from 900px up.
 *
 * The closed position used to be translateY(-118%), and a percentage here is
 * measured against the drawer's OWN height — but the drawer starts --nav-h
 * down the page, so it has to travel its height PLUS --nav-h to get out of
 * sight. With four links the drawer is ~249px, 18% of it is ~45px, and the
 * nav is 60px: the bottom 15px of the menu sat over the top of the header on
 * every load. The fewer the menu items, the more of it showed.
 *
 * So the shift is stated exactly instead of guessed: its own height, plus the
 * offset it starts at, plus 4px for the border and subpixel rounding. That
 * holds for a two-item menu and a ten-item one alike.
 *
 * The offsets are written out longhand rather than with `inset`, because
 * `inset` also sets bottom/left/right, and those leak into the desktop layout
 * where the drawer becomes a normal flex child.
 */
.nav__links{
  position:fixed;
  top:calc(var(--nav-h) + env(safe-area-inset-top, 0px));
  left:0;right:0;bottom:auto;
  display:grid;gap:2px;
  background:var(--panel);
  border-bottom:1px solid var(--line);
  padding:8px var(--gut) 20px;
  transform:translateY(calc(-100% - var(--nav-h) - env(safe-area-inset-top, 0px) - 4px));
  /* Hidden for real when closed, so it cannot be tabbed into off-screen. */
  visibility:hidden;
  transition:transform .38s var(--ease), visibility 0s linear .38s;
  max-height:calc(100vh - var(--nav-h));
  max-height:calc(100dvh - var(--nav-h) - env(safe-area-inset-top, 0px));
  overflow-y:auto;overscroll-behavior:contain;
  padding-bottom:calc(20px + env(safe-area-inset-bottom, 0px));
}
.nav__links[data-open="true"]{
  transform:translateY(0);
  visibility:visible;
  transition:transform .38s var(--ease), visibility 0s;
}
.nav__links a{
  display:flex;align-items:center;min-height:52px;
  font-family:var(--display);font-size:1.32rem;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;
  border-bottom:1px solid var(--line-soft);
}
.nav__links a:last-child{border-bottom:0}
.nav__links .nav__acct{color:var(--bronze)}

.nav__right{display:flex;align-items:center;gap:8px}

.nav__bag{
  display:flex;align-items:center;gap:6px;
  min-height:var(--tap);min-width:var(--tap);padding:0 10px;
  font-family:var(--display);font-size:.9rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  border:1px solid var(--line);border-radius:var(--r);
}
.nav__bagtxt{display:none}
.nav__bag b{
  min-width:20px;height:20px;padding:0 5px;
  display:grid;place-items:center;
  background:var(--flame);color:#14100C;
  border-radius:10px;font-size:.72rem;font-family:var(--text);
}
.nav__bag b[data-zero]{background:transparent;color:var(--bone-quiet);border:1px solid var(--line)}

.burger{width:var(--tap);height:var(--tap);display:grid;place-content:center;gap:5px;border:1px solid var(--line);border-radius:var(--r)}
.burger i{display:block;width:20px;height:2px;background:var(--bone);transition:transform .3s var(--ease),opacity .2s}
.burger[aria-expanded="true"] i:nth-child(1){transform:translateY(7px) rotate(45deg)}
.burger[aria-expanded="true"] i:nth-child(2){opacity:0}
.burger[aria-expanded="true"] i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

@media (max-width:479px){ .nav__book{display:none} }

@media (min-width:900px){
  :root{--nav-h:76px;--gut:32px}
  /* The brand is the one thing in the bar that must never wrap: three stacked
     words where a lockup should be is what a broken header looks like. */
  .nav__row > a:first-child{flex:none;white-space:nowrap}
}

/*
 * The bar becomes a row of links at 1200px, not at 900px.
 *
 * The nav carries nine destinations now. Measured at 1024px they need 709px of
 * bar on their own; with the brand at 201px and the bag and Book button at
 * 176px that is roughly 1,100px before any breathing room — so at 1024 the
 * last three, FAQ, About and Visit, were being laid out underneath the bag and
 * the Book button.
 *
 * Worth recording how that was nearly dismissed. The container reported a tidy
 * 533px width and "no overflow", because a shrunk flex item lets its children
 * paint outside its own box without complaining. Only measuring each link's
 * own rectangle showed the last one ending at x=954 while the bag started at
 * x=816: the container box was lying and the screenshot was telling the truth.
 *
 * Between 900 and 1199 the drawer stays. Nothing is lost — every destination
 * is still there, behind a button, at a width where a row of nine was never
 * going to be readable.
 */
@media (min-width:1200px){
  .nav__links{
    position:static;transform:none;background:none;border:0;padding:0;overflow:visible;
    display:flex;align-items:center;gap:clamp(14px,1.8vw,26px);max-height:none;
    min-width:0;flex-wrap:nowrap;
    /*
     * The drawer offsets have to be cancelled, not just the position. A later
     * rule in this file sets position:relative on this element for the hover
     * underline, and `relative` makes the leftover top:--nav-h take effect —
     * which dropped the whole menu 76px BELOW the header, on top of the hero.
     * Clearing the offsets means the menu stays put no matter which
     * positioning scheme wins the cascade.
     */
    inset:auto;
    /* Always visible here: it is a plain row, never a drawer. */
    visibility:visible;
  }
  .nav__links a{
    /* 44px tall even on desktop: it costs nothing and makes the header
       comfortable with a trackpad as well as a finger on a touch laptop. */
    min-height:44px;display:inline-flex;align-items:center;
    font-size:clamp(.76rem,.9vw,.84rem);letter-spacing:clamp(.08em,.14vw,.18em);border:0;
    color:var(--bone-dim);position:relative;white-space:nowrap;
    transition:color .2s;
  }
  .nav__links a::after{bottom:8px}
  .nav__links a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--flame);transform:scaleX(0);transform-origin:left;transition:transform .25s var(--ease)}
  .nav__links a:hover{color:var(--bone)}
  .nav__links a:hover::after{transform:scaleX(1)}
  .nav__links .nav__acct{margin-left:6px;padding-left:26px;border-left:1px solid var(--line)}
  .burger{display:none}
  .nav__bagtxt{display:inline}
  .nav__right{gap:12px;margin-left:26px}
}

/* --- full-bleed panels --------------------------------------------------- */
.panel{position:relative;min-height:100svh;display:flex;align-items:flex-end;overflow:hidden;isolation:isolate}
.panel--center{align-items:center;text-align:center}
.panel__bg{position:absolute;inset:0;z-index:-2;background:var(--ground)}
/* <picture> is an inline wrapper by default, so without this the image inside
   it would size against a shrink-wrapped box instead of the panel. */
.panel__bg picture,.founder__shot picture{display:block;width:100%;height:100%}
.panel__img{width:100%;height:100%;object-fit:var(--img-fit);object-position:center;background:var(--ground)}

/*
 * The banner video sits on top of the still image, which stays underneath as
 * the poster. It fades in only once the browser confirms it is actually
 * playing — so a refused autoplay, a slow connection or a paused low-power
 * phone all leave the still image on screen instead of a black rectangle.
 */
.panel__vid{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:var(--img-fit);object-position:center;
  opacity:0;transition:opacity .9s var(--ease);
  pointer-events:none;
}
.panel__vid[data-playing="true"]{opacity:1}
@media (prefers-reduced-motion:reduce){
  /* Asked for less motion: the still image is the whole banner. */
  .panel__vid{display:none!important}
}
.plate{width:100%;height:100%;background:
  radial-gradient(120% 90% at 50% 0%,#2C3040 0%,#1C1E26 70%),
  repeating-linear-gradient(135deg,rgba(255,255,255,.02) 0 2px,transparent 2px 6px)}
.panel__scrim{position:absolute;inset:0;z-index:-1}
.scrim--center{background:radial-gradient(120% 90% at 50% 55%,rgba(28,30,38,.35) 0%,rgba(28,30,38,.86) 68%,#1C1E26 100%)}
.scrim--left{background:linear-gradient(90deg,rgba(28,30,38,.95) 0%,rgba(28,30,38,.72) 46%,rgba(28,30,38,.35) 100%)}
.scrim--right{background:linear-gradient(270deg,rgba(28,30,38,.95) 0%,rgba(28,30,38,.72) 46%,rgba(28,30,38,.35) 100%)}
.panel__in{position:relative;width:100%;max-width:var(--shell);margin-inline:auto;padding:0 var(--gut) clamp(56px,10vw,110px)}
.panel--center .panel__in{padding-bottom:clamp(40px,8vw,70px)}
.panel--right{justify-content:flex-end}
.panel--right .panel__in{text-align:right;margin-left:auto}
.panel--right .body{margin-left:auto}
.panel--right .eyebrow::before{display:none}
.panel--right .eyebrow::after{content:"";display:inline-block;width:22px;height:2px;background:var(--flame);vertical-align:.28em;margin-left:.7em}

/* --- hero ---------------------------------------------------------------- */
.hero__copy{padding-top:calc(var(--nav-h) + env(safe-area-inset-top, 0px) + 24px)}
.hero__h1{display:flex;flex-direction:column;gap:.35rem;align-items:center}
.hero__kicker{
  font-family:var(--text);font-size:clamp(.66rem,2.6vw,.82rem);font-weight:600;
  letter-spacing:.22em;text-transform:uppercase;color:var(--bronze);
  line-height:1.5;max-width:34ch;
}
.hero__hook{display:block}
.hero__tag{margin:1.2rem auto 0;max-width:52ch;color:var(--bone-dim);font-size:clamp(.98rem,2.6vw,1.14rem)}

.hero__stats{
  display:grid;grid-template-columns:repeat(2,1fr);gap:1px;
  margin:clamp(24px,5vw,38px) auto 0;max-width:640px;
  background:var(--line-soft);border:1px solid var(--line-soft);
}
.hero__stats li{background:var(--ground);padding:14px 10px;display:grid;gap:2px}
.hero__stats b{font-family:var(--display);font-size:1.6rem;font-weight:800;color:var(--bone);line-height:1}
.hero__stats span{font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;color:var(--bone-quiet)}

.hero__cta{display:flex;flex-direction:column;gap:10px;margin-top:clamp(24px,5vw,36px)}
.hero__micro{margin-top:14px;font-size:.84rem;color:var(--bone-quiet)}
.hero__micro a{color:var(--flame);text-decoration:underline;text-underline-offset:3px;display:inline-block;padding:8px 0}

@media (min-width:600px){
  .hero__stats{grid-template-columns:repeat(4,1fr)}
  .hero__cta{flex-direction:row;justify-content:center}
}
@media (min-width:900px){
  .panel--left .panel__in,.panel--right .panel__in{max-width:calc(var(--shell) * .62)}
  .panel--right .panel__in{margin-right:calc((100vw - var(--shell))/2 + var(--gut));margin-left:0}
}

/* --- reviews strip ------------------------------------------------------- */
.rev{background:var(--panel);border-block:1px solid var(--line-soft);padding:clamp(28px,6vw,44px) 0}
.rev__top{
  display:flex;flex-wrap:wrap;gap:10px 20px;align-items:center;justify-content:space-between;
  max-width:var(--shell);margin:0 auto clamp(18px,3vw,26px);padding-inline:var(--gut);
}
.rev__score{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.rev__stars{color:var(--flame);letter-spacing:.18em;font-size:1.05rem}
.rev__n{font-size:.76rem;letter-spacing:.16em;text-transform:uppercase;color:var(--bone-dim)}
.rev__gl{font-size:.82rem;color:var(--bronze);text-decoration:underline;text-underline-offset:4px;min-height:var(--tap);display:inline-flex;align-items:center}

.rev__vp{overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)
        ;mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)}
.rev__track{display:flex;gap:14px;width:max-content;animation:revroll 46s linear infinite}
.rev__vp:hover .rev__track,.rev__vp:focus-within .rev__track{animation-play-state:paused}
@keyframes revroll{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){
  .rev__track{animation:none;width:auto;overflow-x:auto;scroll-snap-type:x mandatory;padding-bottom:10px}
  .rev__c{scroll-snap-align:start}
  .rev__vp{-webkit-mask-image:none;mask-image:none}
}
.rev__av{width:34px;height:34px;flex:none;display:grid;place-items:center;border-radius:50%;background:rgba(232,135,60,.16);border:1px solid rgba(232,135,60,.4);color:var(--flame);font-size:.72rem;font-weight:700}
.rev__nm{display:block;font-size:.86rem;font-weight:600}
.rev__mt{display:block;font-size:.72rem;color:var(--bone-quiet)}

/* --- before / after ------------------------------------------------------ */
.cmp-grid{display:grid;grid-template-columns:1fr;gap:clamp(16px,3vw,24px)}
@media (min-width:560px){.cmp-grid{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1000px){.cmp-grid{grid-template-columns:repeat(4,1fr)}}

.cmpc__cap{margin-top:10px;font-size:.78rem;letter-spacing:.06em;color:var(--bone-quiet);text-align:center}
.cmp{
  position:relative;aspect-ratio:4/5;overflow:hidden;border-radius:var(--r);
  background:var(--panel);border:1px solid var(--line-soft);touch-action:pan-y;
}
.cmp__img{position:absolute;inset:0;width:100%;height:100%;object-fit:var(--img-fit);background:var(--panel)}
.cmp__after{-webkit-clip-path:inset(0 0 0 var(--split));clip-path:inset(0 0 0 var(--split))}
.cmp__lbl{
  position:absolute;bottom:10px;z-index:3;
  font-family:var(--display);font-size:.68rem;font-weight:700;letter-spacing:.18em;text-transform:uppercase;
  background:rgba(28,30,38,.82);border:1px solid var(--line);padding:4px 9px;border-radius:2px;
}
.cmp__lbl--l{left:10px;color:var(--bone-dim)}
.cmp__lbl--r{right:10px;color:var(--flame);border-color:rgba(232,135,60,.55)}
.cmp__line{position:absolute;top:0;bottom:0;left:var(--split);width:2px;background:var(--bone);z-index:4;pointer-events:none}
.cmp__grip{
  position:absolute;top:50%;left:var(--split);z-index:5;
  transform:translate(-50%,-50%);
  width:var(--tap);height:var(--tap);border-radius:50%;
  display:grid;place-items:center;
  background:var(--bone);color:#0A0B0F;font-size:1rem;
  box-shadow:0 3px 14px rgba(0,0,0,.5);pointer-events:none;
}
.cmp__range{
  position:absolute;inset:0;z-index:6;width:100%;height:100%;
  margin:0;opacity:0;cursor:ew-resize;-webkit-appearance:none;appearance:none;background:none;
}
.cmp__range::-webkit-slider-thumb{-webkit-appearance:none;width:var(--tap);height:var(--tap)}
.cmp__range:focus-visible ~ .cmp__grip{outline:3px solid var(--flame);outline-offset:3px}

/* --- founder ------------------------------------------------------------- */
.founder{display:grid;grid-template-columns:1fr;background:var(--panel);border-block:1px solid var(--line-soft)}
/* 70svh of portrait is most of a phone screen given over to one photograph
   before a word of the story has been read. Half that is still a portrait,
   and it lets the photograph and the first paragraph share the screen. */
.founder__shot{aspect-ratio:4/5;max-height:min(46svh,420px);background:var(--ground);overflow:hidden}
.founder__shot .panel__img,.founder__shot .plate{width:100%;height:100%}
.founder__in{padding:clamp(36px,8vw,80px) var(--gut)}
.spec{margin-top:1.1rem;font-family:var(--display);font-size:.86rem;font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:var(--bronze);border-left:2px solid var(--flame);padding-left:12px}
@media (min-width:860px){
  .founder{grid-template-columns:.68fr 1.32fr;align-items:center}
  .founder__shot{aspect-ratio:4/5;height:auto;min-height:0;max-height:none}
  .founder__in{padding-inline:clamp(40px,5vw,72px)}
}

/* --- video rail ---------------------------------------------------------- */
.tt{padding:clamp(56px,10vw,110px) 0}
.tt__hd{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;justify-content:space-between;margin-bottom:clamp(22px,4vw,36px)}
.tt__at{font-size:.84rem;letter-spacing:.14em;text-transform:uppercase;color:var(--bronze);text-decoration:underline;text-underline-offset:5px;min-height:var(--tap);display:inline-flex;align-items:center}
.tt__rail{
  display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;
  padding-bottom:14px;margin-inline:calc(var(--gut) * -1);padding-inline:var(--gut);
  scrollbar-width:thin;scrollbar-color:var(--line) transparent;
}
.tt__rail::-webkit-scrollbar{height:6px}
.tt__rail::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}
.tt__c{flex:0 0 202px;scroll-snap-align:start}
.tt__sh{
  display:block;position:relative;aspect-ratio:9/16;overflow:hidden;
  border-radius:var(--r);border:1px solid var(--line-soft);background:var(--panel);
}
.tt__sh .panel__img,.tt__sh .plate{position:absolute;inset:0;width:100%;height:100%}
.tt__pl{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:46px;height:46px;border-radius:50%;display:grid;place-items:center;
  background:rgba(28,30,38,.62);border:1.5px solid var(--bone);color:var(--bone);
  font-size:.9rem;padding-left:3px;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
}
.tt__c:hover .tt__pl{background:var(--flame);border-color:var(--flame);color:#14100C}
.tt__cap{margin-top:9px;font-size:.8rem;color:var(--bone-dim);line-height:1.45}

/* --- filters + cards ----------------------------------------------------- */
.filters{
  display:flex;gap:8px;overflow-x:auto;padding-bottom:10px;margin-bottom:clamp(20px,3vw,30px);
  margin-inline:calc(var(--gut) * -1);padding-inline:var(--gut);
}
.filter{
  flex:none;min-height:var(--tap);padding:0 16px;
  font-family:var(--display);font-size:.86rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  border:1px solid var(--line);border-radius:var(--r);color:var(--bone-dim);
  transition:all .2s;
}
.filter:hover{color:var(--bone);border-color:var(--bone-dim)}
/* Selected state carries colour AND a solid fill AND a leading tick. */
.filter[aria-pressed="true"]{background:var(--flame);border-color:var(--flame);color:#14100C}
.filter[aria-pressed="true"]::before{content:"\2713";margin-right:.5em;font-weight:800}

.shop{display:grid;grid-template-columns:1fr;gap:clamp(14px,2.4vw,22px)}
@media (min-width:440px){.shop{grid-template-columns:repeat(2,1fr)}}

.prod{
  background:var(--panel);border:1px solid var(--line-soft);border-radius:var(--r);
  display:flex;flex-direction:column;transition:border-color .25s,transform .25s var(--ease);
}
.prod:hover{border-color:var(--bronze-dim);transform:translateY(-3px)}
.prod__shot{aspect-ratio:4/5;overflow:hidden;background:var(--ground);border-bottom:1px solid var(--line-soft)}
.prod__shot img{width:100%;height:100%;object-fit:var(--img-fit)}
.prod__in{padding:18px 20px 22px;display:flex;flex-direction:column;flex:1}

.card__top{display:flex;justify-content:space-between;align-items:baseline;gap:12px;margin-bottom:8px}
.card__name{font-family:var(--display);font-size:1.34rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;line-height:1.1}
/* A product title is a link people tap, so it gets a real target height. */
/* 44px exactly, not 43.6 — the padding alone left it a fraction short of the
   minimum target size, which is the sort of miss that only a measurement finds. */
.card__name a{display:inline-flex;align-items:center;min-height:var(--tap);padding:10px 0;margin:-10px 0}
.card__price{font-family:var(--display);font-size:1.28rem;font-weight:800;color:var(--flame);white-space:nowrap}
.card__price del{color:var(--bone-quiet);font-size:.8em;margin-right:.35em}
.card__price ins{text-decoration:none}
.card__desc{font-size:.92rem;color:var(--bone-dim);flex:1}
.card__foot{margin-top:16px}

/* --- FAQ ----------------------------------------------------------------- */
.faq{max-width:820px;margin-inline:auto;border-top:1px solid var(--line-soft)}
.faq__row{border-bottom:1px solid var(--line-soft)}
.faq__q{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  width:100%;min-height:var(--tap);padding:18px 0;text-align:left;
  font-family:var(--display);font-size:1.16rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  color:var(--bone);
}
.faq__ic{position:relative;flex:none;width:18px;height:18px}
.faq__ic::before,.faq__ic::after{content:"";position:absolute;left:50%;top:50%;background:var(--flame);transition:transform .3s var(--ease)}
.faq__ic::before{width:16px;height:2px;transform:translate(-50%,-50%)}
.faq__ic::after{width:2px;height:16px;transform:translate(-50%,-50%)}
.faq__row[data-open="true"] .faq__ic::after{transform:translate(-50%,-50%) scaleY(0)}
.faq__a{display:grid;grid-template-rows:0fr;transition:grid-template-rows .34s var(--ease)}
.faq__row[data-open="true"] .faq__a{grid-template-rows:1fr}
.faq__a > div{overflow:hidden}
.faq__a p{color:var(--bone-dim);padding-bottom:20px;max-width:62ch}

/* --- visit --------------------------------------------------------------- */
.visit{display:grid;grid-template-columns:1fr;gap:clamp(24px,4vw,40px)}
@media (min-width:900px){.visit{grid-template-columns:1fr 1.15fr;align-items:start}}
/* Scoped away from .btn: `.info a` is more specific than `.btn--flame`,
   so without :not() it repainted the orange button's label near-white. */
.info a:not(.btn){color:var(--bone);text-decoration:underline;text-underline-offset:3px;display:inline-flex;align-items:center;min-height:var(--tap)}
.rota{width:100%;border-collapse:collapse;font-size:.94rem}
.rota td{padding:7px 0;border-bottom:1px solid var(--line-soft);color:var(--bone-dim)}
.rota td:last-child{text-align:right;color:var(--bone);font-variant-numeric:tabular-nums}
.map{border:1px solid var(--line-soft);border-radius:var(--r);overflow:hidden;background:var(--panel);min-height:300px}
.map iframe{width:100%;height:clamp(300px,45vw,460px);border:0;display:block;filter:grayscale(1) invert(.92) contrast(.86)}

/* --- footer -------------------------------------------------------------- */
.foot{border-top:1px solid var(--line-soft);background:var(--panel);padding:clamp(44px,8vw,72px) 0 calc(clamp(44px,8vw,72px) + env(safe-area-inset-bottom));text-align:center}
.foot__id{font-family:var(--display);font-size:clamp(2rem,9vw,3.4rem);font-weight:800;letter-spacing:.16em;text-transform:uppercase;line-height:1}
.foot__sub{font-size:.72rem;letter-spacing:.24em;text-transform:uppercase;color:var(--bronze-dim);margin-top:8px}
.foot__nav,.foot__legal{display:flex;flex-wrap:wrap;gap:6px 22px;justify-content:center;margin-top:26px}
.foot__nav a,.foot__legal a{font-size:.82rem;letter-spacing:.1em;text-transform:uppercase;color:var(--bone-dim);min-height:var(--tap);min-width:var(--tap);display:inline-flex;align-items:center;justify-content:center}
.foot__nav a:hover,.foot__legal a:hover{color:var(--bone)}
.foot__legal{gap:6px 18px;margin-top:10px;list-style:none;padding:0}
.foot__c{margin-top:22px;font-size:.74rem;color:var(--bone-quiet)}

/* --- the corner consultation --------------------------------------------- */
/* Two lines, because "Ask Fash" named the medium and not the offer. What the
   salon gives away is a free consultation; somebody losing their hair is not
   short of ways to send a message, they are short of a reason to believe the
   first conversation costs nothing. */
.wa-float{
  position:fixed;z-index:70;right:14px;
  bottom:calc(14px + env(safe-area-inset-bottom));
  display:inline-flex;align-items:center;gap:10px;
  padding:9px 18px 9px 13px;border-radius:30px;
  background:#0C6B39;color:#fff;border:1px solid rgba(255,255,255,.34);
  text-decoration:none;
  box-shadow:0 10px 30px rgba(0,0,0,.5);
  transform:translateY(140%);transition:transform .4s var(--ease),background-color .3s,box-shadow .3s;
}
.wa-float[data-show="true"]{transform:translateY(0)}
.wa-float:hover{background:#0E7D43;box-shadow:0 14px 36px rgba(12,107,57,.45)}
.wa-float:focus-visible{outline:2px solid #fff;outline-offset:3px}

.wa-float__ic{
  flex:none;display:grid;place-items:center;width:34px;height:34px;border-radius:50%;
  background:rgba(255,255,255,.16);
  transition:transform .5s var(--ease);
}
.wa-float:hover .wa-float__ic{transform:rotate(-10deg) scale(1.06)}

.wa-float__txt{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.wa-float__txt b{
  font-family:var(--display);font-size:.84rem;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;
}
.wa-float__txt em{font-style:normal;font-size:.68rem;opacity:.8;white-space:nowrap}

/* On a narrow phone the label would crowd the thumb zone, so it collapses to
   the mark — still a 52px target, still the same link. */
@media (max-width:430px){
  .wa-float{padding:0;width:54px;height:54px;justify-content:center;gap:0}
  .wa-float__txt{display:none}
  .wa-float__ic{width:100%;height:100%;background:none}
}

/* --- section rail (desktop only) ----------------------------------------- */
.rail{position:fixed;right:18px;top:50%;transform:translateY(-50%);z-index:60;display:none;flex-direction:column;gap:2px}
/* The rail is a real navigation control, so it gets a real target height. The
   markers stay 34px apart visually; the extra height is transparent padding,
   which is what makes it usable on a touchscreen laptop without redesigning it. */
.rail a{display:flex;align-items:center;justify-content:flex-end;gap:10px;min-height:var(--tap);padding-right:2px;color:var(--bone-quiet);font-size:.66rem;letter-spacing:.16em;text-transform:uppercase}
.rail{gap:0}
.rail i{display:block;width:22px;height:2px;background:currentColor;transition:width .25s var(--ease)}
.rail span{opacity:0;transform:translateX(6px);transition:all .25s var(--ease)}
.rail a:hover,.rail a[aria-current="true"]{color:var(--flame)}
.rail a[aria-current="true"] i{width:38px;height:3px}
.rail a:hover span,.rail a[aria-current="true"] span{opacity:1;transform:none}
/* The rail only appears once the window is wide enough that it cannot sit on
   top of the 1240px content shell — 1240px content shell — measured, not guessed: the labelled rail is
   143px wide, so it needs a window of at least 1600px to clear it. */
@media (min-width:1600px){.rail{display:flex}}

/* --- scroll reveal ------------------------------------------------------- */
[data-in]{opacity:0;transform:translateY(22px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
[data-in][data-d="1"]{transition-delay:.1s}
[data-in][data-d="2"]{transition-delay:.2s}
[data-in].is-in{opacity:1;transform:none}
/* If JavaScript never runs, nothing may stay invisible. */
.no-js [data-in]{opacity:1;transform:none}

/* The fixed header must never sit on top of the first block of a subpage. */
main > .sec:first-child{padding-top:calc(var(--nav-h) + env(safe-area-inset-top, 0px) + clamp(40px,7vw,80px))}

/* --- WooCommerce, minimally themed --------------------------------------- */
.woocommerce .shell{max-width:var(--shell)}
.woocommerce a.button,.woocommerce button.button,.woocommerce input.button,.woocommerce #respond input#submit{
  background:var(--flame);color:#14100C;border-radius:var(--r);min-height:var(--tap);
  font-family:var(--display);font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:0 22px;
}
.woocommerce a.button:hover,.woocommerce button.button:hover{background:#F19B55;color:#14100C}
.woocommerce .woocommerce-message,.woocommerce .woocommerce-info{border-top-color:var(--flame);background:var(--panel);color:var(--bone)}
.woocommerce .woocommerce-error{border-top-color:var(--flame);background:var(--panel);color:var(--bone)}
.woocommerce table.shop_table,.woocommerce table.shop_table td,.woocommerce table.shop_table th{border-color:var(--line-soft);color:var(--bone)}
.woocommerce form .form-row input.input-text,.woocommerce form .form-row textarea,.woocommerce form .form-row select{
  background:var(--ground);border:1px solid var(--line);color:var(--bone);border-radius:var(--r);min-height:var(--tap);padding:10px 12px;
}
.woocommerce-account .woocommerce-MyAccount-navigation ul{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:24px}
.woocommerce-account .woocommerce-MyAccount-navigation a{display:inline-flex;align-items:center;min-height:var(--tap);padding:0 14px;border:1px solid var(--line);border-radius:var(--r);font-size:.86rem}

/* --- print --------------------------------------------------------------- */
@media print{
  .nav,.rail,.wa-float,.foot__nav{display:none!important}
  body{background:#fff;color:#000}
  [data-in]{opacity:1;transform:none}
}

/* --- search box ---------------------------------------------------------- */
/* `hidden` must beat the flex display on cards and the block on the notice. */
[hidden]{display:none!important}

.find{max-width:560px;margin:0 auto clamp(18px,3vw,26px)}
.find__lbl{display:block;font-family:var(--display);font-size:.78rem;font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:var(--bronze);margin-bottom:8px}
.find__in{
  width:100%;min-height:var(--tap);padding:12px 16px;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  color:var(--bone);font-family:var(--text);font-size:1rem;
}
.find__in::placeholder{color:var(--bone-quiet)}
.find__in:focus{border-color:var(--flame);outline:none;box-shadow:0 0 0 3px rgba(232,135,60,.22)}
.find__hint{margin-top:8px;font-size:.8rem;color:var(--bone-quiet)}
.find__hint b{color:var(--bone-dim);font-weight:600}
.shop__none{text-align:center;color:var(--bone-dim);padding:34px 0;font-size:1rem}
.shop__none a{color:var(--flame);text-decoration:underline;text-underline-offset:3px}

/* --- booking picker (single product) ------------------------------------- */
.bk{
  background:var(--panel);border:1px solid var(--line);
  border-left:3px solid var(--flame);border-radius:var(--r);
  padding:20px 22px;margin:0 0 22px;max-width:520px;
}
.bk__t{font-family:var(--display);font-size:1.24rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;margin-bottom:14px}
.bk__l{display:block;font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:var(--bronze);margin:14px 0 6px}
.bk__l:first-of-type{margin-top:0}
.bk__l span{color:var(--bone-quiet);letter-spacing:.06em;text-transform:none}
.bk__f{
  width:100%;min-height:var(--tap);padding:11px 13px;
  background:var(--ground);border:1px solid var(--line);border-radius:var(--r);
  color:var(--bone);font-family:var(--text);font-size:1rem;
}
.bk__f:focus{border-color:var(--flame);outline:none;box-shadow:0 0 0 3px rgba(232,135,60,.22)}
.bk__f:disabled{color:var(--bone-quiet);cursor:not-allowed}
.bk__f[type="date"]::-webkit-calendar-picker-indicator{filter:invert(1);opacity:.7;cursor:pointer}
textarea.bk__f{min-height:74px;resize:vertical}
.bk__note{margin-top:14px;font-size:.82rem;color:var(--bone-quiet);border-top:1px solid var(--line-soft);padding-top:12px}
.bk__err{margin-top:10px;font-size:.86rem;color:var(--flame);font-weight:600}
.bk__err::before{content:"\26A0";margin-right:.5em}

/* ==========================================================================
   v2.0 — icons, social, live map, bag drawer, upgraded motion
   ========================================================================== */

.vh{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.ic{display:block;flex:none}

/* --- scroll progress ----------------------------------------------------- */
.prog{position:fixed;inset:0 auto auto 0;z-index:90;height:3px;width:0;background:var(--flame);transition:width .1s linear;pointer-events:none}
.prog::after{content:"";position:absolute;right:0;top:0;width:36px;height:100%;background:linear-gradient(90deg,transparent,var(--bone))}

/* --- social icons -------------------------------------------------------- */
.social{display:flex;flex-wrap:wrap;gap:8px;padding:0;margin:0;list-style:none}
.social__l{
  width:var(--tap);height:var(--tap);display:grid;place-items:center;
  border:1px solid var(--line);border-radius:50%;color:var(--bone-dim);
  transition:color .2s,border-color .2s,background-color .2s,transform .2s var(--ease);
}
.social__l:hover{color:var(--bone);border-color:var(--bone);background:rgba(242,238,233,.07);transform:translateY(-2px)}
.social__l--whatsapp:hover{color:#25D366;border-color:#25D366}
.social__l--instagram:hover{color:#E1306C;border-color:#E1306C}
.social__l--youtube:hover{color:#FF3B30;border-color:#FF3B30}
.social__l--google:hover{color:var(--flame);border-color:var(--flame)}
.social--nav{margin-top:14px;padding-top:14px;border-top:1px solid var(--line-soft)}
.social--foot{justify-content:center;margin-top:24px}
@media (min-width:900px){.social--nav{display:none}}

/* --- reviews additions --------------------------------------------------- */
.rev__n b{color:var(--bone);font-size:1.05rem;font-family:var(--display);letter-spacing:.02em}

/* --- video rail additions ------------------------------------------------ */
.tt__links{display:flex;gap:8px;flex-wrap:wrap}
.tt__at{
  display:inline-flex;align-items:center;gap:7px;min-height:var(--tap);padding:0 14px;
  border:1px solid var(--line);border-radius:var(--r);
  font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:var(--bone-dim);
  text-decoration:none;transition:all .2s;
}
.tt__at:hover{color:var(--bone);border-color:var(--bone)}

/* --- visit: directions, distance, open-now ------------------------------- */
.visit__dir{margin-top:14px;gap:8px}
.visit__dir .ic{margin-right:2px}
.map__none{display:grid;place-items:center;min-height:300px;padding:24px;text-align:center;color:var(--bone-quiet);font-size:.9rem}

/* --- checkout trust + shipping note -------------------------------------- */
.trust{display:grid;gap:9px;margin:0 0 20px;padding:16px 18px;list-style:none;background:var(--panel);border:1px solid var(--line-soft);border-left:3px solid var(--flame);border-radius:var(--r)}
.trust li{display:flex;align-items:center;gap:10px;font-size:.88rem;color:var(--bone-dim)}
.trust__i{display:grid;place-items:center;width:24px;height:24px;flex:none;border-radius:50%;background:rgba(232,135,60,.16);color:var(--flame)}
.shipnote{margin:0 0 22px;padding:14px 18px;background:var(--panel);border:1px solid var(--line-soft);border-left:3px solid var(--flame);border-radius:var(--r);color:var(--bone-dim);font-size:.92rem}

/* --- bag drawer ---------------------------------------------------------- */
.bagd{
  position:fixed;inset:0 0 0 auto;z-index:120;width:min(400px,100%);
  background:var(--panel);border-left:1px solid var(--line);
  transform:translateX(101%);transition:transform .4s var(--ease);
  display:flex;flex-direction:column;padding-top:env(safe-area-inset-top);
}
.bagd[data-open="true"]{transform:none}
.bagd__x{position:fixed;inset:0;z-index:119;background:rgba(6,7,10,.66);opacity:0;pointer-events:none;transition:opacity .35s}
.bagd__x[data-open="true"]{opacity:1;pointer-events:auto}
.bagd__hd{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:18px 20px;border-bottom:1px solid var(--line-soft)}
.bagd__hd h2{font-family:var(--display);font-size:1.3rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase}
.bagd__close{width:var(--tap);height:var(--tap);display:grid;place-items:center;border:1px solid var(--line);border-radius:var(--r);font-size:1.2rem}
.bagd__body{flex:1;overflow-y:auto;padding:18px 20px}
.bagd__row{display:flex;gap:12px;align-items:flex-start;padding:12px 0;border-bottom:1px solid var(--line-soft);font-size:.92rem}
.bagd__row b{display:block;font-weight:600}
.bagd__row span{color:var(--bone-quiet);font-size:.82rem}
.bagd__ft{padding:16px 20px calc(20px + env(safe-area-inset-bottom));border-top:1px solid var(--line-soft);display:grid;gap:9px}
.bagd__tot{display:flex;justify-content:space-between;font-family:var(--display);font-size:1.1rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-bottom:4px}

/* --- toast --------------------------------------------------------------- */
.toast{
  position:fixed;z-index:130;left:50%;bottom:calc(84px + env(safe-area-inset-bottom));
  transform:translate(-50%,140%);
  max-width:min(420px,calc(100vw - 28px));
  display:flex;align-items:center;gap:11px;
  padding:13px 18px;border-radius:var(--r);
  background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--flame);
  box-shadow:0 12px 40px rgba(0,0,0,.55);
  font-size:.9rem;transition:transform .38s var(--ease);
}
.toast[data-show="true"]{transform:translate(-50%,0)}
.toast[data-kind="bad"]{border-left-color:#FF6B5A}
.toast__i{display:grid;place-items:center;width:26px;height:26px;flex:none;border-radius:50%;background:rgba(232,135,60,.18);color:var(--flame)}
.toast[data-kind="bad"] .toast__i{background:rgba(255,107,90,.18);color:#FF8A7A}
@media (min-width:900px){.toast{left:auto;right:22px;bottom:22px;transform:translate(0,180%)}.toast[data-show="true"]{transform:none}}

/* --- motion upgrade ------------------------------------------------------ */
/* Words rise in sequence rather than the whole heading fading as one block. */
.sp{display:inline-block;overflow:hidden;vertical-align:bottom}
.sp i{display:inline-block;font-style:inherit;transform:translateY(105%);transition:transform .8s var(--ease)}
[data-split].is-in .sp i{transform:none}

/* Background drifts slower than the page. Set by script, capped in CSS so a
   very tall screen can never pull the image off its own panel. */
.panel__bg{will-change:transform}
.panel__img{transform:scale(1.08) translate3d(0,var(--par,0px),0)}

.btn{position:relative;overflow:hidden}
.btn--flame::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(105deg,transparent 38%,rgba(255,255,255,.42) 50%,transparent 62%);
  transform:translateX(-120%);
}
.btn--flame:hover::after{transform:translateX(120%);transition:transform .7s var(--ease)}

.prod,.rev__c,.tt__c{will-change:transform}
.prod__shot img,.tt__sh .panel__img{transition:transform .7s var(--ease)}
.prod:hover .prod__shot img,.tt__c:hover .tt__sh .panel__img{transform:scale(1.05)}

.hero__stats li{position:relative;overflow:hidden}
.hero__stats li::after{
  content:"";position:absolute;left:0;bottom:0;height:2px;width:100%;
  background:var(--flame);transform:scaleX(0);transform-origin:left;
  transition:transform .8s var(--ease);
}
.hero__copy.is-in .hero__stats li::after{transform:scaleX(1)}
.hero__stats li:nth-child(2)::after{transition-delay:.1s}
.hero__stats li:nth-child(3)::after{transition-delay:.2s}
.hero__stats li:nth-child(4)::after{transition-delay:.3s}

/* Two lighter settings, both driven from the panel. */
body[data-anim="soft"] .panel__img{transform:scale(1.03)}
body[data-anim="soft"] .sp i{transition-duration:.5s}
body[data-anim="off"] [data-in]{opacity:1!important;transform:none!important}
body[data-anim="off"] .sp i{transform:none!important}
body[data-anim="off"] .panel__img{transform:none}
body[data-anim="off"] .rev__track{animation:none}

@media (prefers-reduced-motion:reduce){
  .sp i{transform:none!important}
  .panel__img{transform:none!important}
  .prog{transition:none}
}

/* --- product card: gallery photos ---------------------------------------- */
.prod__shot{position:relative}

/* ==========================================================================
   v2.1 — brand mark, animated icons, salon cursor
   ========================================================================== */

/* --- brand mark ---------------------------------------------------------- */
/* The badge artwork is a circle on a white square. Clipping to the circle
   drops the white corners without touching a pixel of the logo itself. */
/* The PNG carries its own alpha now, so no clip is needed — and clipping at
   49.5% was shaving the rim off the badge. */
.brandmark{display:block;flex:none;background:transparent}
.brandmark--nav{transition:transform .5s var(--ease)}
.nav__brand:hover .brandmark--nav{transform:rotate(-6deg) scale(1.05)}
.foot__mark{display:flex;justify-content:center;margin-bottom:18px}
.brandmark--foot{filter:drop-shadow(0 8px 26px rgba(0,0,0,.55))}
@media (min-width:900px){.foot__mark{margin-bottom:22px}}

/* --- icons that move ----------------------------------------------------- */
/* Every social pill: the icon lifts and settles, and the ring draws round it
   from a single point rather than just changing colour. */
.social__l{position:relative;overflow:hidden}
.social__l svg{transition:transform .38s var(--ease)}
.social__l::before{
  content:"";position:absolute;inset:-1px;border-radius:50%;
  border:1px solid currentColor;
  -webkit-clip-path:circle(0% at 50% 100%);clip-path:circle(0% at 50% 100%);
  transition:-webkit-clip-path .45s var(--ease),clip-path .45s var(--ease);
}
.social__l:hover svg,.social__l:focus-visible svg{transform:translateY(-2px) scale(1.12)}
.social__l:hover::before,.social__l:focus-visible::before{-webkit-clip-path:circle(75% at 50% 100%);clip-path:circle(75% at 50% 100%)}

.tt__at .ic{transition:transform .35s var(--ease)}
.tt__at:hover .ic{transform:scale(1.18) rotate(-6deg)}
.nav__bag svg{transition:transform .3s var(--ease)}
.nav__bag:hover svg{transform:translateY(-1px) rotate(-6deg)}
.visit__dir .ic{transition:transform .4s var(--ease)}
.visit__dir:hover .ic{transform:translateY(-3px)}

/* The play badge fills from the centre instead of snapping colour. */
.tt__pl{position:relative;overflow:hidden;isolation:isolate}
.tt__pl::before{
  content:"";position:absolute;inset:0;z-index:-1;border-radius:50%;
  background:var(--flame);transform:scale(0);transition:transform .4s var(--ease);
}
.tt__c:hover .tt__pl::before{transform:scale(1)}
.tt__c:hover .tt__pl{color:#14100C;border-color:var(--flame);background:transparent}

/* --- the cursor ---------------------------------------------------------- */
/*
 * A barber's cursor: a thin ring that trails the pointer, opens into a label
 * over things you can act on, and turns into a left-right handle over the
 * before/after slider. Pointer devices only — a phone has no cursor to
 * decorate, and someone who asked for less motion gets none of it.
 */
.cur,.cur__dot{
  position:fixed;top:0;left:0;z-index:200;pointer-events:none;
  border-radius:50%;mix-blend-mode:difference;
  opacity:0;transition:opacity .3s;
}
.cur{
  width:34px;height:34px;margin:-17px 0 0 -17px;
  border:1.5px solid var(--bone);
  transition:width .3s var(--ease),height .3s var(--ease),margin .3s var(--ease),
             opacity .3s,background-color .3s,border-color .3s;
}
.cur__dot{width:5px;height:5px;margin:-2.5px 0 0 -2.5px;background:var(--bone)}
.cur__t{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(.6);
  font-family:var(--display);font-size:.62rem;font-weight:700;
  letter-spacing:.18em;text-transform:uppercase;color:#0A0B0F;
  opacity:0;transition:opacity .25s,transform .3s var(--ease);white-space:nowrap;
}
.cur[data-on="true"],.cur__dot[data-on="true"]{opacity:1}
/* over a link or button */
.cur[data-mode="link"]{width:56px;height:56px;margin:-28px 0 0 -28px;background:rgba(242,238,233,.14)}
/* over something with a word to say */
.cur[data-mode="label"]{width:78px;height:78px;margin:-39px 0 0 -39px;background:var(--bone);border-color:var(--bone);mix-blend-mode:normal}
.cur[data-mode="label"] .cur__t{opacity:1;transform:translate(-50%,-50%) scale(1)}
.cur[data-mode="label"] ~ .cur__dot{opacity:0}
/* over the before/after handle */
.cur[data-mode="drag"]{width:70px;height:70px;margin:-35px 0 0 -35px;background:var(--bone);border-color:var(--bone);mix-blend-mode:normal}
.cur[data-mode="drag"] .cur__t{opacity:1;transform:translate(-50%,-50%) scale(1)}

body.has-cur,body.has-cur a,body.has-cur button,body.has-cur .cmp{cursor:none}
body.has-cur input,body.has-cur textarea,body.has-cur select{cursor:auto}
@media (hover:none),(pointer:coarse){.cur,.cur__dot{display:none}}
@media (prefers-reduced-motion:reduce){.cur,.cur__dot{display:none}}
body[data-anim="off"] .cur,body[data-anim="off"] .cur__dot{display:none}

/* ==========================================================================
   Booking wizard
   ========================================================================== */
.bkw{max-width:760px;margin-inline:auto}
.bkw--empty{text-align:center;color:var(--bone-dim);padding:40px 0}

.bkw__steps{display:flex;gap:6px;margin:0 0 clamp(24px,5vw,38px);padding:0;list-style:none}
.bkw__steps li{
  flex:1;display:flex;align-items:center;gap:9px;
  padding:11px 8px 13px;border-top:2px solid var(--line);
  font-family:var(--display);font-size:.74rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bone-quiet);
  transition:color .3s,border-color .3s;
}
.bkw__steps li span{
  width:24px;height:24px;flex:none;display:grid;place-items:center;border-radius:50%;
  border:1px solid var(--line);font-size:.74rem;transition:all .3s var(--ease);
}
.bkw__steps li[aria-current="step"]{color:var(--bone);border-top-color:var(--flame)}
.bkw__steps li[aria-current="step"] span{background:var(--flame);border-color:var(--flame);color:#14100C}
.bkw__steps li[data-done="true"]{color:var(--bronze);border-top-color:var(--bronze-dim)}
.bkw__steps li[data-done="true"] span{border-color:var(--bronze);color:transparent;position:relative}
.bkw__steps li[data-done="true"] span::before{content:"\2713";position:absolute;inset:0;display:grid;place-items:center;color:var(--bronze);font-size:.8rem}
@media (max-width:720px){
  .bkw__steps li{flex-direction:column;gap:4px;text-align:center;font-size:.52rem;letter-spacing:.06em;padding:9px 2px 10px}
  .bkw__steps li span{width:22px;height:22px;font-size:.62rem}
}

.bkw__h{font-family:var(--display);font-size:clamp(1.6rem,5vw,2.4rem);font-weight:800;letter-spacing:.02em;text-transform:uppercase;line-height:1.05}
.bkw__sub{margin-top:.6rem;color:var(--bone-dim);font-size:.96rem}
.bkw__nav{display:flex;gap:10px;margin-top:clamp(22px,4vw,32px);flex-wrap:wrap}
.bkw__nav .btn{flex:1 1 160px}
.bkw__nav .btn:disabled{opacity:.4;cursor:not-allowed;transform:none}

/* --- service cards ------------------------------------------------------- */
.bkw__svcs{display:grid;gap:12px;margin-top:clamp(18px,3vw,26px)}
.svc{
  display:flex;gap:14px;align-items:stretch;text-align:left;width:100%;
  background:var(--panel);border:1px solid var(--line-soft);border-left:3px solid transparent;
  border-radius:var(--r);padding:14px 16px;position:relative;
  transition:border-color .25s,background-color .25s,transform .25s var(--ease);
}
.svc:hover{border-color:var(--bronze-dim);transform:translateY(-2px)}
.svc:active{transform:translateY(0) scale(.99)}
.svc[aria-checked="true"]{border-color:var(--line);border-left-color:var(--flame);background:rgba(232,135,60,.07)}
.svc__pic{width:76px;flex:none;border-radius:2px;overflow:hidden;background:var(--ground)}
.svc__pic img{width:100%;height:100%;object-fit:var(--img-fit)}
.svc__in{display:flex;flex-direction:column;gap:5px;flex:1;min-width:0}
.svc__top{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
/* A long service name plus its price never fit on one line at 375px —
   stack them instead of letting the price squeeze onto the name's
   wrapped first line. */
@media (max-width:400px){.svc__top{flex-direction:column;align-items:flex-start;gap:3px}}
.svc__name{font-family:var(--display);font-size:1.2rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--bone)}
.svc__price{font-family:var(--display);font-size:1.16rem;font-weight:800;color:var(--flame);white-space:nowrap}
.svc__desc{font-size:.88rem;color:var(--bone-dim);line-height:1.5}
.svc__mins{font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;color:var(--bone-quiet)}
.svc__tick{
  position:absolute;top:12px;right:12px;
  width:24px;height:24px;display:grid;place-items:center;border-radius:50%;
  background:var(--flame);color:#14100C;font-size:.8rem;
  opacity:0;transform:scale(.5);transition:all .3s var(--ease);
}
.svc[aria-checked="true"] .svc__tick{opacity:1;transform:none}
@media (min-width:640px){.bkw__svcs{grid-template-columns:1fr 1fr}.svc__pic{width:64px}}

/* --- calendar ------------------------------------------------------------ */
.cal{background:var(--panel);border:1px solid var(--line-soft);border-radius:var(--r);padding:clamp(8px,2.5vw,14px);margin-top:clamp(18px,3vw,26px)}
.cal__hd{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}
.cal__m{font-family:var(--display);font-size:1.1rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.cal__arrow{
  width:var(--tap);height:var(--tap);display:grid;place-items:center;
  background:var(--ground);border:1px solid var(--line);border-radius:var(--r);
  color:var(--bone);font-size:1.3rem;line-height:1;transition:all .2s;
}
.cal__arrow:hover:not(:disabled){border-color:var(--bone);background:rgba(242,238,233,.06)}
.cal__arrow:active:not(:disabled){transform:scale(.94)}
.cal__arrow:disabled{opacity:.35;cursor:not-allowed}
.cal__dow{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-bottom:6px}
.cal__dow span{text-align:center;font-size:.62rem;letter-spacing:.12em;text-transform:uppercase;color:var(--bone-quiet)}
.cal__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;transition:opacity .2s}
.cal__grid[aria-busy="true"]{opacity:.5}
.cal__d{
  /* Seven columns inside a 375px screen leaves ~40px per cell, and these are
     the primary date targets — so height is floored at 44px and the square
     aspect is dropped rather than shrinking the tap area to fit. */
  min-height:44px;display:grid;place-items:center;
  background:var(--ground);border:1px solid transparent;border-radius:var(--r);
  color:var(--bone);font-size:.92rem;font-variant-numeric:tabular-nums;
  transition:all .2s;
}
.cal__d:hover:not(:disabled){border-color:var(--bronze-dim)}
.cal__d:active:not(:disabled){transform:scale(.92)}
.cal__d:disabled{color:var(--bone-quiet);opacity:.4;cursor:not-allowed;background:transparent}
/* Availability never relies on colour alone — a free day carries a dot. */
.cal__d[data-free="true"]{position:relative;font-weight:600}
.cal__d[data-free="true"]::after{content:"";position:absolute;bottom:5px;width:4px;height:4px;border-radius:50%;background:var(--flame)}
.cal__d[aria-pressed="true"]{background:var(--flame);border-color:var(--flame);color:#14100C;font-weight:700}
.cal__d[aria-pressed="true"]::after{background:#14100C}
.cal__d--pad{background:none;pointer-events:none}

/* --- time slots ---------------------------------------------------------- */
.slots{margin-top:18px}
.slots__none{color:var(--bone-quiet);font-size:.9rem;padding:14px 0}
.slots__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:8px}
.slot{
  min-height:var(--tap);display:grid;place-items:center;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  font-family:var(--display);font-size:1rem;font-weight:700;letter-spacing:.06em;color:var(--bone);
  font-variant-numeric:tabular-nums;transition:all .2s;
}
.slot:hover{border-color:var(--bone)}
.slot:active{transform:scale(.95)}
.slot[aria-pressed="true"]{background:var(--flame);border-color:var(--flame);color:#14100C}
.slot[aria-pressed="true"]::before{content:"\2713";margin-right:.4em}

/* --- details --------------------------------------------------------------*/
.bkw__sum{
  background:var(--panel);border:1px solid var(--line-soft);border-left:3px solid var(--flame);
  border-radius:var(--r);padding:14px 16px;margin:18px 0 22px;line-height:1.7;font-size:.94rem;
}
.bkw__sum b{font-family:var(--display);font-size:1.1rem;letter-spacing:.04em;text-transform:uppercase;display:block}
.bkw__sum span{color:var(--bone-dim)}
.bkw__f{margin-bottom:14px}
.bkw__f label{display:block;font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:var(--bronze);margin-bottom:6px}
.bkw__f label span{color:var(--bone-quiet);letter-spacing:.04em;text-transform:none}
.bkw__f input,.bkw__f textarea{
  width:100%;min-height:var(--tap);padding:12px 14px;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  color:var(--bone);font-family:var(--text);font-size:1rem;
}
.bkw__f input:focus,.bkw__f textarea:focus{border-color:var(--flame);outline:none;box-shadow:0 0 0 3px rgba(232,135,60,.22)}
.bkw__f input[aria-invalid="true"]{border-color:#FF8A7A}
.bkw__hint{margin-top:6px;font-size:.8rem;color:var(--bone-quiet)}
.bkw__err{margin:4px 0 0;color:#FF8A7A;font-size:.9rem;font-weight:600}
.bkw__err::before{content:"\26A0";margin-right:.5em}
.trust--bk{margin-top:22px}
.trust--bk li::before{content:"\2713";color:var(--flame);font-weight:700;margin-right:.2em}

/* --- manage / cancel / reschedule ----------------------------------------*/
.mgmt{max-width:640px;margin:clamp(56px,10vw,90px) auto 0;padding-top:clamp(40px,7vw,56px);border-top:1px solid var(--line-soft)}
.mgmt__find{display:grid;gap:2px;margin-top:18px;background:var(--line-soft);border:1px solid var(--line-soft);border-radius:var(--r);overflow:hidden}
.mgmt__find .bkw__f{margin:0;background:var(--panel);padding:12px 16px}
.mgmt__find .btn{margin:2px;border-radius:2px}
@media (min-width:560px){.mgmt__find{grid-template-columns:1fr 1fr auto;align-items:stretch}.mgmt__find .btn{margin:0;border-radius:var(--r)}}
.mgmt__out{margin-top:22px}
.mng__top{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:14px;flex-wrap:wrap}
.mng__ref{font-family:var(--display);font-size:1.2rem;font-weight:800;letter-spacing:.08em}
.mng__badge{font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;padding:5px 11px;border-radius:20px;border:1px solid var(--line)}
.mng__badge--ok{color:#3FBF6F;border-color:rgba(63,191,111,.4);background:rgba(63,191,111,.1)}
.mng__badge--off{color:var(--bone-quiet)}
.mng__note{color:var(--bone-dim);font-size:.92rem;line-height:1.6;margin-top:4px}

/* ==========================================================================
   The four bases — four words and a door

   Three versions of this section have now been measured. A 2x2 photo mosaic
   per card (tiles under 80px on a phone — a texture, not a photograph). Then
   one 4:5 photograph per card, which was honest but took a full phone screen
   per base, so reading four words cost two screens of scrolling. Now: no
   photograph at all.

   The picture was never doing the work. One hairpiece base looks much like
   another at card size, and the salon does not have four studio shots to
   spare. What somebody needs at this point in the page is the names of the
   four things, how many of each, and a way in — and all of that fits on one
   line each, four across, on any screen.
   ========================================================================== */
.bases{
  display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(8px,1.4vw,12px);
}
@media (min-width:760px){ .bases{grid-template-columns:repeat(4,1fr)} }

.base{
  position:relative;display:flex;align-items:center;gap:clamp(9px,1.4vw,13px);
  padding:clamp(12px,1.8vw,16px) clamp(12px,1.8vw,18px);
  border-radius:var(--r);border:1px solid var(--line-soft);background:var(--panel);
  text-decoration:none;overflow:hidden;
  transition:border-color .35s,transform .35s var(--ease),background-color .35s;
}
.base:hover{border-color:var(--bronze-dim);transform:translateY(-3px);background:rgba(242,238,233,.03)}
.base:active{transform:translateY(-1px)}
.base:focus-visible{outline:2px solid var(--flame);outline-offset:3px}

/* A flame edge slides up the left as the pointer arrives — the same gesture
   the service rows use, so the two sections read as one language. */
.base::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--flame);
  transform:scaleY(0);transform-origin:bottom;transition:transform .4s var(--ease);
}
.base:hover::before,.base:focus-visible::before{transform:scaleY(1)}

.base__ic{
  flex:none;display:grid;place-items:center;width:42px;height:42px;border-radius:50%;
  background:var(--ground);border:1px solid var(--line);color:var(--bone-dim);
  transition:color .35s,border-color .35s,transform .5s var(--ease);
}
.base:hover .base__ic{color:var(--flame);border-color:rgba(232,135,60,.5);transform:rotate(-8deg) scale(1.06)}

/* Name over count, in one column, so the tile is two short lines and not four
   flex items fighting for a row. */
.base__txt{display:block;min-width:0;margin-right:auto}
.base__name{
  display:block;font-family:var(--display);font-size:clamp(.98rem,2.2vw,1.16rem);
  font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--bone);line-height:1.15;
}
.base__n{
  display:block;margin-top:2px;font-size:.68rem;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--bone-quiet);transition:color .3s;
}
.base:hover .base__n{color:var(--bronze)}
.base__go{
  flex:none;margin-left:auto;display:grid;place-items:center;color:var(--bone-quiet);
  opacity:0;transform:translateX(-6px);
  transition:opacity .3s var(--ease),transform .3s var(--ease),color .3s;
}
.base:hover .base__go,.base:focus-visible .base__go{opacity:1;transform:none;color:var(--flame)}
/* No hover on a touchscreen, so the arrow is simply always there. */
@media (hover:none){ .base__go{opacity:.5;transform:none} }

/* ==========================================================================
   Review wall — proof, not just praise
   Each card can carry the actual result photo and the service that produced
   it, so a stranger reads "this happened to someone like me" rather than a
   floating compliment.
   ========================================================================== */
.rev__gmark{display:grid;place-items:center;width:26px;height:26px;flex:none;border-radius:50%;background:#fff;color:#1a73e8}
.rev__plugin{padding-block:8px}

.rev__c{
  flex:0 0 300px;display:flex;flex-direction:column;
  background:var(--ground);border:1px solid var(--line-soft);
  border-top:2px solid var(--flame);border-radius:var(--r);
  overflow:hidden;
  transition:transform .35s var(--ease),border-color .3s,box-shadow .35s;
}
.rev__c:hover{transform:translateY(-4px);border-color:var(--bronze-dim);box-shadow:0 18px 44px rgba(0,0,0,.45)}
@media (min-width:640px){.rev__c{flex-basis:340px}}

/* The result photo, when there is one. */
.rev__shot{position:relative;display:block;aspect-ratio:16/10;overflow:hidden;background:var(--panel);flex:none}
.rev__shot img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.rev__c:hover .rev__shot img{transform:scale(1.06)}
.rev__shot::after{
  content:"";position:absolute;inset:auto 0 0 0;height:56%;
  background:linear-gradient(0deg,rgba(28,30,38,.85),transparent);
}
.rev__zoom{
  position:absolute;top:10px;right:10px;z-index:2;
  display:grid;place-items:center;width:26px;height:26px;border-radius:50%;
  background:rgba(28,30,38,.7);border:1px solid rgba(232,135,60,.5);color:var(--flame);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
}

.rev__body{display:flex;flex-direction:column;flex:1;padding:16px 20px 18px}
.rev__c--shot .rev__body{padding-top:14px}

.rev__badge{display:flex;align-items:center;gap:8px;color:var(--flame);font-size:.82rem;letter-spacing:.14em;margin-bottom:9px}
.rev__src{display:grid;place-items:center;width:20px;height:20px;border-radius:50%;background:#fff;color:#1a73e8;flex:none}
.rev__c q{display:block;quotes:none;font-size:.96rem;color:var(--bone);line-height:1.6;flex:1}
/*
 * A text-only card sits in the same row as cards carrying a photo, so flex
 * stretches it to match and it ends up mostly empty. Letting the quote grow
 * into that space turns the dead gap into a pull-quote instead.
 */
.rev__c:not(.rev__c--shot) q{
  font-size:1.12rem;line-height:1.5;letter-spacing:-.005em;
  display:flex;align-items:center;
}
.rev__c:not(.rev__c--shot) q::before{
  content:"\201C";
  font-family:var(--display);font-size:2.6rem;line-height:0;
  color:var(--flame);opacity:.5;margin-right:.18em;align-self:flex-start;
  position:relative;top:.42em;
}

/* The service chip: what they actually had done. */
.rev__svc{
  display:inline-flex;align-items:center;gap:6px;align-self:flex-start;
  /* The service name is free text up to 40 characters — without a cap, a
     long one sizes the chip to max-content and pushes past the card. */
  max-width:100%;flex-wrap:wrap;
  margin-top:12px;padding:5px 11px;border-radius:20px;
  background:rgba(232,135,60,.12);border:1px solid rgba(232,135,60,.4);
  color:var(--flame);font-size:.72rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
}
.rev__svc .ic{width:12px;height:12px}

.rev__w{display:flex;align-items:center;gap:10px;margin-top:14px;padding-top:13px;border-top:1px solid var(--line-soft)}
.rev__pic{width:36px;height:36px;flex:none;border-radius:50%;object-fit:cover;border:1px solid var(--line)}

@media (prefers-reduced-motion:reduce){
  .rev__c:hover{transform:none}
  .rev__c:hover .rev__shot img{transform:none}
}

/* ==========================================================================
   Visit — live status and colour-coded cards
   The strip answers the one question every visitor has at this point:
   "can I turn up right now?" — and keeps answering it as the clock moves.
   ========================================================================== */
.livebar{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  max-width:760px;margin:0 auto clamp(24px,4vw,36px);
  padding:14px 18px;border-radius:var(--r);
  background:var(--panel);border:1px solid var(--line);
  border-left:3px solid var(--bone-quiet);
  font-size:.96rem;color:var(--bone-dim);
}
.livebar b{color:var(--bone);font-weight:600}
.livebar__txt{flex:1;min-width:200px}
.livebar__clock{
  font-variant-numeric:tabular-nums;font-size:.78rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--bone-quiet);
}
.livebar__dot{
  width:11px;height:11px;flex:none;border-radius:50%;
  background:var(--bone-quiet);position:relative;
}
/* Open: a green dot that breathes, so the state reads at a glance. */
.livebar[data-state="open"]{border-left-color:#3FBF6F;background:rgba(63,191,111,.07)}
.livebar[data-state="open"] .livebar__dot{background:#3FBF6F}
.livebar[data-state="open"] .livebar__dot::after{
  content:"";position:absolute;inset:-4px;border-radius:50%;
  border:2px solid #3FBF6F;animation:pulse 2.2s ease-out infinite;
}
/* Closing soon: amber, and it never relies on colour alone — the words
   say "Closing soon" too. */
.livebar[data-state="closing"]{border-left-color:var(--flame);background:rgba(232,135,60,.08)}
.livebar[data-state="closing"] .livebar__dot{background:var(--flame)}
.livebar[data-state="closed"]{border-left-color:var(--bone-quiet)}
@keyframes pulse{0%{transform:scale(.8);opacity:.9}100%{transform:scale(2.1);opacity:0}}
@media (prefers-reduced-motion:reduce){.livebar__dot::after{animation:none;opacity:.5}}

.visitsec .visit{display:grid;grid-template-columns:1fr;gap:clamp(20px,3vw,32px)}
@media (min-width:960px){.visitsec .visit{grid-template-columns:1fr 1.1fr;align-items:start}}

.vcards{display:grid;gap:12px}
.vcard{
  position:relative;padding:20px 20px 22px;
  background:var(--panel);border:1px solid var(--line-soft);
  border-radius:var(--r);border-top:2px solid var(--line);
  transition:border-color .3s,transform .3s var(--ease);
}
.vcard:hover{transform:translateY(-2px)}
.vcard h3{
  font-family:var(--display);font-size:.82rem;font-weight:700;
  letter-spacing:.22em;text-transform:uppercase;margin-bottom:10px;
}
.vcard address,.vcard p{color:var(--bone-dim);font-style:normal}
.vcard a:not(.btn):not(.social__l){color:var(--bone);text-decoration:underline;text-underline-offset:3px;display:inline-flex;align-items:center;min-height:var(--tap)}
.vcard__ic{
  position:absolute;top:16px;right:18px;
  display:grid;place-items:center;width:38px;height:38px;border-radius:50%;
  background:var(--ground);border:1px solid var(--line);
  transition:transform .4s var(--ease),color .3s,border-color .3s;
}
.vcard:hover .vcard__ic{transform:rotate(-8deg) scale(1.08)}

/* Each card gets its own accent so the three read as three different jobs. */
.vcard--addr{border-top-color:var(--flame)}
.vcard--addr h3,.vcard--addr .vcard__ic{color:var(--flame)}
.vcard--addr:hover{border-color:rgba(232,135,60,.45)}
.vcard--hours{border-top-color:var(--bronze)}
.vcard--hours h3,.vcard--hours .vcard__ic{color:var(--bronze)}
.vcard--hours:hover{border-color:var(--bronze-dim)}
.vcard--talk{border-top-color:#3FBF6F}
.vcard--talk h3,.vcard--talk .vcard__ic{color:#3FBF6F}
.vcard--talk:hover{border-color:rgba(63,191,111,.45)}
.vcard--talk .social{margin-top:14px}
.vcard .btn{margin-top:14px}

/* Today's row is called out by weight and a marker, not by colour alone. */
.rota tr[data-today="true"] td{color:var(--bone);font-weight:600}
.rota tr[data-today="true"] td:first-child{position:relative;padding-left:14px}
.rota tr[data-today="true"] td:first-child::before{
  content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);
  width:5px;height:5px;border-radius:50%;background:var(--bronze);
}

.here{
  margin-top:10px;font-size:.84rem;color:var(--bone-dim);
  border-left:2px solid var(--flame);padding-left:11px;
}
.here b{color:var(--bone)}

/* ==========================================================================
   Admin-only "why is this section missing" card.
   Deliberately unlike the rest of the site — dashed, tagged, unmistakably
   scaffolding — because it must never be mistaken for real content.
   ========================================================================== */
.mrs-gap{padding:clamp(28px,5vw,44px) 0}
.mrs-gap__box{
  position:relative;max-width:640px;margin-inline:auto;text-align:center;
  padding:26px 24px 28px;border-radius:var(--r);
  border:2px dashed var(--line);background:rgba(232,135,60,.04);
}
.mrs-gap__tag{
  position:absolute;top:-10px;left:50%;transform:translateX(-50%);
  background:var(--flame);color:#14100C;
  font-family:var(--display);font-size:.64rem;font-weight:700;
  letter-spacing:.18em;text-transform:uppercase;padding:4px 12px;border-radius:20px;
  white-space:nowrap;
}
.mrs-gap__box h3{
  font-family:var(--display);font-size:1.3rem;font-weight:800;
  letter-spacing:.04em;text-transform:uppercase;margin-bottom:8px;
}
.mrs-gap__box p{color:var(--bone-dim);font-size:.94rem;max-width:52ch;margin:0 auto 16px}

/* ==========================================================================
   v2.4 — more motion, more graphic
   ========================================================================== */

/* --- a hairline that draws itself under every section eyebrow ------------ */
.eyebrow::before{transform-origin:left;transition:transform .6s var(--ease) .1s}
/* Only ever collapsed while its own reveal is still pending — never by
   default, so a dash can't go missing if the observer never fires. */
.eyebrow[data-in]:not(.is-in)::before{transform:scaleX(0)}

/* --- section headings get a soft glow as they land ----------------------- */
.d2{position:relative}
[data-split].is-in{animation:settle .9s var(--ease) both}
@keyframes settle{
  0%{letter-spacing:.06em}
  100%{letter-spacing:-.01em}
}

/* --- cards rise in sequence rather than all at once --------------------- */
.shop .prod[data-in],.cmp-grid .cmpc{transition-delay:0s}
.shop .prod:nth-child(2){transition-delay:.06s}
.shop .prod:nth-child(3){transition-delay:.12s}
.shop .prod:nth-child(4){transition-delay:.18s}
.shop .prod:nth-child(5){transition-delay:.24s}
.shop .prod:nth-child(6){transition-delay:.3s}
.bases .base{opacity:0;transform:translateY(18px) scale(.98);transition:opacity .6s var(--ease),transform .6s var(--ease)}
[data-in].is-in .base,.bases.is-in .base{opacity:1;transform:none}
.bases.is-in .base:nth-child(2){transition-delay:.07s}
.bases.is-in .base:nth-child(3){transition-delay:.14s}
.bases.is-in .base:nth-child(4){transition-delay:.21s}

/* --- the flame button gets a travelling edge light ---------------------- */
.btn--flame{position:relative;isolation:isolate}
.btn--flame::before{
  content:"";position:absolute;inset:-2px;z-index:-1;border-radius:inherit;
  background:conic-gradient(from var(--spin,0deg),transparent 0 72%,rgba(255,255,255,.65) 82%,transparent 92% 100%);
  opacity:0;transition:opacity .3s;
}
.btn--flame:hover::before{opacity:1;animation:spin 1.6s linear infinite}
@keyframes spin{to{--spin:360deg}}
@property --spin{syntax:'<angle>';initial-value:0deg;inherits:false}

/* --- the section rail gets a live progress thread ------------------------ */
.rail::before{
  content:"";position:absolute;right:10px;top:0;bottom:0;width:1px;
  background:linear-gradient(var(--flame) var(--railp,0%),var(--line) var(--railp,0%));
  transition:background .2s linear;
}

/* --- before/after handle pulses until it is touched --------------------- */
.cmp:not([data-touched]) .cmp__grip::after{
  content:"";position:absolute;inset:-6px;border-radius:50%;
  border:2px solid var(--bone);opacity:.55;
  animation:pulse 2.4s ease-out infinite;
}
.cmp__grip{position:absolute}

/* No page-level fade here on purpose: hiding <body> until an animation runs
   means one CSS hiccup blanks the entire site, which is a far worse failure
   than a section appearing without a transition. */

@media (prefers-reduced-motion:reduce){
  [data-split].is-in{animation:none}
  .btn--flame:hover::before{animation:none;opacity:0}
  .cmp:not([data-touched]) .cmp__grip::after{animation:none;opacity:0}
  .bases .base{opacity:1;transform:none}
}
body[data-anim="off"] .bases .base{opacity:1;transform:none}
body[data-anim="off"] [data-split].is-in{animation:none}

/* ==========================================================================
   Video cards — each one says where it goes and opens that clip directly
   ========================================================================== */
.tt__c{flex:0 0 218px;scroll-snap-align:start;text-decoration:none}
@media (min-width:640px){.tt__c{flex-basis:242px}}

.tt__sh{
  display:block;position:relative;aspect-ratio:9/16;overflow:hidden;
  border-radius:var(--r);border:1px solid var(--line-soft);background:var(--panel);
  transition:border-color .3s,transform .4s var(--ease),box-shadow .4s;
}
.tt__c:hover .tt__sh{
  border-color:var(--bronze-dim);transform:translateY(-4px);
  box-shadow:0 20px 46px rgba(0,0,0,.5);
}

/* A wash that lifts the chip and caption off busy footage. */
.tt__glow{
  position:absolute;inset:0;z-index:1;
  background:linear-gradient(0deg,rgba(6,7,10,.86) 0%,rgba(6,7,10,.25) 38%,transparent 62%);
  transition:opacity .35s;
}
.tt__c:hover .tt__glow{opacity:.75}

/* Platform chip — icon AND the name, so it never depends on brand colour. */
.tt__chip{
  position:absolute;top:10px;left:10px;z-index:3;
  display:inline-flex;align-items:center;gap:6px;
  padding:5px 10px;border-radius:20px;
  background:rgba(28,30,38,.72);border:1px solid var(--line);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);
  font-family:var(--display);font-size:.62rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bone);
  transition:background-color .3s,border-color .3s,color .3s;
}
.tt__c[data-src="youtube"]:hover .tt__chip{background:#FF3B30;border-color:#FF3B30;color:#fff}
.tt__c[data-src="tiktok"]:hover .tt__chip{background:var(--bone);border-color:var(--bone);color:#0A0B0F}
.tt__c[data-src="instagram"]:hover .tt__chip{background:#E1306C;border-color:#E1306C;color:#fff}

/* "Watch →" slides up from the bottom edge on hover. */
.tt__go{
  position:absolute;left:10px;right:10px;bottom:10px;z-index:3;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  padding:9px 12px;border-radius:var(--r);
  background:var(--flame);color:#14100C;
  font-family:var(--display);font-size:.7rem;font-weight:800;
  letter-spacing:.14em;text-transform:uppercase;
  transform:translateY(calc(100% + 12px));opacity:0;
  transition:transform .38s var(--ease),opacity .3s;
}
.tt__c:hover .tt__go,.tt__c:focus-visible .tt__go{transform:none;opacity:1}

.tt__pl{
  position:absolute;left:50%;top:50%;z-index:2;transform:translate(-50%,-50%);
  width:52px;height:52px;border-radius:50%;display:grid;place-items:center;
  background:rgba(28,30,38,.55);border:1.5px solid var(--bone);color:var(--bone);
  font-size:.95rem;padding-left:3px;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
  transition:transform .4s var(--ease),opacity .3s,background-color .3s;
}
.tt__c:hover .tt__pl{transform:translate(-50%,-50%) scale(1.12);background:var(--flame);border-color:var(--flame);color:#14100C}

.tt__cap{
  margin-top:10px;font-size:.84rem;color:var(--bone-dim);line-height:1.45;
  transition:color .25s;
}
.tt__c:hover .tt__cap{color:var(--bone)}

/* Touch screens get no hover, so the affordance is simply always on. */
@media (hover:none){
  .tt__go{transform:none;opacity:1}
  .tt__cap{color:var(--bone-dim)}
}

/* ==========================================================================
   Five-stage booking: progress bar, step label, summary, confirmation
   ========================================================================== */
.bkw__bar{height:3px;background:var(--line-soft);border-radius:2px;overflow:hidden;margin:0 0 clamp(22px,4vw,32px)}
.bkw__fill{display:block;height:100%;width:20%;background:var(--flame);transition:width .5s var(--ease)}

.bkw__k{
  font-family:var(--display);font-size:.68rem;font-weight:700;
  letter-spacing:.22em;text-transform:uppercase;color:var(--bronze);margin-bottom:.5rem;
}

/* The order summary the customer confirms before paying. */
.sum{
  background:var(--panel);border:1px solid var(--line-soft);
  border-radius:var(--r);padding:6px 18px;margin:18px 0 14px;
}
.sum__r{
  display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  padding:11px 0;border-bottom:1px solid var(--line-soft);font-size:.94rem;
}
.sum__r:last-child{border-bottom:0}
.sum__r span:first-child{color:var(--bone-quiet);letter-spacing:.06em;text-transform:uppercase;font-size:.74rem}
.sum__r span:last-child{color:var(--bone);text-align:right}
.sum__r--total{border-top:1px solid var(--line);margin-top:2px}
.sum__r--total span:last-child{
  font-family:var(--display);font-size:1.35rem;font-weight:800;color:var(--flame);
}
.bkw__terms{color:var(--bone-dim);font-size:.88rem;margin-bottom:20px;border-left:2px solid var(--bronze-dim);padding-left:12px}

/* Step 05 — reached only after the money has actually cleared. */
.bkw__done{text-align:center}
.done__tick{
  width:76px;height:76px;margin:0 auto 18px;border-radius:50%;
  display:grid;place-items:center;font-size:2.1rem;
  background:rgba(63,191,111,.12);border:2px solid #3FBF6F;color:#3FBF6F;
  animation:tickin .55s var(--ease) both;
}
@keyframes tickin{0%{transform:scale(.5);opacity:0}60%{transform:scale(1.08)}100%{transform:scale(1);opacity:1}}
.bkw__done .sum{text-align:left;max-width:460px;margin-inline:auto}
@media (prefers-reduced-motion:reduce){.done__tick{animation:none}}

/* A Customizer logo arrives as WordPress's own markup with no sizing of its
   own, so an ordinary 1000px upload would otherwise blow the header apart.
   Every logo route is pinned to the same box, and the height comes from one
   custom property the panel writes — so the setting is never fought over by
   an !important somewhere else. */
.custom-logo-link{display:flex;align-items:center;line-height:0}
.custom-logo,
.brandmark--nav{
  height:var(--logo-h,44px)!important;
  width:auto!important;
  max-width:150px;
  object-fit:contain;
}

/* ==========================================================================
   v2.8 — more life, without costing legibility or speed
   Everything below is transform/opacity only, so it stays on the compositor
   and never triggers layout. All of it collapses under reduced-motion.
   ========================================================================== */

/* Service cards lift and light their edge as you go down the list. */
.svc{overflow:hidden}
.svc::after{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--flame);transform:scaleY(0);transform-origin:top;
  transition:transform .35s var(--ease);
}
.svc:hover::after{transform:scaleY(1)}
.svc[aria-checked="true"]::after{transform:scaleY(1)}

/* The chosen slot and day get a quick confirming pop. */
.slot[aria-pressed="true"],.cal__d[aria-pressed="true"]{animation:pick .32s var(--ease)}
@keyframes pick{0%{transform:scale(.86)}60%{transform:scale(1.06)}100%{transform:scale(1)}}

/* Step numbers tick over rather than just changing colour. */
.bkw__steps li[aria-current="step"] span{animation:pick .4s var(--ease)}

/* Product and category cards get a soft light that follows the card edge. */
.prod,.base,.rev__c{position:relative}
.prod::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 0 0 1px transparent;transition:box-shadow .3s;
}
.prod:hover::after{box-shadow:inset 0 0 0 1px rgba(224,191,172,.28)}

/* Headings underline themselves as they arrive. */
.sec__head .d2::after{
  content:"";display:block;height:2px;width:0;margin-top:.5rem;
  background:linear-gradient(90deg,var(--flame),transparent);
  transition:width .8s var(--ease) .25s;
}
.sec__head--c .d2::after{margin-inline:auto;background:linear-gradient(90deg,transparent,var(--flame),transparent)}
.sec__head .d2.is-in::after{width:clamp(80px,22%,180px)}
.sec__head--c .d2.is-in::after{width:clamp(120px,30%,260px)}

/* The bag count pops when it changes. */
.nav__bagn{transition:transform .25s var(--ease)}
.nav__bag:hover .nav__bagn{transform:scale(1.12)}

/* Inputs glow rather than just outlining, which reads as "live". */
.bkw__f input,.bkw__f textarea,.find__in{transition:border-color .25s,box-shadow .25s,background-color .25s}
.bkw__f input:focus,.bkw__f textarea:focus{background:var(--ground)}

/* WhatsApp button breathes very slightly so the eye finds it. */
.wa-float[data-show="true"]{animation:float 4s ease-in-out infinite}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}

@media (prefers-reduced-motion:reduce){
  .slot[aria-pressed="true"],.cal__d[aria-pressed="true"],
  .bkw__steps li[aria-current="step"] span{animation:none}
  .wa-float[data-show="true"]{animation:none}
  .sec__head .d2::after{transition:none;width:clamp(80px,22%,180px)}
}
body[data-anim="off"] .wa-float[data-show="true"]{animation:none}
body[data-anim="off"] .sec__head .d2::after{width:clamp(80px,22%,180px);transition:none}

/* The brand mark is a link home, so it should behave like one — it lifts,
   turns very slightly, and presses in when tapped. */
.nav__brand{transition:opacity .2s}
.nav__brand:active{opacity:.75}
.nav__brand:active .brandmark--nav{transform:scale(.94)}
.brandmark--nav{transition:transform .45s var(--ease),filter .45s}
.nav__brand:hover .brandmark--nav{
  transform:rotate(-7deg) scale(1.07);
  filter:drop-shadow(0 4px 14px rgba(232,135,60,.35));
}
.nav__brand:focus-visible{outline:3px solid var(--flame);outline-offset:4px;border-radius:var(--r)}
@media (prefers-reduced-motion:reduce){
  .nav__brand:hover .brandmark--nav{transform:none}
}

/* ==========================================================================
   v3.1 — a sharper menu and a stronger category showcase
   ========================================================================== */

/* --- desktop menu: a moving indicator instead of four separate underlines - */
@media (min-width:900px){
  .nav__links{position:relative;gap:30px}
  .nav__links a{
    font-weight:600;color:var(--bone-dim);
    padding-inline:2px;
    transition:color .25s var(--ease),transform .25s var(--ease);
  }
  .nav__links a::after{
    height:2px;bottom:9px;
    background:linear-gradient(90deg,var(--flame),var(--bronze));
    border-radius:2px;
  }
  .nav__links a:hover{color:var(--bone);transform:translateY(-1px)}
  /* The section you are actually looking at stays lit. */
  .nav__links a[aria-current="true"]{color:var(--bone)}
  .nav__links a[aria-current="true"]::after{transform:scaleX(1)}
}

/* --- mobile drawer: numbered, staggered, and clearly tappable ------------ */
@media (max-width:899px){
  .nav__links{padding-top:14px}
  .nav__links a{
    position:relative;padding-left:34px;
    opacity:0;transform:translateX(-12px);
    transition:opacity .35s var(--ease),transform .35s var(--ease),background-color .2s;
  }
  .nav__links[data-open="true"] a{opacity:1;transform:none}
  .nav__links[data-open="true"] a:nth-child(1){transition-delay:.05s}
  .nav__links[data-open="true"] a:nth-child(2){transition-delay:.10s}
  .nav__links[data-open="true"] a:nth-child(3){transition-delay:.15s}
  .nav__links[data-open="true"] a:nth-child(4){transition-delay:.20s}
  .nav__links[data-open="true"] a:nth-child(5){transition-delay:.25s}
  /* A hairline counter down the left edge gives the drawer structure. */
  .nav__links a::before{
    counter-increment:navnum;content:counter(navnum,decimal-leading-zero);
    position:absolute;left:0;top:50%;transform:translateY(-50%);
    font-size:.62rem;letter-spacing:.1em;color:var(--flame);opacity:.65;
  }
  .nav__links{counter-reset:navnum}
  .nav__links a[aria-current="true"]{color:var(--flame)}
  .nav__links a[aria-current="true"]::before{opacity:1}
}
@media (prefers-reduced-motion:reduce){
  .nav__links a{opacity:1!important;transform:none!important}
}

/* --- base cards: a hairline that grows under the name -------------------- */
.base::before{
  content:"";position:absolute;inset:0;z-index:3;pointer-events:none;
  border-radius:inherit;
  box-shadow:inset 0 0 0 1px rgba(242,238,233,.06);
  transition:box-shadow .3s;
}
.base:hover::before{box-shadow:inset 0 0 0 1px rgba(224,191,172,.32)}
.base__name{
  display:flex;align-items:baseline;gap:.5em;
}
/* A hairline rule grows under the base name on hover. */
.base__name::after{
  content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--flame),transparent);
  transform:scaleX(0);transform-origin:left;transition:transform .45s var(--ease);
}
.base:hover .base__name::after,
.base:focus-visible .base__name::after{transform:scaleX(1)}

/* The filter row reads as one control, not five loose buttons. */
.filters{
  gap:6px;padding:5px;border-radius:calc(var(--r) + 4px);
  background:var(--panel);border:1px solid var(--line-soft);
  width:max-content;max-width:100%;
  /* Cancel the edge-to-edge bleed inherited from the plain scroll row above.
     A bleed is right for a bare row of chips and wrong for a bordered panel:
     it put the panel's own left edge off-screen. It still scrolls inside
     itself when the families do not fit. */
  margin-inline:0;padding-inline:5px;
}
.filter{border-color:transparent;background:transparent}
.filter:hover{background:rgba(242,238,233,.05);border-color:transparent}
.filter[aria-pressed="true"]{box-shadow:0 2px 10px rgba(232,135,60,.3)}
@media (max-width:620px){.filters{width:auto}}

/* Staff-only explanation under an empty slot list. */
.slots__fix{
  margin-top:10px;padding:12px 14px;border-radius:var(--r);
  background:rgba(232,135,60,.08);border:1px dashed var(--flame);
  color:var(--bone-dim);font-size:.86rem;line-height:1.6;
}
.slots__fix a{color:var(--flame);text-decoration:underline;text-underline-offset:3px}


/* ==========================================================================
   Motion, by meaning
   ==========================================================================
   Everything below exists because a lift-and-glow on every box says the same
   thing about a price, a photograph and a question — which is to say nothing.
   Each rule here is chosen to describe what the thing actually is:

     a number   counts up          — it earns its value
     a price    gains weight       — this is the part you are deciding on
     a list     is traced          — a finger running down a menu
     a witness  lights in sequence — one star at a time, left to right
     a slider   peeks              — it shows you it moves before you touch it
     a question leans in           — it opens toward you

   The global prefers-reduced-motion rule near the top of this file already
   flattens every duration to .01ms, so nothing here needs its own guard; the
   two behaviours driven by JavaScript check the same query themselves.
   -------------------------------------------------------------------------- */

/* --- a number earns its value ------------------------------------------- */
/* Tabular figures stop the box juddering as digits change width while the
   count runs. Without this, "1 → 18 → 187 → 2018" reflows four times. */
.hero__stats b{
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
}
/* The count-up itself already lives near the top of main.js and predates this
   section; the only thing it was missing was a figure width that does not
   change as the digits roll. */

/* --- the price is the part you are deciding on --------------------------- */
/* The card already lifts. The price gains weight on top of that, because the
   card is the object and the price is the decision inside it. */
.card__price,.svc__price{
  display:inline-block;
  transition:transform .32s var(--ease),color .32s var(--ease);
  transform-origin:right center;
}
.prod:hover .card__price,
.svc:hover .svc__price,
.rate__go:hover .rate__price{
  transform:scale(1.09);
  color:#F19B55;
}

/* --- a list is traced ---------------------------------------------------- */
/* A rule draws along the row from the left, the way a finger moves down a
   printed price list. It is 2px and flame, so it is not carrying meaning by
   hue alone — the row also still lifts and its border changes. */
.svc{position:relative}
.svc::before{
  content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
  background:var(--flame);transform:scaleX(0);transform-origin:left center;
  transition:transform .45s var(--ease);
  pointer-events:none;
}
.svc:hover::before,
.svc:focus-within::before{transform:scaleX(1)}

/* --- a witness lights in sequence ---------------------------------------- */
/* This is the aggregate Google rating in the section header, not a per-card
   badge — there is exactly one of these on the page, and it is the strongest
   trust signal the salon has. The five stars are a single text node, so they
   cannot be staggered individually without splitting the markup; sweeping the
   fill across the group keeps the markup honest and still reads as earned. */
.rev__stars{
  display:inline-block;
  background:linear-gradient(90deg,var(--flame) 0 var(--lit,100%),var(--bone-quiet) var(--lit,100%) 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  transition:--lit 1.1s var(--ease);
}
/* Browsers that cannot animate a custom property, or cannot clip a gradient
   to text, simply get solid flame stars — which is what they had before. */
@supports not (background-clip:text){
  .rev__stars{background:none;-webkit-text-fill-color:currentColor;color:var(--flame)}
}
/* transition:none is the important half. Without it, adding the attribute
   animates the stars from lit down to grey — the sweep runs backwards and
   then snaps. Suppressed here, the start value lands instantly; removing the
   attribute restores the transition above and the fill sweeps up to full. */
.rev__stars[data-lighting]{--lit:0%;transition:none}
/* A custom property only animates once it has a declared type. */
@property --lit{syntax:'<percentage>';initial-value:100%;inherits:false}

/* --- a slider shows you it moves ----------------------------------------- */
/* The grip already pulses until first touch. On a fine pointer, hovering the
   frame also eases the split open a few percent and then back, so the whole
   image demonstrates the gesture rather than only the handle advertising it.
   Touch devices are excluded: there is no hover there, and the pulse already
   does this job. */
@media (hover:hover) and (pointer:fine){
  .cmp:not([data-touched]):hover .cmp__after{
    animation:peek 1.5s var(--ease);
  }
  .cmp:not([data-touched]):hover .cmp__line,
  .cmp:not([data-touched]):hover .cmp__grip{
    animation:peekline 1.5s var(--ease);
  }
}
@keyframes peek{
  0%,100%{-webkit-clip-path:inset(0 0 0 var(--split));clip-path:inset(0 0 0 var(--split))}
  45%    {-webkit-clip-path:inset(0 0 0 calc(var(--split) + 7%));clip-path:inset(0 0 0 calc(var(--split) + 7%))}
}
@keyframes peekline{
  0%,100%{transform:translateX(0)}
  45%    {transform:translateX(7%)}
}
/* The line and grip are positioned with left:var(--split) and their own
   translate, so the keyframe above would clobber the centring. These restore
   it by composing the nudge on top of the existing offset. */
.cmp__line{will-change:transform}
.cmp__grip{will-change:transform}

/* --- a question leans in ------------------------------------------------- */
/* There was no hover state here at all, which made the FAQ the only list on
   the page that did not answer the pointer. It leans toward the reader and
   the rule under it brightens; the +/− icon already handles open/closed. */
.faq__q{
  transition:padding-left .3s var(--ease),color .3s var(--ease);
  padding-left:0;
}
.faq__q:hover,
.faq__q:focus-visible{padding-left:8px;color:var(--bone)}
.faq__row{transition:border-color .3s var(--ease)}
.faq__row:hover{border-color:var(--bronze-dim)}
/* The icon grows very slightly on hover so the target reads as pressable
   before the pointer reaches it. */
.faq__ic{transition:transform .3s var(--ease)}
.faq__q:hover .faq__ic{transform:scale(1.15)}

/* --- the secondary button was the only control with no transition -------- */
.btn--ghost{
  transition:border-color .3s var(--ease),background-color .3s var(--ease),
             color .3s var(--ease),transform .18s var(--ease);
}
/* Both buttons acknowledge the press itself. A control that moves under the
   finger confirms the tap landed, which matters most on touch, where there
   is no hover state to have already given feedback. */
.btn:active{transform:translateY(1px) scale(.985)}


/* ==========================================================================
   A ground of its own for each section
   ==========================================================================
   The brief asked for imagery specific to every section. Most of these are
   drawn rather than photographed, because a photograph here would be either
   a stock picture of somebody else's salon — which is a claim this business
   cannot honestly make — or a flat dark gradient shipped as half a megabyte.
   Both are worse than four lines of CSS.

   Each ground is a different geometry so the sections do not read as one long
   scroll of the same charcoal: light falls from a different corner, the weave
   runs a different way, the warmth sits at a different depth. All of it is
   painted by the browser, costs no bytes, stays sharp at any size, and cannot
   fail to load.

   Everything here is very low contrast on purpose. It should be felt rather
   than noticed, and it must never fight the text sitting on top of it.
   -------------------------------------------------------------------------- */

/* Shared: a woven tooth, so the flat areas are not dead flat. Two hairline
   gradients crossing at right angles read as cloth at this opacity. */
.rev,#results,#systems,#faq,.visitsec{
  position:relative;isolation:isolate;
}
.rev::after,#results::after,#systems::after,#faq::after,.visitsec::after{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg,rgba(242,238,233,.014) 0 1px,transparent 1px 3px),
    repeating-linear-gradient(0deg,rgba(242,238,233,.010) 0 1px,transparent 1px 3px);
}

/* Reviews — warmth gathering at the top, the way a room feels when someone
   has just said something kind. */
.rev::after{
  background-image:
    radial-gradient(120% 62% at 50% -8%,rgba(232,135,60,.075),transparent 62%),
    repeating-linear-gradient(90deg,rgba(242,238,233,.014) 0 1px,transparent 1px 3px);
}

/* Results — the section that has to feel like evidence. Light from directly
   above, even and unflattering, nothing hidden in a corner. */
#results::after{
  background-image:
    linear-gradient(180deg,rgba(242,238,233,.030),transparent 30%),
    radial-gradient(84% 46% at 50% 0%,rgba(224,191,172,.055),transparent 68%);
}

/* Systems — the product grid. A faint square grid, like the cutting mat the
   bases are laid out on. */
#systems::after{
  background-image:
    linear-gradient(rgba(242,238,233,.022) 1px,transparent 1px),
    linear-gradient(90deg,rgba(242,238,233,.022) 1px,transparent 1px);
  background-size:46px 46px,46px 46px;
  -webkit-mask-image:radial-gradient(96% 76% at 50% 42%,#000 38%,transparent 100%);
          mask-image:radial-gradient(96% 76% at 50% 42%,#000 38%,transparent 100%);
}

/* FAQ — the quietest ground on the page, because this is where somebody
   reads carefully. A single soft bloom low and left, nothing else. */
#faq::after{
  background-image:radial-gradient(72% 54% at 12% 96%,rgba(232,135,60,.055),transparent 66%);
}

/* Visit — light arriving from outside, low and from the left, like daylight
   through the door he is about to walk through. */
.visitsec::after{
  background-image:
    linear-gradient(104deg,rgba(224,191,172,.070),transparent 44%),
    repeating-linear-gradient(0deg,rgba(242,238,233,.010) 0 1px,transparent 1px 3px);
}

/* The video rail sits between two dark sections and needs an edge, not a
   texture — a hairline top and bottom is enough to separate it. */
.tt{
  border-top:1px solid var(--line-soft);
  border-bottom:1px solid var(--line-soft);
}


/* ==========================================================================
   The banner breathes
   ==========================================================================
   A still photograph of an empty room is the right picture for this business
   — nobody is in it, nothing is claimed — but a completely static banner also
   reads as a page that has not finished loading. A very slow drift fixes that
   without adding a single byte or asking anyone to watch anything.

   It is deliberately slower than a viewer can consciously track: a full cycle
   takes over half a minute, so the room is never seen to move, only felt to
   be alive. Anything faster on a salon banner starts to feel like an advert.

   The mechanics matter. The image already carries a scroll parallax on its
   `transform`, so animating `transform` here would overwrite it. `scale` and
   `translate` are independent properties that the browser applies BEFORE
   `transform`, so the two compose instead of colliding — the drift multiplies
   onto the existing 1.08 and the parallax keeps its own channel untouched.
   -------------------------------------------------------------------------- */
@keyframes heroBreathe{
  0%,100%{ scale:1;      translate:0 0 }
  50%    { scale:1.065;  translate:-1.1% -0.8% }
}

#hero .panel__img{
  animation:heroBreathe 38s var(--ease) infinite;
  /* Only the two properties that actually change — promoting `transform` too
     would fight the parallax for the same compositor layer. */
  will-change:scale,translate;
}

/*
 * An infinite animation is exactly the case the global reduced-motion rule
 * handles badly: it forces every duration to .01ms, which does not stop an
 * infinite loop, it makes it cycle thousands of times a second. Same reason
 * the before/after grip pulse is switched off explicitly a few hundred lines
 * up rather than left to the blanket rule.
 */
@media (prefers-reduced-motion:reduce){
  #hero .panel__img{ animation:none; will-change:auto }
}

/* Once a banner video is actually playing it covers the still completely, so
   there is nothing left to drift — stop paying for the animation. */
#heroBg[data-video-playing] .panel__img{ animation:none }


/* ==========================================================================
   The banner arrives
   ==========================================================================
   Everything else on this page reveals on scroll. The banner cannot — it is
   already on screen when the page loads — so until now it simply appeared,
   fully formed, the instant the CSS parsed. Against a moving photograph that
   reads as a screenshot rather than a film.

   So it assembles instead: the line above the headline, then the headline
   itself, then the sentence, the figures, and last the two buttons. Roughly
   a second and a half end to end, which is slow enough to feel deliberate and
   short enough that nobody waiting to book is kept from the button.

   Two decisions worth keeping:

   It is CSS-driven: no class is added by script and nothing waits on a
   listener, so in the ordinary case there is nothing to fail. But `both` fill
   holds the element at its from-state — opacity 0 — until the delay elapses,
   which means an environment that never runs the animation leaves the
   headline invisible. That is the single worst failure this page could have,
   so it is not left to chance: printing switches the sequence off, and a
   short timer in main.js clears it from anything still at zero. The animation
   is the enhancement; being readable is not.

   The headline resolves out of a blur. That is the one piece of theatre here
   and it earns its place: text sharpening into focus is what a lens does, and
   this banner is meant to read as a camera arriving in the room.
   -------------------------------------------------------------------------- */

/* A firmer curve than the site's --ease. It decelerates harder and settles
   flatter, which is what makes a slow move read as expensive rather than
   sluggish. Reserved for this sequence. */
:root{ --e-cine:cubic-bezier(.16,1,.3,1) }

@keyframes heroRise{
  from{ opacity:0; transform:translateY(14px) }
  to  { opacity:1; transform:none }
}
@keyframes heroFocus{
  from{ opacity:0; transform:translateY(18px); filter:blur(7px) }
  to  { opacity:1; transform:none;             filter:blur(0) }
}

.hero__kicker,
.hero__hook,
.hero__tag,
.hero__stats,
.hero__cta,
.hero__micro{
  animation:heroRise .78s var(--e-cine) both;
}

/* The headline gets the lens treatment, and longer to do it in. */
.hero__hook{ animation:heroFocus 1.15s var(--e-cine) both }

/* Staggered so the eye is led down the composition in the order it should be
   read: where we are, what we promise, what it means, the proof, the action. */
.hero__kicker{ animation-delay:.10s }
.hero__hook  { animation-delay:.24s }
.hero__tag   { animation-delay:.62s }
.hero__stats { animation-delay:.78s }
.hero__cta   { animation-delay:.92s }
.hero__micro { animation-delay:1.04s }

/* Only while they are actually moving. Left on permanently these would hold a
   compositor layer for the life of the page for no reason. */
.hero__hook,.hero__cta{ will-change:transform,opacity }

/*
 * The blur is the expensive part of this sequence — it is a full-frame filter
 * on the largest text on the page. On a phone that can cost a dropped frame
 * on the very first paint, which is the worst possible moment. Narrow screens
 * get the same choreography without it.
 */
@media (max-width:620px){
  .hero__hook{ animation-name:heroRise; animation-duration:.85s }
}

@media (prefers-reduced-motion:reduce){
  .hero__kicker,.hero__hook,.hero__tag,
  .hero__stats,.hero__cta,.hero__micro{
    animation:none;
    will-change:auto;
  }
}

/* Print never runs the sequence, and a banner that prints blank is useless. */
@media print{
  .hero__kicker,.hero__hook,.hero__tag,
  .hero__stats,.hero__cta,.hero__micro{ animation:none }
}

/* The escape hatch the timer in main.js uses. */
.hero__copy[data-shown] .hero__kicker,
.hero__copy[data-shown] .hero__hook,
.hero__copy[data-shown] .hero__tag,
.hero__copy[data-shown] .hero__stats,
.hero__copy[data-shown] .hero__cta,
.hero__copy[data-shown] .hero__micro{
  animation:none;
  opacity:1;
  transform:none;
  filter:none;
}


/* ==========================================================================
   Product gallery — four photographs in one frame
   ==========================================================================
   A system is bought on detail: the base, the hairline, how it sits once it
   is on. One photograph cannot carry that, and the old card showed one, with
   a second hidden behind a hover most people never performed.

   Two ways in, because the two pointer types want different things:

   Hovering a quarter of the frame shows that photograph. It is invisible,
   requires no instruction, and costs nothing to discover — the picture simply
   changes as the eye moves across it. The hover targets live inside the link,
   so a click still opens the product.

   Tapping a dot does the same thing explicitly. Dots are real buttons outside
   the link, they are the only route on a touchscreen where hover does not
   exist, and they are reachable from the keyboard. Which photograph is
   showing is marked by a filled bar and by aria-pressed, never by colour on
   its own.
   -------------------------------------------------------------------------- */

.prod__shot{ position:relative }

/* Fills the 4/5 box that .prod__shot establishes. Without this the frame has
   no height of its own and the images inside it size against nothing. */
.prod__frame{
  position:absolute; inset:0; display:block; overflow:hidden;
  background:var(--panel);
}

/* All four are stacked; exactly one is opaque. Cross-fading rather than
   swapping keeps the change calm on a page that is already dark and busy. */
.prod__img{
  width:100%; height:100%;
  object-fit:var(--img-fit); object-position:center;
  transition:opacity .32s var(--ease);
}
.prod__img:not(:first-child){ position:absolute; inset:0 }
.prod__img{ opacity:0 }
.prod__img.is-on{ opacity:1 }

/* The hover targets: equal vertical slices, invisible, inside the link. */
.prod__zones{ position:absolute; inset:0; display:flex; z-index:2 }
.prod__zone{ flex:1 1 0; display:block }

/* --- the dots ------------------------------------------------------------ */
.prod__dots{
  position:absolute; left:0; right:0; bottom:10px; z-index:3;
  display:flex; justify-content:center; gap:6px;
  padding:0 12px; pointer-events:none;
}
.prod__dot{
  pointer-events:auto;
  /* 44px of touchable height around a 3px bar: the target is the tap area,
     the bar is only what you see. Anything smaller fails on a phone. */
  position:relative; flex:0 1 34px; height:var(--tap);
  display:grid; place-items:center;
  cursor:pointer; background:none; border:0; padding:0;
}
.prod__dot::before{
  content:""; display:block; width:100%; height:3px; border-radius:2px;
  background:rgba(242,238,233,.34);
  box-shadow:0 1px 3px rgba(28,30,38,.6);
  transition:background .28s var(--ease), transform .28s var(--ease);
}
.prod__dot:hover::before{ background:rgba(242,238,233,.6) }
.prod__dot.is-on::before{
  background:var(--bone);
  /* A second, non-colour signal for the selected bar. */
  transform:scaleY(1.9);
}

/* On a touchscreen there is no hover, so the invisible slice targets are
   dead weight — the dots are the whole control. */
@media (hover:none){
  .prod__zones{ display:none }
}

@media (prefers-reduced-motion:reduce){
  .prod__img,.prod__dot::before,.prod__cap{ transition:none }
}


/* ==========================================================================
   The shop
   ==========================================================================
   The archive used to be WooCommerce's own markup with almost no styling, so
   the same site had two different product grids: the home page had the
   four-photograph card and the shop had a thumbnail and a title. Both now
   render mrs_product_card(), and what follows is the grid and the browse row
   around it.
   -------------------------------------------------------------------------- */

/* WooCommerce keeps its <ul class="products"> and its <li>; only the contents
   are ours. Styling the list rather than replacing it means blocks, badges and
   extensions that hook the loop all keep working. */
.woocommerce ul.products{
  display:grid; gap:var(--gut);
  grid-template-columns:repeat(auto-fill,minmax(min(100%,250px),1fr));
  margin:0; padding:0; list-style:none;
}
.woocommerce ul.products::before,
.woocommerce ul.products::after{ content:none } /* kill the legacy clearfix */
.woocommerce ul.products li.product{
  width:auto; margin:0; padding:0; float:none; clear:none;
}
.woocommerce ul.products li.product .prod{ height:100% }

/* --- browse by base family ---------------------------------------------- */
.shopnav{
  display:flex; flex-wrap:wrap; gap:6px;
  margin:0 0 calc(var(--gut) * 1.2);
  padding:5px; width:max-content; max-width:100%;
  background:var(--panel); border:1px solid var(--line-soft);
  border-radius:calc(var(--r) + 4px);
}
.shopnav__a{
  display:inline-flex; align-items:center; gap:7px;
  min-height:var(--tap); padding:0 14px; border-radius:var(--r);
  font-family:var(--display); font-size:.82rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--bone-dim); text-decoration:none;
  border:1px solid transparent;
  transition:background-color .28s var(--ease), color .28s var(--ease);
}
.shopnav__a:hover{ background:rgba(242,238,233,.05); color:var(--bone) }
.shopnav__a.is-on{
  background:var(--flame); color:#14100C;
  box-shadow:0 2px 10px rgba(232,135,60,.3);
}
/* The tick is the point: which family you are in must not be carried by
   colour alone. It is only printed on the selected link. */
.shopnav__tick{ font-size:.9em; line-height:1 }
.shopnav__n{
  font-family:var(--text); font-size:.72rem; letter-spacing:0;
  opacity:.65; font-variant-numeric:tabular-nums;
}
.shopnav__a.is-on .shopnav__n{ opacity:.8 }

/* --- the family a product belongs to, on the card ----------------------- */
.prod__cat{
  display:inline-block; margin-bottom:7px;
  font-family:var(--display); font-size:.66rem; letter-spacing:.18em;
  text-transform:uppercase; color:var(--bronze-dim);
}

/* Out of stock says so in words rather than a greyed-out button. */
.prod__out{
  display:inline-flex; align-items:center; min-height:var(--tap);
  font-family:var(--display); font-size:.78rem; letter-spacing:.14em;
  text-transform:uppercase; color:var(--bone-quiet);
}

@media (max-width:520px){
  .shopnav{ width:auto }
  .shopnav__a{ padding:0 11px; font-size:.76rem }
}


/* ==========================================================================
   Moving between pages
   ==========================================================================
   Home, shop and a product are three separate documents, and until now moving
   between them was a hard cut: white flash, everything gone, everything back.
   On a site whose whole argument is composure, that is the one moment it
   stops feeling composed.

   Two declarations fix it. The browser takes a picture of the outgoing page,
   a picture of the incoming one, and cross-fades between them — no framework,
   no router, no single-page rewrite, and nothing at all in a browser that has
   not implemented it yet.
   -------------------------------------------------------------------------- */
@view-transition{ navigation:auto }

/*
 * The header and the bag are the same objects on every page, so they are named
 * and the browser morphs them in place instead of fading them out and back in.
 * Everything else cross-fades around them, which is what makes the header feel
 * like furniture rather than part of the page.
 */
.nav{ view-transition-name:site-header }
.nav__bag{ view-transition-name:site-bag }

::view-transition-group(root){ animation-duration:.42s }
::view-transition-old(root),
::view-transition-new(root){
  animation-timing-function:var(--e-cine,cubic-bezier(.16,1,.3,1));
}
/* The outgoing page should leave a little faster than the new one arrives,
   or the two overlap into mud in the middle of the fade. */
::view-transition-old(root){ animation-duration:.28s }

@media (prefers-reduced-motion:reduce){
  @view-transition{ navigation:none }
}


/* ==========================================================================
   Typographic detail
   ==========================================================================
   The two things that separate typesetting from text-on-a-page, both of them
   one line each and both ignored by browsers that do not know them.
   -------------------------------------------------------------------------- */

/* Headlines wrap into even lines instead of leaving a single orphan word on
   the last line. "No one has to know." is four words on a phone; without this
   it breaks as three-and-one about half the time. */
.d1,.d2,.hero__hook,.sec__head h2,.card__name,.prod__in h3,.faq__q{
  text-wrap:balance;
}
/* Body copy gets the cheaper cousin: it only prevents the orphan, without the
   line-length juggling that would be wasted on long paragraphs. */
.body,.hero__tag,.card__desc,.faq__a p,.rev__body{
  text-wrap:pretty;
}


/* ==========================================================================
   Rendering only what is on screen — measured, then rejected
   ==========================================================================
   content-visibility:auto was applied to the eight long sections below the
   banner, with contain-intrinsic-size to keep the scrollbar honest while they
   were skipped.

   Measuring it is what killed it. The real page is 18,462px; with a flat
   900px estimate per section the browser thought it was 15,734px — nearly
   2,800px, or fifteen per cent, short. The FAQ alone is 2,583px tall and was
   being guessed at 900. A visitor scrolling down would have watched the
   scrollbar correct itself the whole way.

   Per-section estimates would fix the arithmetic and break later: the numbers
   would be right until the salon adds a product, a review or a question, and
   then be quietly wrong again with nothing to catch it.

   And the win it was bought for is not there to win. LCP already measures
   348ms and the banner is not waiting on anything below it. Paying with a
   jumpy first scroll — on a first visit, which is the visit that matters —
   to speed up a paint that is already fast is the wrong side of the trade.

   Left here as a note so this is not rediscovered and re-added.
   -------------------------------------------------------------------------- */


/* ==========================================================================
   Container queries on the product card — tried, measured, reverted
   ==========================================================================
   The card is printed into two different grids now, so it is styled against
   the window while what actually matters is the width of the column it landed
   in. A container query is the right instrument for that, and it was applied:
   container-type:inline-size on .prod, with rules to drop the description in a
   tight column and let the card breathe in a wide one.

   It collapsed the card to 2px wide. Not in the preview pane — in explicitly
   sized frames at 1400, 900 and 420px, every one of them, with the tight-column
   rules firing everywhere because the card believed it was 2px across. Inline
   containment stops the contents contributing to the width, and something in
   this card's internals — most likely the absolutely positioned frame inside
   the aspect-ratio box — leaves nothing else to size it from.

   Reverted rather than patched around. The benefit was padding and a hidden
   description at the extremes; the cost was a broken product grid, which is
   the page that takes money. If it is worth revisiting, the container belongs
   on a wrapper with a definite width, not on the card itself, and it needs
   measuring at several widths before it is believed.
   -------------------------------------------------------------------------- */


/* ==========================================================================
   Chapter 01 — the city
   ==========================================================================
   Manchester from above, drawing closer as the page is scrolled, with the
   wordmark dissolving as the camera descends into the banner below it.

   The movement uses animation-timeline, so the browser runs it on the
   compositor from scroll position alone. No script is involved, which means
   there is nothing on the main thread to stutter and — the part that matters
   more — nothing that can take hold of the scroll. Position drives the
   animation; the animation never drives the position.

   Where scroll timelines are not supported the section simply sits still: a
   full-bleed photograph with the wordmark over it, which is a perfectly good
   opening frame. Nothing is hidden behind the animation, so nothing is lost
   without it.
   -------------------------------------------------------------------------- */

.city{
  /*
   * Two viewports tall, holding a sticky stage one viewport tall — so the
   * whole chapter costs exactly one screen of scrolling. Long preambles lose
   * the visitor who came to book.
   *
   * The height is declared rather than derived, which is what keeps this off
   * the CLS ledger: the space is reserved before the photograph arrives.
   */
  position:relative;
  height:200vh;
  background:var(--ground);
}
@supports (height:100dvh){ .city{ height:200dvh } }

.city__stage{
  position:sticky; top:0;
  height:100vh; overflow:hidden;
  /* The wordmark sits high, over the sky, rather than centred over the
     brightest part of the city. */
  display:grid; grid-template-rows:auto 1fr; align-items:start;
  justify-items:center;
  padding-top:clamp(84px,16vh,190px);
}
@supports (height:100dvh){ .city__stage{ height:100dvh } }

.city__bg{ position:absolute; inset:0; z-index:0 }
.city__bg picture{ display:block; width:100%; height:100% }

/* cover, not contain — this is a backdrop, not one of the salon's own
   photographs, so the framing rule that protects those does not apply. */
.city__img{
  width:100%; height:100%;
  object-fit:cover; object-position:center 40%;
  transform-origin:50% 45%;
}

/*
 * Two scrims doing two different jobs, because one doing both killed the
 * photograph.
 *
 * A night city is bright edge to edge and the brightest thing in this frame —
 * the lit station roof — sits dead centre. Darkening the whole picture enough
 * to read white type over that centre turned Manchester into a black
 * rectangle: the type became legible and the reason for the type disappeared.
 *
 * So the type moved to the sky, and the wash over the city was removed
 * altogether. Measuring the photograph explains why: its mean luminance is 63
 * of 255 — a night city is already only a quarter as bright as white, and
 * putting any meaningful scrim over it just darkens something dark until it
 * reads as a black rectangle.
 *
 * What is left is a band, not a wash. Strong across the top where the wordmark
 * sits, gone entirely by a third of the way down, so the city itself is shown
 * at full brightness. The bottom fades to the page colour so the banner below
 * arrives without a seam. The type holds its own with two shadows rather than
 * by having the photograph dimmed underneath it.
 */
.city__scrim{
  position:absolute; inset:0; z-index:1;
  background:linear-gradient(180deg,
      rgba(28,30,38,.92) 0%,
      rgba(28,30,38,.86) 14%,
      rgba(28,30,38,.62) 26%,
      /* The last line of the block sits at 34%. Measured there, it came out at
         4.47:1 against a 4.5 requirement — failing by three hundredths, which
         is the kind of miss that only shows up if you actually measure it.
         The band now holds until 44% so the whole block is on protected
         ground, and the city still has 44%-68% entirely to itself. */
      rgba(28,30,38,.34) 38%,
      transparent 46%,
      transparent 68%,
      rgba(28,30,38,.45) 84%,
      var(--ground) 100%);
}

.city__copy{
  position:relative; z-index:2; text-align:center;
  padding:0 var(--gut);
}
.city__mark{
  font-family:var(--display); font-weight:800;
  font-size:clamp(2.6rem,9vw,6.4rem); line-height:.94;
  letter-spacing:.02em; text-transform:uppercase; color:var(--bone);
  /* Two shadows: a tight one for edge definition against small bright
     details, and a wide soft one to lift the whole word off the city. */
  text-shadow:0 1px 3px rgba(0,0,0,.9), 0 4px 42px rgba(0,0,0,.75);
}
.city__sub{
  margin-top:.5em;
  font-family:var(--display); font-weight:600;
  font-size:clamp(.82rem,2vw,1.15rem);
  letter-spacing:.42em; text-transform:uppercase; color:var(--bronze);
  text-shadow:0 1px 16px rgba(0,0,0,.7);
}
.city__place{
  margin-top:1.6em;
  font-size:clamp(.7rem,1.5vw,.84rem);
  letter-spacing:.3em; text-transform:uppercase; color:var(--bone-dim);
}

/* A real link, not an ornament: it is the affordance that says this screen is
   not the whole page, and on a keyboard it skips the chapter entirely. */
.city__go{
  position:absolute; left:50%; bottom:clamp(20px,5vh,44px); z-index:3;
  transform:translateX(-50%);
  display:inline-flex; flex-direction:column; align-items:center; gap:8px;
  min-height:var(--tap); padding:8px 16px;
  font-family:var(--display); font-size:.72rem;
  letter-spacing:.26em; text-transform:uppercase;
  color:var(--bone-dim); text-decoration:none;
  transition:color .3s var(--ease);
}
.city__go:hover,.city__go:focus-visible{ color:var(--bone) }
.city__arrow{ font-size:1.1em; animation:cityNudge 2.6s var(--ease) infinite }
@keyframes cityNudge{
  0%,100%{ transform:translateY(0);   opacity:.55 }
  50%    { transform:translateY(5px); opacity:1 }
}

/* --- the descent ---------------------------------------------------------
   Driven entirely by how far the section has travelled through the viewport.
   The photograph grows a little and the type leaves before it, so the frame
   is clear by the time the banner arrives underneath. */
@supports (animation-timeline: view()){
  .city__img{
    animation:cityDescend linear both;
    animation-timeline:view();
    animation-range:entry 0% exit 100%;
  }
  .city__copy{
    animation:cityLift linear both;
    animation-timeline:view();
    animation-range:entry 40% exit 60%;
  }
  .city__go{
    animation:cityFade linear both;
    animation-timeline:view();
    animation-range:entry 40% entry 90%;
  }
}
@keyframes cityDescend{
  from{ transform:scale(1.02) }
  to  { transform:scale(1.20) }
}
@keyframes cityLift{
  from{ opacity:1; transform:translateY(0) }
  to  { opacity:0; transform:translateY(-40px) }
}
@keyframes cityFade{ from{ opacity:1 } to{ opacity:0 } }

@media (prefers-reduced-motion:reduce){
  .city{ height:100vh }
  @supports (height:100dvh){ .city{ height:100dvh } }
  .city__img,.city__copy,.city__go{ animation:none }
  .city__arrow{ animation:none }
}


/* ==========================================================================
   Chapter 02 — arrival
   ==========================================================================
   The exit of the city chapter rather than a chapter of its own. The wordmark
   has already gone; what resolves in its place is where this actually is —
   the real street, the real postcode — and then the banner arrives underneath
   without a visible join.

   It occupies the same sticky stage as chapter 01, so it costs no additional
   scrolling. That matters more than it sounds: every extra screen at the top
   of a homepage is another screen between somebody and the booking form.
   -------------------------------------------------------------------------- */

.city__arrive{
  position:absolute; left:50%; top:50%; z-index:2;
  transform:translate(-50%,-50%);
  width:min(90vw,var(--shell)); text-align:center;
  /* Starts invisible only where the animation can bring it back. Without a
     scroll timeline it is simply always visible, which is a fine second
     ending for the chapter. */
  opacity:1;
}
.city__label{
  font-family:var(--display); font-size:.68rem; font-weight:700;
  letter-spacing:.36em; text-transform:uppercase; color:var(--flame);
  margin-bottom:1.1em;
}
.city__addr{
  font-family:var(--display); font-weight:700;
  font-size:clamp(1.3rem,3.6vw,2.4rem); line-height:1.25;
  letter-spacing:.04em; text-transform:uppercase; color:var(--bone);
  text-shadow:0 1px 3px rgba(0,0,0,.9), 0 4px 34px rgba(0,0,0,.7);
}
.city__addr--dim{ color:var(--bronze); font-size:clamp(.92rem,2.2vw,1.4rem); margin-top:.3em }

@supports (animation-timeline: view()){
  /* Hidden until the chapter starts leaving, then held. The wordmark has
     finished its own exit by this point, so the two never overlap. */
  .city__arrive{
    animation:cityArrive linear both;
    animation-timeline:view();
    animation-range:exit 15% exit 62%;
  }
  /* The photograph gives way as the address resolves, so the banner below
     inherits a dark frame rather than a lit city cut in half. */
  .city__bg{
    animation:cityRecede linear both;
    animation-timeline:view();
    animation-range:exit 20% exit 88%;
  }
}
@keyframes cityArrive{
  0%  { opacity:0; transform:translate(-50%,-42%) }
  55% { opacity:1; transform:translate(-50%,-50%) }
  100%{ opacity:1; transform:translate(-50%,-50%) }
}
@keyframes cityRecede{
  from{ opacity:1 }
  to  { opacity:.12 }
}

@media (prefers-reduced-motion:reduce){
  .city__arrive,.city__bg{ animation:none }
  /* With the chapter collapsed to one still screen there is no room for both,
     and the wordmark is the one that should be there. */
  .city__arrive{ display:none }
}


/* ==========================================================================
   Chapter 03 — the entrance
   ==========================================================================
   The brief offers video scrubbing, an image sequence, layered perspective or
   WebGL for the illusion of walking inside. All four need footage of the
   inside of this salon, and there is none — so rather than fake a camera move
   through a room nobody has photographed, the room simply opens.

   The banner's own photograph is revealed through a widening vertical
   aperture: a narrow slot in the middle of a dark frame that opens outward as
   the section is reached. It reads as a door, which is the honest metaphor —
   the visitor is arriving at a private studio, not flying through it. It also
   needs no asset that does not already exist, and it is one property.

   A door rather than a circular iris on purpose: an iris is a film-projector
   flourish and would read as pastiche on a salon's homepage. A doorway widening
   is architectural, and it is what actually happens when you walk in.

   Only when the city chapter is above it. Without that, the banner is the top
   of the page and there is nothing to enter from — it should simply be there.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()){
  .panel--entered .panel__bg{
    animation:enterRoom linear both;
    animation-timeline:view();
    /* Open early and finish well before the section settles, so the doorway is
       gone by the time anyone reads the headline. A transition still running
       under live text is a distraction, not an effect. */
    animation-range:entry 5% entry 80%;
  }

  /*
   * The banner's own entrance fires on page load. With the city chapter above
   * it that happens two viewports away, unseen, and the copy is fully formed
   * by the time anyone arrives. Here it is re-timed to the scroll instead, so
   * the words assemble as the room opens rather than before it.
   */
  .panel--entered .hero__kicker,
  .panel--entered .hero__hook,
  .panel--entered .hero__tag,
  .panel--entered .hero__stats,
  .panel--entered .hero__cta,
  .panel--entered .hero__micro{
    animation-timeline:view();
    animation-delay:0s;
    animation-duration:auto;
  }
  .panel--entered .hero__kicker{ animation-range:entry 30% entry 62% }
  .panel--entered .hero__hook  { animation-range:entry 36% entry 74% }
  .panel--entered .hero__tag   { animation-range:entry 50% entry 82% }
  .panel--entered .hero__stats { animation-range:entry 56% entry 88% }
  .panel--entered .hero__cta   { animation-range:entry 62% entry 94% }
  .panel--entered .hero__micro { animation-range:entry 66% entry 96% }
}

@keyframes enterRoom{
  /* A slot roughly a door's width in the middle of the frame, opening out.
     inset() rather than clip-path:circle() so the edges stay vertical. */
  from{ clip-path:inset(0 42% 0 42%) }
  to  { clip-path:inset(0 0 0 0) }
}

@media (prefers-reduced-motion:reduce){
  .panel--entered .panel__bg{ animation:none; clip-path:none }
  .panel--entered .hero__kicker,
  .panel--entered .hero__hook,
  .panel--entered .hero__tag,
  .panel--entered .hero__stats,
  .panel--entered .hero__cta,
  .panel--entered .hero__micro{ animation:none }
}


/* ==========================================================================
   One scroll language, across every section
   ==========================================================================
   Everything on this page already reveals itself — but it does so by popping.
   An observer watches for an element to cross a threshold and then flips a
   class, so each block appears at a fixed moment regardless of how fast the
   page is moving. Twelve sections doing that in sequence reads as a stack of
   independent blocks arriving one after another.

   Tying the same reveal to scroll position instead is the whole difference
   between a page that animates and a page that moves. The block is 40% faded
   in when it is 40% of the way through its entry, at any scroll speed, and
   reverses if the reader scrolls back up. Nothing pops, because nothing is
   waiting for a threshold.

   This replaces the transition rather than adding to it: the observer keeps
   running and keeps adding .is-in, which is what browsers without scroll
   timelines still use. Where timelines exist the animation simply outranks
   that class, because animations beat ordinary declarations in the cascade.
   Both systems present, one visible, no flag to keep in sync.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()){
  [data-in]{
    animation:revealIn linear both;
    animation-timeline:view();
    /* Finished well before the block reaches the middle of the screen. A
       reveal still running under something being read is a distraction. */
    animation-range:entry 8% entry 46%;
    /* The transition would fight the animation for the same properties. */
    transition:none;
  }

  /* The existing stagger, expressed as scroll distance rather than seconds —
     each step starts a little later through its own entry. */
  [data-in][data-d="1"]{ animation-range:entry 14% entry 52% }
  [data-in][data-d="2"]{ animation-range:entry 20% entry 58% }

  /*
   * The escape hatch, matching the banner's. `both` fill parks these at
   * opacity 0 before their range, so anything that stops the animation
   * running would leave a section invisible — and unlike the banner there
   * are a dozen of them. The observer's .is-in cannot rescue it because the
   * animation outranks a plain class, so the rescue has to be !important.
   */
  [data-in].is-shown{
    animation:none !important;
    opacity:1 !important;
    transform:none !important;
  }
}

@keyframes revealIn{
  from{ opacity:0; transform:translateY(22px) }
  to  { opacity:1; transform:none }
}

/*
 * Under reduced motion the global killswitch flattens the duration, but these
 * are scroll-driven rather than time-driven, so duration means nothing to
 * them — they would still track the scroll. They have to be switched off by
 * name, and the content simply sits there.
 */
@media (prefers-reduced-motion:reduce){
  [data-in]{ animation:none; opacity:1; transform:none }
}

/* ==========================================================================
   v4.1 — the list, the card, the map, the green button

   Four changes, each answering the same complaint: the page used one shape —
   a bordered box — for every job on it. Services were boxes, categories were
   boxes, products were boxes, the address was a box. A page where everything
   is a card has no hierarchy, because a card is not a rank.
   ========================================================================== */

/* --- services, read down ------------------------------------------------- */
/* Was a three-across card grid. A price list is the one thing on a website
   that everybody already knows how to read, and the format it is read in is a
   list: name left, price right, a rule between. Turning that into cards asks
   the reader to compare across a row when the whole question is "how much". */
.rate{
  list-style:none;margin:0 auto;padding:0;max-width:820px;
  border-top:1px solid var(--line-soft);
}
.rate__row{border-bottom:1px solid var(--line-soft)}

/* The whole row is the button.
   Every service on this list can be booked and paid for, so the line that
   quotes the price is the line that starts the booking — rather than a price
   list with a single "book" button underneath it that makes you find the
   service again in a list you have just read. */
.rate__go{
  position:relative;display:flex;flex-wrap:wrap;align-items:baseline;
  gap:6px clamp(14px,3vw,32px);
  padding:clamp(16px,2.4vw,22px) 0;
  text-decoration:none;
  transition:background-color .3s,padding-inline .35s var(--ease);
}
/* The row lifts toward the reader rather than moving — nothing shifts the
   line it sits on, so the list never jitters as the pointer runs down it. */
.rate__go:hover{background:rgba(242,238,233,.03);padding-inline:12px}
.rate__go:focus-visible{outline:2px solid var(--flame);outline-offset:-2px;border-radius:4px}

/* A flame edge slides up the left, the same gesture the base tiles use. */
.rate__go::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--flame);
  transform:scaleY(0);transform-origin:bottom;transition:transform .4s var(--ease);
}
.rate__go:hover::before,.rate__go:focus-visible::before{transform:scaleY(1)}

.rate__main{flex:1 1 15rem;min-width:0}
.rate__name{
  display:block;font-family:var(--display);font-size:clamp(1.05rem,2.2vw,1.28rem);
  font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--bone);line-height:1.2;
}
.rate__desc{display:block;margin-top:5px;font-size:.92rem;line-height:1.55;color:var(--bone-dim);text-wrap:pretty}

.rate__end{display:flex;align-items:baseline;gap:clamp(10px,2vw,18px);margin-left:auto}
.rate__meta{
  font-family:var(--display);font-size:.7rem;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--bone-quiet);white-space:nowrap;
}
.rate__price{
  font-family:var(--display);font-size:clamp(1.1rem,2.4vw,1.34rem);font-weight:800;
  color:var(--flame);white-space:nowrap;font-variant-numeric:tabular-nums;
}
/* "Book" appears on approach on a pointer, and is simply always there on a
   touchscreen, where there is no approach to detect. */
.rate__book{
  display:inline-flex;align-items:center;gap:4px;white-space:nowrap;
  font-family:var(--display);font-size:.7rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bone-quiet);
  max-width:0;opacity:0;overflow:hidden;
  transition:max-width .4s var(--ease),opacity .3s var(--ease),color .3s;
}
.rate__go:hover .rate__book,.rate__go:focus-visible .rate__book{max-width:7em;opacity:1;color:var(--flame)}
@media (hover:none){ .rate__book{max-width:7em;opacity:.6} }

/* --- product cards, one size smaller ------------------------------------- */
/* Three across on a wide screen gave each system a 420px-wide photograph — a
   poster for a thing that costs less than the consultation. Four across, with
   a 3/4 frame instead of 4/5, fits the range on one screen so you can compare
   them, which is what a shop grid is for. */
@media (min-width:960px){ .shop{grid-template-columns:repeat(3,1fr)} }
@media (min-width:1180px){ .shop{grid-template-columns:repeat(4,1fr)} }
@media (min-width:1180px){
  .prod .prod__shot{aspect-ratio:3/4}
  .prod .prod__in{padding:14px 15px 16px}
  .prod .card__name{font-size:1.06rem}
  .prod .card__price{font-size:1.02rem}
  .prod .card__desc{font-size:.85rem}
}

/* On a phone the card lies down.
   One column at 375px gave every system a 419px-tall photograph, so the grid
   was one product per screen and comparing two of them meant remembering the
   first. Side by side, the photograph stays portrait — which is the shape a
   hairpiece actually is — and a whole card fits in a third of the screen. */
@media (max-width:439px){
  .prod{flex-direction:row;align-items:stretch}
  .prod .prod__shot{flex:0 0 38%;border-bottom:0;border-right:1px solid var(--line-soft)}
  .prod .prod__in{flex:1;min-width:0;padding:13px 14px 14px}
  .prod .card__name{font-size:1.02rem}
  .prod .card__price{font-size:1rem}
  .prod .card__desc{font-size:.84rem}
  /* The caption is the whole reason four photographs are worth having — it
     is what says which one shows the system on a head — so it stays. It just
     gets smaller and is allowed to wrap inside a 150px-wide frame. */
  .prod .prod__cap{left:6px;right:6px;max-width:none;padding:4px 7px;font-size:.58rem;letter-spacing:.1em}
}

/* Name over price rather than beside it: at this width they were sharing a
   line about 150px wide and both were wrapping mid-word. */
.prod .card__top{display:block;margin-bottom:6px}
.prod .card__name{margin-top:2px}
.prod .card__price{display:block;margin-top:4px}

/* --- what the photograph is showing -------------------------------------- */
/* Four photographs of one hairpiece all look like one hairpiece. The caption
   comes from the WordPress media caption, so "Fitted" appears on the shot
   that is actually of a fitted system and nowhere else. */
.prod__cap{
  position:absolute;left:10px;bottom:calc(var(--tap) + 4px);z-index:3;
  max-width:calc(100% - 20px);
  padding:5px 10px;border-radius:2px;
  background:rgba(28,30,38,.74);border:1px solid var(--line);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  font-family:var(--display);font-size:.66rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bone);
  opacity:0;transform:translateY(4px);
  transition:opacity .3s var(--ease),transform .3s var(--ease);
  pointer-events:none;
}
.prod__cap.is-on{opacity:1;transform:none}

/* --- the specification, on the card -------------------------------------- */
.prod__spec{
  margin:0 0 10px;display:flex;flex-wrap:wrap;gap:4px 6px;
}
.prod__spec div{
  display:flex;align-items:baseline;gap:5px;
  padding:3px 8px;border-radius:2px;
  background:rgba(242,238,233,.045);border:1px solid var(--line-soft);
}
.prod__spec dt{font-size:.62rem;letter-spacing:.12em;text-transform:uppercase;color:var(--bone-quiet)}
.prod__spec dd{margin:0;font-size:.76rem;font-weight:600;color:var(--bone-dim)}

/* --- the way through to the full page ------------------------------------ */
.card__foot{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.prod__more{
  display:inline-flex;align-items:center;gap:5px;min-height:var(--tap);
  font-family:var(--display);font-size:.72rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bone-quiet);
  text-decoration:none;transition:color .3s;
}
.prod__more::after{content:"\2192";transition:transform .3s var(--ease)}
.prod__more:hover{color:var(--flame)}
.prod__more:hover::after{transform:translateX(3px)}

/* --- WhatsApp, in its own colour ----------------------------------------- */
/* It was a ghost button, which on this page means "secondary". For a salon
   that takes most of its enquiries this way, that was the wrong rank. The
   green is WhatsApp's own, kept as an outline over the dark ground so it
   never outshouts the flame CTA that books an actual appointment. */
/* Two lines of text beside one mark. A column flex put the icon on a line of
   its own above the label, which is not a button, it is a stack. Grid keeps
   the mark in its own column spanning both rows. */
.btn--wa{
  display:grid;grid-template-columns:auto 1fr;
  grid-template-areas:"ic label" "ic sub";
  align-items:center;justify-items:start;column-gap:9px;
  height:auto;padding-block:9px;text-align:left;
  border-color:rgba(63,191,111,.45);color:#7BE0A4;
  background:rgba(63,191,111,.08);
}
.btn--wa .ic{grid-area:ic}
.btn--wa > span{grid-area:label}
.btn--wa .btn__sub{grid-area:sub}
.btn--wa:hover{border-color:#3FBF6F;background:rgba(63,191,111,.16);color:#9CEBBD}
.btn--wa .btn__sub{
  font-family:var(--body);font-size:.7rem;font-style:normal;font-weight:400;
  letter-spacing:.02em;text-transform:none;color:var(--bone-quiet);
}

/* --- the map, with the address in real type ------------------------------ */
/* The embed is desaturated to belong to the page, which also means you cannot
   read a street name off it. So the address is set below it in type, and the
   colour comes back when you touch the map — a small signal that it is a live
   map and not a picture of one. */
.map{position:relative;min-height:0;display:flex;flex-direction:column}
.map iframe{
  height:clamp(260px,38vw,400px);
  transition:filter .5s var(--ease);
}
.map:hover iframe,.map:focus-within iframe{filter:grayscale(.25) invert(.92) contrast(.9)}

.map__cap{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:8px 16px;padding:13px 16px;
  border-top:1px solid var(--line-soft);background:var(--ground);
}
.map__where{display:flex;align-items:center;gap:8px;min-width:0;color:var(--bone-dim);font-size:.9rem}
.map__where .ic{color:var(--flame);flex:none}
.map__where span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.map__go{
  display:inline-flex;align-items:center;gap:5px;min-height:var(--tap);
  font-family:var(--display);font-size:.72rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bone);
  text-decoration:none;white-space:nowrap;transition:color .3s;
}
.map__go::after{content:"\2197";transition:transform .3s var(--ease)}
.map__go:hover{color:var(--flame)}
.map__go:hover::after{transform:translate(2px,-2px)}
.map__none{border-radius:0}

@media (prefers-reduced-motion:reduce){
  .rate__go,.rate__go::before,.rate__book,.map iframe,
  .prod__more::after,.map__go::after,.base,.base::before,.base__ic,.base__go{transition:none}
}

/* ==========================================================================
   v4.3 — the same scroll language, applied further

   Two changes, both scroll-driven rather than clock-driven, which is the
   difference between a page that animates and a page that moves: the reader
   controls it and it reverses when they scroll back.

   Deliberately NOT here: a parallax on .panel__img. The theme already has
   one — script sets --par and the image carries
   `transform: scale(1.08) translate3d(0, var(--par), 0)`. A scroll-timeline
   animation on `transform` would have silently outranked it, and taken the
   banner's `heroBreathe` and the video thumbnails' hover zoom with it. That
   breathe animation already animates `scale` and `translate` individually
   rather than `transform`, specifically so it does not fight the parallax;
   adding a third claimant to the same property was the wrong instinct.
   ========================================================================== */

@supports (animation-timeline: view()){

  /* --- section headings draw their own rule ------------------------------ */
  /* The hairline under every eyebrow was drawn by a transition fired off the
     .is-in class. Tied to scroll instead it draws *as* you arrive, at reading
     speed, and undraws if you scroll back — the same swap [data-in] makes a
     few hundred lines up, for the same reason. The transition is switched off
     here or the two fight over transform. */
  .sec__head .eyebrow::before,.tt__hd .eyebrow::before{
    animation:ruleIn linear both;
    animation-timeline:view();
    animation-range:entry 10% entry 40%;
    transition:none;
  }
  @keyframes ruleIn{
    from{ transform:scaleX(0) }
    to  { transform:scaleX(1) }
  }

  /* --- the store grid arrives card by card ------------------------------- */
  /* Each card already carries [data-in]; this only widens the range so a tall
     grid does not finish revealing before the reader has reached the bottom
     of it. */
  .shop .prod[data-in]{ animation-range:entry 6% entry 42% }
}

/*
 * A scroll-driven animation tracks position, so flattening its duration does
 * nothing at all. It has to be switched off by name.
 */
@media (prefers-reduced-motion:reduce){
  .sec__head .eyebrow::before,.tt__hd .eyebrow::before{
    animation:none;
    transform:none;
  }
}

/* ==========================================================================
   v4.4 — the gallery, the door, and the sections that moved
   ========================================================================== */

/* --- the work, in photographs -------------------------------------------- */
/* A masonry-ish grid that stays honest about aspect: every photo keeps its own
   4:5 frame, so a portrait shot is never cropped square just to tile neatly. */
.gal{
  display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(8px,1.4vw,14px);
}
@media (min-width:700px){ .gal{grid-template-columns:repeat(3,1fr)} }
@media (min-width:1100px){ .gal{grid-template-columns:repeat(4,1fr)} }

.gal__i{margin:0;position:relative;border-radius:var(--r);overflow:hidden;background:var(--panel)}
.gal__i[hidden]{display:none!important}

.gal__l{
  position:relative;display:block;aspect-ratio:4/5;overflow:hidden;
  background:var(--ground);
}
.gal__l img{
  width:100%;height:100%;object-fit:var(--img-fit);display:block;
  transition:transform .7s var(--ease),filter .4s;
}
.gal__i:hover .gal__l img{transform:scale(1.05)}
.gal__l:focus-visible{outline:2px solid var(--flame);outline-offset:-3px}

/* The magnifier says the photo opens; it is the only affordance a picture
   without a caption has. */
.gal__zoom{
  position:absolute;top:10px;right:10px;z-index:2;
  width:32px;height:32px;display:grid;place-items:center;border-radius:50%;
  background:rgba(28,30,38,.62);border:1px solid var(--line);color:var(--bone);
  font-size:1.1rem;line-height:1;
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);
  opacity:0;transform:scale(.85);
  transition:opacity .3s var(--ease),transform .3s var(--ease),background-color .3s;
}
.gal__i:hover .gal__zoom,.gal__l:focus-visible .gal__zoom{opacity:1;transform:none}
.gal__i:hover .gal__zoom{background:var(--flame);border-color:var(--flame);color:#14100C}
@media (hover:none){ .gal__zoom{opacity:.75;transform:none} }

.gal__c{
  position:absolute;inset:auto 0 0 0;z-index:2;padding:10px 12px;
  display:flex;flex-direction:column;gap:3px;
  background:linear-gradient(0deg,rgba(6,7,10,.93) 0%,rgba(6,7,10,.6) 55%,transparent 100%);
  pointer-events:none;
}
.gal__tag{
  align-self:flex-start;font-family:var(--display);font-size:.6rem;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--flame);
}
.gal__t{font-size:.8rem;line-height:1.35;color:var(--bone)}
.gal__none{margin-top:18px;text-align:center;color:var(--bone-quiet);font-size:.92rem}

/* The gallery filter row is the store's chip row, same control, same look. */
.gfilters{justify-content:center;margin-inline:auto}

/* --- the door, beside the map -------------------------------------------- */
/* Somebody standing on Seymour Grove with a phone is not helped by a
   desaturated map tile; they are helped by seeing the door they are looking
   at. So the two sit side by side and the photograph gets equal weight. */
.findus{display:grid;grid-template-columns:1fr;gap:clamp(12px,2vw,18px)}
@media (min-width:820px){ .findus{grid-template-columns:1fr 1fr} }

.door{
  margin:0;position:relative;border-radius:var(--r);overflow:hidden;
  border:1px solid var(--line-soft);background:var(--panel);
  min-height:260px;
}
.door img{
  width:100%;height:100%;min-height:260px;object-fit:var(--img-fit);display:block;
  transition:transform .8s var(--ease);
}
.door:hover img{transform:scale(1.04)}
.door__c{
  position:absolute;inset:auto 0 0 0;padding:12px 14px;
  background:linear-gradient(0deg,rgba(6,7,10,.9) 0%,transparent 100%);
}
.door__tag{
  font-family:var(--display);font-size:.7rem;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--flame);
}
/* With no photograph uploaded the map takes the whole width rather than
   sitting in half of a two-column grid with a hole beside it. */
.map--wide{grid-column:1 / -1}

@media (prefers-reduced-motion:reduce){
  .gal__l img,.door img,.gal__zoom{transition:none;transform:none}
}
