/* ==========================================================================
   Gloss & Tips — public site design system
   Warm ivory / blush / champagne-gold / charcoal, editorial serif + clean sans.
   Kept separate from css/owner.css and css/auth.css — admin styling untouched.
   ========================================================================== */

:root {
  --cream: #fbf7f0;
  --cream-soft: #f6f0e6;
  --white: #ffffff;
  --charcoal: #191919;
  --dusty-pink: #e7a1b1;
  --blush: #f7d6de;
  --gold: #c9a66b; /* softened champagne gold used for lines/type/icons */
  --gold-strong: #d4af37; /* reserved for rare, bolder accents */
  --gold-light: #e0b45b; /* warmer highlight gold — hover glows, active states */
  --taupe: #a89f97;

  --ink-soft: rgba(25, 25, 25, 0.62);
  --ink-faint: rgba(25, 25, 25, 0.4);

  --radius-xl: 32px;
  --radius-lg: 24px;
  --radius-md: 16px;
  --radius-sm: 10px;

  --shadow-soft: 0 24px 60px -32px rgba(25, 25, 25, 0.28);
  --shadow-tiny: 0 8px 24px -14px rgba(25, 25, 25, 0.22);

  --container: 1220px;

  --font-serif: "Playfair Display", "Cormorant Garamond", Georgia, serif;
  --font-sans: "Inter", "Manrope", -apple-system, "Segoe UI", sans-serif;

  --nav-height: 90px;
}

/* ---- Base --------------------------------------------------------------- */

body.public {
  background: var(--cream);
  color: var(--charcoal);
  font-family: var(--font-sans);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

.wrap {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 24px;
}

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

a {
  color: inherit;
}

h1, h2, h3, h4 {
  font-family: var(--font-serif);
  font-weight: 600;
  line-height: 1.15;
  margin: 0;
}

p {
  margin: 0;
}

button, .btn, .btn-outline, .btn-ghost {
  font-family: var(--font-sans);
}

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

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 600;
}

.eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--gold);
  display: inline-block;
}

.section {
  padding: 96px 0;
}

.section-head {
  max-width: 640px;
  margin: 0 auto 48px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.section-head h2 {
  font-size: clamp(2rem, 3.4vw, 2.85rem);
}

.section-head p {
  color: var(--ink-soft);
  font-size: 1.05rem;
}

.divider-gold {
  width: 64px;
  height: 1px;
  background: var(--gold);
  border: none;
  margin: 0 auto;
}

/* Reveal-on-scroll (progressive enhancement; content is visible without JS) */
.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}
.reveal.is-visible {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---- Buttons -------------------------------------------------------------- */

.btn,
.btn-outline,
.btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 15px 30px;
  border-radius: 999px;
  font-size: 0.82rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease, transform 0.25s ease;
}

.btn {
  background: var(--charcoal);
  color: var(--white);
}
.btn:hover {
  background: #000;
  transform: translateY(-1px);
}

.btn-outline {
  background: transparent;
  color: inherit;
  border-color: currentColor;
}
.btn-outline:hover {
  background: rgba(255, 255, 255, 0.12);
  transform: translateY(-1px);
}

.btn-ghost {
  background: transparent;
  color: var(--charcoal);
  border-color: rgba(25, 25, 25, 0.18);
}
.btn-ghost:hover {
  border-color: var(--charcoal);
}

/* ---- Placeholder photography ---------------------------------------------
   No real salon photography is in assets/ yet. These panels stand in for
   real <img> photography later — see the final report for exact filenames.
   --------------------------------------------------------------------- */

.ph-photo {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle at 24% 22%, rgba(231, 161, 177, 0.55), transparent 55%),
    radial-gradient(circle at 78% 68%, rgba(212, 175, 55, 0.22), transparent 50%),
    linear-gradient(160deg, #efe6da 0%, #f6eee5 45%, #ecdfe0 100%);
  display: flex;
  align-items: flex-end;
}

.ph-photo::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(115deg, rgba(25,25,25,0.03) 0 2px, transparent 2px 26px);
  mix-blend-mode: multiply;
}

.ph-photo .ph-mark {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-serif);
  font-size: clamp(2.2rem, 6vw, 4rem);
  color: rgba(25, 25, 25, 0.14);
  letter-spacing: 0.05em;
}

.ph-photo .ph-caption {
  position: relative;
  margin: 0 0 18px 18px;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(25, 25, 25, 0.4);
  background: rgba(255, 255, 255, 0.55);
  padding: 6px 12px;
  border-radius: 999px;
}

/* ---- Navbar --------------------------------------------------------------- */

/* ONE light treatment, in every state the bar is ever in on this site: at the
   top of the homepage, fixed and scrolling, and over every section below.
   It used to be transparent-with-white-links at the top and flip to a
   champagne bar at 40px of scroll — both of which belonged to the old
   night-storefront hero. A warm nude veil, frosted, with espresso links reads
   as part of the blush room and never changes character underneath the
   reader. .scrolled adds nothing but a whisper of separation (below). */
.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--nav-height);
  display: flex;
  align-items: center;
  background: rgba(247, 237, 229, 0.86);
  -webkit-backdrop-filter: blur(10px) saturate(1.05);
  backdrop-filter: blur(10px) saturate(1.05);
  border-bottom: 1px solid rgba(60, 40, 35, 0.08);
  transition: background 0.35s ease, box-shadow 0.35s ease, backdrop-filter 0.35s ease;
}

.site-nav .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  width: 100%;
}

/* Scrolled = the same bar, only now separated from what is passing beneath
   it: a few points more opacity, a hairline a shade firmer, and a soft warm
   shadow. No colour change, no type change — nothing the eye reads as the
   navbar becoming a different object. */
.site-nav.scrolled {
  background: rgba(247, 237, 229, 0.93);
  box-shadow: 0 4px 18px rgba(60, 40, 35, 0.06);
  border-bottom-color: rgba(60, 40, 35, 0.12);
}

/* Over the hero or the Experience section's salon photography, the navbar
   switches to a dark translucent warm treatment instead of the cream one
   above — set via JS (see updateNavOnScroll in public.js) whenever the
   navbar band overlaps a dark/photo section. */
/* Structure, height and behaviour unchanged — only the three colours. The
   near-black rgba(15,10,7) belonged to the old night-storefront hero; over the
   blush interior it read as a shadow lying on the room. Deep warm taupe at a
   slightly higher opacity keeps the bar legible against a pale photograph and
   belongs to the same palette as the copy beneath it. --nav-gold is the
   champagne the underline, active link and CTA edge all pick up below; it is
   defined ONLY here, so the light/scrolled bar still uses --gold exactly as
   before. */
.site-nav.on-dark,
.site-nav.on-dark.scrolled {
  --nav-fg: #f4e9e3;
  --nav-gold: #c09b62;
  background: rgba(74, 57, 51, 0.68);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: none;
  border-bottom: 1px solid rgba(192, 155, 98, 0.38);
}

/* ---- Brand logo (logo.png) --------------------------------------------------
   logo.png is a transparent 1024x1024 canvas; the actual wordmark + pink
   nail-polish icon only occupies a small band in the middle (roughly
   x:190-892, y:300-588 of the source). ".brand-logo" crops that whitespace
   away with a fixed-aspect overflow:hidden frame + an oversized/offset img,
   so every context below just sets a width and gets the full, correctly
   cropped wordmark — never the tiny artwork-on-a-huge-canvas look.
   ------------------------------------------------------------------------- */

.brand-lockup {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  text-decoration: none;
  flex-shrink: 0;
}

.brand-logo-frame {
  position: relative;
  display: inline-block;
}

.brand-logo {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 702 / 288;
}
.brand-logo img {
  position: absolute;
  width: 145.9%;
  max-width: none;
  height: auto;
  left: -27.07%;
  top: -104.17%;
  pointer-events: none;
}

/* Navbar: sized so the visible wordmark reads at ~110-140px on desktop.
   Two logo assets crossfade in the SAME fixed frame — logo.png (dark
   wordmark) for light backgrounds, assets/darkV.png (light wordmark) for
   dark/photo backgrounds. The two PNGs have different canvas sizes and
   different transparent padding around the wordmark (measured alpha bounds:
   logo.png ≈ 648×237px on a 1024×1024 canvas; darkV.png ≈ 894×328px on a
   1254×1254 canvas). Cropping to those bounds EXACTLY left zero margin, so
   darkV.png's tall "T" ascender and the pink icon's tip were flush against
   the frame edge and visibly clipped by overflow:hidden. Each measured box
   below is padded 5% on every side before computing width/left/top, so both
   wordmarks land in the same box WITH breathing room. No filters, no glow
   hack: each is the real asset for its background, and only opacity
   crossfades (never size/position), so switching is visually seamless with
   zero layout shift. */
.brand-logo-frame--nav {
  width: 146px;
  aspect-ratio: 712.8 / 260.7;
}
.brand-logo-frame--nav .brand-logo--nav {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  opacity: 0;
  transition: opacity 0.22s ease;
}
.brand-logo-frame--nav .logo-on-light {
  opacity: 1;
}
.site-nav.on-dark .brand-logo-frame--nav .logo-on-light {
  opacity: 0;
}
.site-nav.on-dark .brand-logo-frame--nav .logo-on-dark {
  opacity: 1;
}
.brand-logo-frame--nav .logo-on-light img,
.brand-logo--hero img {
  width: 143.66%;
  left: -25.9%;
  top: -120.5%;
}
.brand-logo-frame--nav .logo-on-dark img {
  width: 127.52%;
  left: -17.62%;
  top: -110.82%;
}
@media (prefers-reduced-motion: reduce) {
  .brand-logo-frame--nav .brand-logo--nav {
    transition: none;
  }
}

/* Mobile drawer header: cream background, a little more room to breathe. */
.brand-logo--drawer {
  width: 148px;
}

/* Footer: sits on the blush-cream footer surface, wants a touch more presence
   than the nav mark. */
.brand-logo--footer {
  width: 150px;
}

/* Our Story: a quiet brand moment, larger and centered. */
.brand-lockup--story {
  align-items: center;
}
.brand-logo--story {
  width: 230px;
  max-width: 60vw;
}

/* Hero: the page heading. Same padded crop as the light nav mark (see the
   shared img rule above), so the "T" ascender and polish tip never clip. */
.brand-logo--hero {
  aspect-ratio: 712.8 / 260.7;
  width: clamp(260px, 29.5vw, 420px);
  margin-inline: auto;
}

/* Booking placeholder: centered card context. */
.brand-lockup--booking {
  align-items: center;
}
.brand-logo--booking {
  width: 170px;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 34px;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.98rem;
  font-weight: 500;
  letter-spacing: 0.03em;
}

.nav-links a {
  text-decoration: none;
  color: var(--nav-fg, var(--charcoal));
  position: relative;
  padding-bottom: 4px;
}
/* --nav-gold is set only by the on-dark bar (see above); everywhere else this
   falls back to --gold, so the light/scrolled navbar is untouched. */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--nav-gold, var(--gold));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s ease;
}
.nav-links a:hover::after {
  transform: scaleX(1);
}
/* Gold on hover for the hero bar and the dark bar; the scrolled cream bar
   keeps its underline-only hover exactly as before. */
.site-nav:not(.scrolled) .nav-links a:hover,
.site-nav.on-dark .nav-links a:hover {
  color: var(--nav-gold);
}
.nav-links a.nav-link-active {
  color: var(--nav-gold, var(--gold));
}
.nav-links a.nav-link-active::after {
  transform: scaleX(1);
}

/* Same gold the desktop bar uses, so the drawer reflects the scroll-spy
   state rather than inventing a second highlight treatment. */
.nav-drawer-links a.nav-link-active {
  color: var(--gold);
}

.nav-right {
  display: flex;
  align-items: center;
  gap: 18px;
}

.nav-cta {
  display: inline-flex;
  padding: 15px 34px;
  font-size: 0.88rem;
}
/* On the light bars the global charcoal .btn was the heaviest object on the
   page. A soft cream fill inside a champagne hairline, espresso label: the
   same ghost-pill language the dark bar already uses, in reverse. */
.site-nav:not(.on-dark) .nav-cta.btn {
  background: rgba(255, 250, 246, 0.62);
  color: var(--nav-fg);
  border: 1px solid rgba(185, 146, 88, 0.85);
  box-shadow: none;
}
.site-nav:not(.on-dark) .nav-cta.btn:hover,
.site-nav:not(.on-dark) .nav-cta.btn:focus-visible {
  background: rgba(255, 250, 246, 0.92);
  border-color: rgba(169, 124, 58, 0.95);
  color: #33241f;
}
.site-nav.on-dark .nav-cta.btn {
  background: transparent;
  color: var(--nav-fg);
  border: 1px solid var(--nav-gold);
  transition: background 0.25s ease, box-shadow 0.25s ease, transform 0.2s ease;
}
.site-nav.on-dark .nav-cta.btn:hover {
  background: rgba(192, 155, 98, 0.16);
  box-shadow: 0 0 16px rgba(192, 155, 98, 0.32);
  transform: translateY(-1px);
}

.nav-menu-btn {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  color: var(--nav-fg, var(--charcoal));
}
.nav-menu-btn svg {
  width: 26px;
  height: 26px;
}

/* Espresso links with a warm brown-gold for hover/active — champagne, but
   pulled deep enough to hold its own against a light bar. Set once: the
   scrolled state deliberately inherits the same pair, so the type never
   changes colour as the page moves. */
.site-nav {
  --nav-fg: #43312c;
  --nav-gold: #a97c3a;
}

/* ---- Mobile nav drawer ------------------------------------------------- */

.nav-drawer {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--cream);
  display: flex;
  flex-direction: column;
  padding: 28px 24px;
  transform: translateX(100%);
  transition: transform 0.35s ease;
}
.nav-drawer.open {
  transform: translateX(0);
}
.nav-drawer-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.nav-drawer-close {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--charcoal);
}
.nav-drawer-links {
  list-style: none;
  margin: 48px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 26px;
}
.nav-drawer-links a {
  text-decoration: none;
  color: var(--charcoal);
  font-family: var(--font-serif);
  font-size: 1.8rem;
}
.nav-drawer-foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* ---- Section hairline ----------------------------------------------------
   Champagne divider used ONLY between major homepage sections (never inside
   one). Inset rather than edge-to-edge so it reads editorial rather than
   like a browser-width <hr>. ------------------------------------------- */

.section-rule {
  width: 90%;
  max-width: 1400px;
  height: 1px;
  margin: 0 auto;
  border: 0;
  background: rgba(201, 166, 107, 0.45);
}

/* ---- Hero ------------------------------------------------------------- */

/* One full-bleed photograph behind everything, with the framed panel sitting
   on top of it. This replaced a split composition — flat blush on the left,
   photograph on the right, meeting at a feathered seam. The panel is centred
   both ways in the hero; --hero-frame sets only its width.

   The navbar is a solid light band, so the hero starts BELOW it (margin-top)
   instead of running underneath it, and takes the rest of the viewport. */
.hero {
  position: relative;
  margin-top: var(--nav-height);
  min-height: calc(100vh - var(--nav-height));
  min-height: calc(100svh - var(--nav-height));
  display: flex;
  align-items: center;
  overflow: hidden;
  /* Only ever seen while new-bg.png is still loading, and matched to the
     photograph's own wall tone so that moment is not a flash of white. */
  background: #f1d2c6;

  /* Hero-only palette, read by every rule below. The photograph is a soft
     blush interior, so the copy is warm espresso ink on light rather than
     cream on dark — these tokens are the whole colour story. */
  --hero-ink: #3b2a26;        /* headline — espresso, deliberately not black */
  --hero-ink-soft: #654f49;   /* supporting paragraph */
  --hero-ink-muted: #765d56;  /* eyebrow */
  --hero-cream: #fff8f4;
  /* Champagne #b99258 is the hero's gold — softer than --gold-strong. It is
     always needed at partial alpha (hairline, button edges), so it is written
     as rgba() at each use rather than kept as a token here. */
  /* The panel sits at the exact centre of the hero; --hero-frame is its
     width, and everything inside it measures to --hero-measure. */
  --hero-frame: clamp(460px, 47vw, 670px);
  --hero-measure: min(100%, var(--hero-frame));
}

/* The photograph: one layer, edge to edge, no overlay.
   assets/new-bg.png is 1672x941 (≈16:9) — the fluted wood wall on the left
   where the panel sits, then the studio wordmark, the marble reception desk,
   the flowers and the polish shelves.

   5px of blur — a soft defocus that keeps the colours and composition
   readable while the glass panel reads as the sharpest thing in the hero.

   Its own layer (this element carries nothing but the image), so the filter
   cannot reach the panel, the copy, the buttons, the navbar or the scroll cue
   — all of which live in .hero-inner above it. The 24px negative inset pushes
   the blur's own faded edge outside the hero's overflow:hidden box. */
.hero-bg {
  position: absolute;
  inset: -24px;
  z-index: 0;
  background: url("../assets/new-bg.png") center center / cover no-repeat;
  filter: blur(5px);
}

/* Deliberately overrides the .wrap it shares a class with: the panel is
   centred in the full hero, not on the 1220px grid. Bottom padding clears the
   scroll cue; the top matches it so the panel stays at true centre. */
.hero-inner {
  position: relative;
  z-index: 2;
  max-width: none;
  margin: 0;
  padding: 72px 32px;
  color: var(--hero-ink-soft);
  width: 100%;
  display: flex;
  justify-content: center;
}

/* ---- The framed panel -----------------------------------------------------
   A thin dark rule around a warm off-white veil, floated in the empty blush
   wall on the left of the photograph. Everything that makes it feel like a
   framed print rather than a UI card is in the restraint: the fill stays
   translucent (the room reads through it), the outline is a hairline rather
   than a border, the radius is modest, and the shadow is a wide, warm, almost
   invisible pool rather than a drop shadow.

   backdrop-filter is a progressive enhancement — where it is unsupported the
   panel is simply a slightly more opaque off-white, which is why the fill
   alpha is 0.55 rather than something that only works with blur behind it.
   ------------------------------------------------------------------------ */
/* Vertical padding is set separately from — and larger than — the horizontal:
   the frame wanted more height without getting wider, so the copy has more
   room above the eyebrow and below the last button while the measure inside
   it is unchanged. */
.hero-panel {
  width: var(--hero-measure);
  text-align: center;
  padding: clamp(52px, 5.4vw, 80px) clamp(36px, 3.8vw, 58px);
  border-radius: 18px;
  background: rgba(255, 252, 250, 0.55);
  border: 1px solid rgba(47, 38, 35, 0.55);
  -webkit-backdrop-filter: blur(7px) saturate(1.05);
  backdrop-filter: blur(7px) saturate(1.05);
  box-shadow: 0 30px 64px -46px rgba(59, 42, 38, 0.5);
  transition: transform 0.25s ease, background 0.25s ease, border-color 0.25s ease,
    box-shadow 0.25s ease, -webkit-backdrop-filter 0.25s ease, backdrop-filter 0.25s ease;
}

/* Polish, not affordance: the panel is a frame, not a button, so the lift is
   2px, the fill gains 13 points of opacity and the outline firms up a little.
   Nothing scales, nothing glows. :focus-within carries the same state so
   tabbing to either CTA inside reads the same as hovering. */
.hero-panel:hover,
.hero-panel:focus-within {
  transform: translateY(-2px);
  background: rgba(255, 252, 250, 0.68);
  border-color: rgba(47, 38, 35, 0.78);
  box-shadow: 0 34px 70px -44px rgba(59, 42, 38, 0.58);
  -webkit-backdrop-filter: blur(9px) saturate(1.06);
  backdrop-filter: blur(9px) saturate(1.06);
}

@media (prefers-reduced-motion: reduce) {
  .hero-panel {
    transition: none;
  }
  .hero-panel:hover,
  .hero-panel:focus-within {
    transform: none;
  }
}

/* 0.73rem/0.22em rather than the 0.75rem/0.28em the other eyebrows use: this
   one has to fit the frame's inner width on a single line. Hairlines on both
   sides keep it balanced now the panel is centred. */
.hero-eyebrow {
  color: var(--hero-ink-muted);
  font-size: 0.73rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-bottom: 38px;
}
/* Champagne rather than the harder --gold-strong, and held just under full
   opacity so the rule reads as a drawn hairline, not a bar. */
.hero-eyebrow::before,
.hero-eyebrow::after {
  content: "";
  flex: none;
  width: 26px;
  height: 1px;
  background: rgba(185, 146, 88, 0.85);
}

/* The logo artwork is the page heading (its alt text is the h1's name). */
.hero-logo {
  margin: 0;
  line-height: 0;
}

/* The slogan under the logo: Bodoni Moda (loaded for the hero only), small,
   widely tracked, espresso ink. padding-inline-start offsets the trailing
   letter-space so the line sits optically centred. */
.hero-slogan {
  margin: 30px 0 0;
  padding-inline-start: 0.14em;
  font-family: "Bodoni Moda", "Didot", "Bodoni MT", "Playfair Display", Georgia, serif;
  font-optical-sizing: none;
  font-variation-settings: "opsz" 34;
  font-weight: 600;
  font-size: clamp(1.15rem, 1.85vw, 1.65rem);
  letter-spacing: 0.14em;
  line-height: 1.35;
  text-wrap: balance;
  overflow-wrap: anywhere; /* owner-entered text: never overflow the panel */
  text-transform: uppercase;
  color: var(--hero-ink);
}

/* The tagline comes from salon_settings.tagline_en. Until it arrives the
   markup fallback holds its space invisibly, so the saved text never flashes
   in over a different one. If the script or network fails, the fallback is
   revealed anyway after 2.5s. */
.hero-slogan[data-setting-pending] {
  visibility: hidden;
  animation: setting-fallback-reveal 0s linear 2.5s forwards;
}
@keyframes setting-fallback-reveal {
  to { visibility: visible; }
}

.hero-actions {
  margin-top: 50px;
  display: flex;
  justify-content: center;
}

/* Espresso pill with a champagne edge — sits in the same palette as the room
   and the panel's own dark hairline rather than the global charcoal. 48px
   minimum height keeps it an easy tap target. (box-shadow is added to the
   inherited transition list.) */
.hero-actions .btn {
  min-height: 52px;
  min-width: 300px;
  padding: 15px 32px;
  font-size: 0.79rem;
  background: #3a2a26;
  color: var(--hero-cream);
  border-color: rgba(185, 146, 88, 0.85);
  box-shadow: 0 14px 30px -22px rgba(59, 42, 38, 0.5);
  transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease,
    transform 0.25s ease, box-shadow 0.25s ease;
}
/* Lifted brown + a warm, wide shadow — no glow, and the gold edge stays put. */
.hero-actions .btn:hover,
.hero-actions .btn:focus-visible {
  background: #4b3630;
  color: var(--hero-cream);
  border-color: rgba(185, 146, 88, 0.95);
  box-shadow: 0 18px 34px -20px rgba(96, 66, 54, 0.5);
}


/* ---- Services section: editorial two-column category grid ----------------
   Two wide landscape cards per row rather than a 3-up portrait grid — the
   photography is the main visual event here, text is a light overlay.
   Clicking a card expands that category's individual services (from
   public.services) into #services-detail below the grid — see
   loadCategoryServices()/selectCategory() in js/public.js. Scoped to
   .services-section / .service-tile / .service-detail-* only, so nothing
   here touches the homepage's other sections, Packages, or the owner
   dashboard's own (unrelated) styling. --------------------------------- */

.services-section {
  position: relative;
  overflow: hidden;
  /* Tightened vs the global .section rhythm so the intro + both card rows
     read as one composition inside a single desktop viewport. Top padding
     clears the fixed navbar (same approach as the Experience section), with
     extra room above and below so the photographic background extends a
     little past the one-screen composition rather than being cut exactly at
     it. The card-sizing formula further down is deliberately NOT retuned to
     this padding — the cards keep their existing dimensions. */
  padding: calc(var(--nav-height) + 52px) 0 56px;
  min-height: 107vh;
  min-height: 107svh;
}

/* Background photograph layer. Kept as its own pseudo-element so the blur
   applies ONLY to the photo — the intro type and the category images above
   it stay perfectly sharp. Inset/scale hide the soft blur edges. */
.services-section::before {
  content: "";
  position: absolute;
  /* Bleed shared EXACTLY with ::after below — see the note there. */
  inset: -40px;
  z-index: 0;
  background: url("../assets/service.jpg") center center / cover no-repeat;
  filter: blur(10px);
  pointer-events: none;
}

/* Neutral dark veil. The inset MUST stay identical to ::before above: the
   image layer needs to bleed past the section so the blur's own faded edge is
   never visible, and if the tint stops at the section box instead, that bleed
   shows through below it as a lighter untinted strip — which is exactly the
   seam that appeared at the bottom once a category was expanded. Same box for
   both layers means overflow: hidden on the section clips them together, and
   the photograph terminates as one continuous image. No transform on either
   layer: promoting one to its own compositing layer let it escape that clip.
   The veil deepens the photo for contrast without tinting it
   champagne/gold/pink, and stays light enough that the candles, brushes and
   towels read clearly. */
.services-section::after {
  content: "";
  position: absolute;
  inset: -40px;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(15, 10, 7, 0.42) 0%,
    rgba(15, 10, 7, 0.34) 45%,
    rgba(15, 10, 7, 0.44) 100%
  );
}

/* Everything inside the section sits above the two background layers. A wider
   container than the site default here only — the four photographs are the
   point of this section, so they get more of the screen. */
.services-section > .wrap {
  position: relative;
  z-index: 1;
  max-width: 1320px;
}

.services-head.section-head {
  margin-bottom: 24px;
  gap: 9px;
}

/* Intro type now sits on a darkened photograph — warm ivory rather than
   charcoal. The eyebrow stays champagne gold. Wording, fonts, sizes and
   alignment are unchanged; colours only. */
.services-head h2 {
  color: var(--cream);
}
.services-head p {
  color: rgba(251, 247, 240, 0.84);
}

.services-head::before {
  content: "";
  display: block;
  width: 1px;
  height: 16px;
  margin: 0 auto;
  background: linear-gradient(180deg, transparent, var(--gold));
}

/* Two cards per row, built on four tracks: each card spans two, so its width
   is exactly what a plain two-column grid gave it. When the number of active
   categories (whatever Supabase returns) is odd, the last card moves to tracks
   2-3 — same size, centred under the row above. Pure CSS, so it follows any
   re-render of the grid with no JS. */
.services-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: 18px;
  row-gap: 16px;
  max-width: 1320px;
  margin: 0 auto;
  align-items: start;
}

.service-tile {
  grid-column: span 2;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: none;
  margin: 0;
  padding: 0;
  background: none;
  font: inherit;
  text-align: start;
  color: var(--white);
  text-decoration: none;
  cursor: pointer;
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* Lifts the sharp category photographs off the softly blurred photographic
     background behind them — a wide soft pool plus a tight contact shadow. */
  box-shadow:
    0 10px 28px rgba(0, 0, 0, 0.22),
    0 3px 10px rgba(0, 0, 0, 0.12);
  transition: box-shadow 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

.service-tile:last-child:nth-child(odd) {
  grid-column: 2 / span 2;
}

.service-tile .ph-photo {
  position: absolute;
  inset: 0;
}

.service-tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Very light warmth — never a heavy filter over the owner's real photos. */
  filter: saturate(0.97) sepia(0.035);
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.service-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Transparent through the top ~40%, gradually darkening toward the
     bottom — the photo stays bright; only the caption zone darkens. */
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 44%,
    rgba(20, 14, 10, 0.14) 64%,
    rgba(15, 10, 8, var(--tile-overlay-strength, 0.74)) 100%
  );
  transition: background 0.5s ease;
}

.service-tile:hover,
.service-tile:focus-visible {
  box-shadow:
    0 18px 46px rgba(0, 0, 0, 0.3),
    0 4px 12px rgba(0, 0, 0, 0.16);
}
.service-tile:hover img,
.service-tile:focus-visible img {
  transform: scale(1.032);
}
.service-tile:hover::after,
.service-tile:focus-visible::after {
  --tile-overlay-strength: 0.82;
}
.service-tile:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 4px;
}

/* Champagne hairline around every category card.
   Deliberately reuses the ::before ring the active state already used, rather
   than adding a real border to the element: .service-tile has
   `overflow: hidden` and a full-bleed <img> at `inset: 0`, so a real border
   would inset the photograph by 1px and eat into the aspect-ratio box. An
   absolutely positioned pseudo-element costs no layout at all, inherits the
   radius exactly, and cannot stack with the active ring because it IS the
   active ring. Same gold as the hero "Book an Appointment" CTA. */
.service-tile::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  border: 1px solid rgba(201, 166, 107, 0.7);
  pointer-events: none;
  transition: border-color 0.3s ease;
}

/* Selected category keeps its stronger ring — width and colour only. */
.service-tile.is-active::before {
  border-width: 2px;
  border-color: var(--gold);
}

.service-tile-body {
  position: relative;
  z-index: 1;
  padding: clamp(18px, 1.9vw, 26px);
  max-width: 82%;
  /* Keeps the caption legible where a photo runs bright behind it, so the
     bottom gradient can stay light instead of swallowing the image. */
  text-shadow: 0 1px 10px rgba(15, 10, 8, 0.35);
}

.service-tile .tile-index {
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  color: var(--gold-light);
  text-transform: uppercase;
  margin-bottom: 6px;
  display: block;
}

.service-tile h3 {
  font-size: clamp(1.4rem, 1.75vw, 1.85rem);
  line-height: 1.12;
  color: var(--white);
}

.service-tile p {
  margin-top: 7px;
  font-size: 0.86rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
  max-width: 40ch;
  /* The cards are shorter now; a long category description is capped at three
     lines so it can never push the caption block past the top of the card.
     The copy itself still comes from Supabase untouched. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

.service-tile .tile-link {
  margin-top: 13px;
  font-size: 0.73rem;
  letter-spacing: 0.12em;
  font-weight: 600;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--white);
  transition: color 0.24s ease;
}

/* "Explore Services →" warms to champagne gold whenever the card is hovered,
   focused or currently expanded — the arrow inherits currentColor, so text
   and arrow shift together. */
.service-tile:hover .tile-link,
.service-tile:focus-visible .tile-link,
.service-tile.is-active .tile-link {
  color: var(--gold-light);
}

.tile-arrow {
  display: inline-block;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.service-tile:hover .tile-arrow,
.service-tile:focus-visible .tile-arrow {
  transform: translateX(5px);
}

/* ---- Desktop one-screen fit ---------------------------------------------
   Both card rows have to land inside one viewport together with the navbar
   and the intro, without shrinking the type. 317px is the section's non-card
   vertical cost: padding-top incl. navbar (112) + intro block (165) + row
   gap (16) + bottom padding (24); everything left over is split between the
   two rows, so the photography takes all the height the viewport allows.

   Two limits work together so the card proportion floats instead of jumping
   at a breakpoint: max-height caps a row at half the leftover height, and
   max-width narrows the grid just enough that a capped card never gets
   thinner than ~2.45:1. Tall viewports (≥900px) land on the natural 21:10
   with the grid at full width; short laptops (768px) settle near 2.45:1 with
   a slightly narrower grid. Scoped to the Services grid only. */
@media (min-width: 1025px) {
  .services-grid {
    max-width: min(1320px, calc((100vh - 317px) * 2.45 + 18px));
  }
  .service-tile {
    aspect-ratio: 21 / 10;
    max-height: calc((100vh - 317px) / 2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .service-tile,
  .service-tile img,
  .service-tile::after,
  .service-tile::before,
  .tile-arrow {
    transition: none;
  }
}

/* ---- Services detail: individual services for the selected category ----
   Deliberately calmer/more functional than the category photography above
   it — information first, minimal decoration. */

.services-detail {
  position: relative;
  z-index: 1;
  margin-top: 60px;
  /* js/public.js scrolls this panel to the top of the viewport when a
     category is opened; without this the fixed navbar sat on top of the
     heading. CSS-only fix — the scroll call itself is untouched. */
  scroll-margin-top: calc(var(--nav-height) + 44px);
}

.services-detail-inner {
  padding-top: 64px;
  border-top: 1px solid rgba(201, 166, 107, 0.35);
}

.services-detail-head {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
  margin-bottom: 44px;
}

.services-detail-eyebrow {
  font-size: 0.74rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 600;
}

.services-detail-head h3 {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  color: var(--cream);
  text-shadow: 0 1px 12px rgba(15, 10, 7, 0.45);
}

.services-detail-close {
  position: absolute;
  top: 2px;
  inset-inline-end: 0;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 0.76rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(251, 247, 240, 0.72);
  padding: 6px 10px;
  transition: color 0.2s ease;
}
.services-detail-close:hover,
.services-detail-close:focus-visible {
  color: var(--cream);
}

.services-detail-loading {
  text-align: center;
  color: rgba(251, 247, 240, 0.82);
  padding: 12px 0 4px;
}

.service-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  column-gap: 20px;
  row-gap: 24px;
  padding-bottom: 16px;
}

.service-detail-card {
  background: var(--white);
  border: 1px solid #e8e0d5;
  border-radius: 18px;
  padding: 26px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.service-detail-card-top h4 {
  font-family: var(--font-serif);
  font-size: 1.15rem;
  color: var(--charcoal);
  line-height: 1.25;
}

.service-detail-desc {
  margin-top: 6px;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

.service-detail-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.service-detail-tag {
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--cream-soft);
  border-radius: 999px;
  padding: 5px 11px;
}

.service-detail-meta {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid rgba(25, 25, 25, 0.06);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.service-detail-price {
  font-family: var(--font-serif);
  font-size: 1.3rem;
  color: var(--charcoal);
}

.service-detail-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--charcoal);
  text-decoration: none;
  padding: 10px 18px;
  border: 1px solid var(--charcoal);
  border-radius: 999px;
  transition: background 0.25s ease, color 0.25s ease;
  flex-shrink: 0;
}
.service-detail-cta:hover,
.service-detail-cta:focus-visible {
  background: var(--charcoal);
  color: var(--white);
}
.service-detail-cta span {
  display: inline-block;
  transition: transform 0.3s ease;
}
.service-detail-cta:hover span {
  transform: translateX(3px);
}

.empty-panel {
  text-align: center;
  padding: 64px 32px;
  border: 1px solid rgba(25, 25, 25, 0.1);
  border-radius: var(--radius-lg);
  background: var(--white);
  color: var(--ink-soft);
}
.empty-panel h3 {
  font-size: 1.3rem;
  color: var(--charcoal);
  margin-bottom: 10px;
}

/* ---- Packages -------------------------------------------------------------- */

.packages-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 26px;
}

.package-card-pub {
  background: var(--white);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-tiny);
  display: flex;
  flex-direction: column;
}

.package-card-pub .ph-photo,
.package-card-pub img.pkg-photo {
  height: 210px;
  width: 100%;
  object-fit: cover;
}

.package-card-pub-body {
  padding: 26px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}

.package-card-pub h3 {
  font-size: 1.35rem;
}

.package-card-pub .pkg-desc {
  color: var(--ink-soft);
  font-size: 0.92rem;
}

.package-card-pub .pkg-includes {
  font-size: 0.82rem;
  color: var(--ink-faint);
  border-top: 1px solid rgba(25, 25, 25, 0.08);
  padding-top: 12px;
  margin-top: 4px;
}

.package-card-pub .pkg-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: auto;
  padding-top: 14px;
}

.package-card-pub .pkg-price {
  font-family: var(--font-serif);
  font-size: 1.5rem;
  color: var(--charcoal);
}

.package-card-pub .pkg-duration {
  font-size: 0.76rem;
  color: var(--ink-faint);
  letter-spacing: 0.04em;
}

.package-card-pub .btn {
  margin-top: 16px;
  width: 100%;
}

/* ---- Experience section ----------------------------------------------------- */

.experience-section {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: #15100c;
  color: var(--cream);
  /* Account for the fixed navbar (var(--nav-height)) plus responsive
     breathing room, so the eyebrow never sits underneath/against the navbar
     — shrinks gracefully on short laptop viewports instead of the section
     just growing taller. */
  padding-top: calc(var(--nav-height) + clamp(72px, 9vh, 118px));
  padding-bottom: clamp(96px, 11vh, 150px);
  /* The section's own top lands at the viewport top when #experience is
     opened (the fixed navbar overlays it), so a full viewport of height is
     what makes the dark canvas reach the fold — otherwise the next, light
     section shows as a strip underneath. Set a little past a full screen so
     the dark canvas reads as breathing room around the gallery rather than
     a composition cut exactly at the top and bottom of the viewport. */
  min-height: 112vh;
  min-height: 112svh;
}

/* ---- Immersive background: the active gallery photo, softly blurred ----
   Two stacked layers cross-fade opacity when the active slide changes so
   the transition never shows a flash of the section's base color. Scaled
   up slightly (1.03) so the blur's edge-softening never reveals empty
   edges. Kept to a SLIGHT blur (4px) — the salon architecture must stay
   recognizable, not dissolve into an abstract color field. */
.experience-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  /* Purely decorative (blurred active photo + overlay) — must never
     intercept clicks meant for the sharp gallery above it. */
  pointer-events: none;
}
.experience-bg-layer {
  position: absolute;
  inset: -4%;
  background-size: cover;
  background-position: center;
  filter: blur(4px);
  transform: scale(1.03);
  opacity: 0;
  transition: opacity 0.7s ease;
}
.experience-bg-layer.is-active {
  opacity: 1;
}
.experience-bg-overlay {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(12, 9, 6, 0.86) 0%, rgba(18, 13, 9, 0.6) 26%, rgba(18, 13, 9, 0.68) 68%, rgba(10, 7, 5, 0.92) 100%),
    linear-gradient(110deg, rgba(58, 22, 30, 0.14) 0%, transparent 45%, rgba(201, 166, 107, 0.12) 100%),
    radial-gradient(ellipse at 50% 100%, rgba(0, 0, 0, 0.35) 0%, transparent 60%);
}

.experience-head {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: center;
  gap: 12px;
  margin-bottom: clamp(28px, 4vh, 42px);
}
.experience-head h2,
.experience-head p {
  margin: 0 auto;
  max-width: 46ch;
}
.experience-eyebrow.eyebrow {
  justify-content: center;
  color: var(--gold);
}
.experience-eyebrow::after {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--gold);
  display: inline-block;
}
.experience-head h2 {
  color: #fbf4e8;
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
}
.experience-head p {
  color: rgba(250, 240, 226, 0.72);
  font-size: 0.95rem;
  max-width: 46ch;
}

/* ---- Large immersive gallery ---------------------------------------------
   Near full-bleed (breaks out of .wrap on purpose) so ~5 tall panels read
   as one panoramic composition rather than a row of small cards. */
.experience-gallery {
  position: relative;
  z-index: 1;
  width: 97vw;
  max-width: 1680px;
  margin: 0 auto;
}

.experience-track {
  display: flex;
  gap: 5px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding: 2px;
  border-radius: 14px;
}
.experience-track::-webkit-scrollbar {
  display: none;
}

.experience-slide {
  position: relative;
  flex: 0 0 auto;
  width: calc(20% - 4px);
  height: clamp(460px, 44vw, 560px);
  border-radius: 12px;
  overflow: hidden;
  scroll-snap-align: start;
  border: 1px solid rgba(201, 166, 107, 0.4);
  transition: border-color 0.25s ease;
}

/* Real photos (not the placeholder fallback) are openable in the lightbox —
   understated affordance only: a hint of scale, a brighter gold border, and
   a tiny expand glyph, never a heavy "view" button over the photo. */
.experience-slide.is-clickable {
  cursor: zoom-in;
}
.experience-slide.is-clickable img {
  transition: transform 0.4s ease;
}
@media (hover: hover) {
  .experience-slide.is-clickable:hover {
    border-color: rgba(224, 180, 91, 0.85);
  }
  .experience-slide.is-clickable:hover img {
    transform: scale(1.02);
  }
  .experience-slide.is-clickable:hover .slide-expand {
    opacity: 1;
  }
}
.slide-expand {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(15, 10, 7, 0.55);
  border: 1px solid rgba(201, 166, 107, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #f5ead9;
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}
.slide-expand svg {
  width: 12px;
  height: 12px;
}

.experience-slide .ph-photo {
  position: absolute;
  inset: 0;
}

.experience-slide img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.experience-slide::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(8, 6, 5, 0.82) 0%, rgba(8, 6, 5, 0.18) 42%, transparent 62%);
}

.experience-slide-caption {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 20px;
}

.experience-slide .slide-index {
  font-family: var(--font-serif);
  font-size: 0.8rem;
  color: var(--gold-light);
  margin-bottom: 4px;
  display: block;
}

.experience-slide h3 {
  color: #fbf4e8;
  font-family: var(--font-serif);
  font-size: 1.3rem;
}

.experience-slide .slide-caption {
  margin-top: 4px;
  font-size: 0.8rem;
  color: rgba(250, 240, 226, 0.78);
}

/* Circular controls sit partially over the gallery edges */
.experience-controls {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  pointer-events: none;
  z-index: 2;
}
.experience-controls button {
  pointer-events: auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(201, 166, 107, 0.55);
  background: rgba(15, 10, 7, 0.55);
  backdrop-filter: blur(6px);
  color: #f5ead9;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
#exp-prev {
  margin-left: -18px;
}
#exp-next {
  margin-right: -18px;
}
.experience-controls button:hover {
  background: rgba(15, 10, 7, 0.75);
  border-color: var(--gold-light);
  box-shadow: 0 0 18px rgba(201, 166, 107, 0.45);
}
.experience-controls svg {
  width: 16px;
  height: 16px;
}

/* Pagination dots */
.experience-dots {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 22px;
}
.experience-dots button {
  width: 7px;
  height: 7px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: rgba(250, 240, 226, 0.32);
  cursor: pointer;
  transition: width 0.25s ease, background 0.25s ease;
}
.experience-dots button.is-active {
  width: 26px;
  background: var(--gold);
}

/* ---- Experience lightbox ---------------------------------------------------
   Fullscreen viewer for the real experience_gallery photos, reusing the same
   image_url already loaded for the gallery card — no extra downloads. Sits
   above the fixed navbar; body scroll is locked while open (see
   .lightbox-locked on <body> in js/public.js). */
.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(8, 6, 4, 0.94);
  backdrop-filter: blur(3px);
  align-items: center;
  justify-content: center;
  padding: 32px;
}
.lightbox.is-open {
  display: flex;
}

.lightbox-figure {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: 94vw;
  max-height: 90vh;
  margin: 0;
}

.lightbox-image {
  display: block;
  max-width: 90vw;
  max-height: 78vh;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 6px;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.6);
}

.lightbox-caption {
  text-align: center;
  max-width: 60ch;
}
.lightbox-index {
  display: block;
  font-family: var(--font-serif);
  font-size: 0.82rem;
  color: var(--gold-light);
  margin-bottom: 4px;
}
.lightbox-caption h3 {
  color: #fbf4e8;
  font-size: 1.15rem;
}
.lightbox-caption p {
  margin-top: 4px;
  font-size: 0.85rem;
  color: rgba(250, 240, 226, 0.72);
}
.lightbox-caption:empty,
.lightbox-caption h3:empty,
.lightbox-caption p:empty {
  display: none;
}

.lightbox-close,
.lightbox-nav {
  position: fixed;
  z-index: 2;
  border-radius: 50%;
  background: rgba(15, 10, 7, 0.55);
  border: 1px solid rgba(201, 166, 107, 0.55);
  color: #f5ead9;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(6px);
  transition: background 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
.lightbox-close:hover,
.lightbox-nav:hover {
  background: rgba(15, 10, 7, 0.75);
  border-color: var(--gold-light);
  box-shadow: 0 0 18px rgba(201, 166, 107, 0.45);
}

.lightbox-close {
  top: 20px;
  right: 20px;
  width: 46px;
  height: 46px;
}
.lightbox-close svg {
  width: 18px;
  height: 18px;
}

.lightbox-nav {
  top: 50%;
  transform: translateY(-50%);
  width: 50px;
  height: 50px;
}
.lightbox-nav svg {
  width: 18px;
  height: 18px;
}
.lightbox-prev {
  left: 24px;
}
.lightbox-next {
  right: 24px;
}

.lightbox-dots {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
}
.lightbox-dots button {
  width: 7px;
  height: 7px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: rgba(250, 240, 226, 0.32);
  cursor: pointer;
  transition: width 0.25s ease, background 0.25s ease;
}
.lightbox-dots button.is-active {
  width: 26px;
  background: var(--gold);
}

@media (max-width: 760px) {
  .lightbox {
    padding: 16px;
  }
  .lightbox-image {
    max-height: 68vh;
  }
  .lightbox-close {
    top: max(14px, env(safe-area-inset-top));
    right: max(14px, env(safe-area-inset-right));
    width: 40px;
    height: 40px;
  }
  .lightbox-nav {
    width: 40px;
    height: 40px;
  }
  .lightbox-prev {
    left: 10px;
  }
  .lightbox-next {
    right: 10px;
  }
  .lightbox-dots {
    bottom: max(14px, env(safe-area-inset-bottom));
  }
  .lightbox-caption h3 {
    font-size: 1rem;
  }
}

body.lightbox-locked {
  overflow: hidden;
}

/* ---- Our story -------------------------------------------------------------- */

/* ---- Our Story: editorial two-column composition ------------------------
   Heading column on the left, the founder's story on the right, separated by
   a hairline champagne rule. Deliberately NOT centred — the asymmetry and the
   left-aligned type are what make it read as a magazine spread rather than a
   text block floating in a cream field. Collapses to one left-aligned column
   on small screens. ---------------------------------------------------- */

.story-section {
  /* Extra room above so the composition starts clear of the fixed navbar
     rather than beginning right under it — also gives the #story anchor a
     comfortable landing. Bottom spacing unchanged. */
  padding: clamp(150px, 13vw, 205px) 0 clamp(88px, 9vw, 118px);
}

.story-inner {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: clamp(36px, 5vw, 80px);
  align-items: start;
  text-align: start;
}

.story-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  /* Holds the heading beside the copy as the story scrolls past — a small
     editorial touch, and harmless where sticky is unsupported. */
  position: sticky;
  top: calc(var(--nav-height) + 48px);
}

.story-head .brand-logo--story {
  width: 190px;
  max-width: 55vw;
}

.story-head .eyebrow {
  margin-top: 4px;
}

.story-inner h2 {
  font-size: clamp(2.3rem, 3.7vw, 3.3rem);
  line-height: 1.06;
  /* Breaks "Why Gloss / & Tips?" onto two lines so the heading reads as a
     deliberate masthead rather than a single thin line. */
  max-width: 11ch;
}

.story-body {
  position: relative;
  padding-inline-start: clamp(26px, 3.6vw, 54px);
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 58ch;
}

/* The one decorative element: a hairline champagne rule dividing the columns,
   fading out at both ends so it never reads as a hard border. */
.story-body::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: linear-gradient(
    180deg,
    rgba(201, 166, 107, 0) 0%,
    rgba(201, 166, 107, 0.55) 14%,
    rgba(201, 166, 107, 0.55) 86%,
    rgba(201, 166, 107, 0) 100%
  );
}

.story-inner p {
  color: rgba(25, 25, 25, 0.74);
  font-size: 1.06rem;
  line-height: 1.85;
  margin: 0;
}

/* Opening line as a lead paragraph — larger and darker than the body, which
   gives the column somewhere to start. */
.story-inner .story-lead {
  font-size: clamp(1.18rem, 1.6vw, 1.4rem);
  line-height: 1.6;
  color: rgba(25, 25, 25, 0.88);
}

/* The story's turning point — champagne gold in the display serif, set apart
   from the paragraphs around it. */
.story-inner .story-accent {
  font-family: var(--font-serif);
  font-size: clamp(1.35rem, 2.1vw, 1.7rem);
  line-height: 1.35;
  color: var(--gold);
  margin: 6px 0;
}

/* Closing line — the emotional landing, so it carries more weight and sits
   darker than the body copy. */
.story-inner .story-close {
  color: var(--charcoal);
  font-size: 1.14rem;
  line-height: 1.7;
  font-weight: 500;
}

/* ---- Location ---------------------------------------------------------------- */

.location-section {
  background: var(--cream-soft);
  padding: 110px 0;
}

.location-grid {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 40px;
  align-items: stretch;
}

.location-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: 44px;
  box-shadow: var(--shadow-tiny);
  display: flex;
  flex-direction: column;
  gap: 26px;
  justify-content: center;
}

.location-card h2 {
  font-size: clamp(1.9rem, 3.2vw, 2.4rem);
}

.location-detail {
  display: flex;
  gap: 14px;
}
.location-detail .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold);
  margin-top: 9px;
  flex-shrink: 0;
}
.location-detail strong {
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 4px;
}
.location-detail p {
  color: var(--charcoal);
  font-size: 0.98rem;
}

.map-frame {
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-tiny);
  /* Stretches to the info card beside it, with a floor that keeps a premium
     landscape proportion when the card happens to be short. */
  min-height: clamp(360px, 34vh, 440px);
  position: relative;
  background: var(--cream);
}
/* Overrides the embed's fixed 600x450 attributes — full-bleed inside the
   rounded frame, no default border, nothing layered on top so dragging,
   zooming and the Google controls all stay live. */
.map-frame iframe {
  width: 100%;
  height: 100%;
  min-height: clamp(360px, 34vh, 440px);
  border: 0;
  display: block;
}
.map-frame .map-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 14px;
  text-align: center;
  padding: 24px;
}

/* ---- Final CTA ---------------------------------------------------------------- */

.final-cta {
  position: relative;
  /* Was a flat 150px top and bottom, which left the section half empty —
     291px of copy inside 591px of box. Now the height comes from the content
     with controlled padding: 84px on a wide desktop, easing to 56px on a
     phone. No min-height, so the box never reserves space it doesn't use. */
  padding: clamp(56px, 6vw, 84px) 0;
  text-align: center;
  overflow: hidden;
  /* Plain — same surface as the Our Packages section, which shows
     body.public's var(--cream). No pattern, gradient or texture. */
  background: var(--cream);
}

/* Champagne dashes framing the copy. Two narrow columns pinned to the outer
   edges: the background paints thin diagonal strokes at 32°, and the mask cuts
   across them at 122° so they read as dashes rather than continuous stripes.
   Absolutely positioned, so they add no height; behind the content via z-index
   and inert via pointer-events, so the CTA stays fully clickable. The section's
   existing overflow:hidden clips them, so they can never cause sideways scroll. */
.final-cta::before,
.final-cta::after {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: clamp(88px, 11vw, 158px);
  /* A short band, not a full-height field — a dense tall block of strokes
     reads as texture or caution tape rather than a few editorial slashes. */
  height: clamp(56px, 7vw, 88px);
  z-index: 0;
  pointer-events: none;
  opacity: 0.32;
  background-image: repeating-linear-gradient(
    32deg,
    var(--gold) 0 1.5px,
    transparent 1.5px 22px
  );
  -webkit-mask-image: repeating-linear-gradient(122deg, #000 0 26px, transparent 26px 44px);
  mask-image: repeating-linear-gradient(122deg, #000 0 26px, transparent 26px 44px);
}
.final-cta::before {
  left: clamp(8px, 3vw, 56px);
}
.final-cta::after {
  right: clamp(8px, 3vw, 56px);
}

/* The heading is capped at 18ch and nearly fills a phone's width, so the
   dashes would crowd it. Hidden rather than shrunk below 760px. */
@media (max-width: 760px) {
  .final-cta::before,
  .final-cta::after {
    display: none;
  }
}

/* The patterned placeholder panel and its dark scrim are not used here. */
.final-cta .ph-photo {
  display: none;
}
.final-cta-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Tightened from 18px so the four elements read as one group. */
  gap: 14px;
}
.final-cta h2 {
  color: var(--charcoal);
  font-size: clamp(2.1rem, 4.6vw, 3.4rem);
  max-width: 18ch;
}
.final-cta p {
  color: #625a54;
}
/* The white-pill override existed for the old dark scrim; on the warm-white
   background it would be invisible, so the button falls back to the site's
   standard charcoal pill. */
/* Sits on top of the 14px flex gap, so 24px of air above the button —
   down from 36px. Size, colour and hover are untouched. */
.final-cta .btn {
  margin-top: 10px;
}
/* Default state untouched (charcoal pill). The champagne-gold hover/focus
   treatment now lives with the other black booking CTAs — see
   "Black booking CTAs: champagne-gold hover" near the end of this file. */

/* ---- Footer --------------------------------------------------------------------- */

/* Soft blush-cream, deliberately warmer/pinker than the --cream-soft
   Location section directly above it, so the two surfaces read as separate
   without a hard rule between them. */
.site-footer {
  background: #faf3ee;
  padding: 104px 0 34px;
}

.footer-mark {
  text-align: center;
  margin-bottom: 60px;
}

.footer-cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 48px;
  padding-bottom: 64px;
}

/* Hairline dividers between columns only — no boxes, no card surfaces. */
.footer-cols .footer-col + .footer-col {
  border-left: 1px solid rgba(25, 25, 25, 0.09);
  padding-left: 48px;
}

.footer-col h4 {
  font-family: var(--font-sans);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 22px;
}

.footer-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 13px;
}

.footer-col a {
  position: relative;
  display: inline-block;
  text-decoration: none;
  color: var(--charcoal);
  font-size: 0.92rem;
  opacity: 0.78;
  transition: opacity 0.25s ease, color 0.25s ease;
}

/* Restrained hover: a hairline gold underline wipes in from the left and the
   link settles to full weight. No colour flash, no movement. */
.footer-col a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  width: 100%;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.footer-col a:hover,
.footer-col a:focus-visible {
  opacity: 1;
  color: var(--gold);
}
.footer-col a:hover::after,
.footer-col a:focus-visible::after {
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  .footer-col a,
  .footer-col a::after {
    transition: none;
  }
}

.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  padding-top: 30px;
  border-top: 1px solid rgba(25, 25, 25, 0.09);
  font-size: 0.74rem;
  color: var(--ink-faint);
}

.footer-legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: var(--ink-faint);
  font-size: 0.74rem;
  letter-spacing: 0.02em;
}
.footer-sep {
  opacity: 0.5;
}

.footer-meta {
  display: flex;
  align-items: center;
  gap: 18px;
}

.staff-login-link {
  color: var(--ink-faint);
  text-decoration: none;
  font-size: 0.76rem;
}
.staff-login-link:hover {
  color: var(--taupe);
}

/* ---- Booking placeholder page ------------------------------------------------- */

.booking-placeholder {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 40px 24px;
  background:
    radial-gradient(circle at 20% 20%, rgba(231, 161, 177, 0.35), transparent 55%),
    var(--cream);
}
.booking-placeholder-card {
  max-width: 460px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}
.booking-placeholder h1 {
  font-size: clamp(1.8rem, 4vw, 2.4rem);
}
.booking-placeholder p {
  color: var(--ink-soft);
}

/* ---- Responsive ------------------------------------------------------------------ */

@media (max-width: 1024px) {
  /* The full link row and CTA need ~1040px; below that they overflowed the
     bar, so tablets and phones use the drawer instead. */
  .nav-links,
  .nav-cta {
    display: none;
  }
  .nav-menu-btn {
    display: inline-flex;
  }
  /* The nav row is space-between; with the links, language switch and CTA all
     hidden here that left the wordmark hard against the left edge. Pin it
     to the true horizontal centre of the viewport and let the hamburger hold
     the right. Scoped to .site-nav so the drawer's own lockup is unaffected. */
  .site-nav .wrap {
    position: relative;
    justify-content: flex-end;
  }
  .site-nav .brand-lockup {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
  }

  /* Tablet: a slightly smaller frame. */
  .hero {
    --hero-frame: clamp(420px, 58vw, 600px);
  }
  .hero-inner {
    padding-inline: 24px;
  }
  /* Keeps the eyebrow on one line inside the narrower frame. */
  .hero-eyebrow {
    font-size: 0.65rem;
    letter-spacing: 0.17em;
  }

  .services-grid {
    column-gap: 18px;
    row-gap: 16px;
  }
  .services-head.section-head {
    margin-bottom: 24px;
  }
  .packages-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .location-grid {
    grid-template-columns: 1fr;
  }
  .footer-cols {
    gap: 32px;
  }
  .footer-cols .footer-col + .footer-col {
    padding-left: 32px;
  }
}

@media (max-width: 760px) {
  :root {
    --nav-height: 68px;
  }
  /* Phones keep the original, smaller mark. */
  .brand-logo-frame--nav {
    width: 128px;
  }

  .section {
    padding: 64px 0;
  }

  .brand-logo--nav {
    width: 116px;
  }

  /* Same full-bleed photograph, but a portrait crop of a 16:9 frame keeps
     roughly the middle third of it. Biased right of centre so what a phone
     shows is the wordmark, the desk and the flowers rather than a wall of
     slats. */
  .hero-bg {
    background-position: 62% center;
  }
  /* Symmetric vertical padding keeps the panel at true centre while clearing
     the scroll cue. */
  .hero-inner {
    padding: 64px 18px;
  }
  /* Near-full width (capped for large phones), softer radius, less padding. */
  .hero {
    --hero-frame: min(420px, calc(100vw - 40px));
  }
  .hero-panel {
    padding: 38px 22px 40px;
    border-radius: 16px;
  }
  /* Smaller type and shorter hairlines so the eyebrow fits a 375px phone. */
  .hero-eyebrow {
    font-size: 0.6rem;
    letter-spacing: 0.16em;
    gap: 8px;
    margin-bottom: 18px;
  }
  .hero-eyebrow::before,
  .hero-eyebrow::after {
    width: 14px;
  }
  .brand-logo--hero {
    width: min(270px, 70vw);
  }
  .hero-slogan {
    margin-top: 18px;
    font-size: clamp(0.92rem, 4vw, 1.05rem);
  }
  .hero-actions {
    margin-top: 30px;
  }
  .hero-actions .btn {
    width: 100%;
    min-width: 0;
    max-width: 300px;
  }


  .services-section {
    /* Clear the (shorter) mobile navbar so #services still lands correctly. */
    padding: calc(var(--nav-height) + 44px) 0 80px;
    min-height: 0;
  }
  .services-head.section-head {
    margin-bottom: 26px;
  }
  /* One full-width card per row on phones; the odd-card centring above
     doesn't apply in a single column. */
  .services-grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 14px;
  }
  .service-tile,
  .service-tile:last-child:nth-child(odd) {
    grid-column: auto;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius-md);
  }
  .service-tile-body {
    max-width: 100%;
    padding: 12px;
  }
  /* Category descriptions are the main height offender at this width. */
  .service-tile p {
    display: none;
  }
  .service-tile h3 {
    font-size: 1.05rem;
    line-height: 1.15;
  }
  .service-tile .tile-index {
    font-size: 0.6rem;
    margin-bottom: 4px;
  }
  .service-tile .tile-link {
    margin-top: 8px;
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    gap: 5px;
  }
  .services-detail {
    margin-top: 44px;
  }
  .services-detail-inner {
    padding-top: 44px;
  }
  .services-detail-head {
    margin-bottom: 26px;
  }
  .services-detail-close {
    position: static;
    margin-top: 10px;
  }
  /* Expanded "Choose Your Treatment": three compact informational cards. */
  .service-detail-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: 8px;
    row-gap: 10px;
  }
  .service-detail-card {
    padding: 10px;
    gap: 8px;
    border-radius: 12px;
  }
  .service-detail-card-top h4 {
    font-size: 0.82rem;
    line-height: 1.2;
  }
  /* No room for prose in a ~110px column. */
  .service-detail-desc { display: none; }
  .service-detail-tags { gap: 4px; }
  .service-detail-tag {
    font-size: 0.56rem;
    padding: 3px 6px;
  }
  .service-detail-meta {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }
  .service-detail-price { font-size: 0.88rem; }
  .service-detail-cta {
    padding: 7px 12px;
    font-size: 0.62rem;
    letter-spacing: 0.06em;
  }

  /* Packages: two per row, image-led, detail deferred to desktop. */
  .packages-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
  }
  .package-card-pub .ph-photo,
  .package-card-pub img.pkg-photo {
    height: 112px;
  }
  .package-card-pub-body {
    padding: 12px;
    gap: 8px;
  }
  .package-card-pub h3 {
    font-size: 1rem;
    line-height: 1.18;
  }
  .package-card-pub .pkg-desc,
  .package-card-pub .pkg-includes {
    display: none;
  }
  .package-card-pub .pkg-meta {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }
  .package-card-pub .pkg-price { font-size: 1rem; }
  .package-card-pub .pkg-duration { font-size: 0.68rem; }
  .package-card-pub .btn {
    width: 100%;
    padding: 10px 12px;
    font-size: 0.66rem;
    letter-spacing: 0.06em;
  }

  /* Slightly tighter gutters so the grids get usable width. Inline-only: the
     `padding` shorthand here reset .hero-inner's padding-bottom to 0 (both are
     single-class selectors, and this rule comes later), which jammed the hero
     CTAs against the bottom edge. */
  .wrap { padding-inline: 18px; }

  /* Very narrow phones: keep everything readable rather than preserving
     column counts for their own sake. */
  @media (max-width: 340px) {
    .packages-grid { grid-template-columns: 1fr; }
    .package-card-pub .ph-photo,
    .package-card-pub img.pkg-photo { height: 150px; }
    .service-detail-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }

  /* Stacked groups, left-aligned for scanning. Vertical rules become
     horizontal separators between groups. */
  .site-footer {
    padding: 72px 0 30px;
  }
  .footer-mark {
    text-align: left;
    margin-bottom: 40px;
  }
  .footer-cols {
    grid-template-columns: 1fr;
    gap: 0;
    padding-bottom: 40px;
  }
  .footer-cols .footer-col + .footer-col {
    border-left: none;
    padding-left: 0;
    border-top: 1px solid rgba(25, 25, 25, 0.09);
    margin-top: 30px;
    padding-top: 30px;
  }
  .footer-col ul {
    gap: 4px;
  }
  /* Comfortable touch targets without making the links look like buttons. */
  .footer-col a {
    padding: 7px 0;
    font-size: 0.95rem;
  }
  .footer-col a::after {
    bottom: 4px;
  }

  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
  .footer-legal {
    gap: 6px;
  }

  .location-card {
    padding: 32px 26px;
  }

  .story-section {
    padding: clamp(96px, 22vw, 118px) 0 72px;
  }
  .story-inner {
    grid-template-columns: 1fr;
    gap: 26px;
  }
  .story-head {
    position: static;
    gap: 14px;
  }
  .story-inner h2 {
    max-width: none;
  }
  .story-body {
    /* The vertical rule belongs to a two-column composition; on one column it
       would just be a stray line down the left edge. */
    padding-inline-start: 0;
    max-width: none;
  }
  .story-body::before {
    display: none;
  }
  .map-frame,
  .map-frame iframe {
    min-height: 300px;
  }

  .experience-section {
    padding-top: calc(var(--nav-height) + clamp(44px, 7vh, 66px));
    padding-bottom: clamp(64px, 9vh, 90px);
  }
  .experience-head {
    margin-bottom: clamp(20px, 4vh, 30px);
  }
  .experience-head h2 {
    font-size: 1.7rem;
  }
  .experience-eyebrow.eyebrow {
    flex-wrap: wrap;
    row-gap: 6px;
  }
  .experience-gallery {
    width: 100vw;
  }
  .experience-track {
    gap: 10px;
    padding: 2px 5vw 6px;
    scroll-padding-left: 5vw;
  }
  .experience-slide {
    width: 88vw;
    height: clamp(380px, 62vw, 460px);
  }
  .experience-controls {
    display: none;
  }
}

/* ---- Card hover: treatments + packages ------------------------------------
   Same visual language as the Experience gallery slides above — a brightening
   champagne border, a small scale on photography only, and easing in the same
   0.25–0.4s band — adapted to two card types the gallery doesn't have.

   Treatment cards are white type-on-white, so they get lift + depth + gold
   border but deliberately NO scale: scaling a text card softens the glyphs.
   Package cards carry photography, so the image takes the scale (inside the
   card's existing overflow: hidden) while the card itself only lifts.

   Everything is transform/box-shadow based — nothing here reflows a
   neighbour or changes a card's box, so the grids never shift.
   ------------------------------------------------------------------------ */

@media (hover: hover) and (pointer: fine) {
  .service-detail-card {
    transition:
      transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 0.35s cubic-bezier(0.22, 1, 0.36, 1),
      border-color 0.25s ease;
  }
  .service-detail-card:hover,
  .service-detail-card:focus-within {
    transform: translateY(-4px);
    border-color: rgba(224, 180, 91, 0.7);
    box-shadow: 0 14px 34px -18px rgba(30, 20, 15, 0.45);
  }
  /* The BOOK pill acknowledges the card hover without stealing from it; its
     own :hover fill is untouched. */
  .service-detail-card:hover .service-detail-cta {
    border-color: var(--gold);
  }

  .package-card-pub {
    transition:
      transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .package-card-pub .ph-photo,
  .package-card-pub img.pkg-photo {
    transition: transform 0.4s ease;
  }
  .package-card-pub:hover,
  .package-card-pub:focus-within {
    transform: translateY(-6px);
    /* Soft depth plus a hairline champagne ring — drawn as a shadow so the
       card's dimensions never change. */
    box-shadow: var(--shadow-soft), 0 0 0 1px rgba(224, 180, 91, 0.5);
  }
  .package-card-pub:hover .ph-photo,
  .package-card-pub:hover img.pkg-photo,
  .package-card-pub:focus-within img.pkg-photo {
    transform: scale(1.04);
  }
}

@media (prefers-reduced-motion: reduce) {
  .service-detail-card,
  .package-card-pub,
  .package-card-pub .ph-photo,
  .package-card-pub img.pkg-photo {
    transition: none;
  }
  .service-detail-card:hover,
  .service-detail-card:focus-within,
  .package-card-pub:hover,
  .package-card-pub:focus-within {
    transform: none;
  }
  .package-card-pub:hover .ph-photo,
  .package-card-pub:hover img.pkg-photo,
  .package-card-pub:focus-within img.pkg-photo {
    transform: none;
  }
}

/* ---- Luxury shimmer --------------------------------------------------------
   One system for every heading that carries the sweep. Each target declares --shimmer-base as the
   EXACT colour it already had, and every base stop of the gradient uses it —
   so an idle heading, a reduced-motion heading, and a heading in a browser
   without background-clip:text are all pixel-identical to before. Only the
   highlight colour and the band width change per context.

   Geometry is untouched: background-clip paints inside the existing glyphs.
   No wrapper, no change to font, size, weight, line-height, spacing, wrapping
   or alignment. The animation drives background-position only — no layout,
   no paint of anything but the text itself.
   ------------------------------------------------------------------------ */

.shimmer {
  --shimmer-base: var(--charcoal);
  --shimmer-hi: var(--gold);
  --shimmer-spread: 12%;
  /* One sweep + one pause. The keyframes below spend 44% of this on the
     sweep, so 3.4s–4.0s gives a 1.50–1.76s sweep and a 1.90–2.24s pause. */
  --shimmer-cycle: 3.6s;
}

/* Light type over the dark Services photo and the dark Experience section.
   The highlight is the warmer, brighter champagne token, which reads as a
   metallic sheen crossing the letters rather than a wash. */
.shimmer--on-dark {
  --shimmer-base: var(--cream);
  --shimmer-hi: var(--gold-light);
}

/* The final-CTA tagline is small supporting copy, so its highlight band is
   roughly half as wide as the headings'. */
.shimmer--subtle {
  --shimmer-spread: 7%;
}

/* Gated: if background-clip:text is unavailable the text simply keeps its own
   colour, and is never left transparent/invisible. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .shimmer {
    background-image: linear-gradient(
      100deg,
      var(--shimmer-base) 0,
      var(--shimmer-base) calc(50% - var(--shimmer-spread)),
      var(--shimmer-hi) 50%,
      var(--shimmer-base) calc(50% + var(--shimmer-spread)),
      var(--shimmer-base) 100%
    );
    background-size: 300% 100%;
    background-repeat: no-repeat;
    /* Rest position — the highlight sits off the right edge, so an idle
       heading shows nothing but its own colour. */
    background-position: 0% 0;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
  }

  /* Added by js/public.js only while the heading is in or near the viewport. */
  .shimmer.is-shimmering {
    animation: shimmer-sweep var(--shimmer-cycle) linear infinite;
  }
}

@keyframes shimmer-sweep {
  0% { background-position: 0% 0; }
  /* Sweep ends here; the rest of the cycle holds the highlight off the left
     edge, which is the pause between passes. */
  44% { background-position: 100% 0; }
  100% { background-position: 100% 0; }
}

/* Per-element base colours and de-synced cycles, kept next to each other so
   the mapping is auditable. Values match each element's existing colour rule.
   "Our Services" and "Visit the Studio" are deliberately NOT in this list —
   both headings stay completely un-shimmered.

   The hero headline left this list with the Didone treatment: a champagne
   sweep across espresso hairlines on a pale blush photograph both dropped the
   contrast the client asked for and put gold in the main heading, which the
   brief rules out. It now renders as flat espresso ink. */
.experience-head h2.shimmer { --shimmer-base: #fbf4e8; --shimmer-cycle: 3.7s; }
#packages .section-head h2.shimmer { --shimmer-cycle: 3.8s; }
.story-inner h2.shimmer { --shimmer-cycle: 3.5s; }
.final-cta h2.shimmer { --shimmer-cycle: 3.4s; }
.final-cta p.shimmer { --shimmer-base: #625a54; --shimmer-cycle: 3.9s; }

@media (prefers-reduced-motion: reduce) {
  .shimmer {
    animation: none !important;
    background-image: none;
    /* Back to the element's original flat colour — never transparent. */
    -webkit-text-fill-color: var(--shimmer-base);
    color: var(--shimmer-base);
  }
}

/* ---- Black booking CTAs: champagne-gold hover ------------------------------
   One treatment for every dark booking pill, lifted verbatim from the
   "Book Your Appointment" button in the final CTA band: the charcoal pill
   warms to var(--gold) with var(--charcoal) text. No new gold is introduced,
   and the 0.25s background/color easing comes from the shared .btn transition
   at the top of this file, so the timing matches everywhere.

   The navbar CTA is no longer part of it in EITHER state: over the dark
   sections it is a gold-bordered ghost pill with its own glow hover, and on
   the light bars it is now a cream ghost pill with a champagne hairline (see
   .nav-cta above) — both own their hover, and a gold fill would flatten
   either. The hero's "Book an Appointment" is excluded for the same reason:
   it is an espresso pill with a champagne edge and its own lift.

   :hover sits behind the pointer query so a tap on a phone never latches the
   gold state; :focus-visible is deliberately outside it, because keyboard
   focus has to work on touch devices too.
   ------------------------------------------------------------------------ */

@media (hover: hover) and (pointer: fine) {
  .final-cta .btn:hover,
  .nav-drawer-foot .btn:hover,
  .package-card-pub .btn:hover {
    background: var(--gold);
    color: var(--charcoal);
  }
}

.final-cta .btn:focus-visible,
.nav-drawer-foot .btn:focus-visible,
.package-card-pub .btn:focus-visible {
  background: var(--gold);
  color: var(--charcoal);
}

/* ---- Come See Us + map hover -----------------------------------------------
   Same interaction language as the treatment/package cards above: a -4px lift,
   soft depth plus a hairline champagne ring drawn as a second box-shadow so
   neither box ever changes size, and the map's own content scaling the way
   card photography does. Transforms only — the two-column grid never reflows.
   ------------------------------------------------------------------------ */

@media (hover: hover) and (pointer: fine) {
  .location-card,
  .map-frame {
    transition:
      transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .map-frame iframe {
    transition: transform 0.4s ease;
  }

  .location-card:hover,
  .location-card:focus-within,
  .map-frame:hover,
  .map-frame:focus-within {
    transform: translateY(-4px);
    box-shadow: var(--shadow-soft), 0 0 0 1px rgba(224, 180, 91, 0.5);
  }

  /* Get Directions acknowledges the card hover without stealing from it; its
     own :hover border stays as it is. */
  .location-card:hover .btn-ghost {
    border-color: var(--gold);
  }

  /* Restrained, and clipped by .map-frame's existing overflow:hidden +
     border-radius. Nothing is layered over the iframe, so dragging, zooming
     and the Google controls stay live. */
  .map-frame:hover iframe,
  .map-frame:focus-within iframe {
    transform: scale(1.03);
  }
}

@media (prefers-reduced-motion: reduce) {
  .location-card,
  .map-frame,
  .map-frame iframe {
    transition: none;
  }
  .location-card:hover,
  .location-card:focus-within,
  .map-frame:hover,
  .map-frame:focus-within,
  .map-frame:hover iframe,
  .map-frame:focus-within iframe {
    transform: none;
  }
}

/* ---- Owner-hidden sections ------------------------------------------------
   Set by js/public.js from salon_settings (e.g. the Experience master switch):
   the section, its divider rule and every link to it leave the layout
   entirely, so no gap or orphaned nav item remains. */
.is-owner-hidden {
  display: none !important;
}

/* Footer without any social links: the "Follow Us" column is hidden and the
   remaining two columns share the row. */
.footer-cols--no-social {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 760px) {
  .footer-cols--no-social { grid-template-columns: 1fr; }
}
