/* ============ SOORI STUDIO ============ */
:root {
  --bg: #fbe9e2;
  --bg-deep: #f9ddd3;
  --ink: #3a2c26;
  --ink-soft: #7a655b;
  --cream: #fffaf5;
  --navy: #1f3a5f;
  --red: #c0392b;
  --gold: #c9a15f;
  /* Deeper gold for anything carrying white text — plain --gold only hits
     2.4:1 against white, which fails WCAG AA. This one measures 5.0:1. */
  --gold-deep: #8a6a35;
  --radius: 18px;
  --font-display: "Playfair Display", Georgia, "Times New Roman", serif;
  --font-body: "Avenir Next", "Segoe UI", -apple-system, Helvetica, sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--ink);
  line-height: 1.6;
  overflow-x: hidden;
}
img, canvas { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

h1, h2, h3 { font-family: var(--font-display); font-weight: 600; line-height: 1.15; }
h1 { font-size: clamp(2.6rem, 7vw, 5.2rem); }
h2 { font-size: clamp(1.9rem, 4.4vw, 3.2rem); }

.wrap { max-width: 1140px; margin: 0 auto; padding: 0 24px; }
.wrap-narrow { max-width: 760px; }
.section { padding: 110px 0; }

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.28em;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--red);
  margin-bottom: 14px;
}

/* ---- Logo (the PNG has generous padding built in, so crop via height) ---- */
.brand-logo { height: 124px; margin: -36px 0 -34px -18px; object-fit: contain; }
.loader-img { height: 150px; object-fit: contain; }
.footer-logo { height: 110px; margin: -26px 0 -20px; object-fit: contain; }

/* ---- Buttons ---- */
.btn {
  display: inline-block;
  /* <button> doesn't inherit these by default — set so button and anchor
     variants of .btn render identically */
  font-family: inherit;
  line-height: 1.6;
  background: var(--navy);
  color: #fff;
  padding: 13px 30px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  border: 2px solid var(--navy);
  cursor: pointer;
  transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(31,58,95,0.28); }
.btn-outline { background: transparent; color: var(--navy); }
.btn-outline:hover { background: var(--navy); color: #fff; }
.btn-light { background: var(--cream); color: var(--ink); border-color: var(--cream); }
.btn-small { padding: 9px 22px; font-size: 0.85rem; }

/* ============ LOADER ============ */
.loader {
  position: fixed; inset: 0; z-index: 100;
  background: #fdfdfd; /* matches the hero it uncovers — no pink-to-white flash */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px;
  transition: opacity 0.6s ease, visibility 0.6s ease;
}
.loader.done { opacity: 0; visibility: hidden; }
.loader-logo { display: flex; align-items: center; gap: 12px; }
.loader-word { font-family: var(--font-display); font-size: 1.4rem; letter-spacing: 0.35em; }
.loader-bar { width: 220px; height: 3px; background: #e8d2c5; border-radius: 3px; overflow: hidden; }
.loader-fill { height: 100%; width: 0%; background: var(--navy); transition: width 0.2s ease; }
/* Fixed width + tabular digits: the counter runs 0% -> 100% while centred
   in a flex column, so a width change re-centres it -- Chrome's layout-shift
   metric counted that against the page. Now nothing moves as it ticks. */
.loader-pct { font-size: 0.75rem; letter-spacing: 0.2em; color: var(--ink-soft);
  min-width: 4em; text-align: center; font-variant-numeric: tabular-nums; }

/* ============ NAV ============ */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 34px;
  transition: background 0.4s ease, box-shadow 0.4s ease, padding 0.4s ease;
}
.nav.scrolled {
  background: rgba(255, 250, 245, 0.88);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 2px 20px rgba(58, 44, 38, 0.08);
  padding: 12px 34px;
}
.brand { display: flex; align-items: center; gap: 11px; }
.brand-name { font-family: var(--font-display); font-size: 1.05rem; letter-spacing: 0.22em; font-weight: 700; }
.brand-name em { font-style: normal; font-weight: 400; font-size: 0.7em; letter-spacing: 0.3em; color: var(--ink-soft); }
.nav-links { display: flex; align-items: center; gap: 30px; }
.nav-links a:not(.btn) { font-size: 0.9rem; font-weight: 500; letter-spacing: 0.05em; position: relative; }
.nav-links a:not(.btn)::after {
  content: ""; position: absolute; left: 0; bottom: -5px; height: 2px; width: 0;
  background: var(--red); transition: width 0.3s ease;
}
.nav-links a:not(.btn):hover::after { width: 100%; }

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

/* ---- Music toggle ---- */
.sound-toggle {
  position: relative;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border: 1px solid rgba(31, 58, 95, 0.16);
  border-radius: 50%;
  background: rgba(255, 253, 250, 0.6);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--navy);
  cursor: pointer;
  transition: background 0.25s ease, transform 0.25s ease, border-color 0.25s ease;
}
/* The circle stays 38px visually, but the tap area extends to the 44px
   accessibility minimum. Invisible, and costs no layout space. The inset is
   -4px rather than -3px because this box is positioned against the padding
   box, so the 1px border has to be paid back too. */
.sound-toggle::before {
  content: "";
  position: absolute; inset: -4px;
  border-radius: 50%;
}
.sound-toggle:hover { background: #fff; transform: scale(1.06); border-color: rgba(31, 58, 95, 0.32); }
.sound-toggle svg { width: 17px; height: 17px; }
/* Default (paused): show the slash, hide the waves */
.sound-toggle .wave { opacity: 0; transition: opacity 0.25s ease; }
.sound-toggle .wave-off { opacity: 0.75; transition: opacity 0.25s ease; }
/* Playing: waves gently pulse, slash hidden */
.sound-toggle.playing .wave-off { opacity: 0; }
.sound-toggle.playing .wave-1 { opacity: 1; animation: sonar 2.4s ease-in-out infinite; }
.sound-toggle.playing .wave-2 { opacity: 1; animation: sonar 2.4s ease-in-out 0.3s infinite; }
@keyframes sonar { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }

.nav-burger { display: none; background: none; border: none; cursor: pointer; padding: 6px; position: relative; }
.nav-burger span { display: block; width: 24px; height: 2px; background: var(--ink); margin: 5px 0; transition: 0.3s; }
/* Bars stay where they are; the tap area grows to 44x44 around them. The
   button is only 36x38 on its own, under the touch-target minimum. */
.nav-burger::before {
  content: "";
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 44px; height: 44px;
}

.mobile-menu {
  position: fixed; inset: 0; z-index: 49;
  background: var(--cream);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 34px;
  font-size: 1.4rem; font-family: var(--font-display);
  opacity: 0; visibility: hidden; transition: 0.35s ease;
}
.mobile-menu.open { opacity: 1; visibility: visible; }

/* ============ SCRUB HERO ============ */
/* background: pure white, matching both the canvas backdrop and the room
   render's own background, so the studio floats with no visible seam and
   the scale-down/fade at the scene change dissolves into the page rather
   than revealing the pink body background as a hard band.

   height: sets how much real scrolling each phase fraction in main.js
   (BLINDS_END / EXIT_START) is worth — tune the two together. At 400vh the
   blinds get ~165vh, leaving ~120vh of held final frame for hide-and-seek. */
.scrub { height: 400vh; position: relative; background: #fff; }
.scrub-sticky {
  position: sticky; top: 0; height: 100vh; height: 100svh; overflow: hidden;
  transform-origin: center center; will-change: transform, opacity;
}
/* Press-and-hold is a gameplay gesture in the hero (holding and dragging
   moves the reveal spotlight around the room) -- but iOS Safari reads a
   stationary long-press on a link or image as "preview this link" and
   pops its peek sheet / context menu over the page, hijacking the reveal
   (real-device report: holding near a hotspot label like BOOK NOW opened
   Safari's link preview instead). -webkit-touch-callout kills that
   long-press callout, user-select stops the text-selection magnifier from
   grabbing the stage headings mid-hold. Scoped strictly to the hero;
   quick taps on the hotspots / Welcome In button still navigate fine,
   and text everywhere else on the page stays selectable. */
.scrub-sticky, .scrub-sticky * {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
#scrubCanvas { width: 100%; height: 100%; object-fit: cover; }
#revealCanvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.7s ease;
  /* Spotlight mask — starts offscreen until the cursor moves in */
  -webkit-mask-image: radial-gradient(circle 90px at -999px -999px, #fff 0%, #fff 40%, rgba(255,255,255,0.75) 60%, rgba(255,255,255,0.4) 75%, rgba(255,255,255,0.12) 88%, transparent 100%);
  mask-image: radial-gradient(circle 90px at -999px -999px, #fff 0%, #fff 40%, rgba(255,255,255,0.75) 60%, rgba(255,255,255,0.4) 75%, rgba(255,255,255,0.12) 88%, transparent 100%);
}
/* Vignette removed — the backdrop is flat white now, so a white radial
   fade over it did nothing but tint the studio's own edges. */

.scrub-stage {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 0 24px;
  opacity: 0; visibility: hidden;
  transform: translateY(14px);
  transition: opacity 0.6s ease, visibility 0.6s ease, transform 0.6s ease;
  pointer-events: none;
}
.scrub-stage.active { opacity: 1; visibility: visible; transform: none; }
.scrub-stage .btn { pointer-events: auto; }

/* Stage 1 — split headline that parts to the sides as you scroll */
.stage-1 .eyebrow { position: absolute; top: 13%; left: 50%; transform: translateX(-50%); white-space: nowrap; }
.hl {
  position: absolute; margin: 0;
  font-size: clamp(2.6rem, 5.6vw, 5.6rem);
  color: var(--navy);
  will-change: transform, opacity;
}
.hl-left { left: 4.5vw; top: 32%; text-align: left; }
.hl-right { right: 4.5vw; bottom: 26%; text-align: right; }
.stage-1 .scroll-hint { position: absolute; bottom: 7%; left: 50%; transform: translateX(-50%); margin: 0; }

/* Stages 2 & 3 — anchored to the sides to balance the studio */
.stage-2 { align-items: flex-start; text-align: left; }
.stage-2 .stage-block { margin-left: 4.5vw; max-width: 34vw; }
.stage-2 p { color: var(--ink-soft); font-size: 1.02rem; margin-top: 14px; }
.stage-3 { align-items: flex-end; text-align: right; }
.stage-3 .stage-block { margin-right: 4.5vw; max-width: 36vw; display: flex; flex-direction: column; align-items: flex-end; }
.scrub-stage h1, .scrub-stage h2 {
  color: var(--navy);
  text-shadow: 0 0 14px rgba(255, 248, 243, 0.95), 0 2px 34px rgba(255, 248, 243, 0.95), 0 0 70px rgba(255, 248, 243, 0.9);
}
.stage-2 h2, .stage-3 h2 { margin-bottom: 14px; }
.stage-3 .btn { background: var(--navy); color: #fff; border-color: var(--navy); margin-top: 10px; }
.stage-2 p { color: var(--ink-soft); font-size: 1.05rem; }
.stage-3 .btn { margin-top: 22px; pointer-events: auto; }

/* ---- Text-stacked mode -----------------------------------------------
   Applied via JS (layoutStageText) whenever the room's measured side
   margin is too narrow for the corner/side-anchored layout above — e.g.
   landscape tablets, where the room's contain-fit box nearly fills the
   viewport width. Text moves to sit centered in the open space above and
   below the room instead, guaranteed clear by roomFit's own margin math.
   Every offset here is in PX, never %, because a percentage of a tall
   viewport grows large enough to collide with the neighbouring element
   (the eyebrow crashed into the headline, the scroll hint into the quote). */
.text-stacked .stage-1 .eyebrow { top: 100px; }
.text-stacked .hl {
  position: absolute; left: 50%; right: auto;
  transform: translateX(-50%);
  text-align: center;
  font-size: clamp(1.6rem, 3.2vw, 2.5rem);
  max-width: 90vw;
}
.text-stacked .hl-left { top: 130px; }
.text-stacked .hl-right { bottom: 68px; }
.text-stacked .stage-1 .scroll-hint { bottom: 14px; }

.text-stacked .stage-2,
.text-stacked .stage-3 {
  align-items: center; text-align: center;
  padding-left: 24px; padding-right: 24px;
}
/* Each band is bounded by the nav above and the room below (and vice versa),
   with the content centred inside — so it can never spill onto the room no
   matter how the copy wraps. --room-top/--room-bottom come from JS. */
/* Anchored to the top of its band, mirroring stage-3's bottom anchoring. The
   gap between the nav and the room can collapse to almost nothing on a short
   viewport, and centring taller copy in a short band pushed the heading up
   underneath the nav bar. Growing downward instead keeps the first line
   pinned clear of the nav and lets any overflow fall onto the room. */
.text-stacked .stage-2 {
  top: 96px; bottom: auto;
  height: calc(var(--room-top, 230px) - 104px);
  justify-content: flex-start; padding-top: 0; padding-bottom: 0;
}
/* Anchored to the bottom rather than centred in the band. On a short
   viewport the room leaves less than the copy needs, and centring split the
   overflow evenly — so the bottom half ran past the sticky's overflow:hidden
   and sliced the "Welcome In" button in two. Growing upward instead lets the
   text overlap the room (harmless, it's a pale graphic) while the CTA always
   keeps its full height and a real gap from the edge. */
.text-stacked .stage-3 {
  top: calc(var(--room-bottom, 60%) + 10px); bottom: 0;
  justify-content: flex-end; padding-top: 0;
  padding-bottom: calc(28px + env(safe-area-inset-bottom, 0px));
}
.text-stacked .stage-2 .stage-block,
.text-stacked .stage-3 .stage-block {
  max-width: min(560px, 88vw); margin-left: 0; margin-right: 0;
  align-items: center;
}
.text-stacked .stage-2 h2, .text-stacked .stage-3 h2 { text-align: center; font-size: clamp(1.2rem, 2.6vw, 1.75rem); }
.text-stacked .stage-2 p { text-align: center; font-size: 0.82rem; margin-top: 6px; line-height: 1.45; }
.text-stacked .stage-3 .btn { margin-top: 14px; }

/* Reveal mode: spotlight + hotspots switch on mid-way through the blinds */
.reveal-on #revealCanvas { opacity: 1; }
.hotspots { position: absolute; inset: 0; pointer-events: none; }
/* The anchor centers on the dot ALONE (translate -50%,-50% sizes to the
   13px dot, nothing else) so it lands exactly on the animal regardless of
   label width. The label is an absolutely-positioned child floated above
   it — it can't affect the anchor's centering math, so it never drifts
   over the animal's face the way an inline flex layout did. */
.hotspot {
  position: absolute; transform: translate(-50%, -50%);
  opacity: 0; visibility: hidden;
  transition: opacity 0.6s ease 0.2s, visibility 0.6s ease 0.2s;
  pointer-events: none;
}
.reveal-on .hotspot { opacity: 1; visibility: visible; pointer-events: auto; }
.hotspot i {
  position: relative;
  display: block;
  /* Small on purpose, same size on every screen — reads as a quiet hint
     rather than a bold marker, especially now that mobile/tablet dots sit
     directly on the animal instead of beside it. */
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--red);
  outline: 1px solid rgba(255,255,255,0.95);
  outline-offset: 1.5px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.3);
  animation: hotspot-blink 1.3s ease-in-out infinite;
  flex: none;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
/* Expanding ring pulse, layered behind the dot — reads as a "ping" so the
   spot is findable even in peripheral vision, not just close up. */
.hotspot i::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 1px solid var(--red);
  animation: hotspot-ping 1.3s ease-out infinite;
}
/* Hovering/focusing a hotspot gets the dot out of the way entirely, so the
   animal it was pointing at is finally unobstructed — the label takes over
   as the thing you look at and click. */
@media (hover: hover) {
  .hotspot:hover i, .hotspot:focus-visible i {
    opacity: 0; transform: scale(0.4); animation: none;
  }
  .hotspot:hover i::after, .hotspot:focus-visible i::after { animation: none; opacity: 0; }
}
@keyframes hotspot-blink {
  0%, 100% { opacity: 0.6; transform: scale(0.9); }
  50% { opacity: 1; transform: scale(1.15); }
}
@keyframes hotspot-ping {
  0% { transform: scale(1); opacity: 0.7; }
  100% { transform: scale(2.4); opacity: 0; }
}
/* "How to play" hint for the reveal: fades in with the hotspots, floats
   gently, and is dismissed by main.js on first spotlight use. Never
   interactive — must not steal the touch drag that drives the reveal. */
.reveal-hint {
  position: absolute; left: 50%; top: 90%;
  transform: translate(-50%, -50%) translateY(6px);
  display: flex; align-items: center; gap: 6px;
  background: rgba(255, 253, 250, 0.6);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(31, 58, 95, 0.1);
  color: var(--navy);
  font-size: 0.68rem; font-weight: 500; letter-spacing: 0.05em;
  padding: 6px 14px; border-radius: 999px;
  box-shadow: 0 4px 14px rgba(31, 58, 95, 0.1);
  opacity: 0; visibility: hidden;
  transition: opacity 0.5s ease, visibility 0.5s ease, transform 0.5s ease;
  pointer-events: none;
  white-space: nowrap;
  z-index: 5;
}
.reveal-hint span { font-size: 0.85em; }
.reveal-hint em { font-style: normal; }
.reveal-hint.show {
  opacity: 0.85; visibility: visible;
  transform: translate(-50%, -50%);
  animation: hint-float 3.2s ease-in-out infinite;
}
@keyframes hint-float {
  0%, 100% { transform: translate(-50%, -50%) translateY(0); }
  50% { transform: translate(-50%, -50%) translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
  .reveal-hint.show { animation: none; }
}
/* Bottom placement collides with the stage-3 CTA, which is bottom-anchored
   whenever the layout is stacked — not just under 640px, which is why the
   old width-based breakpoint missed the 641-820px range and let the pill
   land on top of "Welcome In". Tied to the stacked layout itself now.
   Sits just above the room graphic's own top edge (--room-top, set live by
   main.js's roomFit) rather than a flat offset -- a fixed 104px cleared the
   nav fine but left a large disconnected gap above the room on most phones
   (--room-top routinely lands 200-300px+ down), reading like the hint
   belonged to nothing. The max() floor keeps the old nav-clearance
   guarantee on short viewports where --room-top comes out small. */
.text-stacked .reveal-hint { top: max(110px, calc(var(--room-top, 230px) - 36px)); }
@media (max-width: 640px) {
  .reveal-hint { font-size: 0.62rem; padding: 5px 12px; }
}
.hotspot span {
  position: absolute; left: 50%; bottom: 100%;
  transform: translateX(-50%) translateY(-2px);
  background: rgba(255, 253, 250, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(31,58,95,0.14);
  color: var(--navy);
  font-size: 0.72rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase;
  padding: 4px 12px; border-radius: 999px;
  margin-bottom: 8px; /* gap above the dot */
  opacity: 0;
  transition: opacity 0.3s ease, transform 0.3s ease;
  white-space: nowrap;
  /* Inert until revealed. An opacity:0 element is still hit-testable, and a
     always-live label box floating above every dot would eat drags meant for
     the spotlight. */
  pointer-events: none;
}
/* Invisible bridge spanning the gap between the dot and the label. Without
   it, moving the pointer off the dot toward the text crosses dead space,
   :hover drops, and the label disappears just before it can be clicked. */
.hotspot span::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 100%;
  height: 18px;
}
/* Hover/focus only where hovering is real — on touch, sticky :hover would
   leave labels stranded open after a tap. */
@media (hover: hover) {
  .hotspot:hover span, .hotspot:focus-visible span {
    opacity: 1; transform: translateX(-50%) translateY(-6px);
    pointer-events: auto;
  }
}
.hotspot.show-label span {
  opacity: 1; transform: translateX(-50%) translateY(-6px);
  pointer-events: auto;
}
/* Touch equivalent of the hover state: the first tap retires the dot and
   puts the label up, the second tap on that label follows the link. */
.hotspot.show-label i { opacity: 0; transform: scale(0.4); animation: none; }
.hotspot.show-label i::after { animation: none; opacity: 0; }

.scroll-hint { margin-top: 40px; width: 26px; height: 44px; border: 2px solid var(--ink-soft); border-radius: 14px; display: flex; justify-content: center; }
.scroll-hint span { width: 4px; height: 9px; background: var(--ink-soft); border-radius: 3px; margin-top: 7px; animation: hint 1.6s ease infinite; }
@keyframes hint { 0% { transform: translateY(0); opacity: 1; } 70% { transform: translateY(16px); opacity: 0; } 100% { opacity: 0; } }

/* ============ MARQUEE ============ */
.marquee {
  background: var(--cream);
  border-top: 1px solid #f0ded2;
  border-bottom: 1px solid #f0ded2;
  overflow: hidden;
  padding: 16px 0;
  position: relative;
  z-index: 2;
}
.marquee-track {
  display: flex;
  width: max-content;
  animation: marquee 32s linear infinite;
}
.marquee-group {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  white-space: nowrap;
}
.marquee-group span {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.05rem;
  letter-spacing: 0.04em;
  color: var(--ink);
  padding: 0 0.6em;
}
.marquee-group span:nth-child(even) { color: var(--red); font-style: normal; padding: 0 0.9em; }
@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ============ FILM ============ */
.film { background: var(--cream); padding: 64px 24px; display: flex; justify-content: center; }
.film-frame {
  width: min(1040px, 100%);
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg);
  box-shadow: 0 18px 50px rgba(58,44,38,0.12);
}
.film-frame video { width: 100%; height: 100%; object-fit: cover; display: block; }
.film-placeholder {
  width: 100%; height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.film-logo { height: 150px; object-fit: contain; }
.film-placeholder p {
  margin-top: -10px;
  font-size: 0.8rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--ink-soft);
}

/* ============ ABOUT ============ */
.about { background: var(--cream); }
/* Single flowing column (not the old 2-col grid, which was built for
   exactly 2 short blurbs) -- narrower than the section's own .wrap so
   longer-form prose stays at a comfortable line length. */
.about-body { max-width: 640px; margin-top: 36px; color: var(--ink-soft); font-size: 1.05rem; line-height: 1.75; }
.about-body p { margin: 0 0 1.3em; }
.about-tagline { color: var(--ink); font-weight: 600; line-height: 1.6; }
.about-quote {
  font-family: var(--font-display); font-style: italic;
  color: var(--navy); font-size: 1.3rem; margin-top: 0.3em;
}

/* ============ SERVICES ============ */
.services { background: var(--bg); position: relative; overflow: hidden; isolation: isolate; }
.services-bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.services-overlay {
  position: absolute; inset: 0; z-index: 0;
  /* Cream wash keeps the cards and copy readable over the meadow footage,
     lighter at the top (near the heading) and denser toward the cards.
     Reaches fully opaque by the very bottom (was capped at 0.85) so the
     video dissolves completely into cream before .quote-band's own photo
     begins right below -- previously a sliver of green video was still
     visible at this section's bottom edge, colliding hard with the quote
     photo's busier top edge instead of handing off through a shared calm
     band. */
  background: linear-gradient(180deg,
    rgba(255,250,246,0.55) 0%,
    rgba(255,250,246,0.72) 30%,
    rgba(255,250,246,0.85) 85%,
    rgba(255,250,246,1) 100%);
}
.services .wrap { position: relative; z-index: 1; }
.services-helper {
  margin-top: 18px; max-width: 620px;
  color: var(--ink-soft); font-size: 0.98rem; line-height: 1.6;
}
.services-helper strong { color: var(--navy); }
/* One choice at the top of Services swaps every card's price, duration and
   booking link — so party size is decided once, not per card. */
.party-toggle {
  display: inline-flex; gap: 6px; margin-top: 24px;
  background: rgba(255, 255, 255, 0.65);
  border: 1px solid rgba(31, 58, 95, 0.15);
  border-radius: 999px; padding: 5px;
}
.party-btn {
  font-family: inherit;
  font-size: 0.76rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-soft);
  background: transparent; border: none; cursor: pointer;
  padding: 9px 20px; border-radius: 999px;
  transition: background 0.22s ease, color 0.22s ease;
}
.party-btn.active { background: var(--navy); color: #fff; }
/* Nudge toward the 2-people option — easy to miss since 1 Person is the
   default active state. A small gold pulse draws the eye without being
   a whole redesign; disappears once 2 People is actually selected. */
.party-btn[data-party-set="2"] { position: relative; }
.party-btn[data-party-set="2"]:not(.active)::after {
  content: "";
  position: absolute; top: 2px; right: 4px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--gold);
  animation: party-ping 2.2s ease-out infinite;
}
@keyframes party-ping {
  0%   { box-shadow: 0 0 0 0 rgba(201, 161, 95, 0.6); }
  70%  { box-shadow: 0 0 0 9px rgba(201, 161, 95, 0); }
  100% { box-shadow: 0 0 0 0 rgba(201, 161, 95, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .party-btn[data-party-set="2"]:not(.active)::after { animation: none; }
}
.party-note {
  margin-top: 10px; font-size: 0.85rem; color: var(--ink-soft);
}
.card .btn[hidden] { display: none; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 24px; margin-top: 46px; }
/* Group subheadings inside Services: split "Personal Color Sessions" from
   "Makeup Class" so the class reads as related-but-distinct, not a 5th tier. */
.services-group {
  margin-top: 42px;
  font-size: 0.8rem; font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--navy);
  display: flex; align-items: center; gap: 14px;
}
.services-group::after {
  content: ""; flex: 1; height: 1px;
  background: rgba(31, 58, 95, 0.18);
}
.services-group + .cards { margin-top: 24px; }
/* A one-card group: same card width as its grid siblings, not a full-row
   stretch. */
.cards-single { grid-template-columns: minmax(240px, 350px); }
.card {
  background: var(--cream);
  border-radius: var(--radius);
  padding: 36px 28px;
  display: flex; flex-direction: column; gap: 10px;
  box-shadow: 0 6px 24px rgba(58,44,38,0.07);
  transition: transform 0.35s ease, box-shadow 0.35s ease;
  position: relative;
}
.card:hover { transform: translateY(-8px); box-shadow: 0 18px 40px rgba(58,44,38,0.14); }
.card h3 { font-size: 1.45rem; }
.card-size { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.14em; color: var(--ink-soft); }
.card-price { font-size: 1.7rem; font-weight: 700; color: var(--navy); font-family: var(--font-display); }
.card-desc { color: var(--ink-soft); font-size: 0.95rem; flex: 1; }
.card .btn { text-align: center; margin-top: 8px; }
.card-featured { border: 2px solid var(--gold); }
.card-badge {
  position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
  background: var(--gold-deep); color: #fff; font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  padding: 5px 16px; border-radius: 999px; white-space: nowrap;
}
.card-badge-muted {
  position: static; transform: none; align-self: flex-start;
  background: rgba(31, 58, 95, 0.08); color: var(--navy);
  border: 1px solid rgba(31, 58, 95, 0.18);
  margin-bottom: 2px;
}

/* ============ QUOTE BAND ============ */
.quote-band {
  position: relative;
  background: #fbe6de; color: var(--navy);
  text-align: center; padding: 130px 24px;
  overflow: hidden;
  isolation: isolate;
}
.quote-bg {
  position: absolute; inset: 0;
  background-position: center; background-size: cover; background-repeat: no-repeat;
  transform: scale(1.04); /* hides edge shimmer from the overlay blur */
}
.quote-overlay {
  position: absolute; inset: 0;
  /* Near-solid cream at BOTH edges (was 0.72 at each, still letting the
     photo read clearly right up against the neighboring section) so it
     continues the dissolve .services-overlay finishes on above, and fully
     resolves to cream again before .faq's flat background begins below --
     the photo only really shows through the middle third, where the quote
     text sits. */
  background: linear-gradient(180deg,
    rgba(255,250,246,0.94) 0%,
    rgba(255,250,246,0.5) 22%,
    rgba(255,250,246,0.5) 55%,
    rgba(255,250,246,0.94) 100%);
  backdrop-filter: blur(2.5px);
  -webkit-backdrop-filter: blur(2.5px);
}
.quote-content { position: relative; z-index: 1; }
.quote-band h2 {
  font-style: italic; font-weight: 500; max-width: 820px; margin: 0 auto;
  text-shadow: 0 2px 24px rgba(255,250,246,0.6);
}
.quote-brand {
  margin-top: 18px;
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.3em;
  color: var(--navy);
}

/* ============ FAQ ============ */
.faq { background: var(--cream); }
.faq details {
  border-bottom: 1px solid #eadbce;
  padding: 20px 4px;
}
.faq summary {
  font-weight: 600; font-size: 1.05rem; cursor: pointer; list-style: none;
  display: flex; justify-content: space-between; align-items: center;
}
.faq summary::after { content: "+"; font-size: 1.4rem; color: var(--red); transition: transform 0.3s ease; }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq details p { margin-top: 12px; color: var(--ink-soft); }
.faq h2 { margin-bottom: 26px; }

/* ============ MAP + REVIEWS ============ */
.map-reviews { background: var(--cream); }
/* align-items: start (not stretch) so the reviews card sizes to its own
   minimal content instead of being stretched tall to match the map. */
.map-reviews-grid { display: grid; grid-template-columns: 1.3fr 1fr; gap: 32px; align-items: start; }
.map-frame {
  border-radius: var(--radius);
  overflow: hidden;
  min-height: 340px;
  box-shadow: 0 10px 30px rgba(58,44,38,0.1);
}
.map-frame iframe { width: 100%; height: 100%; min-height: 340px; border: 0; display: block; }
/* Minimal, compact, flat — no gradient or corner glow. Sized to its own
   content (see align-items: start on the grid above) rather than stretched
   to match the taller map beside it. */
.reviews-card {
  background: var(--bg);
  border-radius: var(--radius);
  padding: 26px 26px 28px;
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 8px;
}
/* Source badge — the Google mark labels the source instead of floating
   alone as the card's main subject. */
.rc-badge {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-soft);
}
.google-g { width: 15px; height: 15px; flex: none; }
.reviews-card h3 {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--navy);
  margin-top: 6px; line-height: 1.15;
}
.reviews-card .rc-copy {
  color: var(--ink-soft); font-size: 0.9rem; line-height: 1.55;
  max-width: 32ch;
}
.reviews-card .btn {
  margin-top: 10px;
  padding: 10px 22px; font-size: 0.85rem;
}

/* ============ CONTACT ============ */
.contact { background: var(--bg-deep); }
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: start; }
.contact-list { list-style: none; margin-top: 30px; display: grid; gap: 18px; color: var(--ink-soft); font-size: 1.02rem; }
.contact-list a:hover { color: var(--red); }
.contact-form { display: grid; gap: 14px; background: var(--cream); padding: 34px; border-radius: var(--radius); box-shadow: 0 10px 30px rgba(58,44,38,0.1); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.contact-form input, .contact-form textarea {
  font-family: inherit; font-size: 0.95rem;
  padding: 13px 16px; border: 1px solid #e5d3c4; border-radius: 10px;
  background: #fff; color: var(--ink); resize: vertical;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
/* Border colour alone is too quiet to serve as the only focus cue for
   keyboard users — pair it with a soft navy ring. */
.contact-form input:focus, .contact-form textarea:focus {
  outline: none;
  border-color: var(--navy);
  box-shadow: 0 0 0 3px rgba(31, 58, 95, 0.18);
}
.form-status { font-size: 0.9rem; margin: 0; min-height: 1.2em; }
.form-status.is-success { color: #2f6b52; }
.form-status.is-error { color: #b5476b; }
.contact-form button[disabled] { opacity: 0.6; cursor: not-allowed; }


/* ============ QUOTES BAND (real Google reviews) ============ */
/* Bottom padding trimmed from 90 -> 16: .quotes-marquee now carries its own
   78px of bottom padding to contain the card shadow (see below), so this
   only needs to add a little breathing room on top of that, not duplicate
   it — otherwise the gap before the next section would nearly double. */
.quotes-band { background: var(--cream); padding: 20px 0 16px; }
.quotes-head { text-align: center; margin-bottom: 44px; }
.quotes-head h2 { color: var(--navy); font-size: clamp(1.8rem, 4vw, 2.6rem); }
.quotes-head p { color: var(--ink-soft); margin-top: 8px; }
.quotes-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 22px;
}
/* ---- Homepage marquee ----
   The cards drift sideways forever. The mask is a true alpha mask rather
   than a gradient overlay, so the cards dissolve at the edges instead of
   being hard-cut by overflow — and it keeps working whatever sits behind.
   (/reviews/ keeps the static .quotes-grid; none of this touches it.) */
.quotes-marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
  /* The mask only fades left/right — overflow:hidden still clips top and
     bottom with a hard edge. .quote-card's box-shadow (and its bigger hover
     shadow, plus the -4px hover lift) extends past the card's own layout
     box, so without this padding that shadow got sliced flat instead of
     fading out. Sized generously for the hover state's 0 22px 54px shadow. */
  padding: 40px 0 78px;
}
.quotes-track {
  --card-w: 340px;
  --gap: 22px;
  --per-set: 4;   /* cards in one set — the shift below must match exactly */
  display: flex;
  gap: var(--gap);
  width: max-content;
}
.quotes-set { display: flex; gap: var(--gap); }
.quotes-track .quote-card { flex: 0 0 var(--card-w); }
/* Only runs once main.js has appended the clones. Without them a lone set
   would slide off and leave the band empty. */
.quotes-track.is-looping { animation: quotes-drift 45s linear infinite; }
/* Travelling exactly one set's width means the frame it lands on is
   identical to the one it started from — that's what hides the seam. */
@keyframes quotes-drift {
  to { transform: translateX(calc(-1 * var(--per-set) * (var(--card-w) + var(--gap)))); }
}
/* Let a visitor stop it to actually finish reading a review. */
.quotes-marquee:hover .quotes-track.is-looping { animation-play-state: paused; }
/* Quiet text link closing the section — deliberately not a button or its
   own band, just a small tail on the marquee pointing at /reviews/. The
   padding keeps the tap target comfortable without looking bigger. */
.quotes-more { text-align: center; margin-top: 2px; }
.quotes-more a {
  display: inline-block;
  /* 11px, not 10 — at this font size 10px landed the tap target at 43px,
     one short of the 44px minimum. */
  padding: 11px 16px;
  font-size: 0.85rem; font-weight: 600; letter-spacing: 0.04em;
  color: var(--navy);
  border-bottom: 1px solid rgba(31, 58, 95, 0.3);
  transition: border-color 0.25s ease, color 0.25s ease;
}
.quotes-more a:hover { color: var(--red); border-color: var(--red); }
.quotes-more span { display: inline-block; transition: transform 0.25s ease; }
.quotes-more a:hover span { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .quotes-more a:hover span { transform: none; }
}
.quote-card {
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 28px;
  padding: 30px 26px 26px;
  box-shadow: 0 14px 40px rgba(31, 58, 95, 0.08);
  display: flex; flex-direction: column;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.quote-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 22px 54px rgba(31, 58, 95, 0.14);
}
.quote-stars { color: var(--gold); letter-spacing: 2px; font-size: 0.9rem; margin-bottom: 12px; }
.quote-text {
  color: var(--ink);
  font-size: 0.98rem;
  line-height: 1.65;
  flex: 1;
  margin-bottom: 20px;
}
.quote-text::before { content: "\201C"; }
.quote-text::after { content: "\201D"; }
.quote-person { display: flex; align-items: center; gap: 14px; }
/* Wraps the photo in the same /reviews/#name link as the name. Duplicate
   navigation target, so it's aria-hidden/tabindex=-1 in markup — sighted
   pointer users get the bigger tap target, keyboard and screen-reader
   users get exactly one stop (the name). */
.quote-person-photo { display: flex; flex: none; border-radius: 50%; }
.quote-person img {
  width: 76px; height: 76px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid #fff;
  box-shadow: 0 4px 14px rgba(31, 58, 95, 0.2);
}
.quote-person-name {
  display: inline-block;
  font-weight: 600; color: var(--navy); font-size: 1rem;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.quote-person-name:hover, .quote-person-name:focus-visible {
  color: var(--red); border-color: var(--red);
}
.quote-person-season { color: var(--ink-soft); font-size: 0.84rem; }

/* ============ FOOTER ============ */
.footer {
  background: var(--cream);
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 44px 24px; font-size: 0.85rem; color: var(--ink-soft);
}

/* ============ REVEAL ANIMATIONS ============ */
.reveal { opacity: 0; transform: translateY(34px); transition: opacity 0.8s ease, transform 0.8s ease; }
.reveal.in { opacity: 1; transform: none; }

/* ============ RESPONSIVE ============ */
@media (max-width: 820px) {
  /* Sticky rather than the base rule's fixed: at the top of the page this
     lays out exactly like position:static (in normal flow, taking its own
     real height), so none of the page-specific top-padding tuned for that
     static layout (reviews hero, blog hero, blog post template) needs to
     change. It only behaves like a floating fixed nav -- reachable from
     anywhere -- once you scroll past where it would naturally sit.
     History: this was position:fixed originally, changed to static because
     a fixed nav permanently pinned over content while scrolling was judged
     to cost too much screen space on a small phone. The site owner then
     asked for the nav to be reachable while scrolling after all -- sticky
     is what gets that back without reintroducing the fixed-position bug
     (every page needing extra top-padding to clear a nav that's removed
     from flow from the very start) that static was fixed to solve.
     Desktop keeps the base fixed/floating nav unchanged, as before. The
     mobile slide-out menu (.mobile-menu) is styled separately and stays
     position:fixed on its own -- untouched. */
  .nav { position: sticky; top: 0; }
  .nav { padding: 14px 20px; }
  .nav-links { display: none; }
  .nav-burger { display: block; }
  /* Narrower cards on phones. The keyframe reads --card-w, so the shift
     stays exactly one set wide without touching the animation. */
  .quotes-track { --card-w: 270px; }
  .brand-logo { height: 92px; margin: -28px 0 -26px -12px; }
  .contact-grid, .form-row, .map-reviews-grid { grid-template-columns: 1fr; }
  .map-frame, .map-frame iframe { min-height: 260px; }
  .section { padding: 70px 0; }
  /* Same phase split as desktop, scaled down: ~99vh blinds, ~72vh hold. */
  .scrub { height: 280vh; }
  .stage-1 .eyebrow { top: 11%; font-size: 0.62rem; }
  .hl { font-size: clamp(2.2rem, 10vw, 3rem); }
  .hl-left { left: 7vw; top: 21%; }
  .hl-right { right: 7vw; bottom: 20%; }
  /* The room only fills the middle band of the screen on mobile (contain-fit
     is width-limited), leaving open space above and below it — put stage 2
     & 3 text there instead of centered on top of the room. */
  .stage-2, .stage-3 {
    align-items: center; text-align: center;
    padding-left: 24px; padding-right: 24px;
  }
  .stage-2 { justify-content: flex-start; padding-top: 88px; }
  .stage-3 { justify-content: flex-end; padding-bottom: 6%; }
  .stage-2 .stage-block, .stage-3 .stage-block {
    max-width: 82vw; margin-left: 0; margin-right: 0;
    align-items: center;
  }
  .stage-2 h2, .stage-3 h2 { text-align: center; }
  .stage-2 p { text-align: center; }
  /* Dot size is now the same on every screen (see base .hotspot i above) —
     only the label stays touch-specific here. */
  .hotspot span { font-size: 0.6rem; padding: 3px 9px; letter-spacing: 0.08em; }
}

/* ============ FLOATING CONTACT BAR ============
   Bottom-left on every page (markup injected by nav.js). Mechanics adapted
   from a neon-glass reference the user supplied -- expanding stack,
   tooltips, per-platform glow, rotating + -- restyled from deep-black neon
   into the site's own liquid-glass language per their go-ahead: white
   glass + navy at rest, each platform's real brand color on hover. */
.social-bar {
  position: fixed; left: 22px; bottom: 22px;
  /* Under the nav (50) and the mobile menu overlay (49), so opening the
     menu covers this rather than the reverse. */
  z-index: 48;
  display: flex; flex-direction: column; align-items: center;
}
.sb-items {
  display: flex; flex-direction: column;
  gap: 8px; margin-bottom: 10px;
  opacity: 0; visibility: hidden;
  transform: translateY(14px);
  pointer-events: none;
  transition: opacity 0.35s cubic-bezier(0.23, 1, 0.32, 1),
              transform 0.35s cubic-bezier(0.23, 1, 0.32, 1),
              visibility 0.35s;
}
.social-bar.open .sb-items {
  opacity: 1; visibility: visible; transform: none; pointer-events: auto;
}
@media (hover: hover) {
  .social-bar:hover .sb-items {
    opacity: 1; visibility: visible; transform: none; pointer-events: auto;
  }
}
/* Quiet, Silicon-Valley-minimal per user feedback ("too imminent"):
   small circular hairline buttons, muted monochrome icons, no solid navy
   block, no glow -- the platform's brand color only appears as the icon
   tint on hover. Visual footprint stays tiny; the 44px touch-target
   minimum is met by an invisible ::after hit area around each button. */
.sb-btn {
  position: relative;
  width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(58, 44, 38, 0.14);
  box-shadow: 0 1px 4px rgba(58, 44, 38, 0.08);
  color: var(--ink-soft);
  transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
}
.sb-btn::after { content: ""; position: absolute; inset: -4px; border-radius: 50%; }
.sb-btn svg { width: 16px; height: 16px; }
.sb-btn:hover { background: #fff; }
.sb-line:hover  { color: #06c755; border-color: rgba(6, 199, 85, 0.5); }
.sb-insta:hover { color: #e1306c; border-color: rgba(225, 48, 108, 0.5); }
.sb-tiktok:hover{ color: #00a09b; border-color: rgba(0, 160, 155, 0.5); }
.sb-phone:hover { color: var(--navy); border-color: rgba(31, 58, 95, 0.5); }
.sb-trigger {
  width: 40px; height: 40px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(58, 44, 38, 0.14);
  color: var(--ink-soft); cursor: pointer;
  box-shadow: 0 1px 4px rgba(58, 44, 38, 0.08);
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.sb-trigger::after { content: ""; position: absolute; inset: -4px; border-radius: 50%; }
.sb-trigger { position: relative; }
.sb-trigger:hover { background: #fff; color: var(--navy); border-color: rgba(31, 58, 95, 0.35); }
.sb-trigger svg {
  width: 16px; height: 16px;
  transition: transform 0.35s cubic-bezier(0.23, 1, 0.32, 1);
}
.social-bar.open .sb-trigger svg { transform: rotate(135deg); }
@media (hover: hover) {
  .social-bar:hover .sb-trigger svg { transform: rotate(135deg); }
}
/* Tooltips to the right of the stack; hover-capable devices only (on
   touch they'd just flash under the finger). */
.sb-tip {
  display: none;
  position: absolute; left: calc(100% + 10px); top: 50%;
  transform: translateY(-50%) translateX(-6px);
  background: rgba(255, 255, 255, 0.95);
  color: var(--ink); font-size: 0.72rem; font-weight: 500;
  border: 1px solid rgba(58, 44, 38, 0.12);
  box-shadow: 0 2px 8px rgba(58, 44, 38, 0.08);
  padding: 5px 10px; border-radius: 7px; white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
@media (hover: hover) {
  .sb-tip { display: block; }
  .sb-btn:hover .sb-tip { opacity: 1; transform: translateY(-50%); }
}
@media (max-width: 820px) {
  .social-bar { left: 14px; bottom: 14px; }
}
@media (prefers-reduced-motion: reduce) {
  .sb-items, .sb-trigger svg, .sb-btn, .sb-tip { transition: none; }
}

/* ============ BLOG — EDITORIAL (journal listing + article pages) ============
   Design-publication treatment: hairlines and whitespace instead of glass
   cards, serif reading typography, first-line paragraph indents (ย่อหน้า)
   in the Thai print tradition.

   FONTS: Playfair Display (display) + Lora (article body) are imported from
   each blog page's own inline <style> block, NOT from here — style.css is
   shared sitewide, and importing them here would flip the homepage's
   headings (Georgia fallback today) as a side effect. pjax swaps that
   inline block on client-side navigation, so the import re-applies there
   too. --font-reading falls back to Georgia until/unless the import lands. */
:root {
  --font-reading: "Lora", Georgia, "Times New Roman", serif;
  --hairline: rgba(58, 44, 38, 0.16);
  --cat-guide: #1f3a5f;
  --cat-wardrobe: #a1592f;
  --cat-makeup: #a13f60;
  --cat-hair: #2f6b52;
}

/* Small-caps category label, shared by listing cards/rows and post headers.
   Colored per category via the closest [data-cat] ancestor. */
.cat-label {
  font-size: 0.68rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--cat-guide);
}
[data-cat="wardrobe"] .cat-label { color: var(--cat-wardrobe); }
[data-cat="makeup"]   .cat-label { color: var(--cat-makeup); }
[data-cat="hair"]     .cat-label { color: var(--cat-hair); }

/* ---- Journal (listing) ---- */
.journal-head { text-align: center; padding: 9.5rem 0 2.6rem; }
.journal-head h1 { font-size: clamp(3rem, 8vw, 5.2rem); color: var(--ink); }
.journal-sub { color: var(--ink-soft); max-width: 470px; margin: 1.1rem auto 0; line-height: 1.7; }

.journal-filters {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(16px, 3.5vw, 40px);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  max-width: 1140px; margin: 0 auto 4rem; padding: 15px 24px;
}
.filter-link {
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.72rem; letter-spacing: 0.24em; text-transform: uppercase;
  font-weight: 600; color: var(--ink-soft);
  padding: 6px 2px; position: relative;
  transition: color 0.2s ease;
}
.filter-link:hover { color: var(--ink); }
.filter-link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: currentColor;
  transform: scaleX(0); transition: transform 0.25s ease;
}
.filter-link.active { color: var(--ink); }
.filter-link.active::after { transform: scaleX(1); }
.filter-link[data-filter="guide"].active    { color: var(--cat-guide); }
.filter-link[data-filter="wardrobe"].active { color: var(--cat-wardrobe); }
.filter-link[data-filter="makeup"].active   { color: var(--cat-makeup); }
.filter-link[data-filter="hair"].active     { color: var(--cat-hair); }

.feat {
  display: grid; grid-template-columns: 1.12fr 1fr;
  gap: clamp(28px, 4.5vw, 60px); align-items: center;
  margin-bottom: 5.5rem;
}
.feat-media { overflow: hidden; border-radius: 10px; }
.feat-media img {
  width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover;
  display: block; transition: transform 0.5s ease;
}
.feat:hover .feat-media img { transform: scale(1.025); }
.feat-body .cat-label { display: block; margin-bottom: 14px; }
.feat-body h2 { font-size: clamp(1.9rem, 3.6vw, 2.8rem); line-height: 1.12; color: var(--ink); }
.feat-desc { color: var(--ink-soft); line-height: 1.7; margin-top: 16px; max-width: 480px; }
.feat-meta { font-size: 0.8rem; color: var(--ink-soft); margin-top: 18px; }
.feat-cta {
  display: inline-block; margin-top: 22px;
  font-size: 0.74rem; letter-spacing: 0.22em; text-transform: uppercase; font-weight: 600;
  color: var(--navy); border-bottom: 2px solid var(--gold);
  padding-bottom: 4px; transition: border-color 0.25s ease;
}
.feat:hover .feat-cta { border-color: var(--navy); }

.journal-label {
  display: flex; align-items: center; gap: 18px;
  font-size: 0.72rem; letter-spacing: 0.26em; text-transform: uppercase;
  font-weight: 600; color: var(--ink);
  margin-bottom: 2.2rem;
}
.journal-label::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }

.journal-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 44px 32px; margin-bottom: 5.5rem;
}
.j-card { display: block; }
.j-media { overflow: hidden; border-radius: 8px; margin-bottom: 16px; }
.j-media img {
  width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover;
  display: block; transition: transform 0.45s ease;
}
.j-card:hover .j-media img { transform: scale(1.03); }
.j-media-logo {
  aspect-ratio: 4 / 3; display: flex; align-items: center; justify-content: center;
  background: #fff; border: 1px solid rgba(58, 44, 38, 0.08);
}
.j-media-logo img { width: 34%; max-width: 110px; aspect-ratio: auto; object-fit: contain; }
.j-card .cat-label { display: block; margin-bottom: 8px; }
.j-card h3 { font-size: 1.28rem; line-height: 1.28; color: var(--ink); transition: color 0.2s ease; }
.j-card:hover h3 { color: var(--navy); }
.j-desc {
  font-size: 0.9rem; color: var(--ink-soft); line-height: 1.6; margin-top: 8px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.journal-index { margin-bottom: 7rem; border-top: 1px solid var(--hairline); }
.j-row {
  display: grid; grid-template-columns: 108px 1fr;
  gap: 20px; align-items: baseline;
  padding: 17px 4px; border-bottom: 1px solid var(--hairline);
  transition: background 0.2s ease;
}
.j-row:hover { background: rgba(255, 255, 255, 0.6); }
.j-row-title {
  font-family: var(--font-display); font-weight: 600;
  font-size: 1.06rem; line-height: 1.35; color: var(--ink);
  transition: color 0.2s ease;
}
.j-row:hover .j-row-title { color: var(--navy); }

.feat.is-hidden, .j-card.is-hidden, .j-row.is-hidden { display: none; }

@media (max-width: 900px) {
  .feat { grid-template-columns: 1fr; gap: 22px; margin-bottom: 4rem; }
  .feat-desc { max-width: none; }
  .journal-grid { grid-template-columns: repeat(2, 1fr); gap: 36px 24px; }
}
@media (max-width: 820px) {
  /* The nav is sticky in-flow on mobile (see the ≤820px nav rules) — it
     already pushes this masthead down by its own real height, so the 9.5rem
     top padding sized to clear the desktop fixed nav would just stack an
     oversized gap on top of that. */
  .journal-head { padding: 2.8rem 0 2.2rem; }
}
@media (max-width: 600px) {
  .journal-grid { grid-template-columns: 1fr; gap: 34px; }
  .j-row { grid-template-columns: 1fr; row-gap: 4px; padding: 15px 2px; }
}

/* ---- Article pages ---- */
.post { max-width: 712px; padding-top: 9rem; }
@media (max-width: 820px) {
  /* Same sticky-nav reasoning as .journal-head above. */
  .post { padding-top: 2.5rem; }
  /* Side padding on phones. .wrap sets `padding: 0 24px`, but .section
     (declared later at equal specificity) resets it to `70px 0`, so the
     moment the viewport is narrower than the 712px column the text ran
     flush against the screen edge -- every blog post, privacy, 404.
     Desktop never showed it: the column is centred with margin there. */
  .post { padding-left: 22px; padding-right: 22px; }
}

.post-head { text-align: center; margin-bottom: 2.4rem; }
.post-head .cat-label { display: inline-block; margin-bottom: 16px; }
.post-head .cat-label:hover { text-decoration: underline; text-underline-offset: 4px; }
.post-head h1 { font-size: clamp(2.1rem, 5.4vw, 3.3rem); line-height: 1.12; }
.post-meta { font-size: 0.82rem; color: var(--ink-soft); margin-top: 18px; }
.post-head::after {
  content: ""; display: block; width: 52px; height: 1px;
  background: var(--hairline); margin: 2rem auto 0;
}

.post-hero {
  width: 100%; height: auto; display: block;
  border-radius: 10px; margin: 0 0 2.6em;
  box-shadow: 0 12px 34px rgba(58, 44, 38, 0.1);
}
/* Posts with no real photo (logo placeholder or an excluded third-party
   image) get the brand mark on a white card instead of nothing, so every
   post keeps the same visual rhythm rather than jumping straight to text. */
.post-hero-logo {
  height: 200px; display: flex; align-items: center; justify-content: center;
  background: #fff; border: 1px solid rgba(58, 44, 38, 0.08);
}
.post-hero-logo img { width: 42%; max-width: 220px; height: auto; opacity: 0.92; }

/* Reading typography: serif body, generous leading, and a first-line indent
   (ย่อหน้า) opening every paragraph — the Thai/print convention the reading
   experience is built around. Utility paragraphs (meta, quotes, CTA,
   close line) opt back out below. */
.post p {
  font-family: var(--font-reading);
  font-size: 1.06rem; line-height: 1.85;
  margin: 0 0 0.9em; text-indent: 2.4em;
}
.post ul, .post ol {
  font-family: var(--font-reading);
  font-size: 1.02rem; line-height: 1.8;
  margin: 0.2em 0 1.4em; padding-left: 1.5em;
}
.post li { margin-bottom: 0.45em; }
.post p a:not(.btn), .post li a {
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--gold); text-decoration-thickness: 1.5px;
}
.post p a:not(.btn):hover, .post li a:hover { text-decoration-color: var(--navy); }

.post h2 { font-size: clamp(1.5rem, 3.2vw, 1.95rem); line-height: 1.22; margin: 2.4em 0 0.8em; }
.post h2::before {
  content: ""; display: block; width: 44px; height: 2px;
  background: var(--gold); margin-bottom: 20px;
}
.post h3 { font-size: 1.22rem; line-height: 1.3; margin: 1.8em 0 0.6em; }

.post .pull-quote, .post blockquote {
  font-family: var(--font-display); font-style: italic;
  font-size: clamp(1.3rem, 2.7vw, 1.62rem); line-height: 1.5;
  text-align: center; text-indent: 0; color: var(--navy);
  max-width: 26ch; margin: 2.4em auto; padding: 1.5em 0.5em;
  border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline);
}

.post figure { margin: 2.4em 0; }
.post figure img { width: 100%; height: auto; border-radius: 10px; display: block; }
.post figcaption {
  font-family: var(--font-body);
  font-size: 0.78rem; letter-spacing: 0.08em; color: var(--ink-soft);
  text-align: center; margin-top: 12px;
}

.post .post-meta { text-indent: 0; margin-bottom: 0; }
.post .close-line {
  font-family: var(--font-display); font-style: italic;
  font-size: 1.3rem; color: var(--navy);
  text-align: center; text-indent: 0; margin: 3em 0 1.8em;
}
.post .close-line::before {
  content: ""; display: block; width: 44px; height: 2px;
  background: var(--gold); margin: 0 auto 30px;
}
.post .post-cta { text-align: center; text-indent: 0; margin-bottom: 0; }
.post .btn { font-family: var(--font-body); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  /* No drifting for anyone who's asked motion to stop. Falls back to a plain
     centred, wrapping row of the real quotes — the cloned sets are hidden
     so the same reviews aren't repeated across the page. */
  .quotes-track.is-looping { animation: none; }
  .quotes-track { width: auto; flex-wrap: wrap; justify-content: center; }
  .quotes-set { flex-wrap: wrap; justify-content: center; }
  .quotes-set[aria-hidden="true"] { display: none; }
  .quotes-marquee {
    overflow: visible;
    -webkit-mask-image: none; mask-image: none;
  }
}
