/* Ellie's Hair & Beauty — Opalya-style layout system on the existing rust/ivory brand.
   The defining choice, taken straight from the reference: there is no separate
   body-text colour. Headings, paragraphs and labels are all --ink; hierarchy
   comes from size, weight and alpha alone. Resist adding a second text colour. */

:root {
  /* Surfaces */
  --cream: #faf5f0;          /* page background */
  --accent: #f1e2d8;         /* hero + footer highlight block (was --blush) */
  --accent-soft: #f7ebe3;
  --wash: rgba(92, 51, 39, 0.05);   /* tinted section background */
  --hairline: rgba(92, 51, 39, 0.14);

  /* Ink — one colour, three alphas */
  --ink: #5c3327;
  --ink-70: rgba(92, 51, 39, 0.7);
  --ink-45: rgba(92, 51, 39, 0.45);

  /* Retained from the old palette for small accents only */
  --rust: #9c5c47;
  --gold: #b6926a;

  --font-display: "Libre Caslon Text", "Times New Roman", serif;
  --font-mark: "Federo", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Funnel Sans", "Helvetica Neue", Arial, sans-serif;

  --radius-card: 40px;
  --radius-pill: 1000px;
  --gutter: clamp(24px, 4.4vw, 64px);
  --header-h: 76px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.32;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

/* ---------- Film grain ----------
   A fine noise wash over the whole page, which stops the large flat ivory and
   blush areas reading as dead digital fill and gives them a slight paper tooth.

   Generated with an inline SVG feTurbulence rather than a texture file: no
   extra request, nothing to keep in the repo, and it stays crisp at any DPI.
   `stitchTiles="stitch"` is what makes the 300px tile repeat seamlessly —
   without it every tile edge shows as a faint grid.

   Kept deliberately faint. Grain reads much stronger on a phone screen than on
   a desktop monitor, so if this needs adjusting, move the opacity in steps of
   0.01 and check on a handset before deciding.

   pointer-events: none is load-bearing — this sits above everything, including
   the header and the sticky booking bar, so without it the entire site becomes
   unclickable. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: none;
  opacity: 0.045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  background-repeat: repeat;
}

.container {
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.container-narrow { max-width: 940px; }

/* ---------- Type ---------- */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
  margin: 0 0 0.3em;
  line-height: 1.12;
  letter-spacing: -0.02em;
}

h1 { font-size: clamp(2.75rem, 5.6vw, 4.25rem); }
h2 { font-size: clamp(2rem, 3.4vw, 2.5rem); }
h3 { font-size: clamp(1.6rem, 2.4vw, 2.5rem); }
h4 { font-size: 1.05rem; }

/* Section H2s alternate between the serif and the geometric sans in the
   reference; .h-mark opts an individual heading into the sans. */
.h-mark { font-family: var(--font-mark); letter-spacing: -0.02em; }

p { color: var(--ink); margin: 0 0 1em; }
p.muted { color: var(--ink-70); }

.lede { font-size: clamp(1.05rem, 1.5vw, 1.25rem); line-height: 1.4; max-width: 46ch; }

/* Uppercase micro-label — 12px, 500, wide tracking. Used above every section
   heading and as the small tag inside cards. */
.label {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.12;
  color: var(--ink);
  margin: 0 0 1.1em;
}
.label-dim { color: var(--ink-70); }

/* The big Federo numerals in the fact row and the price cards */
.figure-lg { font-family: var(--font-display); font-size: clamp(2.25rem, 3.4vw, 3rem); line-height: 1.12; letter-spacing: -0.02em; }
.figure-md { font-family: var(--font-mark); font-size: 1.5rem; line-height: 1.12; }

/* ---------- Buttons ----------
   The label is duplicated in the DOM inside an overflow-hidden box. On hover the
   stack slides up by exactly one label height, so the visible text is swapped by
   its own copy rising from below. Both copies must be identical in size or the
   swap visibly jitters — hence the shared .btn-label metrics. */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 19px 34px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  background: var(--ink);
  color: var(--cream);
  cursor: pointer;
  font-family: inherit;
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

.btn-swap {
  display: block;
  overflow: hidden;
  height: 0.98rem;
  position: relative;
}
.btn-swap span {
  display: block;
  height: 0.98rem;
  line-height: 0.98rem;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: transform 0.42s cubic-bezier(0.6, 0.05, 0.15, 1);
}
.btn:hover .btn-swap span { transform: translateY(-0.98rem); }

.btn svg { width: 17px; height: 17px; flex-shrink: 0; }

.btn-solid { background: var(--ink); color: var(--cream); }
.btn-solid:hover { background: var(--rust); }

.btn-outline {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}
.btn-outline:hover { background: var(--ink); color: var(--cream); }

.btn-light {
  background: var(--cream);
  color: var(--ink);
}
.btn-light:hover { background: var(--accent); }

.btn-sm { padding: 14px 26px; }

/* Text link with the same swap treatment, no pill */
.link-swap {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ink);
}
.link-swap .btn-swap span { font-size: 0.8rem; }

/* ---------- Header ----------
   Fixed and fully transparent — it sits over the accent-coloured hero block, so
   it must not paint its own background at the top of the page. It gains a cream
   backdrop only once scrolled (.is-scrolled, set in site.js). */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  transition: background 0.35s ease, box-shadow 0.35s ease;
}
.site-header.is-scrolled {
  background: rgba(250, 245, 240, 0.86);
  backdrop-filter: blur(12px);
  box-shadow: 0 1px 0 var(--hairline);
}

.nav-row {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  width: 100%;
}

/* The menu is a real element so it can become the mobile sheet, but on desktop
   it must not be a grid item itself — display:contents promotes its children
   into .nav-row's grid so the two link groups can flank the centred logo. */
.nav-menu { display: contents; }

/* Centred logotype, Federo, uppercase — the reference's signature header move */
.brand {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 10px;
}
.brand img {
  height: 34px;
  width: 34px;
  border-radius: 50%;
  object-fit: cover;
}
.brand-word {
  font-family: var(--font-mark);
  font-size: 1.35rem;
  line-height: 1.12;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--ink);
  white-space: nowrap;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 30px;
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Both groups hug the logo, so the whole lockup reads as one centred cluster.

   `grid-row: 1` is load-bearing, not decoration. The logo follows both lists in
   the DOM (they're promoted out of .nav-menu by display:contents), so with rows
   left automatic, grid's sparse placement puts the right-hand list in column 3
   and then finds the cursor already past column 2 — dropping the logo onto a
   second row and stacking the header. Pin all three to row 1 explicitly. */
.nav-left { grid-column: 1; grid-row: 1; justify-content: flex-end; padding-right: 20px; }
.nav-right { grid-column: 3; grid-row: 1; justify-content: flex-start; padding-left: 20px; }
.nav-links a {
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
}
.nav-links a.active .btn-swap span { transform: translateY(-0.98rem); }
.nav-links a:hover .btn-swap span { transform: translateY(-0.98rem); }

/* Pinned to the right rather than given its own column, so the logo stays on
   the true centre line instead of being pushed left by the CTA's width.
   `right` is the gutter, not 0: an absolutely positioned child resolves against
   the padding box, so right:0 would sit flush to the viewport edge and ignore
   the container's own gutter. */
.nav-actions {
  position: absolute;
  right: var(--gutter);
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Mobile: the centred logotype moves left and the nav collapses to a sheet */
.nav-toggle {
  display: none;
  grid-column: 3;
  grid-row: 1;
  justify-self: end;
  width: 44px;
  height: 44px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-pill);
  background: transparent;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--ink);
}
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  display: block;
  width: 16px;
  height: 1.5px;
  background: currentColor;
  transition: transform 0.3s ease, opacity 0.2s ease;
}
.nav-toggle span::before { transform: translateY(-5px); }
.nav-toggle span::after { transform: translateY(3.5px); }
.nav-open .nav-toggle span { background: transparent; }
.nav-open .nav-toggle span::before { transform: translateY(0) rotate(45deg); }
.nav-open .nav-toggle span::after { transform: translateY(-1.5px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-toggle { display: flex; }
  .brand { grid-column: 1; justify-self: start; }
  .nav-actions { display: none; }
  /* The sheet becomes a real box again so both link groups stack inside one
     panel — the reason .nav-menu exists at all. */
  .nav-menu {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    position: fixed;
    inset: var(--header-h) 0 auto;
    padding: 20px var(--gutter) 34px;
    background: var(--cream);
    border-bottom: 1px solid var(--hairline);
    transform: translateY(-12px);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.3s ease, opacity 0.3s ease;
  }
  .nav-open .nav-menu { transform: none; opacity: 1; pointer-events: auto; }
  .nav-links,
  .nav-left,
  .nav-right {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 4px;
    padding: 0;
  }
  /* The swap animation is a hover affordance — pointless in a tap-only sheet,
     and it would leave the label parked mid-slide after a tap. */
  .nav-links a { font-family: var(--font-display); font-size: 1.85rem; text-transform: none; letter-spacing: -0.02em; padding: 8px 0; }
  .nav-links a .btn-swap { height: auto; overflow: visible; }
  .nav-links a .btn-swap span { font-size: inherit; height: auto; line-height: 1.2; text-transform: none; letter-spacing: inherit; transform: none !important; }
  .nav-links a .btn-swap span + span { display: none; }
}

/* ---------- Hero ----------
   Full-width accent block running under the fixed header, copy centred, then a
   row of five tall pill-cropped photos across the bottom. */

.hero {
  background: var(--accent);
  padding: calc(var(--header-h) + 68px) 0 0;
  text-align: center;
  overflow: hidden;
}

.hero h1 { margin: 0 auto 0.24em; max-width: 14ch; }
.hero .lede { margin: 0 auto; color: var(--ink-70); }
.hero-cta { display: flex; justify-content: center; flex-wrap: wrap; gap: 14px; margin-top: 34px; }

/* Auto-scrolling photo row. Same seamless-loop mechanism as .marquee: two
   identical tracks inside a max-content viewport translated by exactly -50%.
   Both tracks and the viewport share the 16px gap so the seam is invisible —
   change one and you must change all three. */
.pill-row {
  margin-top: 62px;
  overflow: hidden;
}
.pill-viewport {
  display: flex;
  /* No gap here on purpose. With a gap the viewport is 2×track + 1×gap, so a
     -50% shift travels track + HALF a gap, while one loop period is track + a
     whole gap — an 8px snap back on every cycle. Carrying the gap as trailing
     padding on each track instead makes the track box exactly one period wide,
     so -50% lands perfectly. */
  width: max-content;
  animation: marquee 46s linear infinite;
}
.pill-track {
  display: flex;
  gap: 16px;
  padding-right: 16px;
  flex: 0 0 auto;
}
.pill {
  flex: 0 0 auto;
  width: 160px;
  height: 240px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--accent-soft);
}
.pill img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 1100px) {
  .pill { width: 132px; height: 198px; }
}
@media (max-width: 780px) {
  .hero { padding-top: calc(var(--header-h) + 44px); }
  .pill-row {
    margin-top: 44px;
    /* Break out of the container gutter so the row bleeds edge to edge */
    margin-left: calc(-1 * var(--gutter));
    margin-right: calc(-1 * var(--gutter));
  }
  /* Fewer pills fit on screen, so the same distance needs less time to read as
     the same speed. */
  .pill-viewport { animation-duration: 30s; }
  .pill { width: 118px; height: 176px; }
}

/* ---------- Marquee ----------
   Two identical tracks side by side, the pair translated by exactly -50% so the
   loop is seamless. Duplicate the item list in the markup, not in JS. */

.marquee {
  background: var(--accent);
  padding: 26px 0;
  overflow: hidden;
  white-space: nowrap;
}
.marquee-viewport { display: flex; width: max-content; animation: marquee 38s linear infinite; }
.marquee:hover .marquee-viewport { animation-play-state: paused; }
.marquee-track { display: flex; align-items: center; flex: 0 0 auto; }
.marquee-track > * {
  display: inline-flex;
  align-items: center;
  gap: 26px;
  padding: 0 26px;
  font-family: var(--font-mark);
  font-size: clamp(1.15rem, 1.7vw, 1.5rem);
  color: var(--ink);
}
.marquee-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink);
  opacity: 0.5;
  flex: 0 0 auto;
}
@keyframes marquee {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .marquee-viewport,
  .pill-viewport { animation: none; }
  /* Without the animation the row is just a static overflowing strip, so let it
     be swiped instead of stranding the later photos off-screen. */
  .pill-row { overflow-x: auto; scrollbar-width: none; }
  .pill-row::-webkit-scrollbar { display: none; }
}

/* ---------- Generic sections ---------- */

.section { padding: clamp(80px, 9vw, 130px) 0; }
.section-wash { background: var(--wash); }
.section-tight { padding: clamp(56px, 6vw, 84px) 0; }

.section-head { max-width: 620px; }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.section-head p { color: var(--ink-70); font-size: 1.05rem; }

/* Heading on the left, supporting copy + CTA on the right */
.head-split {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 40px;
  align-items: end;
  margin-bottom: clamp(40px, 5vw, 64px);
}
.head-split .side { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; }
@media (max-width: 860px) {
  .head-split { grid-template-columns: 1fr; gap: 24px; align-items: start; }
}

/* ---------- Story + fact row ---------- */

.story { text-align: center; }
.story h2 { max-width: 18ch; margin-left: auto; margin-right: auto; }
.story p { max-width: 52ch; margin-left: auto; margin-right: auto; color: var(--ink-70); }
.story .btn { margin-top: 10px; }

.facts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  margin-top: clamp(56px, 7vw, 96px);
}
.fact {
  padding: 6px 28px;
  text-align: center;
  border-left: 1px solid var(--hairline);
}
.fact:first-child { border-left: 0; }
.fact .fact-label { display: block; margin-top: 10px; font-size: 1.05rem; color: var(--ink-70); }
@media (max-width: 780px) {
  .facts { grid-template-columns: repeat(2, 1fr); row-gap: 40px; }
  .fact:nth-child(odd) { border-left: 0; }
}

/* ---------- Work / gallery with sticky heading ----------
   The heading pins briefly as you enter the section, then releases and scrolls
   away just before the photo grid arrives. Deliberately CSS-only — no
   ScrollTrigger pin. See CLAUDE.md: every production breakage on this site
   traced back to a pinned trigger.

   Earlier this let the grid ride up OVER the pinned heading, which buried the
   heading text under four or five photos for most of the section. The fix is
   .work-head-wrap: a sticky element only sticks for the height of its
   containing block, so bounding it here caps how long the heading holds. Give
   the wrapper more height for a longer pin, less for a shorter one — but do not
   remove it, or the heading pins for the whole section and the overlap returns. */

.work { background: var(--wash); padding: 0; overflow: clip; }
.work-inner { position: relative; }

/* Heading height plus the distance the fade runs over (see initHeadingFade in
   site.js). Taller = a slower, more gradual fade; shorter = quicker. This is
   the single knob for the fade's pace. */
.work-head-wrap { position: relative; height: 152svh; }

.work-head {
  position: sticky;
  top: var(--header-h);
  z-index: 1;
  height: calc(100svh - var(--header-h));
  min-height: 460px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 var(--gutter);
}
.work-head h2 { max-width: 16ch; }
.work-head p { color: var(--ink-70); max-width: 40ch; }

.work-grid {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  align-items: start;
  padding: 0 var(--gutter) clamp(80px, 9vw, 130px);
  margin-top: 0;
  /* The grid is lifted by up to this distance as the heading fades, so the
     photos rise into view in step with the text vanishing (initHeadingFade in
     site.js). The transform ends parked at exactly -1 × this value, and the
     matching negative margin-bottom is what stops that leaving a hole at the
     bottom of the section — layout reserves the reduced height that the
     transform actually occupies. Change one and you must change the other;
     site.js reads the distance FROM this margin, so it stays in sync. */
  margin-bottom: -52svh;
  will-change: transform;
}

/* The negative margin above is only safe because JS applies the matching
   transform. If site.js never runs, the layout would reserve half a screen less
   height than the grid actually occupies and the next section would land on top
   of the photos — so hand the space back when the no-js class survives. */
.no-js .work-grid {
  margin-bottom: 0;
}

/* Staggered column offsets — the mismatched tops are what stop this reading as
   a plain grid. Columns 1 and 4 ride high, 2 and 3 sit lower. */
.work-col { display: flex; flex-direction: column; gap: 24px; }
.work-col:nth-child(2) { margin-top: 92px; }
.work-col:nth-child(3) { margin-top: 168px; }
.work-col:nth-child(4) { margin-top: 40px; }

.work-card {
  position: relative;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--accent-soft);
  aspect-ratio: 2 / 3;
}
.work-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
.work-card:hover img { transform: scale(1.05); }

.work-card figcaption {
  position: absolute;
  left: 18px;
  bottom: 18px;
  padding: 9px 16px;
  border-radius: var(--radius-pill);
  background: rgba(250, 245, 240, 0.92);
  backdrop-filter: blur(6px);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
}

/* A card that should run taller than the 2:3 default */
.work-card.tall { aspect-ratio: 2 / 3.5; }
.work-card.short { aspect-ratio: 2 / 2.4; }

@media (max-width: 1100px) {
  .work-grid { grid-template-columns: repeat(3, 1fr); }
  .work-col:nth-child(4) { display: none; }
}
@media (max-width: 780px) {
  /* No pinning on mobile — the wrapper must collapse to its content or it
     leaves a screen and a bit of empty space above the photos. */
  .work-head-wrap { height: auto; }
  .work-head { height: auto; min-height: 0; position: static; padding: clamp(72px, 12vw, 110px) var(--gutter) 40px; }
  /* No pin means no fade and no lift, so the grid must sit in normal flow —
     margin-bottom back to 0 or the section loses half a screen of height.
     site.js reads this margin, so zeroing it also disables the transform. */
  .work-grid { grid-template-columns: repeat(2, 1fr); gap: 14px; margin-top: 0; margin-bottom: 0; will-change: auto; }
  .work-col { gap: 14px; }
  .work-col:nth-child(2) { margin-top: 40px; }
  .work-col:nth-child(3) { display: none; }
  .work-card { border-radius: 26px; }
}

/* ---------- Price / tier cards ---------- */

.tier-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  align-items: start;
}
@media (max-width: 980px) { .tier-grid { grid-template-columns: 1fr; max-width: 560px; margin: 0 auto; } }

.tier {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  padding: 38px 34px;
  background: transparent;
}
.tier.featured { background: var(--accent); border-color: transparent; }

.tier-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.tier h3 { margin: 0; }
.tier-badge {
  flex: 0 0 auto;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--ink);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.tier-price { display: flex; align-items: baseline; gap: 6px; margin-top: 14px; }
.tier-price .from { font-size: 0.95rem; color: var(--ink-70); font-family: var(--font-body); }
.tier .btn { width: 100%; justify-content: center; margin-top: 26px; }

.tier-list { list-style: none; margin: 26px 0 0; padding: 26px 0 0; border-top: 1px solid var(--hairline); }
.tier.featured .tier-list { border-top-color: rgba(92, 51, 39, 0.18); }
.tier-list li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  font-size: 1rem;
  padding: 5px 0;
}
.tier-list li .amount { font-family: var(--font-mark); white-space: nowrap; }
.tier-sub { margin: 24px 0 10px; }

/* ---------- Testimonials with sticky heading ---------- */

.voices { background: var(--wash); }
.voices-inner {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(40px, 6vw, 90px);
  align-items: start;
}
.voices-head {
  position: sticky;
  top: calc(var(--header-h) + 60px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 20px;
}
.voices-head h2 { max-width: 12ch; }

.voice {
  padding: 34px 0;
  border-top: 1px solid var(--hairline);
}
.voice:first-child { border-top: 0; padding-top: 0; }
.voice-who { font-size: 1.1rem; }
.voice-tag { display: block; margin: 8px 0 14px; }
.voice p { margin: 0; font-size: 1rem; line-height: 1.45; }
.voice .stars { color: var(--gold); letter-spacing: 3px; font-size: 0.85rem; margin-bottom: 12px; }

@media (max-width: 860px) {
  .voices-inner { grid-template-columns: 1fr; }
  .voices-head { position: static; }
}

/* ---------- Full-bleed offer band ---------- */

.offer {
  position: relative;
  min-height: clamp(420px, 52vw, 620px);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: hidden;
  padding: 80px var(--gutter);
}
.offer-bg { position: absolute; inset: 0; z-index: 0; }
.offer-bg img { width: 100%; height: 100%; object-fit: cover; }
.offer-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(250, 245, 240, 0.62), rgba(250, 245, 240, 0.86));
}
.offer-inner { position: relative; z-index: 1; max-width: 620px; }
.offer h2 { font-family: var(--font-mark); }
.offer p { color: var(--ink-70); font-size: 1.05rem; }
.offer .btn { margin-top: 16px; }

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

.site-footer {
  background: var(--ink);
  color: var(--cream);
  padding: clamp(64px, 8vw, 100px) 0 0;
}
.site-footer .label { color: var(--cream); }
.footer-grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: clamp(32px, 5vw, 70px);
  padding-bottom: 60px;
}
@media (max-width: 860px) { .footer-grid { grid-template-columns: 1fr; gap: 40px; } }

.footer-brand { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.footer-brand img { height: 34px; width: 34px; border-radius: 50%; }
.footer-brand span {
  font-family: var(--font-mark);
  font-size: 1.35rem;
  text-transform: uppercase;
  color: var(--cream);
}
.site-footer p { color: rgba(250, 245, 240, 0.72); max-width: 34ch; font-size: 0.98rem; line-height: 1.45; }

.footer-links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.footer-links a,
.footer-links li {
  font-family: var(--font-mark);
  font-size: 1.35rem;
  line-height: 1.3;
  color: var(--accent);
}
.footer-links a { transition: opacity 0.25s ease; }
.footer-links a:hover { opacity: 0.65; }
.footer-links li.plain { color: rgba(250, 245, 240, 0.6); font-size: 1.05rem; }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 26px 0 30px;
  border-top: 1px solid rgba(250, 245, 240, 0.16);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: rgba(250, 245, 240, 0.6);
}

/* Build credit. The whole phrase is the link so the hit area is a comfortable
   size, but only the studio name carries the accent tone — colouring all of it
   would make the credit the loudest thing in the bar, louder than Ellie's own
   copyright. The name alone is enough to signal it is clickable, and hover
   lifts the entire phrase so the full hit area is obvious once pointed at.

   No underline: at 0.72rem uppercase it is visually heavy and competes with
   the copyright line beside it. */
.footer-credit a {
  color: inherit;
  transition: color 0.25s ease;
}
.footer-credit a span {
  color: var(--accent);
  transition: opacity 0.25s ease;
}
.footer-credit a:hover { color: var(--accent); }
.footer-credit a:hover span { opacity: 0.75; }
.footer-credit a:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 3px;
}

@media (max-width: 620px) {
  /* Three items wrapping unevenly looks accidental; stack and left-align. */
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ---------- Page hero (inner pages) ---------- */

.page-hero {
  background: var(--accent);
  padding: calc(var(--header-h) + 74px) 0 clamp(64px, 8vw, 96px);
  text-align: center;
}
.page-hero h1 { max-width: 16ch; margin: 0 auto 0.24em; }
.page-hero .lede { margin: 0 auto; color: var(--ink-70); }
.page-hero .hero-cta { margin-top: 28px; }

/* ---------- Two-column feature ---------- */

.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(36px, 5vw, 80px);
  align-items: center;
}
.two-col.narrow-media { grid-template-columns: 0.8fr 1fr; }
@media (max-width: 860px) {
  .two-col, .two-col.narrow-media { grid-template-columns: 1fr; }
}
.media-round {
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--accent-soft);
  aspect-ratio: 2 / 2.5;
}
.media-round img { width: 100%; height: 100%; object-fit: cover; }
.media-pill {
  border-radius: var(--radius-pill);
  overflow: hidden;
  aspect-ratio: 2 / 3;
  max-width: 340px;
}
.media-pill img { width: 100%; height: 100%; object-fit: cover; }

.list-check { list-style: none; padding: 0; margin: 26px 0; display: flex; flex-direction: column; gap: 0; }
.list-check li {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 15px 0;
  border-top: 1px solid var(--hairline);
  font-size: 1.02rem;
  line-height: 1.4;
}
.list-check li:last-child { border-bottom: 1px solid var(--hairline); }
.list-check svg { flex-shrink: 0; margin-top: 4px; color: var(--ink); width: 17px; height: 17px; }

/* ---------- Service cards ---------- */

.service-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
@media (max-width: 980px) { .service-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .service-grid { grid-template-columns: 1fr; } }

.service-card {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  padding: 34px 30px;
  transition: background 0.35s ease;
}
.service-card:hover { background: var(--accent); }
.service-card .icon { color: var(--ink); margin-bottom: 22px; }
.service-card .icon svg { width: 30px; height: 30px; }
.service-card h3 { font-size: 1.55rem; margin-bottom: 0.3em; }
.service-card p { color: var(--ink-70); font-size: 0.98rem; line-height: 1.42; margin: 0; }

/* ---------- Steps ---------- */

.step-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
@media (max-width: 860px) { .step-grid { grid-template-columns: 1fr; } }
.step {
  border-top: 1px solid var(--hairline);
  padding: 26px 0 0;
}
.step .num { font-family: var(--font-display); font-size: 2.5rem; line-height: 1; color: var(--ink-45); margin-bottom: 18px; }
.step h3 { font-size: 1.5rem; }
.step p { color: var(--ink-70); font-size: 0.98rem; line-height: 1.42; margin: 0; }

/* ---------- Price list (services page) ---------- */

.price-block { margin-bottom: clamp(48px, 6vw, 72px); }
.price-block h3 {
  font-size: clamp(1.9rem, 3vw, 2.4rem);
  padding-bottom: 20px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--hairline);
}
.pricing-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 clamp(30px, 5vw, 70px); }
@media (max-width: 760px) { .pricing-grid { grid-template-columns: 1fr; } }
.price-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 18px;
  padding: 16px 0;
  border-bottom: 1px solid var(--hairline);
}
.price-row .name { font-size: 1.02rem; }
.price-row .amount { font-family: var(--font-mark); font-size: 1.2rem; white-space: nowrap; }
.price-note { font-size: 0.92rem; color: var(--ink-70); padding-top: 18px; }

/* ---------- Gallery page ---------- */

.filter-bar { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-bottom: 48px; }
.filter-btn {
  border: 1px solid var(--hairline);
  background: transparent;
  padding: 12px 24px;
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  cursor: pointer;
  color: var(--ink);
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
.filter-btn.active, .filter-btn:hover { background: var(--ink); border-color: var(--ink); color: var(--cream); }

.gallery-grid { columns: 4 260px; column-gap: 20px; }
.gallery-grid figure {
  margin: 0 0 20px;
  break-inside: avoid;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--accent-soft);
}
.gallery-grid img { width: 100%; transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1); }
.gallery-grid figure:hover img { transform: scale(1.05); }
@media (max-width: 620px) {
  .gallery-grid { columns: 2 150px; column-gap: 12px; }
  .gallery-grid figure { margin-bottom: 12px; border-radius: 22px; }
}

/* ---------- Contact ---------- */

.contact-card {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  padding: clamp(38px, 5vw, 60px);
  text-align: center;
}
.contact-card .icon {
  width: 62px; height: 62px;
  border-radius: 50%;
  background: var(--accent);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 24px;
  color: var(--ink);
}
.contact-card h2 { font-family: var(--font-mark); }
.contact-card p { color: var(--ink-70); max-width: 48ch; margin-left: auto; margin-right: auto; }

/* ---------- Sticky mobile Instagram bar ---------- */

.sticky-ig {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 200;
  display: none;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  background: rgba(250, 245, 240, 0.95);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--hairline);
}
.sticky-ig .btn { width: 100%; justify-content: center; }
@media (max-width: 900px) {
  .sticky-ig { display: block; }
  body { padding-bottom: 80px; }
}

/* ---------- Reveal-on-scroll start state ----------
   Declared in CSS so elements are never painted at full opacity before JS runs.
   .is-in is added by the IntersectionObserver in site.js; if JS fails entirely,
   the no-js fallback below keeps everything visible. */

[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.85s cubic-bezier(0.22, 1, 0.36, 1), transform 0.85s cubic-bezier(0.22, 1, 0.36, 1);
}
[data-reveal].is-in { opacity: 1; transform: none; }

/* Media scales up from 0.75 as it enters, as in the reference */
[data-reveal-media] img { transform: scale(0.78); transition: transform 1.1s cubic-bezier(0.22, 1, 0.36, 1); }
[data-reveal-media].is-in img { transform: scale(1); }
/* The hover zoom and the entrance scale both drive `transform` on the same
   element, so the hover rule has to win only once the entrance has finished. */
[data-reveal-media].is-in:hover img { transform: scale(1.05); }

.no-js [data-reveal],
.no-js [data-reveal-media] img { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  [data-reveal], [data-reveal].is-in { opacity: 1; transform: none; transition: none; }
  [data-reveal-media] img { transform: none; transition: none; }
  .btn-swap span { transition: none; }
}

/* ==========================================================================
   ROSE GOLD TRIAL (option A) — pink → gold gradient
   --------------------------------------------------------------------------
   TO REVERT TO THE SOLID BROWN: delete this entire block. Nothing above
   depends on it, and no HTML references it.

   Applied only to the h1, the primary buttons and the footer. Body copy,
   section headings, borders and labels stay solid --ink, because gradient
   text needs background-clip, which cannot be read by assistive tech and
   turns to mush below display sizes.
   ========================================================================== */

:root {
  --grad-rose: linear-gradient(115deg, #B76E79 0%, #C98F7A 45%, #D9A066 100%);
}

/* Everything below is scoped to [data-theme="rose"], so BROWN IS THE DEFAULT.
   The theme attribute is set on <html> by the switcher (see .theme-switch
   below and initThemeSwitch in site.js). With the switcher removed and no
   attribute present, the site renders exactly as it did before. */

/* Gradient headline. The colour declaration is the fallback: if a browser
   lacks background-clip:text it simply renders solid rose rather than
   invisible text, which is the failure mode worth guarding against. */
:root[data-theme="rose"] h1 {
  color: #B76E79;
  background-image: var(--grad-rose);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Primary CTA */
:root[data-theme="rose"] .btn-solid {
  background-color: #B76E79;
  background-image: var(--grad-rose);
}
:root[data-theme="rose"] .btn-solid:hover {
  background-color: #A85F6A;
  background-image: var(--grad-rose);
  filter: brightness(0.93);
}

/* Footer block */
:root[data-theme="rose"] .site-footer {
  background-color: #B76E79;
  background-image: var(--grad-rose);
}

/* --------------------------------------------------------------------------
   Client-demo theme switcher — TEMPORARY, REMOVE BEFORE FINAL HANDOVER.
   Delete this rule, the initThemeSwitch call in site.js, and the small
   theme-restore <script> in each page's <head>.

   Sits above the grain overlay (z-index 500) or the noise would wash it out.
   -------------------------------------------------------------------------- */
.theme-switch {
  position: fixed;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 600;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  border-radius: 999px;
  background: rgba(250, 245, 240, 0.92);
  backdrop-filter: blur(8px);
  border: 1px solid var(--hairline);
  box-shadow: 0 8px 24px -12px rgba(92, 51, 39, 0.5);
}
.theme-switch button {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
  font: inherit;
  text-indent: -9999px;
  overflow: hidden;
  transition: transform 0.2s ease, border-color 0.2s ease;
}
.theme-switch button:hover { transform: scale(1.08); }
.theme-switch button[aria-pressed="true"] { border-color: var(--ink); }
.theme-switch .sw-brown { background: #5c3327; }
.theme-switch .sw-rose { background: linear-gradient(135deg, #B76E79, #D9A066); }
.theme-switch .sw-label {
  font-size: 8px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  color: var(--ink-70);
  text-indent: 0;
}
@media (max-width: 640px) {
  .theme-switch { left: 10px; padding: 6px; }
  .theme-switch button { width: 28px; height: 28px; }
}
