/* =========================================================================
   SAGE & CLAY — motion layer               nail-salon-website-03
   =========================================================================
   Loaded after main.css. Everything that moves is described here, so the
   whole site can be made still by removing one <link>. Same rule as the
   rest of the CSS: no colour literals — tokens only (color-mix where a
   translucent version of a token is needed).

   Contents
   00  The reveal engine + variants
   01  Word-mask headlines
   02  Header: scroll progress, condense
   03  Drawer
   04  Buttons, arrows, nav
   05  Photo & card hover life
   06  Parallax
   07  Hero ambience (flourish draw, badge float, open-badge pulse)
   08  Service rows, ritual, values, pricelist
   09  Stories carousel
   10  Lightbox
   11  Mobile call bar
   12  FAQ
   13  Reduced motion & print
   ========================================================================= */

/* ---------- 00 The reveal engine ------------------------------------ */
/* main.css ships a plain fade-up. This replaces it with a directional
   engine driven by four custom properties, so one keyframe-free
   transition covers every variant. Content is visible unless JS has
   armed the animation (.js-ready), and the 3s failsafe in <head> still
   force-shows everything if anything goes wrong. */

.js-ready .reveal {
  opacity: 0;
  transform: translate3d(var(--rv-x, 0), var(--rv-y, 26px), 0) scale(var(--rv-s, 1));
  filter: blur(var(--rv-blur, 0));
  transition:
    opacity   .85s var(--ease-out) var(--d, 0s),
    transform .85s var(--ease-out) var(--d, 0s),
    filter    .85s var(--ease-out) var(--d, 0s);
}
.js-ready .reveal.is-in,
.js-ready.reveal-fallback .reveal {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Variants — set on the element with data-anim */
.js-ready [data-anim="left"]   { --rv-x: -36px; --rv-y: 0; }
.js-ready [data-anim="right"]  { --rv-x: 36px;  --rv-y: 0; }
.js-ready [data-anim="rise"]   { --rv-y: 48px; }
.js-ready [data-anim="zoom"]   { --rv-y: 14px; --rv-s: .93; }
.js-ready [data-anim="soft"]   { --rv-y: 12px; --rv-blur: 7px; }

/* Curtain: the photo wipes up behind its mat while the image settles
   out of a slow zoom. Used on the big editorial images. */
.js-ready .reveal[data-anim="curtain"] {
  opacity: 1;
  transform: none;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.15s var(--ease-out) var(--d, 0s);
}
.js-ready .reveal[data-anim="curtain"].is-in,
.js-ready.reveal-fallback .reveal[data-anim="curtain"] { clip-path: inset(0 0 0 0); }
.js-ready .reveal[data-anim="curtain"] img {
  transform: scale(1.14);
  transition: transform 1.6s var(--ease-out) var(--d, 0s);
}
.js-ready .reveal[data-anim="curtain"].is-in img,
.js-ready.reveal-fallback .reveal[data-anim="curtain"] img { transform: none; }

/* Text variant: the parent stays put and the word masks do the work. */
.js-ready .reveal[data-anim="text"] { opacity: 1; transform: none; filter: none; }

/* ---------- 01 Word-mask headlines ---------------------------------- */
/* JS wraps each word of [data-split] in .word > .word__i. If the split
   never runs the markup is untouched and the heading simply sits there. */

.word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: .09em;
  margin-bottom: -.09em;
}
.word__i {
  display: inline-block;
  transform: translateY(110%);
  transition: transform .95s var(--ease-out);
  transition-delay: calc(var(--i, 0) * .055s + var(--d, 0s));
}
.is-in .word__i,
.reveal-fallback .word__i { transform: none; }

/* ---------- 02 Header ------------------------------------------------ */

.header {
  transition:
    box-shadow var(--dur-fast) var(--ease),
    background var(--dur) var(--ease),
    backdrop-filter var(--dur) var(--ease);
}
.header.is-scrolled {
  background: color-mix(in srgb, var(--c-canvas) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
}
.header__bar { transition: min-height var(--dur) var(--ease); }
.header.is-scrolled .header__bar { min-height: calc(var(--headbar-h) - 10px); }
.brand img { transition: height var(--dur) var(--ease), padding var(--dur) var(--ease); }
.header.is-scrolled .brand img { height: 34px; padding: 5px 8px; }
@media (max-width: 900px) {
  .header.is-scrolled .brand img { height: 26px; }
}

/* Reading progress — built by site.js, sits on the header's bottom rule */
.progress {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: transparent;
  pointer-events: none;
}
.progress__bar {
  height: 100%;
  width: 100%;
  transform: scaleX(var(--p, 0));
  transform-origin: left;
  background: linear-gradient(90deg, var(--c-sage), var(--c-clay));
}

/* ---------- 03 Drawer ------------------------------------------------ */

.drawer {
  display: block;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  border-bottom-color: transparent;
  transition:
    max-height var(--dur) var(--ease),
    opacity var(--dur-fast) var(--ease),
    border-color var(--dur) var(--ease);
}
.drawer.is-open {
  max-height: 32rem;
  opacity: 1;
  border-bottom-color: var(--c-line);
}
.drawer nav a {
  transform: translateY(-8px);
  opacity: 0;
  transition: transform var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
}
.drawer.is-open nav a {
  transform: none;
  opacity: 1;
  transition-delay: calc(var(--i, 0) * .045s + .08s);
}
.navtoggle svg { transition: transform var(--dur-fast) var(--ease); }
.navtoggle:hover svg { transform: rotate(90deg); }
.navtoggle[aria-expanded="true"]:hover svg { transform: rotate(0deg); }

/* ---------- 04 Buttons, arrows, nav ---------------------------------- */

/* A slow sheen crosses the button on hover — the only "shiny" moment
   in the whole design language, so it stays subtle. */
.btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(115deg,
    transparent 32%,
    color-mix(in srgb, var(--c-white) 38%, transparent) 50%,
    transparent 68%);
  transform: translateX(-130%);
  transition: transform .75s var(--ease);
}
.btn:hover::before { transform: translateX(130%); }
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(1px); }

.tlink .arrow, .stories__nav .arrow { transition: transform var(--dur-fast) var(--ease); }
.stories__nav:hover .arrow { transform: translateX(5px); }

.nav a::after { transition: transform var(--dur) var(--ease-out); }
.header__phone { transition: color var(--dur-fast) var(--ease); }

.topline a { position: relative; }
.social a { transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease-out); }
.social a:hover { transform: translateY(-3px); }

.skip { transition: left var(--dur-fast) var(--ease); }

/* ---------- 05 Photo & card hover life -------------------------------- */

.mat { transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); }
.mat--hover img { transition: transform 1.1s var(--ease-out), filter var(--dur) var(--ease); }
.mat--hover:hover img { transform: scale(1.06); filter: var(--photo-grade) brightness(1.04); }
.mat--hover:hover { transform: translateY(-5px); box-shadow: var(--shadow); }

.collage figure .caption { transition: color var(--dur-fast) var(--ease); }
.collage figure:hover .caption { color: var(--c-clay-deep); }

.masonry > figure { cursor: zoom-in; }
.masonry > figure .mat { transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); }
.masonry > figure:hover .mat { transform: translateY(-6px); box-shadow: var(--shadow); }
.masonry figcaption .num { transition: transform var(--dur) var(--ease-out); display: inline-block; }
.masonry > figure:hover figcaption .num { transform: translateX(-4px); }

.value, .infocard {
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease);
}
.value:hover { transform: translateY(-5px); box-shadow: var(--shadow); border-color: var(--c-line-strong); }
.value .leaf { transition: transform var(--dur) var(--ease-out); }
.value:hover .leaf { transform: rotate(-8deg) scale(1.12); }

.pillar .leaf { transition: transform var(--dur) var(--ease-out); }
.pillar:hover .leaf { transform: translateY(-3px) rotate(6deg); }

/* ---------- 06 Parallax ----------------------------------------------- */
/* site.js writes --par (a pixel offset) on every [data-parallax]. */

[data-parallax] { will-change: transform; }
.band__bg[data-parallax] {
  height: 128%;
  top: -14%;
  transform: translate3d(0, var(--par, 0), 0);
}
.hero__media .mat[data-parallax],
.mood .mat[data-parallax] { transform: translate3d(0, var(--par, 0), 0); }
.hero__media .leaf-flourish[data-parallax] { transform: rotate(-8deg) translate3d(0, var(--par, 0), 0); }

/* ---------- 07 Hero ambience ------------------------------------------ */

/* The flourish draws itself in. stroke-dasharray inherits into the
   <use> shadow tree, so no per-path selectors are needed. */
.hero__media .leaf-flourish {
  stroke-dasharray: 220;
  stroke-dashoffset: 220;
}
.js-ready .hero__media.is-in .leaf-flourish,
.js-ready.reveal-fallback .hero__media .leaf-flourish,
html:not(.js-ready) .hero__media .leaf-flourish {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 2.4s var(--ease-out) .3s;
}

.rule-leaf::before, .rule-leaf::after {
  transform: scaleX(0);
  transition: transform 1s var(--ease-out) .1s;
}
.rule-leaf::before { transform-origin: right; }
.rule-leaf::after  { transform-origin: left; }
.is-in .rule-leaf::before, .is-in .rule-leaf::after,
.reveal-fallback .rule-leaf::before, .reveal-fallback .rule-leaf::after,
html:not(.js-ready) .rule-leaf::before, html:not(.js-ready) .rule-leaf::after { transform: scaleX(1); }

@keyframes badge-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
.hero__badge { animation: badge-float 7s ease-in-out 1.2s infinite; }

@keyframes sage-breathe {
  0%, 100% { opacity: .35; transform: scale(1); }
  50%      { opacity: .7;  transform: scale(1.012); }
}
.hero__media::before {
  animation: sage-breathe 9s ease-in-out infinite;
  transform-origin: 60% 40%;
}

/* "Open now" really is live — give the dot a heartbeat. */
@keyframes dot-pulse {
  0%   { box-shadow: 0 0 0 0 currentColor; opacity: 1; }
  70%  { box-shadow: 0 0 0 6px transparent; opacity: .85; }
  100% { box-shadow: 0 0 0 0 transparent; opacity: 1; }
}
.open-badge.is-open::before { animation: dot-pulse 2.6s ease-out infinite; }

/* ---------- 08 Service rows, ritual, values, pricelist ---------------- */

.svcrow a { position: relative; }
.svcrow a::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--c-clay);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform var(--dur) var(--ease-out);
}
.svcrow a:hover::before { transform: scaleY(1); }
.svcrow__thumb img { transition: transform var(--dur) var(--ease-out); }
.svcrow a:hover .svcrow__thumb img { transform: scale(1.09); }
.svcrow__num { transition: color var(--dur-fast) var(--ease), transform var(--dur) var(--ease-out); display: inline-block; }
.svcrow a:hover .svcrow__num { color: var(--c-clay); transform: translateX(2px); }

.ritual__step::before { transition: transform var(--dur) var(--ease-out), opacity var(--dur) var(--ease); }
.ritual__step:hover::before { transform: translateY(-4px); opacity: .8; }

.pricelist li { transition: background var(--dur-fast) var(--ease); }
.pricelist .price { transition: color var(--dur-fast) var(--ease); }
.pricelist li:hover .price { color: var(--c-clay-deep); }

.menurail a { transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), padding-left var(--dur-fast) var(--ease); }
.hours tr { transition: background var(--dur-fast) var(--ease); }

/* ---------- 09 Stories carousel --------------------------------------- */

/* Every quote sits in the same grid cell, so the stage is always as tall as
   the longest story and nothing jumps when one crossfades into the next.
   Stacking with grid rather than position:absolute on purpose — the slides
   are also .quote, and main.css gives `.stories .quote` position:relative,
   which would win the cascade against a plain `.stories__slide` rule. */
.stories__stage { display: grid; align-items: start; }
.stories__slide {
  display: block;
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transform: translateY(16px);
  transition: opacity .55s var(--ease-out), transform .55s var(--ease-out),
              visibility 0s linear .55s;
  pointer-events: none;
}
.stories__slide.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition: opacity .55s var(--ease-out), transform .55s var(--ease-out),
              visibility 0s;
}
.stories__who { transition: opacity var(--dur-fast) var(--ease); }
.stories__who.is-swapping { opacity: 0; }

.stories__dots {
  display: flex;
  justify-content: center;
  gap: .6rem;
  margin: 0 0 1.6rem;
}
.stories__dots button {
  width: 9px; height: 9px;
  padding: 0;
  border: 1px solid var(--c-sage-deep);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease-out);
}
.stories__dots button:hover { transform: scale(1.25); }
.stories__dots button[aria-current="true"] { background: var(--c-clay); border-color: var(--c-clay); }

/* ---------- 10 Lightbox ------------------------------------------------ */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
  background: color-mix(in srgb, var(--c-deep) 88%, transparent);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease), visibility var(--dur) var(--ease);
}
.lightbox.is-open { opacity: 1; visibility: visible; }

.lightbox__fig {
  margin: 0;
  max-width: min(1000px, 100%);
  text-align: center;
  transform: scale(.94) translateY(12px);
  opacity: 0;
  transition: transform .5s var(--ease-out), opacity .5s var(--ease-out);
}
.lightbox.is-open .lightbox__fig { transform: none; opacity: 1; }
.lightbox__fig img {
  max-height: 76vh;
  width: auto;
  margin-inline: auto;
  border-radius: var(--radius);
  background: var(--c-paper);
  padding: var(--mat-pad);
  filter: var(--photo-grade);
  box-shadow: var(--shadow-lg);
}
.lightbox__cap {
  color: var(--c-deep-soft);
  font-size: var(--fs-small);
  letter-spacing: .06em;
  margin-top: 1rem;
}

.lightbox button {
  position: absolute;
  background: color-mix(in srgb, var(--c-paper) 14%, transparent);
  border: 1px solid var(--c-deep-line);
  border-radius: var(--radius-xs);
  color: var(--c-paper);
  width: 46px; height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 500 1.2rem var(--font-body);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease-out);
}
.lightbox button:hover { background: color-mix(in srgb, var(--c-paper) 26%, transparent); transform: scale(1.06); }
.lightbox__close { top: clamp(1rem, 3vw, 2rem); right: clamp(1rem, 3vw, 2rem); }
.lightbox__prev  { left: clamp(.5rem, 2vw, 2rem); top: 50%; margin-top: -23px; }
.lightbox__next  { right: clamp(.5rem, 2vw, 2rem); top: 50%; margin-top: -23px; }
body.lightbox-open { overflow: hidden; }

/* ---------- 11 Mobile call bar ---------------------------------------- */
/* Stays out of the way until the guest has actually read something. */

.callbar {
  transform: translateY(120%);
  transition: transform var(--dur) var(--ease-out);
}
body.is-scrolled-far .callbar,
html:not(.js-ready) .callbar { transform: none; }

/* ---------- 12 FAQ ----------------------------------------------------- */

@keyframes faq-open {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}
.faq details[open] .faq__body { animation: faq-open .45s var(--ease-out); }
.faq summary { transition: color var(--dur-fast) var(--ease); }
.faq summary:hover { color: var(--c-clay-deep); }
.faq summary::after { transition: transform var(--dur) var(--ease-out); }

/* ---------- 13 Reduced motion & print ---------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .js-ready .reveal,
  .js-ready .reveal[data-anim="curtain"],
  .js-ready .reveal[data-anim="curtain"] img {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  .word__i { transform: none !important; transition: none !important; }
  .word { overflow: visible; }
  .hero__badge, .hero__media::before, .open-badge.is-open::before,
  .faq details[open] .faq__body { animation: none !important; }
  .hero__media .leaf-flourish { stroke-dasharray: none; stroke-dashoffset: 0; }
  .rule-leaf::before, .rule-leaf::after { transform: scaleX(1) !important; }
  [data-parallax] { transform: none !important; }
  .band__bg[data-parallax] { height: 100%; top: 0; }
  .callbar { transform: none !important; }
  .btn::before { display: none; }
  .btn:hover, .mat--hover:hover, .value:hover, .masonry > figure:hover .mat,
  .social a:hover, .navtoggle:hover svg { transform: none !important; }
  .stories__slide { transition: none; }
  .lightbox, .lightbox__fig { transition: none; }
  .progress__bar { transition: none; }
}

@media print {
  .progress, .lightbox, .stories__dots { display: none !important; }
  .reveal, .word__i { opacity: 1 !important; transform: none !important; clip-path: none !important; }
  /* Print every story rather than whichever one happened to be showing. */
  .stories__stage { display: block; }
  .stories__slide { opacity: 1; visibility: visible; }
}
