/* ==========================================================================
   REVOLVER HAIR STUDIO
   Sections alternate obsidian / porcelain. The photography carries the page.
   ========================================================================== */

:root {
  --obsidian:  #0A0B0E;   /* a hair cooler and deeper, reads richer than black */
  --smoke:     #15171C;   /* mid dark, so dark sections have internal depth      */
  --porcelain: #F5F2ED;   /* light type on ink. No longer a section ground       */
  --graphite:  #23242A;
  /* Light sections are sky, not white. The owners said the white sections read
     like a search results page and asked for a pastel blue in their salon
     colours. This is lifted from the pale blue-grey on their business card.
     Graphite on it measures 12.4:1. */
  --sky:       #DCE8EE;
  --sky-deep:  #C9D9E2;   /* the second light tone: panels and the proof band   */
  --slate:     #5A6C6D;   /* the mid tone from the card. Rules only, 4.4:1       */
  /* Purple survives on one thing, the Book now button. The card has no purple;
     the buttons were the part of the demo the owners said they liked. */
  --purple:     #8375BE;  /* Book now on obsidian, 4.9:1                        */
  --purple-ink: #4B3B87;  /* Book now on sky, 7.4:1                             */

  --serif: "Instrument Serif", Georgia, serif;
  --sans:  "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  --ease: cubic-bezier(.22, 1, .36, 1);
  --pad: 20px;
  --max: 1440px;
}

@media (min-width: 760px)  { :root { --pad: 40px; } }
@media (min-width: 1200px) { :root { --pad: 60px; } }

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  overflow-x: clip;
  background: var(--obsidian);
  color: var(--porcelain);
  font-family: var(--sans);
  font-weight: 300;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; }
figure { margin: 0; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }

:focus-visible { outline: 2px solid var(--purple); outline-offset: 3px; }

.skip { position: absolute; left: -9999px; top: 0; z-index: 100;
        background: var(--porcelain); color: var(--obsidian); padding: 12px 18px; }
.skip:focus { left: 12px; top: 12px; }

/* --- button --------------------------------------------------------------- */

.btn {
  display: inline-block;
  padding: 17px 38px;
  border-radius: 999px;
  background: var(--purple);
  color: var(--obsidian);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  border: 1px solid var(--purple);
  transition: background-color .4s var(--ease), color .4s var(--ease), border-color .4s var(--ease);
}
.btn:hover { background: transparent; color: var(--porcelain); border-color: rgba(245, 242, 237, .5); }

/* ==========================================================================
   FLOATING NAV
   ========================================================================== */

.nav {
  position: fixed;
  z-index: 50;
  top: 14px;
  left: var(--pad); right: var(--pad);
  display: flex; align-items: center; gap: 20px;
  padding: 10px 10px 10px 22px;
  border-radius: 999px;
  background: rgba(10, 11, 14, 0.88);
  border: 1px solid rgba(245, 242, 237, 0.14);
  backdrop-filter: blur(14px) saturate(130%);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  transition: background-color .28s var(--ease), border-color .28s var(--ease),
              border-radius .4s var(--ease), top .4s var(--ease),
              left .4s var(--ease), right .4s var(--ease);
}
.nav.is-stuck {
  background: rgba(10, 11, 14, 0.92);
  border-color: rgba(245, 242, 237, 0.2);
}

.nav__logo { display: flex; align-items: center; min-height: 44px; padding: 8px 0; flex: none; }
.nav__logo img { width: 116px; height: auto; }

.nav__menu { margin-left: auto; }

.nav__menu-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;
  border-radius: 999px;
  border: 1px solid rgba(245, 242, 237, .28);
  color: var(--porcelain);
  cursor: pointer; list-style: none; flex: none;
  transition: background-color .3s var(--ease), border-color .3s var(--ease), color .3s var(--ease);
}
.nav__menu-btn::-webkit-details-marker { display: none; }

/* an icon announces nothing, so the label stays for screen readers */
.nav__menu-word {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* middle rule is the element, outer two are its pseudos */
.nav__menu-bars, .nav__menu-bars::before, .nav__menu-bars::after {
  width: 15px; height: 1.5px; background: currentColor; border-radius: 2px;
  transition: transform .3s var(--ease), background-color .2s var(--ease), top .3s var(--ease);
}
.nav__menu-bars { position: relative; }
.nav__menu-bars::before, .nav__menu-bars::after { content: ""; position: absolute; left: 0; }
.nav__menu-bars::before { top: -5px; }
.nav__menu-bars::after  { top: 5px; }

.nav__menu[open] .nav__menu-btn { background: var(--porcelain); border-color: var(--porcelain); color: var(--obsidian); }
.nav__menu[open] .nav__menu-bars { background: transparent; }
.nav__menu[open] .nav__menu-bars::before { top: 0; transform: rotate(45deg); }
.nav__menu[open] .nav__menu-bars::after  { top: 0; transform: rotate(-45deg); }

/* the panel hangs off the bar itself, so it tracks the pill once it lifts */
.nav__links {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0;
  display: flex; flex-direction: column;
  padding: 6px 0;
  border-radius: 18px;
  background: rgba(10, 11, 14, .97);
  border: 1px solid rgba(245, 242, 237, .16);
  box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
}
.nav__links a {
  display: flex; align-items: center; min-height: 46px; padding: 0 20px;
  font-size: 11px; font-weight: 500; letter-spacing: .15em; text-transform: uppercase;
  color: rgba(245, 242, 237, .74);
  transition: color .3s var(--ease), background-color .3s var(--ease);
}
.nav__links a:hover { color: var(--porcelain); }
/* socials inside the dropdown: the bar hides its icons on a phone, and the
   owners asked for TikTok in the nav */
.nav__follow { display: flex; flex-wrap: wrap; gap: 0 18px; padding: 8px 20px 10px; border-top: 1px solid rgba(245, 242, 237, .14); margin-top: 4px; }
.nav__follow a { min-height: 40px; padding: 0; font-size: 10.5px; color: rgba(245, 242, 237, .62); }
@media (min-width: 860px) { .nav__follow { display: none; } }

.nav__cta {
  margin-left: auto;
  padding: 14px 24px;
  border-radius: 999px;
  background: var(--purple); color: var(--obsidian);
  font-size: 10.5px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
  /* without this the flex row squeezes the pill and breaks the label over three
     lines rather than letting anything else give */
  white-space: nowrap;
  transition: background-color .35s var(--ease), color .35s var(--ease);
}

/* At 375px the bar needs 334px of its 335. Dropping "now" buys back 43px and
   keeps the phone number readable, which matters more here than the longer
   label: the closing dock still says Book now in full, and calls are the thing
   this salon actually converts on. */
.nav__cta-now { display: none; }
@media (min-width: 860px) { .nav__cta-now { display: inline; } }
.nav__cta:hover { background: var(--porcelain); }

/* Call now, the second half of the CTA pair. A salon booking decision is
   often a phone call, so the number is a first-class action, not footer trivia. */
.nav__tel {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 16px;
  border-radius: 999px;
  border: 1px solid rgba(245, 242, 237, .28);
  color: var(--porcelain);
  font-size: 11px; font-weight: 500; letter-spacing: .1em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transition: background-color .3s var(--ease), border-color .3s var(--ease), color .3s var(--ease);
}
.nav__tel svg { width: 14px; height: 14px; flex: none; }
.nav__tel:hover { background: var(--porcelain); border-color: var(--porcelain); color: var(--obsidian); }

@media (min-width: 860px) {
  .nav { left: 50%; right: auto; transform: translateX(-50%); width: min(calc(100% - var(--pad) * 2), 1180px); }
  /* desktop collapses the disclosure back to the flat row it has always been.
     A closed <details> collapses its content box at the ::details-content
     layer, which display on the child cannot override, so the links measured
     zero wide up here where there is no button to open them. Older engines
     used display:none on the children instead, which the rule below covers. */
  .nav__menu-btn { display: none; }
  .nav__menu::details-content { content-visibility: visible; block-size: auto; }
  .nav__links {
    position: static; flex-direction: row; gap: 26px;
    padding: 0; border: 0; border-radius: 0; box-shadow: none; background: none;
  }
  .nav__links a { padding: 0; min-height: 44px; font-size: 11px; letter-spacing: .13em; }
  .nav__links .nav__more { display: none; }
  .nav__cta { margin-left: 0; }
  .nav__logo img { width: 132px; }
  /* the bar tops out at 1180px, and with the wordmark, the socials, four
     links and Book now there is no width at which the digits fit. The glyph
     keeps its 44px target and its label; the number is in the dock, the
     footer and on Contact. */
  .nav__tel span { display: none; }
  .nav__tel { padding: 0 14px; gap: 0; }
}

/* Mobile: a compact utility bar, not an oversized floating capsule. Full bleed
   at top:0 so page content can never peek above or around it. */
@media (max-width: 859px) {
  .nav {
    top: 0; left: 0; right: 0;
    /* the bar carries four things at 360px and was fitting with 1px to spare,
       which meant the wordmark was quietly being squashed to make room. Two
       pixels off each gap and the phone pill buys back real headroom. */
    gap: 8px;
    padding: 6px var(--pad);
    border-radius: 0;
    border-width: 0 0 1px;
  }
  /* At the top of the page the bar sits flush, edge to edge, so the tabs read
     as part of the page header. Once the hero is behind you it lifts off the
     edges and becomes the same floating rounded object desktop has always had.
     is-stuck is already set by the observer that drives the dock, so this is
     presentation only. */
  .nav.is-stuck {
    top: 9px;
    left: 9px; right: 9px;
    border-radius: 24px;
    border-width: 1px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
  }
  .nav__logo { min-height: 40px; padding: 0; }
  /* One auto margin, not two. The button and the phone pill both claimed one,
     which split the slack and stranded the button mid bar. The button takes it
     and the phone gives it up, so everything after the wordmark closes into a
     single group on the right: menu, phone, Book. Beside the wordmark it read
     as a stray control with a void next to it. */
  .nav__menu { margin-left: auto; }
  .nav__logo img { width: 74px; }
  /* Jaden, 8 Sep: the owners want the Paul Mitchell lettering in the bar on
     phones as well. The row at 360px has no slack, so the phone pill gives up
     its digits (the number is in the dock, the footer and on Contact) and the
     mark sits beside the logo at cap height, with no divider or padding. */
  .nav__tel span { display: none; }
  .nav__tel { padding: 0 12px; gap: 0; min-width: 40px; justify-content: center; }
  .nav__tel { margin-left: 0; gap: 5px; padding: 0 9px; min-height: 40px; font-size: 9.5px; letter-spacing: 0; }
  .nav__tel svg { width: 12px; height: 12px; }
  .nav__cta { margin-left: 0; min-height: 40px; display: inline-flex; align-items: center; padding: 0 13px; font-size: 9.5px; letter-spacing: .08em; }
}

/* the number stays readable down to 360; below that the row genuinely runs out
   and the phone keeps its 44px target as the glyph alone */
@media (max-width: 359px) {
  .nav__tel span { display: none; }
  .nav__tel { padding: 0 13px; }
}

/* ==========================================================================
   01 HERO, the photograph runs the width of the screen, the type sits in it
   ========================================================================== */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 120px var(--pad) clamp(30px, 5vh, 56px);
  overflow: hidden;
  background: var(--obsidian);
}

.hero__media { position: absolute; inset: 0; z-index: 0; }

/* The curtain clips <picture>, not the figure, so the scrim on .hero__media
   that carries the headline is never clipped with the photograph. The tone
   plate rides inside the clip too, so nothing flashes ahead of the reveal. */
.hero__media picture {
  display: block;
  position: absolute; inset: 0;
  background: var(--smoke);
}

/* The closed state is scoped to the pre-ready body and the reveal runs without
   a fill, so once the curtain finishes the clip is gone rather than leaving a
   permanent composited layer over the LCP image for the rest of the session. */
body:not(.is-ready) .hero__media picture {
  clip-path: polygon(0 100%, 100% 88%, 100% 100%, 0 100%);
}

.hero__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 28%;
  transform: scale(1.04);
}

/* the photograph dissolves into the obsidian rather than ending at an edge */
.hero__media::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(to top, rgba(10,11,14,.97) 2%, rgba(10,11,14,.86) 24%, rgba(10,11,14,.45) 48%, rgba(10,11,14,.06) 76%),
    linear-gradient(to right, rgba(10,11,14,.55) 0%, rgba(10,11,14,.18) 52%, rgba(10,11,14,0) 82%);
}

.hero__copy { position: relative; z-index: 1; max-width: 100%; }

.hero__eyebrow {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 20px;
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: rgba(245, 242, 237, .72);
}
.hero__eyebrow .rule { width: 26px; height: 1px; background: rgba(245, 242, 237, .45); }

/* Paul Mitchell on a phone: top left of the photograph, under the bar, only
   while the nav's own wordmark is hidden. Shadowed so it holds on any crop. */

.hero__h1 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(3.2rem, 8.4vw, 6.8rem);
  line-height: 0.94;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  margin: 0 0 22px;
}
/* block, not inline-block: the line break is a composition decision, not
   something left to whatever width the viewport happens to be */
.hero__h1 .w { display: block; overflow: hidden; }
.hero__h1 .w > span { display: block; }
.hero__h1 em { font-style: italic; letter-spacing: -0.012em; }
.hero__sub i { font-style: italic; color: var(--porcelain); }

.hero__sub {
  max-width: 40ch;
  margin-bottom: 30px;
  color: rgba(245, 242, 237, .78);
  font-size: clamp(0.95rem, 2.3vw, 1.05rem);
}

/* the baseline. One hairline the full width of the hero, it gives the
   photograph a floor to stand on and pulls the copy column and the image
   into a single frame instead of two adjacent zones. */
.hero__meta {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin: clamp(30px, 5vh, 58px) 0 0;
  padding-top: 14px;
  border-top: 1px solid rgba(245, 242, 237, .16);
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: rgba(245, 242, 237, .6);
}
.hero__meta-scroll { color: rgba(245, 242, 237, .42); }

@media (min-width: 860px) {
  .hero { padding-bottom: clamp(40px, 6vh, 68px); }

  /* the photograph occupies the right ~60% and feathers into the obsidian.
     The feather boundary is pulled left of the copy column's right edge on
     purpose, so the last line of the headline runs onto lit photograph
     rather than stopping politely beside it. */
  .hero__media { left: 0; }
  /* pushed right so the second model sits under the scrim rather than behind
     the headline. At 1920 the plate and the viewport are both 16:9, so there
     is no crop to position and this has no effect there. */
  .hero__media img { object-position: 90% 45%; }
  /* deeper than it was: the new plate carries a second model on the left, and
     the headline has to sit on ground rather than on her face */
  .hero__media::after {
    background:
      linear-gradient(to right, rgba(10,11,14,.93) 0%, rgba(10,11,14,.8) 22%, rgba(10,11,14,.4) 44%, rgba(10,11,14,0) 68%),
      linear-gradient(to top, rgba(10,11,14,.62) 0%, rgba(10,11,14,0) 34%);
  }

  .hero__copy { max-width: 50%; }
}

@media (min-width: 1200px) {
  .hero__copy { max-width: 53%; }
}

/* ==========================================================================
   MOTION, the hero settles, the type rises through its mask
   ========================================================================== */

.hero__eyebrow, .hero__sub, .hero .btn, .hero__meta { opacity: 0; transform: translateY(22px); }
.hero__h1 .w > span { transform: translateY(108%); }

/* the settle lives on the <img>; the scroll parallax drives the <figure>,
   so a filled keyframe can never override the inline transform again.
   The curtain lives on <picture>, a third layer, clear of both. */
.is-ready .hero__media picture             { animation: curtain 1.15s var(--ease); }
.is-ready .hero__media img                 { animation: settle 1.8s var(--ease) forwards; }
.is-ready .hero__eyebrow                   { animation: rise .6s var(--ease) .15s forwards; }
.is-ready .hero__h1 .w:nth-child(1) > span { animation: unmask .82s var(--ease) .26s forwards; }
.is-ready .hero__h1 .w:nth-child(2) > span { animation: unmask .82s var(--ease) .34s forwards; }
.is-ready .hero__h1 .w:nth-child(3) > span { animation: unmask .82s var(--ease) .42s forwards; }
.is-ready .hero__sub                       { animation: rise .62s var(--ease) .58s forwards; }
.is-ready .hero .btn                       { animation: pop .5s cubic-bezier(.34,1.35,.64,1) .7s forwards; }
.is-ready .hero__meta                      { animation: rise .6s var(--ease) .8s forwards; }

@keyframes settle { to { transform: scale(1); } }
@keyframes rise   { to { opacity: 1; transform: none; } }
@keyframes unmask { to { transform: none; } }

/* a photographic curtain: the frame sweeps up off the bottom edge on a slight
   tilt and settles level, so no permanent diagonal is left in the composition */
@keyframes curtain {
  from { clip-path: polygon(0 100%, 100% 88%, 100% 100%, 0 100%); }
  to   { clip-path: polygon(0 0,   100% 0,   100% 100%, 0 100%); }
}

@media (prefers-reduced-motion: reduce) {
  .hero__eyebrow, .hero__sub, .hero .btn, .hero__meta { opacity: 1; transform: none; animation: none; }
  .hero__h1 .w > span { transform: none; animation: none; }
  .hero__media img { transform: none; animation: none; }
  .hero__media picture { clip-path: none; animation: none; }
}

/* ==========================================================================
   02 WORK + SERVICES, porcelain. The page inverts here.
   ========================================================================== */

.light {
  background: var(--sky);
  color: var(--graphite);
  padding: clamp(72px, 11vw, 132px) 0 clamp(64px, 9vw, 112px);
}

.label {
  display: flex; align-items: center; gap: 12px;
  font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase;
  color: rgba(35, 36, 42, .82);
  margin: 0 0 14px;
}
.label::before { content: ""; width: 22px; height: 1px; background: var(--slate); }

.label--dark { color: rgba(245, 242, 237, .6); }
.label--dark::before { background: rgba(245, 242, 237, .45); }

.h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.3rem, 6vw, 4.2rem);
  line-height: 1;
  letter-spacing: -0.012em;
  margin: 0;
  color: var(--graphite);
}
.h2--light { color: var(--porcelain); }
.h2--xl { font-size: clamp(2.6rem, 7.4vw, 5.2rem); }

.edit__head { padding: 0 var(--pad) clamp(34px, 5vw, 52px); }

/* --- the rail ------------------------------------------------------------- */

.rail__track {
  display: flex;
  gap: 14px;
  margin: 0; padding: 0 var(--pad);
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: var(--pad);
  scrollbar-width: none;
  cursor: grab;
}
.rail__track::-webkit-scrollbar { display: none; }
.rail__track.is-dragging { cursor: grabbing; scroll-snap-type: none; }

/* editorial swipe width, the next result peeks deliberately rather than
   sitting as an unexplained sliver at the edge */
.rail__card { flex: 0 0 79vw; scroll-snap-align: start; }

.rail__card img {
  width: 100%; height: auto;
  aspect-ratio: 4/5;
  object-fit: cover;
  pointer-events: none;
}

/* caption sits on the page, not in a box, title small sans uppercase,
   category smaller and lighter beneath it */
.rail__meta {
  display: flex; flex-direction: column; gap: 3px;
  margin-top: 14px;
  text-transform: uppercase;
}
.rail__meta b { font-size: 14px; font-weight: 600; letter-spacing: 0.12em; color: var(--graphite); }
.rail__meta span { font-size: 12px; font-weight: 400; letter-spacing: 0.14em; color: rgba(35, 36, 42, .82); }

.rail__foot {
  display: flex; align-items: center; gap: 16px;
  padding: clamp(26px, 4vw, 40px) var(--pad) 0;
}

.rail__dial { width: 26px; height: 26px; flex: none; }
.rail__dial img { width: 100%; height: 100%; filter: invert(1); opacity: .8; }

.rail__bar { position: relative; flex: 1; max-width: 210px; height: 1px; background: rgba(35, 36, 42, .2); }
.rail__bar span {
  position: absolute; inset: 0 auto 0 0;
  width: 22%;
  background: var(--graphite);
  transform-origin: left;
}

.rail__hint { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(35, 36, 42, .8); }

@media (min-width: 760px)  { .rail__card { flex-basis: 46vw; } .rail__track { gap: 18px; } }
@media (min-width: 1100px) { .rail__card { flex-basis: 30vw; } }

/* the Edit has to finish before the next section starts, without this the
   chamber row and the transformations lede run into each other on a phone */
@media (max-width: 1023px) {
  .rail-story { padding-bottom: clamp(26px, 5vw, 46px); }
}

/* Only one floating action at a time. The nav already carries Book now and
   Call, so the global drop CTA stands down over this section rather than
   sitting on top of the photography and captions. */

/* ==========================================================================
   THE REVOLVER EDIT, desktop scroll-driven sequence.

   The .is-story class is applied by app.js only at >=1024px with motion
   allowed. Everything below is scoped to it, so the native rail underneath is
   the untouched fallback for mobile, tablet and reduced motion.
   ========================================================================== */

.rail-story { position: relative; }

@media (min-width: 1024px) {
  /* One spread, not a headline stacked above a carousel: the type holds the
     left third and the photography band runs from just inside the type's
     column out to the right edge. Both are centred on the same optical line. */
  .is-story .rail-story__sticky {
    position: sticky; top: 0;
    height: 100svh;
    display: block;
    overflow: hidden;
  }

  .is-story .edit__head {
    position: absolute; z-index: 2;
    top: 50%; left: var(--pad);
    transform: translateY(-50%);
    width: clamp(280px, 30vw, 440px);
    max-width: none;
    margin: 0; padding: 0;
  }

  /* The band starts at 31vw, inside the headline's column but clear of its
     glyphs, so the dominant result crowds the type without ever covering it.
     Its own overflow clip means travelling images can never reach the words. */
  .is-story .rail {
    position: absolute;
    top: 50%; left: 31vw; right: 0;
    transform: translateY(-50%);
    overflow: hidden;
  }

  /* the track stops being a scroll container, vertical scroll moves it now */
  .is-story .rail__track {
    width: max-content;
    overflow: visible;
    scroll-snap-type: none;
    cursor: default;
    padding: 0;
    will-change: transform;
  }

  /* the chamber row lines up with the left edge of the photography, not with
     the page gutter it inherits from the mobile layout */
  .is-story .rail__foot { padding-left: 0; padding-right: 0; }

  /* --lc is how far the card is cut off the band's left edge; past ~16% the
     caption would read as a mid-word fragment, so it goes */
  .is-story .rail__meta { opacity: clamp(0, calc(1 - var(--lc, 0) * 6), 1); }

  /* the type has a third of the frame to hold, at section scale it can carry
     more weight than the shared .h2 ramp allows */
  .is-story .edit__head .h2 { font-size: clamp(3rem, 5vw, 5.2rem); }

  /* Card height is driven by the viewport, not by width x aspect-ratio, otherwise a 36vw card is 700px tall and the sticky frame clips the
     headline and the chamber footer straight off the composition. */
  .is-story .rail__card picture {
    display: block;
    height: clamp(300px, 53svh, 620px);
  }
  .is-story .rail__card img {
    width: 100%; height: 100%;
    aspect-ratio: auto;
    object-fit: cover;
  }

  /* Editorial rhythm. Keyed on 5n so a sixth result restarts the pattern
     rather than falling out of the layout. */
  .is-story .rail__card:nth-child(5n+1) { flex-basis: 32vw; --oy:  0vh; }
  .is-story .rail__card:nth-child(5n+2) { flex-basis: 24vw; --oy:  3vh; }
  .is-story .rail__card:nth-child(5n+3) { flex-basis: 36vw; --oy: -2vh; }
  .is-story .rail__card:nth-child(5n+4) { flex-basis: 25vw; --oy:  4vh; }
  .is-story .rail__card:nth-child(5n+5) { flex-basis: 31vw; --oy:  1vh; }

  /* card-in uses fill-mode "both", and animations outrank normal declarations
     in the cascade, left on, it freezes transform and opacity at the keyframe
     end state and silently blocks every per-frame write below. In story mode
     the sideways travel is the entrance, so the stagger is not needed. */
  .is-story .rail__track.is-lit .rail__card { animation: none; }

  /* --e is centre proximity, 0 at the edges and 1 dead centre, written by
     app.js once per frame. Ranges stay tight enough that a frozen frame still
     looks deliberate rather than mid-animation. */
  .is-story .rail__card {
    transform: translate3d(0, var(--oy, 0vh), 0) scale(calc(.97 + .03 * var(--e, 1)));
    opacity: calc(.8 + .2 * var(--e, 1));
    transition: none;
  }

  /* the image is oversized so 3% of internal parallax never exposes an edge */
  .is-story .rail__card picture { overflow: hidden; }
  .is-story .rail__card img,
  .is-story .rail__card:hover img {
    transform: translate3d(calc(var(--px, 0) * 1%), 0, 0) scale(1.06);
    transition: none;
  }
}

/* reduced motion drops the whole mechanism and hands back the native rail */
@media (prefers-reduced-motion: reduce) {
  .rail-story { height: auto !important; }
  .rail-story__sticky { position: static; height: auto; overflow: visible; }
  .rail__track { transform: none !important; }
  .rail__card { transform: none !important; opacity: 1 !important; }
  .rail__card img { transform: none !important; }
}

/* --- services ------------------------------------------------------------- */

.svc { padding: clamp(64px, 9vw, 108px) var(--pad) 0; max-width: var(--max); margin: 0 auto; }
.svc__rows { margin: 26px 0 0; }

.svc__row {
  padding: clamp(20px, 3vw, 28px) 0;
  border-top: 1px solid rgba(35, 36, 42, .22);
}
.svc__row:last-child { border-bottom: 1px solid rgba(35, 36, 42, .22); }

.svc__name {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.9rem, 5vw, 3rem);
  line-height: 1; margin: 0 0 10px;
  color: var(--graphite);
}
.svc__list { color: rgba(35, 36, 42, .9); font-size: clamp(1.05rem, 2.4vw, 1.2rem); font-weight: 400; }
/* Louis, 1 Sep: "the color cut texture ... looks like the whole page is plain".
   Each row gets a band on hover and focus, pulled out past the text edge so
   the type does not shift, and a firmer rule, so the three read as deliberate
   choices rather than a stack of text. */
.svc__row { padding-inline: 14px; margin-inline: -14px; border-radius: 4px; transition: background-color .3s var(--ease), border-color .3s var(--ease); }
.svc__row:hover, .svc__row:focus-within { background: var(--sky-deep); }

.svc__foot { margin-top: clamp(34px, 5vw, 50px); max-width: 44ch; color: rgba(35, 36, 42, .86); }

@media (min-width: 860px) {
  .svc__row { display: grid; grid-template-columns: 0.9fr 1.4fr; gap: 40px; align-items: baseline; }
  .svc__name { margin: 0; }
}

/* ==========================================================================
   03 PORTFOLIO, obsidian, asymmetric, overlapping
   ========================================================================== */

.folio {
  position: relative;
  background: var(--obsidian);
  padding: clamp(76px, 11vw, 140px) var(--pad) clamp(76px, 11vw, 140px);
}

.folio__copy { max-width: 34ch; margin-bottom: clamp(36px, 5vw, 56px); }
.folio__lede { margin-top: 18px; color: rgba(245, 242, 237, .68); }

.folio__lead, .folio__sup { overflow: hidden; }
.folio__lead img, .folio__sup img {
  width: 100%; height: auto; aspect-ratio: 4/5; object-fit: cover;
  transform: scale(1.1);
  will-change: transform;
}

/* mobile: stacked, but still overlapping so it never reads as a grid */
.folio__lead { width: 78%; }
.folio__sup--a { width: 46%; margin: -16% 0 0 auto; position: relative; }
.folio__sup--b { width: 38%; margin: -10% 0 0 14%; position: relative; }

@media (min-width: 900px) {
  .folio {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: 0 20px;
    align-items: start;
    max-width: var(--max);
    margin: 0 auto;
  }
  .folio__copy { grid-column: 1 / 5; grid-row: 1; margin: 0; align-self: center; }
  .folio__lead { grid-column: 5 / 11; grid-row: 1; width: 100%; }
  .folio__sup--a {
    grid-column: 9 / 13; grid-row: 1;
    width: 100%; margin: 0;
    align-self: end; transform: translateY(22%);
    z-index: 2;
  }
  .folio__sup--b {
    grid-column: 3 / 6; grid-row: 1;
    width: 100%; margin: 0;
    align-self: end; transform: translateY(46%);
    z-index: 2;
  }
  .folio { padding-bottom: clamp(150px, 20vw, 280px); }
}

/* ==========================================================================
   04 FINAL CTA + FOOTER
   ========================================================================== */

.end {
  position: relative;
  background: var(--obsidian);
  padding: clamp(90px, 14vw, 170px) var(--pad) 34px;
  overflow: hidden;
}

.end__media { position: absolute; inset: 0; z-index: 0; }
.end__media img { width: 100%; height: 100%; object-fit: cover; object-position: 68% 22%; }
/* On a phone the band is tall and narrow, so the image covers on height and
   crops sideways instead. 68% pushed the frame onto her hand and cut the face
   off; centring it keeps her in shot. */
@media (max-width: 859px) { .end__media img { object-position: 46% 26%; } }
.end__media::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(to right, rgba(10,11,14,.97) 6%, rgba(10,11,14,.86) 32%, rgba(10,11,14,.42) 68%, rgba(10,11,14,.28) 100%),
    linear-gradient(to top, rgba(10,11,14,.6) 0%, rgba(10,11,14,0) 40%);
}

.end__copy {
  position: relative; z-index: 1;
  max-width: var(--max); margin: 0 auto;
  padding-bottom: clamp(80px, 12vw, 150px);
}
.end__copy .btn { margin-top: 30px; }

.end__foot {
  position: relative; z-index: 1;
  max-width: var(--max); margin: 0 auto;
  padding-top: 24px;
  border-top: 1px solid rgba(245, 242, 237, .16);
  display: grid; gap: clamp(22px, 3vw, 34px);
  font-size: 12.5px; color: rgba(245, 242, 237, .6);
}
/* The wordmark runs the full width of the footer and the Focus Salon lockup
   sits under it at about a third, the way the business card is set. Both may
   ride over the photograph; Louis said that was fine. 116px wide was "maybe a
   third of the page ... kind of looks like the top". */
.end__brand { display: grid; gap: clamp(14px, 2vw, 22px); justify-items: start; }
.end__mark { width: 100%; max-width: 1180px; height: auto; }
.end__lockup { width: clamp(220px, 34%, 420px); height: auto; }
.end__nav { display: flex; flex-wrap: wrap; gap: 4px 28px; }
.end__nav a { font-size: 11px; font-weight: 500; letter-spacing: .13em; text-transform: uppercase; }
.end__details { display: grid; gap: 14px; }
.end__foot a { display: inline-flex; align-items: center; min-height: 44px; transition: color .3s var(--ease); }
.end__foot a:hover { color: var(--porcelain); }
.end__foot address { font-style: normal; }

@media (min-width: 860px) {
  /* address at the left under the logo, phone and socials pushed right */
  .end__details { display: flex; align-items: center; gap: 32px; }
  .end__details .end__tel { margin-left: auto; }
}

/* ==========================================================================
   SCROLL REVEALS
   ========================================================================== */

/* Three tiers, not one blanket fade. Small things move fast and short,
   headings get a masked rise, body copy doesn't animate at all. */

/* headings, the line lifts through its own mask */
.r-line { overflow: hidden; }
.r-line > span { display: block; transform: translateY(106%); transition: transform .8s var(--ease); }
.r-line.is-in > span { transform: none; }

/* labels, quick, and the accent rule draws itself out */
.r-label { opacity: 0; transform: translateY(8px); transition: opacity .34s var(--ease), transform .34s var(--ease); }
.r-label.is-in { opacity: 1; transform: none; }
.r-label::before { transform: scaleX(0); transform-origin: left; transition: transform .5s var(--ease) .12s; }
.r-label.is-in::before { transform: scaleX(1); }

/* the one thing we want noticed */
.r-pop { opacity: 0; transform: scale(.94); transition: opacity .3s var(--ease), transform .52s cubic-bezier(.34,1.35,.64,1); }
.r-pop.is-in { opacity: 1; transform: none; }

.reveal { opacity: 0; transform: translateY(20px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.is-in { opacity: 1; transform: none; }

@keyframes pop { to { opacity: 1; transform: none; } }

.curtain img { clip-path: inset(100% 0 0 0); transition: clip-path 1.05s var(--ease); }
.curtain.is-in img { clip-path: inset(0 0 0 0); }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .curtain img { clip-path: none; transition: none; }
  .rail__dial img { transition: none; }
}

/* --- image loading + failure ---------------------------------------------- */

/* a tone block holds the space while a photograph decodes, so nothing
   flashes or shifts on a slow connection */
/* the hero's plate rides on its <picture> instead, so it is revealed by the
   curtain rather than sitting behind it as a visible grey block */
.hero__media { background: var(--obsidian); }
.end__media, .folio__lead, .folio__sup { background: var(--smoke); }
/* the plate belongs to the photograph only, on the card it painted a filled
   rectangle behind the caption and turned every result into a product tile */
.rail__card { display: block; }
.rail__card picture { background: var(--sky-deep); display: block; }

.is-missing { position: relative; }
.is-missing::after {
  content: "";
  position: absolute; inset: 0;
  background: repeating-linear-gradient(45deg,
    rgba(185,222,236,.06) 0 10px, rgba(185,222,236,.02) 10px 20px);
}

/* --- rail card hover ------------------------------------------------------ */

.rail__card picture { overflow: hidden; }
.rail__card img { transition: transform .7s var(--ease); }
.rail__card:hover img { transform: scale(1.025); }
.rail__track:focus-visible { outline: 2px solid var(--purple); outline-offset: 6px; }

@media (prefers-reduced-motion: reduce) {
  .rail__card img { transition: none; }
  .rail__card:hover img { transform: none; }
}


/* ==========================================================================
   THE DOCK, a quiet pair of actions that stays within reach

   The previous version was a single button that dropped in with a bounce, wore
   a heavy shadow, and then followed the visitor down the entire page, including
   across the photography and the comparison they were trying to drag. This one
   slides rather than bounces, carries no shadow, and gets out of the way in the
   places where it was doing harm.
   ========================================================================== */

.dock {
  position: fixed; z-index: 45;
  left: 50%; bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  display: flex; gap: 6px;
  padding: 6px;
  border-radius: 999px;
  background: var(--obsidian);
  border: 1px solid rgba(245, 242, 237, .16);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, calc(100% + 28px));
  transition: transform .42s var(--ease), opacity .3s var(--ease);
}
.dock.is-in       { opacity: 1; pointer-events: auto; transform: translate(-50%, 0); }
.dock.is-muted    { opacity: 0; pointer-events: none; transform: translate(-50%, calc(100% + 28px)); }

.dock__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 44px; padding: 0 20px;
  border-radius: 999px;
  font-size: 10.5px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  white-space: nowrap;
  transition: background-color .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.dock__btn svg { width: 14px; height: 14px; flex: none; }

.dock__btn--call { color: var(--porcelain); border: 1px solid rgba(245, 242, 237, .28); }
.dock__btn--call:hover { background: var(--porcelain); border-color: var(--porcelain); color: var(--obsidian); }

.dock__btn--book { background: var(--purple); color: var(--obsidian); }
.dock__btn--book:hover { background: var(--porcelain); }

@media (min-width: 860px) {
  .dock { left: auto; right: var(--pad); transform: translate(0, calc(100% + 28px)); }
  .dock.is-in    { transform: translate(0, 0); }
  .dock.is-muted { transform: translate(0, calc(100% + 28px)); }
}

@media (prefers-reduced-motion: reduce) {
  .dock { transition: opacity .2s linear; transform: translate(-50%, 0); }
  .dock.is-muted { transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) and (min-width: 860px) {
  .dock, .dock.is-muted { transform: translate(0, 0); }
}

/* --- rail cards arrive in sequence --------------------------------------- */

.rail__track.is-lit .rail__card {
  animation: card-in .8s var(--ease) both;
}
.rail__track.is-lit .rail__card:nth-child(1) { animation-delay: .00s; }
.rail__track.is-lit .rail__card:nth-child(2) { animation-delay: .07s; }
.rail__track.is-lit .rail__card:nth-child(3) { animation-delay: .14s; }
.rail__track.is-lit .rail__card:nth-child(4) { animation-delay: .21s; }
.rail__track.is-lit .rail__card:nth-child(5) { animation-delay: .28s; }

@keyframes card-in {
  from { opacity: 0; transform: translateY(26px); clip-path: inset(14% 0 0 0); }
  to   { opacity: 1; transform: none;             clip-path: inset(0 0 0 0); }
}

@media (prefers-reduced-motion: reduce) {
  .rail__track.is-lit .rail__card { animation: none; }
  .hero__copy, .hero__media, .hero__media img { transform: none !important; opacity: 1 !important; }
  .hero__h1 .w > span, .hero__meta { transform: none !important; opacity: 1 !important; }
  .hero__media picture { clip-path: none !important; }
  .r-line > span, .r-label, .r-pop { transform: none !important; opacity: 1 !important; transition: none; }
  .r-label::before { transform: scaleX(1) !important; }
}

/* ==========================================================================
   TRANSFORMATIONS, the proof, sitting inside the porcelain block
   ========================================================================== */

/* Full-bleed ink. No max-width, no panel, no radius: this is a break in the
   porcelain run, not a card sitting inside it. */
.diff {
  background: var(--obsidian);
  color: var(--porcelain);
  padding: clamp(64px, 9vw, 118px) var(--pad);
}
.diff__head { margin-bottom: clamp(28px, 4vw, 44px); }
.diff__lede { margin-top: 16px; color: rgba(245, 242, 237, .72); max-width: 38ch; }

.diff__list { display: grid; gap: clamp(40px, 6vw, 64px); }

.ba { display: flex; flex-direction: column; }

.ba__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 4/5;
  overflow: hidden;
  background: var(--smoke);
  touch-action: pan-y;
  user-select: none; -webkit-user-select: none;
  cursor: ew-resize;
}

/* One editorial spread at desktop: the type holds the left third, the
   comparison holds the right. The frame stays portrait because both source
   images are 4/5 and a landscape crop would cut the length the caption
   claims was kept, which is the whole proof. */
@media (min-width: 900px) {
  .diff {
    display: grid;
    grid-template-columns: minmax(280px, 31%) 1fr;
    align-items: center;
    gap: clamp(32px, 5vw, 80px);
    /* the band stays edge to edge while its contents stay centred and capped.
       max-width here would shrink the ink itself and break the full bleed. */
    padding-inline: max(var(--pad), calc((100% - var(--max)) / 2));
  }
  /* explicit placement so the watermark below can share row 1 without pushing
     these into a second row */
  .diff__head { margin-bottom: 0; grid-column: 1; grid-row: 1; }
  /* spans both rows so the frame stays centred beside the copy and the lockup */
  .diff__list { min-width: 0; justify-items: end; grid-column: 2; grid-row: 1 / span 2; }

  .ba__frame { height: min(76svh, 780px); width: auto; }
  .ba { align-items: flex-end; }
}

/* the images are wrapped in <picture> so they can offer webp. display:contents
   keeps the <img> laying out exactly as it did when it was a direct child. */
.ba picture { display: contents; }
.ba__img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.ba__clip { position: absolute; inset: 0; width: 100%; clip-path: inset(0 50% 0 0); will-change: clip-path; }
.ba__clip .ba__img { position: absolute; inset: 0; }

/* Position unchanged, pill gone. A shallow scrim across the top edge carries
   legibility instead, so nothing boxed sits on the hair. */
.ba__frame::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; height: 96px;
  background: linear-gradient(to bottom, rgba(10, 11, 14, .55), rgba(10, 11, 14, 0));
  pointer-events: none; z-index: 1;
}
.ba__tag {
  position: absolute; top: 14px; z-index: 2;
  font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--porcelain);
  pointer-events: none;
}
.ba__tag--before { left: 14px; }
.ba__tag--after  { right: 14px; }

.ba__line {
  position: absolute; top: 0; bottom: 0; left: 50%;
  width: 1px; background: var(--porcelain);
  transform: translateX(-0.5px);
  will-change: left;
}

.ba__handle {
  position: absolute; top: 50%; left: 50%;
  translate: -50% -50%;
  width: 60px; height: 60px;
  display: grid; place-items: center;
  border-radius: 50%;
  /* solid ink, not frosted glass: same shape and mark, honest material */
  background: var(--obsidian);
  border: 1px solid rgba(185, 222, 236, .75);
  cursor: grab;
  transition: border-color .3s var(--ease), transform .3s var(--ease);
}
.ba__handle:hover { border-color: rgba(245, 242, 237, .6); }
.ba__handle:active { cursor: grabbing; }
.ba__handle img { width: 25px; height: 25px; }

.ba__cap {
  display: flex; gap: 14px; align-items: baseline;
  margin-top: 18px; max-width: 46ch;
  color: rgba(245, 242, 237, .82);
  font-size: 1rem;
}
.ba__num { font-family: var(--serif); font-size: 1.4rem; color: var(--porcelain); line-height: 1; }

/* the one-shot reveal. Applied by app.js only for the duration of the sweep,
   then removed, so dragging afterwards stays instant and never lags a finger. */
.ba.is-demo .ba__clip      { transition: clip-path 800ms var(--ease); }
.ba.is-demo .ba__line      { transition: left      800ms var(--ease); }
.ba.is-demo .ba__handle img { transition: transform 800ms var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .ba__handle { transition: none; }
  .ba.is-demo .ba__clip,
  .ba.is-demo .ba__line,
  .ba.is-demo .ba__handle img { transition: none; }
}

/* The frame is height driven, so on wide screens it renders about 624px inside
   a 914px column and sits pinned right. That leaves roughly 290px of nothing
   between the copy and the photograph, which is the dead space Judith pointed
   at. The chamber emblem fills it at watermark weight, never overlapping the
   image, and disappears below 1200px where the gap closes on its own. */
/* The chamber mark with "Paul Mitchell Focus Salon" beneath it. Louis asked
   for this by name: "white on black, and then under that ... in good size
   letters". It is a credential, so it is never faded to a watermark and it
   shows at every width. On a phone it sits between the copy and the slider.
   From 900 it stacks under the copy, beside the slider. From 1200 the frame is
   pinned right and leaves roughly 300px of ink beside it, so the lockup moves
   into that gap, which is the dead space Judith first pointed at. */
.diff__mark {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  margin: 0 0 clamp(28px, 5vw, 44px);
  pointer-events: none;
}
.diff__mark-chamber { width: clamp(64px, 10vw, 96px); height: auto; opacity: .92; }
.diff__mark-lockup  { width: clamp(200px, 56vw, 260px); height: auto; }

@media (min-width: 900px) {
  .diff__mark { grid-column: 1; grid-row: 2; align-self: start; align-items: flex-start; margin: 0; }
}
/* 1340, not 1200. The gap beside the frame is only 253px at 1280 and the lockup
   overlapped the photograph by 23px. From 1340 there is room for it with air
   on both sides; below that it stays under the copy, which was clean at 1000. */
@media (min-width: 1340px) {
  .diff__mark {
    grid-column: 2; grid-row: 1 / span 2;
    justify-self: start; align-self: center; align-items: center;
    margin-left: 8px;
  }
  .diff__mark-chamber { width: clamp(76px, 7vw, 104px); }
  .diff__mark-lockup  { width: clamp(180px, 14vw, 230px); }
}

/* ==========================================================================
   PROOF, verified figures only
   ========================================================================== */

.proof {
  display: flex; flex-wrap: wrap;
  gap: 0 clamp(22px, 5vw, 60px);
  padding: 0 var(--pad) clamp(46px, 6vw, 72px);
  max-width: var(--max); margin: 0 auto;
}

.proof li {
  display: flex; align-items: baseline; gap: 8px;
  padding: 10px 0;
}

.proof b {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.5rem, 3.6vw, 2.1rem);
  line-height: 1;
  color: var(--graphite);
}

.proof span {
  font-size: 10.5px; letter-spacing: 0.13em; text-transform: uppercase;
  color: rgba(35, 36, 42, .86);
}

.end__sub {
  position: relative; z-index: 1;
  margin: 22px 0 0;
  max-width: 46ch;
  color: rgba(245, 242, 237, .78);
}
.end__copy .btn { margin-top: 32px; }

/* the spread is defined once, at 900px, in the Transformations block above.
   This used to redefine the grid at 1100px with a different column ratio,
   which overrode it on exactly the widths that matter most. */

/* ==========================================================================
   BRAND COLOUR, purple carried through as detail
   ========================================================================== */

/* the proof bar sits on the faintest purple wash */
.proof {
  background: var(--sky-deep);
  border-top: 1px solid rgba(90, 108, 109, .16);
  border-bottom: 1px solid rgba(90, 108, 109, .16);
  padding-top: clamp(22px, 3vw, 30px);
  padding-bottom: clamp(22px, 3vw, 30px);
  margin-bottom: clamp(46px, 6vw, 72px);
  max-width: none;
  justify-content: center;
}
.proof li { position: relative; }
.proof li + li::before {
  content: ""; position: absolute; left: calc(-1 * clamp(11px, 2.5vw, 30px));
  top: 50%; translate: 0 -50%;
  width: 1px; height: 22px; background: rgba(90, 108, 109, .26);
}
.proof b { color: var(--graphite); }

/* labels: the eyebrow rule takes the weight that reads on its section */
.label::before { background: var(--slate); }
.label--dark::before { background: rgba(245, 242, 237, .45); }
/* the section is ink now, so the eyebrow rule takes the lighter purple */
.diff .label::before { background: rgba(245, 242, 237, .45); }

/* rail progress in purple */
.rail__bar span { background: var(--graphite); }
/* the mark is a white PNG, it has to be inverted to read on porcelain */
.rail__dial {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid rgba(127, 187, 212, .55);
}
.rail__dial img { width: 20px; height: 20px; filter: invert(1); opacity: .82; }

/* service rows pick up lavender on hover */
.svc__row { transition: border-color .4s var(--ease); }
.svc__row:hover { border-color: rgba(90, 108, 109, .4); }
.svc__name { transition: color .35s var(--ease); }
.svc__row:hover .svc__name { color: var(--obsidian); }

/* footer + nav links resolve to powder blue */
.nav__links a:hover { color: var(--porcelain); }

/* ==========================================================================
   TEXT PLACEMENT, consistent measure and rhythm across every section
   ========================================================================== */

.edit__head, .diff__head, .folio__copy, .svc, .end__copy { max-width: var(--max); }
.edit__head, .svc { margin-inline: auto; }

.h2 + .diff__lede,
.h2 + .studio__lede,
.h2 + .folio__lede { margin-top: clamp(14px, 2vw, 20px); }

.diff__lede, .folio__lede, .svc__foot, .end__sub, .hero__sub { max-width: 46ch; }

.label { margin-bottom: clamp(10px, 1.6vw, 16px); }

/* the services block gets the same left edge as everything above it */
.svc { padding-left: var(--pad); padding-right: var(--pad); }
.svc__foot { margin-top: clamp(38px, 5vw, 56px); }

/* the side margins that used to inset this as a panel are gone: the section
   is full bleed now, and its own padding holds the gutter */
@media (min-width: 1100px) {
  .diff__head { align-self: center; }
}

/* ==========================================================================
   BEHIND REVOLVER, the purple panel, ahead of the closing CTA
   ========================================================================== */

.studio {
  /* Was the one purple panel. With every light section on sky there is no
     run of white left to break, and the card has no purple, so it rejoins
     the light sections. */
  background: var(--sky);
  color: var(--graphite);
  padding: clamp(72px, 10vw, 128px) var(--pad);
  display: grid;
  gap: clamp(32px, 5vw, 64px);
}

/* landscape now, not a 4/5 portrait crop: the team photo is 972x742 and
   forcing it to portrait would cut people out of a group shot */
.studio__photo { position: relative; max-width: 620px; }
.studio__photo picture { display: block; overflow: hidden; background: var(--sky-deep); }
/* no drift scale on this one. A group photograph cropped 5% on every side by
   a parallax zoom loses the people standing at the edges. */
.studio__photo img { width: 100%; height: auto; border-radius: 2px; }
.studio__photo figcaption {
  margin-top: 12px;
  display: flex; align-items: center; gap: 10px;
  color: rgba(35, 36, 42, .72);
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
}
.studio__photo figcaption::before {
  content: ""; width: 18px; height: 1px; background: var(--slate); flex: none;
}

.studio__lede { max-width: 46ch; color: rgba(35, 36, 42, .86); }
.studio__lede + .studio__lede { margin-top: 16px; }
.studio__lede i { font-style: italic; color: var(--graphite); }

@media (min-width: 900px) {
  .studio {
    grid-template-columns: 0.8fr 1fr;
    align-items: center;
    max-width: var(--max);
    margin: 0 auto;
    gap: clamp(48px, 6vw, 96px);
  }
}

/* ==========================================================================
   Vertical mask reveal on section imagery
   ========================================================================== */

/* clip the inner image, never the element the observer watches, a fully
   clipped element never reports as intersecting, so it would never unclip */
.rail__card picture,
.ba__frame,
.studio__photo img { clip-path: inset(100% 0 0 0); transition: clip-path 1s var(--ease); }
.rail__track.is-lit .rail__card picture,
.ba.is-in .ba__frame,
.studio__photo.is-in img { clip-path: inset(0 0 0 0); }

/* the carousel lift, per spec */
.rail__card:hover img { transform: scale(1.03); }

@media (prefers-reduced-motion: reduce) {
  .rail__card picture, .ba__frame, .studio__photo img { clip-path: none; transition: none; }
  .studio__photo img { transform: none; }
}

/* ==========================================================================
   GOOGLE MARK + REVIEWS
   ========================================================================== */

.proof__g { width: 17px; height: 17px; flex: none; align-self: center; }
.proof--google { gap: 8px; }

.says {
  background: var(--sky);
  color: var(--graphite);
  padding: clamp(66px, 9vw, 116px) var(--pad);
}
/* headline left, verified counts right. The team photograph lives in Behind
   Revolver only: the same picture twice on one page read as a repeat. */
.says__top {
  max-width: var(--max); margin: 0 auto clamp(30px, 4vw, 46px);
  display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: 26px;
}
.says__copy { min-width: 0; }

.says__proof {
  display: flex; gap: clamp(22px, 4vw, 44px);
  margin: 0; padding: 0; list-style: none;
}
.says__proof li { display: flex; flex-direction: column; gap: 2px; }
.says__proof b {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.8rem, 4vw, 2.6rem); line-height: 1;
  color: var(--graphite);
  font-variant-numeric: tabular-nums;
}
.says__proof span {
  font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase;
  color: rgba(35, 36, 42, .8);
}
@media (min-width: 1024px) {
  .says__team { justify-self: end; }
  .says__team img { max-width: 100%; }
}

/* One review at a time. Every slide occupies the same grid cell, so the stage
   is as tall as the longest of them and nothing jumps as they cross-fade. */
.says__stage {
  display: grid;
  /* capped so the panel reads as one deliberate object. Full width left the
     quote filling about two thirds of it and the rest looking unfinished. */
  max-width: min(var(--max), 1080px);
  margin: 0 auto 0 max(0px, calc((100% - var(--max)) / 2));
}

/* One defined object rather than a grid of cards. The hard offset edge is the
   single idea worth keeping from the brutalist reference: it gives the review
   presence without shadows, gradients or rounded chrome. */
/* Ink on porcelain. The panel used to be porcelain on porcelain, which is why
   the whole section sat back instead of landing. Inverting it makes the review
   the highest-contrast object on the page after the hero. */
.say {
  grid-area: 1 / 1;
  padding: clamp(30px, 4vw, 56px);
  background: var(--obsidian);
  color: var(--porcelain);
  border: 1px solid var(--obsidian);
  box-shadow: 8px 8px 0 0 var(--slate);
  opacity: 0;
  transform: translateY(14px);
  pointer-events: none;
  transition: opacity .42s var(--ease), transform .42s var(--ease);
}
.say.is-on { opacity: 1; transform: none; pointer-events: auto; }

.say__stars { display: flex; gap: 6px; margin: 0 0 clamp(18px, 2.4vw, 26px); }
.say__star { font-size: 17px; line-height: 1; color: rgba(245, 242, 237, .22); }
.say__star.is-lit { color: var(--porcelain); }

/* the review is the centrepiece: no card, no border, no wash panel */
.say__quote {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 3.4vw, 2.6rem);
  line-height: 1.22;
  letter-spacing: -0.008em;
  margin: 0;
  /* measured, not guessed: 30ch was landing near 41 characters a line, short
     of the 55 to 75 that reads comfortably at this size */
  max-width: 24ch;
  color: var(--porcelain);
}
@media (min-width: 760px)  { .say__quote { max-width: 40ch; } }
@media (min-width: 1200px) { .say__quote { max-width: 44ch; } }

.say__by {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: clamp(22px, 3vw, 32px);
  font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
}
.say__name { color: var(--porcelain); font-weight: 500; }
.say__src  { color: rgba(245, 242, 237, .55); display: flex; align-items: center; gap: 10px; }
.say__src::before { content: ""; width: 18px; height: 1px; background: rgba(245, 242, 237, .45); }

/* the full text stays one click away, never paraphrased down to the excerpt */
.say__more { margin-top: 18px; max-width: 62ch; }
.say__more summary {
  cursor: pointer; width: max-content;
  /* the browser's default disclosure triangle is the one bit of stock UI in
     the section, so it goes; the label alone carries the affordance */
  list-style: none;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: rgba(245, 242, 237, .6);
  transition: color .3s var(--ease);
}
.say__more summary::-webkit-details-marker { display: none; }
.say__more summary::after { content: " +"; }
.say__more[open] summary::after { content: " \2013"; }
.say__more summary:hover { color: var(--porcelain); }
.say__more p {
  margin-top: 12px; padding-top: 14px;
  border-top: 1px solid rgba(245, 242, 237, .18);
  font-size: .95rem; line-height: 1.65; color: rgba(245, 242, 237, .74);
}

.says__foot {
  max-width: var(--max);
  margin: clamp(34px, 5vw, 56px) auto 0;
  padding-top: clamp(22px, 3vw, 30px);
  border-top: 1px solid rgba(35, 36, 42, .14);
  display: grid; gap: clamp(20px, 3vw, 34px);
}
@media (min-width: 760px) {
  .says__foot { grid-template-columns: auto 1fr; align-items: center; }
}
.says__controls { display: flex; align-items: center; gap: 16px; }

/* the reviewers are the navigation. Numbered, ruled, no avatars. */
.says__names {
  display: grid; gap: 0;
  margin: 0; padding: 0; list-style: none;
  counter-reset: none;
}
@media (min-width: 760px) {
  .says__names { grid-template-columns: repeat(4, 1fr); }
}
.says__names li + li { border-top: 1px solid rgba(35, 36, 42, .18); }
@media (min-width: 760px) {
  .says__names li + li { border-top: 0; border-left: 1px solid rgba(35, 36, 42, .18); }
}
.says__names button {
  width: 100%;
  display: flex; align-items: baseline; gap: 12px;
  min-height: 44px; padding: 0 16px;
  background: none; border: 0; cursor: pointer;
  font: inherit; text-align: left;
  font-size: 12px; letter-spacing: .04em;
  color: rgba(35, 36, 42, .78);
  transition: color .3s var(--ease);
}
.says__names button:hover { color: var(--graphite); }
.says__names button.is-on { color: var(--graphite); }
.says__no {
  font-size: 9.5px; letter-spacing: .16em;
  color: rgba(35, 36, 42, .6);
  font-variant-numeric: tabular-nums;
  transition: color .3s var(--ease);
}
.says__names button.is-on .says__no { color: var(--graphite); }
.says__nav {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  padding: 0; border-radius: 50%;
  background: transparent; color: var(--graphite);
  border: 1px solid rgba(35, 36, 42, .22);
  cursor: pointer;
  transition: background-color .3s var(--ease), border-color .3s var(--ease), color .3s var(--ease);
}
.says__nav svg { width: 17px; height: 17px; }
.says__nav:hover { background: var(--graphite); border-color: var(--graphite); color: var(--porcelain); }
.says__index {
  font-size: 10.5px; letter-spacing: .18em;
  color: rgba(35, 36, 42, .78);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .say { transition: opacity .18s linear; transform: none; }
  .say.is-on { transform: none; }
}

/* numbers hold their space so the count-up cannot shift the layout */
.proof b { display: inline-block; min-width: 1.6ch; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   NAV, inverts over the porcelain sections
   ========================================================================== */

/* must out-specify .nav.is-stuck, or the pill keeps its dark background
   while the text flips dark, invisible */
.nav.nav--onlight {
  background: rgba(245, 242, 237, .96);
  border-color: rgba(35, 36, 42, .14);
}
.nav--onlight::before { opacity: 0; }
.nav--onlight .nav__logo img { filter: invert(1); }
.nav--onlight .nav__links a { color: rgba(35, 36, 42, .82); }
.nav--onlight .nav__links a:hover { color: var(--obsidian); }
/* the button is porcelain on ink, so it needs the same inversion the links,
   the CTA and the phone pill already get, or it vanishes on light sections */
.nav--onlight .nav__menu-btn { color: var(--graphite); border-color: rgba(35, 36, 42, .3); }
.nav--onlight .nav__menu[open] .nav__menu-btn { background: var(--obsidian); border-color: var(--obsidian); color: var(--porcelain); }
.nav--onlight .nav__cta { background: var(--purple-ink); color: var(--porcelain); }
.nav--onlight .nav__cta:hover { background: var(--obsidian); }
.nav--onlight .nav__tel { color: var(--graphite); border-color: rgba(35, 36, 42, .26); }
.nav--onlight .nav__tel:hover { background: var(--obsidian); border-color: var(--obsidian); color: var(--porcelain); }
.nav__logo img { transition: filter .45s var(--ease); }

/* ==========================================================================
   SERVICES, the row you are on shows its photograph
   ========================================================================== */

.svc__art { display: none; }

@media (min-width: 1000px) {
  .svc__body { display: grid; grid-template-columns: 1.5fr 0.55fr; gap: clamp(40px, 5vw, 72px); align-items: start; }
  .svc__art {
    display: block; position: relative;
    aspect-ratio: 4/5;
    background: var(--sky-deep);
    overflow: hidden;
  }
  /* the <picture> wrappers must not interfere with the absolute stacking */
  .svc__art picture { display: contents; }
  .svc__art img {
    position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: cover;
    opacity: 0; clip-path: inset(100% 0 0 0);
    transition: opacity .45s var(--ease), clip-path .6s var(--ease);
  }
  .svc__art img.is-on { opacity: 1; clip-path: inset(0 0 0 0); }
}

@media (prefers-reduced-motion: reduce) {
  .svc__art img { transition: opacity .2s linear; clip-path: none; }
  .nav__logo img { transition: none; }
}

/* ==========================================================================
   THE TEAM, a booking surface, not a portrait wall
   ========================================================================== */

.team { grid-column: 1 / -1; padding-top: clamp(56px, 7vw, 90px); }
.team .h2 { margin-bottom: clamp(26px, 3vw, 40px); }

.team__grid { display: grid; gap: clamp(16px, 2.5vw, 26px); grid-template-columns: repeat(2, 1fr); }
@media (min-width: 760px)  { .team__grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .team__grid { grid-template-columns: repeat(4, 1fr); } }

.team__card { display: flex; flex-direction: column; gap: 12px; }
.team__card figure { overflow: hidden; background: var(--sky-deep); }
.team__card img {
  width: 100%; height: auto; aspect-ratio: 4/5; object-fit: cover;
  transition: transform .7s var(--ease);
}
.team__card:hover img { transform: scale(1.03); }

.team__name { font-family: var(--serif); font-size: clamp(1.15rem, 2.6vw, 1.5rem); line-height: 1; color: var(--graphite); }
.team__role { font-size: 10.5px; letter-spacing: .13em; text-transform: uppercase; color: rgba(35, 36, 42, .8); margin-top: 6px; }

.team__book {
  margin-top: 10px;
  align-self: flex-start;
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: 10.5px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--graphite);
  border-bottom: 1px solid rgba(35, 36, 42, .4);
  transition: color .3s var(--ease), border-color .3s var(--ease);
}
.team__book:hover { color: var(--obsidian); border-color: var(--obsidian); }

@media (prefers-reduced-motion: reduce) {
  .team__card img { transition: none; }
  .nav { transition: background-color .28s var(--ease), border-color .28s var(--ease); }
}

/* ==========================================================================
   INNER PAGES: page hero, story, the space, placeholders
   ========================================================================== */

.page-hero {
  position: relative; min-height: min(62vh, 640px);
  background: var(--obsidian); color: var(--porcelain);
  display: grid; align-items: end;
  padding: clamp(96px, 14vw, 160px) var(--pad) clamp(40px, 6vw, 64px);
}
.page-hero__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.page-hero::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(10, 11, 14, .82) 0%, rgba(10, 11, 14, .25) 55%, rgba(10, 11, 14, .1) 100%);
}
.page-hero__copy { position: relative; z-index: 1; max-width: var(--max); width: 100%; margin: 0 auto; }

.story, .space { background: var(--sky); color: var(--graphite); padding: clamp(64px, 9vw, 108px) var(--pad); }
.story__inner, .space__inner { max-width: var(--max); margin: 0 auto; }
.prose { max-width: 62ch; font-size: clamp(1.02rem, 1.4vw, 1.12rem); line-height: 1.7; color: rgba(35, 36, 42, .9); }
.prose p { margin: 0 0 1.1em; }
.prose p:last-child { margin-bottom: 0; }

.space__grid { list-style: none; margin: 26px 0 0; padding: 0; display: grid; gap: clamp(14px, 2vw, 22px); }
.space__grid li { background: var(--sky-deep); border-radius: 4px; overflow: hidden; }
.space__grid img { width: 100%; height: auto; display: block; }
@media (min-width: 900px) { .space__grid { grid-template-columns: repeat(3, 1fr); } }

/* Stylists as a standalone section with paragraph bios, two columns at most so
   three to five sentences have a measure to read at. */
.team--bios { background: var(--sky); color: var(--graphite); padding: clamp(64px, 9vw, 108px) var(--pad); grid-column: auto; }
.team__inner { max-width: var(--max); margin: 0 auto; }
.team__lede { max-width: 46ch; margin: 12px 0 0; color: rgba(35, 36, 42, .86); }
.team--bios .team__grid { margin-top: clamp(26px, 3vw, 40px); grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); gap: clamp(24px, 3vw, 40px); }
.team__bio { margin-top: 10px; font-size: 1rem; }
.team__bio p { margin: 0 0 .9em; }

/* Every placeholder on the site looks like this, so nobody mistakes one for
   finished work. A dashed slate frame, the sky-deep fill, and a label. */
.ph {
  display: grid; place-items: center; text-align: center; padding: 20px;
  background: var(--sky-deep); border: 2px dashed rgba(90, 108, 109, .65); border-radius: 4px;
  color: rgba(35, 36, 42, .7); font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
}
.ph--hero { position: absolute; inset: 0; border-radius: 0; background: var(--smoke); color: rgba(245, 242, 237, .65); border-color: rgba(245, 242, 237, .3); place-items: end; text-align: right; padding: 22px 24px; }
.ph--tile { aspect-ratio: 4 / 3; }
.ph--portrait { aspect-ratio: 4 / 5; }

/* a page that opens on words rather than a photograph keeps the same dark
   header so the bar and the dock behave the same way on every page */
.page-hero--text { min-height: min(40vh, 400px); }
.page-hero__lede { max-width: 46ch; margin: 14px 0 0; color: rgba(245, 242, 237, .8); font-size: clamp(1rem, 1.5vw, 1.1rem); }

/* ==========================================================================
   PRICES, the thing the owners said was too small to read
   ========================================================================== */

.prices { background: var(--sky); color: var(--graphite); padding: clamp(56px, 8vw, 96px) var(--pad) clamp(72px, 10vw, 120px); }
.prices__inner { max-width: var(--max); margin: 0 auto; display: grid; gap: clamp(48px, 7vw, 88px); }
.prices__cat { font-family: var(--serif); font-weight: 400; font-size: clamp(2rem, 5vw, 3.2rem); line-height: 1; margin: 0 0 10px; }
.prices__intro { max-width: 46ch; margin: 0 0 22px; color: rgba(35, 36, 42, .86); }
.prices__list { list-style: none; margin: 0 0 26px; padding: 0; }
.price {
  display: grid; grid-template-columns: auto 1fr auto; align-items: baseline; column-gap: 12px;
  padding: clamp(14px, 2vw, 18px) 0;
  border-top: 1px solid rgba(35, 36, 42, .22);
}
.prices__list li:last-child { border-bottom: 1px solid rgba(35, 36, 42, .22); }
.price__name { font-size: clamp(1.1rem, 1.6vw, 1.25rem); font-weight: 600; color: var(--graphite); }
.price__dots { border-bottom: 1px dotted rgba(35, 36, 42, .38); transform: translateY(-.3em); }
.price__amount { font-family: var(--serif); font-size: clamp(1.35rem, 2vw, 1.6rem); color: var(--graphite); font-variant-numeric: tabular-nums; white-space: nowrap; }
.price__note { grid-column: 1 / -1; margin-top: 4px; font-size: .95rem; color: rgba(35, 36, 42, .78); max-width: 46ch; }
@media (min-width: 900px) {
  .prices__inner { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); column-gap: clamp(40px, 6vw, 80px); align-items: start; }
}

.page-hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 26px 0 0; }

/* ==========================================================================
   CONTACT, booking on the left, a question on the right
   ========================================================================== */

.contact { background: var(--sky); color: var(--graphite); padding: clamp(56px, 8vw, 96px) var(--pad) clamp(72px, 10vw, 120px); }
.contact__inner { max-width: var(--max); margin: 0 auto; display: grid; gap: clamp(48px, 7vw, 88px); }
.contact__lede { max-width: 42ch; margin: 18px 0 26px; color: rgba(35, 36, 42, .86); }
.contact__actions { display: flex; flex-wrap: wrap; gap: 12px; }
/* the ghost button is written for the dark ground; on sky it needs to win
   that rule's specificity, hence the doubled class */
.contact .btn.btn--ghost { color: var(--graphite); border-color: rgba(35, 36, 42, .5); }
.contact .btn.btn--ghost:hover { background: var(--graphite); color: var(--porcelain); border-color: var(--graphite); }
.contact__address { font-style: normal; margin: 30px 0 0; font-size: 1.05rem; line-height: 1.5; }
.contact__address a { color: var(--graphite); text-decoration: underline; text-decoration-color: rgba(35, 36, 42, .35); text-underline-offset: 4px; }
.contact__ask .h2 { margin-bottom: 26px; }
@media (min-width: 900px) {
  .contact__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); column-gap: clamp(48px, 7vw, 110px); align-items: start; }
}

/* the forms, shared by Contact and Careers. Inputs sit a shade lighter than
   the sky so the field reads as a field, with a slate border that darkens on
   focus. Nothing purple: that is for Book now. */
.form { display: grid; gap: 18px; max-width: 640px; }
.form__row { display: grid; gap: 18px; }
@media (min-width: 620px) { .form__row { grid-template-columns: 1fr 1fr; } }
.form__field { display: grid; gap: 8px; }
.form__field > span { font-weight: 600; font-size: .98rem; }
.form__field small { font-weight: 400; color: rgba(35, 36, 42, .7); margin-left: 6px; }
.form input, .form select, .form textarea {
  width: 100%; min-height: 50px; padding: 12px 14px; font: inherit; font-size: 1rem; color: var(--graphite);
  background: rgba(255, 255, 255, .55); border: 1px solid rgba(90, 108, 109, .6); border-radius: 6px;
  box-sizing: border-box; -webkit-appearance: none; appearance: none;
}
.form select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%2323242A' stroke-width='1.6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 14px center; padding-right: 40px; }
.form textarea { resize: vertical; min-height: 140px; }
.form input:focus-visible, .form select:focus-visible, .form textarea:focus-visible { outline: 2px solid var(--graphite); outline-offset: 1px; border-color: var(--graphite); }
.form .btn { justify-self: start; }
.form__fine { margin: 0; font-size: .92rem; color: rgba(35, 36, 42, .78); max-width: 46ch; }
.form__trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* ==========================================================================
   CAREERS, the owners' words and an application
   ========================================================================== */

.careers { background: var(--sky); color: var(--graphite); padding: clamp(56px, 8vw, 96px) var(--pad) clamp(72px, 10vw, 120px); }
.careers__inner { max-width: var(--max); margin: 0 auto; display: grid; gap: clamp(48px, 7vw, 88px); }
.careers__form .h2 { margin-bottom: 26px; }
@media (min-width: 900px) {
  .careers__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); column-gap: clamp(48px, 7vw, 110px); align-items: start; }
}

/* ==========================================================================
   GALLERY, the grid and the lightbox
   ========================================================================== */

.gallery { background: var(--sky); padding: clamp(40px, 6vw, 72px) var(--pad) clamp(72px, 10vw, 120px); }
.gallery__grid { list-style: none; margin: 0 auto; padding: 0; max-width: var(--max); display: grid; gap: clamp(10px, 1.6vw, 18px); grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
.gallery__tile { display: block; aspect-ratio: 4 / 5; overflow: hidden; border-radius: 6px; background: var(--sky-deep); }
.gallery__tile img, .gallery__tile picture { display: block; width: 100%; height: 100%; object-fit: cover; }
a.gallery__tile img { transition: transform .5s ease; }
a.gallery__tile:hover img { transform: scale(1.03); }
a.gallery__tile:focus-visible { outline: 3px solid var(--graphite); outline-offset: 3px; }
.gallery__tile.ph { aspect-ratio: 4 / 5; }

/* the dialog fills the viewport and carries its own ground, so the
   ::backdrop is a belt to its braces rather than the thing holding it up */
.lb { border: 0; padding: 0; max-width: 100vw; max-height: 100vh; width: 100vw; height: 100vh; background: rgba(10, 11, 14, .96); color: var(--porcelain); }
.lb::backdrop { background: rgba(10, 11, 14, .96); }
.lb[open] { display: grid; grid-template-columns: 56px 1fr 56px; align-items: center; }
.lb__fig { margin: 0; display: grid; justify-items: center; align-content: center; gap: 14px; height: 100vh; padding: 56px 0; box-sizing: border-box; }
.lb__img { max-width: 100%; max-height: calc(100vh - 140px); object-fit: contain; border-radius: 4px; }
.lb__cap { font-size: .95rem; color: rgba(245, 242, 237, .78); min-height: 1.4em; text-align: center; padding: 0 12px; }
.lb__nav, .lb__close { width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(245, 242, 237, .35); background: transparent; color: var(--porcelain); font: inherit; font-size: 1.25rem; cursor: pointer; display: grid; place-items: center; justify-self: center; }
.lb__nav:hover, .lb__close:hover { background: var(--porcelain); color: var(--obsidian); }
.lb__nav:focus-visible, .lb__close:focus-visible { outline: 2px solid var(--porcelain); outline-offset: 3px; }
.lb__close { position: absolute; top: 16px; right: 16px; }
.lb[data-single] .lb__nav { visibility: hidden; }
@media (max-width: 619px) {
  .lb[open] { grid-template-columns: 1fr; }
  .lb__nav { position: absolute; bottom: 18px; }
  .lb__nav--prev { left: 18px; } .lb__nav--next { right: 18px; }
  .lb__img { max-height: calc(100vh - 170px); }
}
@media (prefers-reduced-motion: reduce) { a.gallery__tile img { transition: none; } }

/* ==========================================================================
   FIND US, the map facade
   ========================================================================== */

.map { background: var(--sky); color: var(--graphite); padding: clamp(64px, 9vw, 108px) var(--pad); }
.map__inner { max-width: var(--max); margin: 0 auto; display: grid; gap: clamp(28px, 4vw, 48px); }
.map__hours { display: grid; gap: 6px; margin: 20px 0 0; font-size: .98rem; color: rgba(35, 36, 42, .86); }
.map__hours div { display: flex; gap: 16px; }
.map__hours dt { min-width: 13ch; color: rgba(35, 36, 42, .7); }
.map__hours dd { margin: 0; font-weight: 500; }
.map__tel { display: inline-flex; align-items: center; min-height: 44px; margin-top: 10px; font-size: 1.05rem; font-weight: 500; color: var(--graphite); }
/* the frame holds the button until it holds the map */
.map__frame {
  position: relative; min-height: 320px; border-radius: 4px; overflow: hidden;
  background: var(--sky-deep);
  display: grid; place-content: center; justify-items: center; gap: 12px;
}
.map__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.map__hint { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: rgba(35, 36, 42, .62); }
.map__frame.is-loaded > .btn, .map__frame.is-loaded > .map__hint { display: none; }
@media (min-width: 900px) {
  .map__inner { grid-template-columns: minmax(280px, 34%) 1fr; align-items: start; }
  .map__frame { min-height: 440px; }
}

/* ==========================================================================
   TWO DOORS, book today, or send a photo and start a conversation
   ========================================================================== */

.hero__actions, .end__actions {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
}

.btn--ghost {
  background: transparent;
  color: var(--porcelain);
  border-color: rgba(245, 242, 237, .42);
}
.btn--ghost:hover { background: var(--porcelain); color: var(--obsidian); border-color: var(--porcelain); }

.btn--dark {
  margin-top: 22px;
  background: var(--obsidian); color: var(--porcelain); border-color: var(--obsidian);
}
.btn--dark:hover { background: var(--slate); border-color: var(--slate); color: var(--porcelain); }

.svc__foot + .btn--dark { display: inline-block; }

/* ==========================================================================
   NAV SOCIAL, monochrome so the set inverts cleanly with the pill.
   The authentic four-colour Google mark stays in the proof bar, where it
   is doing a job: signalling a review source.
   ========================================================================== */

.nav__social { display: none; align-items: center; gap: 4px; }

.nav__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  color: rgba(245, 242, 237, .72);
  transition: color .3s var(--ease);
}
.nav__social a:hover { color: var(--porcelain); }
.nav__social svg { width: 17px; height: 17px; }

.nav.nav--onlight .nav__social a { color: rgba(35, 36, 42, .68); }
.nav.nav--onlight .nav__social a:hover { color: var(--obsidian); }

/* The dead air only exists below 860, where the links are behind the button.
   Above that they come back inline and the bar is full again, so the original
   1000px gate still holds there. Showing them across both ranges overloaded the
   bar at 880 and squeezed the wordmark to nothing. */
@media (min-width: 620px) and (max-width: 859px) {
  .nav__social { display: flex; }
}

@media (min-width: 1024px) {
  .nav__social { display: flex; }
  .nav__links { margin-right: 4px; }
}

@media (min-width: 860px) and (max-width: 1023px) { .nav__links { margin-right: auto; } }

/* ==========================================================================
   HER COLOURS, CARRIED FURTHER
   ========================================================================== */

/* one purple hairline between sections. This was a two tone gradient while
   the palette had two accents. It is solid now: one accent, and the handbook
   rules out purple gradients by name. */
.light, .folio, .studio, .says, .map, .end {
  border-top: 1px solid rgba(90, 108, 109, .35);
}

::selection { background: var(--sky-deep); color: var(--obsidian); }

/* the dial is the brand device, so it wears the brand colours.
   NOTE: chamber-duo.png is still the old lavender to powder blue pair. It is
   off palette now that blue is retired, and needs regenerating in purple. */
.rail__dial img { filter: none; opacity: 1; }
.rail__dial { border-color: rgba(199, 183, 237, .6); }

/* a whisper of lavender in the dark scrims, so even the black is hers */
.end__media::after {
  background:
    linear-gradient(to right, rgba(10,11,14,.97) 6%, rgba(14,12,22,.88) 32%, rgba(10,11,14,.42) 68%, rgba(10,11,14,.28) 100%),
    linear-gradient(to top, rgba(12,11,18,.62) 0%, rgba(10,11,14,0) 40%);
}

/* footer links resolve to the pair */
.end__foot a:nth-of-type(1):hover { color: var(--porcelain); }
.end__foot a:nth-of-type(2):hover { color: var(--porcelain); }
.end__foot a:nth-of-type(3):hover { color: var(--porcelain); }

/* ==========================================================================
   NAV NOTE, fills the dead centre of the pill
   ========================================================================== */

.nav__note {
  display: none;
  margin: 0 auto 0 28px;
  padding-left: 28px;
  border-left: 1px solid rgba(245, 242, 237, .28);
  /* an image of the real lettering, white, sized to the cap height the typed
     version used to sit at. brightness(0) flips it to ink over light sections. */
  height: 14px; width: auto;
  opacity: .92;
  transition: opacity .3s var(--ease), border-color .3s var(--ease), filter .3s var(--ease);
}
.nav.nav--onlight .nav__note { filter: brightness(0); opacity: .82; border-left-color: rgba(90, 108, 109, .45); }

/* the link row is nested inside the disclosure now, so the margin handoff has
   to move with it or the note simply sits wherever the flex flow leaves it */
/* At "Paul Mitchell color + care" this ran 222px and pushed the bar 35px over at
   a 1180px viewport, which is why it used to wait until 1300. The affiliation is
   the part that carries weight, so the label drops the service descriptor, and
   the roughly 110px it saves lets it appear on ordinary laptops instead of only
   on a maximised large screen. */
@media (min-width: 1180px) { .nav__note { display: block; } .nav__menu { margin-left: 0; } }
/* and on phones, beside the logo at cap height with no divider; the base
   rule above hides it, so this has to come after it */
@media (max-width: 859px) { .nav__note { display: block; height: 9px; margin: 0 0 0 2px; padding-left: 0; border-left: 0; opacity: .9; } }

/* the hero eyebrow carries the Paul Mitchell credential now, which is too long
   to share a line with the studio name on a phone; there it stacks, and the
   rule between them goes, rather than dangle at the end of the first line */
@media (max-width: 859px) {
  .hero__eyebrow { flex-direction: column; align-items: flex-start; gap: 6px; }
  .hero__eyebrow .rule { display: none; }
}
