/* Compiled from assets/scss/main.scss — Sprint 1 foundation
   Source of truth for tokens: assets/scss/base/_variables.scss
   Recompile with: sass assets/scss/main.scss assets/css/main.css */

:root {
  --fl-bg: #0b0b0b;
  --fl-bg-secondary: #141414;
  --fl-surface: #1c1c1c;
  --fl-text: #ffffff;
  --fl-text-secondary: #c8c8c8;
  /* Section eyebrow labels. A literal gold rgba() reads ~2:1 on a light
     surface, so the colour has to be a token the tone block can flip. */
  --fl-label: rgba(201, 161, 74, 0.88);
  /* Measured 6.99:1 on #0b0b0b, up from 5.70:1. (The audit quoted 4.9:1 for
     the old value; recomputing against the real page background gives 5.70.)
     These are the smallest strings on the page, so the headroom matters. */
  --fl-text-muted: #9a9a9a;
  /* VIS-08 — pink is now a three-step scale rather than one neon.
     #ff4fa3 on white text measured 3.04:1, below the 4.5:1 AA floor, and it
     also sat far from the product's actual pink-pearl finish.
       --fl-pink        interactive (buttons, focus): 4.87:1 on white
       --fl-pink-pearl  decoration and large areas: the bottle's real colour
       --fl-pink-neon   kept for hairlines and glows only, never behind text */
  --fl-pink: #d12b74;
  --fl-pink-pearl: #f7c6d4;
  --fl-pink-neon: #ff4fa3;

  --fl-gold: #c9a14a;

  /* VIS-07 — the brief lists silver and glass transparency among the brand
     colours; neither existed as a token, so neither could be used. */
  --fl-silver: #c8ccd0;
  --fl-silver-dim: #9aa0a6;
  --fl-glass: rgba(255, 255, 255, 0.06);
  --fl-glass-border: rgba(255, 255, 255, 0.14);
  --fl-glass-blur: 14px;

  /* Light-tone surface, for sections that break the dark rhythm (VIS-06). */
  --fl-champagne: #f3e9dc;
  --fl-ink: #1a1614;
  --fl-border: rgba(255, 255, 255, 0.08);
  --fl-font-display: "Shippori Mincho B1", "Cormorant Garamond", "Hiragino Mincho ProN", "Yu Mincho", serif;
  --fl-font-body: "Zen Kaku Gothic New", "Manrope", "Hiragino Sans", "Yu Gothic", sans-serif;
  --fl-font-brand: "Cormorant Garamond", "Times New Roman", serif;
  --fl-container: 90rem;
  --fl-content: 75rem;
  --fl-text-max: 43.75rem;

  /* Motion (Sprint 20). One curve for everything that moves, one for the
     mask wipe. Durations and the stagger step are tokens so the rhythm can
     be tuned in one place instead of 18 hard-coded cubic-beziers. */
  --fl-ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* For long alternating drifts.
     Nearly linear on purpose: the slope at t=0 is 0.76, so the loop is at
     three quarters of its average speed immediately. A true ease-in-out
     starts at zero, and because these loops begin at 1.4s that dead spot
     landed exactly on the first seconds anyone spends looking at the page.
     The ends still soften, just not all the way to a stop. */
  --fl-ease-drift: cubic-bezier(0.33, 0.25, 0.67, 0.75);
  --fl-ease-mask: cubic-bezier(0.76, 0, 0.24, 1);
  --fl-dur-fast: 240ms;
  --fl-dur-base: 700ms;
  --fl-dur-slow: 1100ms;
  --fl-stagger: 120ms;

  /* Focus ring. A token so the light-tone block can deepen it. */
  --fl-focus: var(--fl-pink, #d12b74);
}

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

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--fl-font-body);
  font-size: 1.125rem;
  line-height: 1.85;
  color: var(--fl-text);
  background-color: var(--fl-bg);
  -webkit-font-smoothing: antialiased;
}

img,
svg,
video {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--fl-focus, #d12b74);
  outline-offset: 3px;
}

h1,
.h1 {
  margin: 0 0 1.5rem;
  font-family: var(--fl-font-display);
  font-size: clamp(3rem, 6vw, 6rem);
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: 0.02em;
}

h2,
.h2 {
  margin: 0 0 1.5rem;
  font-family: var(--fl-font-display);
  font-size: clamp(2.5rem, 5vw, 4rem);
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.02em;
}

h3,
.h3 {
  margin: 0 0 1rem;
  font-family: var(--fl-font-display);
  font-size: clamp(1.375rem, 2vw, 1.75rem);
  font-weight: 600;
  line-height: 1.45;
  letter-spacing: 0.04em;
}

p {
  margin: 0 0 1rem;
}

.u-text-secondary {
  color: var(--fl-text-secondary);
}

.u-text-muted {
  color: var(--fl-text-muted);
}

.u-text-measure {
  max-width: var(--fl-text-max);
}

.l-container {
  width: 100%;
  max-width: var(--fl-container);
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 3rem);
}

.l-container--narrow {
  max-width: var(--fl-content);
}

.l-container--text {
  max-width: var(--fl-text-max);
}

.l-section {
  padding-block: clamp(3rem, 10vh, 6rem);
}

.l-section--tight {
  padding-block: clamp(2rem, 6vh, 3rem);
}

.l-section__header {
  margin-bottom: 2rem;
}

.l-section__content {
  width: 100%;
}

.l-section__grid {
  display: grid;
  gap: 2rem;
}

@media (min-width: 1024px) {
  .l-section__grid--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.c-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 1rem 1.5rem;
  background: var(--fl-pink);
  color: var(--fl-text);
  text-decoration: none;
}

.c-skip-link:focus {
  left: 1rem;
  top: 1rem;
}

.c-site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  padding-block: 1.5rem;
  background: rgba(11, 11, 11, 0.92);
  border-bottom: 1px solid var(--fl-border);
  backdrop-filter: blur(10px);
  transition: background-color 0.45s ease, border-color 0.45s ease, padding 0.45s ease;
}

body.fl-has-hero .c-site-header:not(.is-scrolled):not(.is-nav-open) {
  background: transparent;
  border-bottom-color: transparent;
  backdrop-filter: none;
}

body.fl-has-hero .c-site-header.is-scrolled {
  background: rgba(11, 11, 11, 0.9);
  border-bottom-color: var(--fl-border);
  backdrop-filter: blur(12px);
}

/* Soft cinematic reveal — progressive enhancement (visible by default if JS fails). */
@media (prefers-reduced-motion: no-preference) {
  .fl-reveal.is-pending {
    opacity: 0;
    transform: translateY(1.75rem);
    transition:
      opacity 0.9s var(--fl-ease),
      transform 0.9s var(--fl-ease);
    will-change: opacity, transform;
  }

  .fl-reveal.is-pending.is-visible {
    opacity: 1;
    transform: translateY(0);
  }
}

/*
 * Global header chrome — must never inherit page `.l-container` / `--fl-container` overrides.
 * Page CSS must not target .c-site-header / brand / nav.
 */
.c-site-header .l-container,
.c-site-header__inner.l-container {
  max-width: 90rem;
  padding-inline: clamp(1rem, 4vw, 3rem);
}

.c-site-footer .l-container,
.c-site-footer__inner.l-container {
  max-width: 90rem;
  padding-inline: clamp(1rem, 4vw, 3rem);
}

.c-site-header__inner {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: clamp(1.75rem, 3.5vw, 3.25rem);
}

.c-site-header__desktop {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
}

.c-site-brand {
  flex: 0 0 auto;
  margin-inline-start: clamp(0.25rem, 1.5vw, 1.25rem);
  font-family: var(--fl-font-brand, var(--fl-font-display));
  font-size: clamp(1.35rem, 2.2vw, 1.85rem);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-decoration: none;
  color: var(--fl-text);
}

/*
 * Desktop: logo + nav as one optical composition.
 * Side rails (1fr | auto auto | 1fr) keep the pair near center —
 * brand slightly left, navigation slightly right — equal visual weight.
 *
 * 900px, not 768px. The seven labels need about 865px before they stop
 * overflowing; between 768 and 864 the row was rendered and did not fit, and
 * the page gained 50-96px of horizontal scroll on every screen in that range —
 * an iPad in portrait among them. Below 900 the menu belongs behind the
 * toggle. The same boundary is already used by the hero.
 */
@media (min-width: 900px) {
  .c-site-header__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto minmax(0, 1fr);
    align-items: baseline;
    justify-content: unset;
    column-gap: clamp(1.35rem, 2.2vw, 2.35rem);
    row-gap: 0;
  }

  .c-site-brand {
    grid-column: 2;
    justify-self: end;
    align-self: baseline;
    margin-inline-start: 0;
    margin-inline-end: 0;
    line-height: 1;
    /* Optical: display face sits a hair above the nav cap-height */
    padding-block: 0.12em 0.08em;
  }

  .c-site-header__desktop {
    grid-column: 3;
    justify-self: start;
    align-self: baseline;
    flex: 0 0 auto;
    min-width: 0;
  }

  .c-site-header__desktop .c-nav {
    display: block;
  }

  .c-site-header__desktop .c-nav__list {
    flex-wrap: nowrap;
    align-items: baseline;
    justify-content: flex-start;
    /* Optical rhythm — not equal mathematical gaps */
    gap: 0;
  }

  .c-site-header__desktop .c-nav__item {
    margin: 0;
  }

  .c-site-header__desktop .c-nav__link,
  .c-site-header__desktop .c-nav__list a {
    /* em padding tracks letter width / label density */
    padding-inline: 0.72em;
    line-height: 1;
    /* First/last breathe toward logo / trailing edge asymmetrically */
  }

  .c-site-header__desktop .c-nav__item:first-child .c-nav__link,
  .c-site-header__desktop .c-nav__list > li:first-child > a {
    padding-inline-start: 0.35em;
  }

  .c-site-header__desktop .c-nav__item:last-child .c-nav__link,
  .c-site-header__desktop .c-nav__list > li:last-child > a {
    padding-inline-end: 0.2em;
  }

  /* Hide toggle from the composition grid; mobile rule already hides it */
  .c-site-header__inner > .c-nav-toggle {
    grid-column: 4;
    justify-self: end;
  }
}

.c-nav__list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: 0.85rem 1.35rem;
  margin: 0;
  padding: 0;
}

.c-nav__list--footer {
  gap: 1rem 1.5rem;
}

.c-nav__list--mobile {
  flex-direction: column;
  align-items: flex-start;
  gap: 1.5rem;
}

/* Optical equality: identical metrics for every label (incl. short “Story”) */
.c-site-header__desktop .c-nav__link,
.c-site-header__desktop .c-nav__list a {
  display: inline-block;
  text-decoration: none;
  font-family: var(--fl-font-body);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--fl-text-secondary);
  transition: color 0.2s ease;
  white-space: nowrap;
}

.c-nav__link,
.c-nav__list a {
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--fl-text-secondary);
  transition: color 0.2s ease;
}

.c-nav__link:hover,
.c-nav__link:focus-visible,
.c-nav__list a:hover,
.c-nav__list a:focus-visible {
  color: var(--fl-text);
}

/* Current page — clear “you are here” without shouting */
.c-site-header__desktop .c-nav__list .is-current > a,
.c-site-header__desktop .c-nav__list .current-menu-item > a,
.c-site-header__desktop .c-nav__list .current_page_item > a,
.c-nav__list--mobile .is-current > a,
.c-nav__list--mobile .current-menu-item > a,
.c-nav__list--mobile .current_page_item > a {
  color: #fff;
  opacity: 1;
}

.c-site-header__desktop .c-nav__list .is-current > a,
.c-site-header__desktop .c-nav__list .current-menu-item > a,
.c-site-header__desktop .c-nav__list .current_page_item > a {
  position: relative;
  color: #fff;
}

.c-site-header__desktop .c-nav__list .is-current > a::after,
.c-site-header__desktop .c-nav__list .current-menu-item > a::after,
.c-site-header__desktop .c-nav__list .current_page_item > a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.45rem;
  height: 1px;
  background: var(--fl-gold, #c9a14a);
  opacity: 0.95;
}

.c-nav__list--mobile .is-current > a,
.c-nav__list--mobile .current-menu-item > a,
.c-nav__list--mobile .current_page_item > a {
  color: var(--fl-gold, #c9a14a);
}

.c-nav-toggle {
  appearance: none;
  border: 1px solid var(--fl-border);
  background: var(--fl-surface);
  color: var(--fl-text);
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 0.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  margin-inline-start: auto;
  flex: 0 0 auto;
}

.c-nav-toggle__bars,
.c-nav-toggle__bars::before,
.c-nav-toggle__bars::after {
  display: block;
  width: 1.1rem;
  height: 1.5px;
  background: var(--fl-text);
  position: relative;
}

.c-nav-toggle__bars::before,
.c-nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
}

.c-nav-toggle__bars::before {
  top: -5px;
}

.c-nav-toggle__bars::after {
  top: 5px;
}

.c-nav-mobile {
  border-top: 1px solid var(--fl-border);
  background: var(--fl-bg-secondary);
  padding: 2rem clamp(1rem, 4vw, 3rem) 3rem;
}

.c-site-header.is-nav-open .c-nav-toggle__bars {
  background: transparent;
}

.c-site-header.is-nav-open .c-nav-toggle__bars::before {
  top: 0;
  transform: rotate(45deg);
}

.c-site-header.is-nav-open .c-nav-toggle__bars::after {
  top: 0;
  transform: rotate(-45deg);
}

body.has-mobile-nav-open {
  overflow: hidden;
}

.c-site-footer {
  margin-top: auto;
  padding-block: 4rem 3rem;
  background: var(--fl-bg-secondary);
  border-top: 1px solid var(--fl-border);
}

.c-site-footer__inner {
  display: grid;
  gap: 3rem;
}

.c-site-footer__top {
  display: grid;
  gap: 2rem;
}

@media (min-width: 1024px) {
  .c-site-footer__top {
    grid-template-columns: 1.2fr 1fr auto;
    align-items: start;
  }
}

.c-site-footer__brand {
  margin: 0 0 1rem;
  font-family: var(--fl-font-display);
  font-size: 1.5rem;
  font-weight: 600;
}

.c-site-footer__tagline {
  margin: 0;
  max-width: 28ch;
}

.c-site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 2rem;
  border-top: 1px solid var(--fl-border);
}

.c-site-footer__legal,
.c-site-footer__copy {
  margin: 0;
  font-size: 0.875rem;
}

.c-social {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin: 0;
  padding: 0;
}

.c-social__link {
  display: inline-flex;
  align-items: center;
  min-height: 2.5rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--fl-border);
  border-radius: 0.75rem;
  text-decoration: none;
  color: var(--fl-text-secondary);
  font-size: 0.9rem;
}

.c-social__link.is-placeholder {
  opacity: 0.55;
  cursor: default;
}

.c-site-main {
  flex: 1 0 auto;
  min-height: 60vh;
}

/* Block-assembled homepage: blocks own spacing; no shell padding. */
.c-site-main--blocks {
  min-height: 0;
  padding-block: 0;
}

.c-entry__title {
  margin: 0 0 2rem;
  font-family: var(--fl-font-display);
  font-size: clamp(2.5rem, 5vw, 4rem);
  font-weight: 600;
  line-height: 1.15;
}

.c-entry__title a {
  text-decoration: none;
}

.c-entry__content {
  color: var(--fl-text-secondary);
}

.c-entry__content > *:last-child {
  margin-bottom: 0;
}

.c-entry__content--blocks {
  color: inherit;
  max-width: none;
}

.c-entry__content--blocks > *:first-child {
  margin-top: 0;
}

.c-entry__content--blocks > *:last-child {
  margin-bottom: 0;
}

.u-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.u-flex {
  display: flex;
}

.u-flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.u-flex-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.u-gap-2 {
  gap: 1rem;
}

.u-gap-3 {
  gap: 1.5rem;
}

.u-mt-0 {
  margin-top: 0;
}

.u-mb-0 {
  margin-bottom: 0;
}

.u-mb-3 {
  margin-bottom: 1.5rem;
}

.u-mb-5 {
  margin-bottom: 3rem;
}

.u-text-center {
  text-align: center;
}

.u-hidden {
  display: none !important;
}

/* Paired with the header composition above — see the note there for why the
   boundary is 900 and not 768. These two classes and .c-nav-mobile are used
   only by header.php and the mobile navigation part. */
@media (max-width: 899px) {
  .u-hide-mobile {
    display: none !important;
  }
}

@media (min-width: 900px) {
  .u-hide-desktop {
    display: none !important;
  }

  .c-nav-mobile {
    display: none !important;
  }
}

/* JA rebrand — calm CJK tracking (overrides Latin-tuned page CSS) */
.c-site-main [class*="__label"],
.c-site-main [class*="__eyebrow"] {
  letter-spacing: 0.12em;
}

.c-site-main h1,
.c-site-main h2,
.c-site-main h3,
.c-site-main [class*="__title"],
.c-site-main [class*="__heading"],
.c-site-main .fl-hero__heading,
.c-site-main .fl-hero__subheading {
  letter-spacing: 0.04em;
}

.c-site-main p,
.c-site-main .u-text-measure,
.c-site-main [class*="__description"],
.c-site-main [class*="__copy"] {
  line-height: 1.85;
  letter-spacing: 0.02em;
}

/* ============================================================
   Editorial chapter marker
   A fine gold hairline opens every section eyebrow, framing each
   section like a chapter — a quiet, consistent luxury detail.
   Additive (::before only); coexists with per-page eyebrow art
   direction (color/size/tracking) since it sets neither.
   ============================================================ */
.c-site-main .fl-story__label,
.c-site-main .fl-product__label,
.c-site-main .fl-how-to-enjoy__label,
.c-site-main .fl-gallery__label,
.c-site-main .fl-shop__label,
.c-site-main .fl-news__label,
.c-site-main .fl-faq__label,
.c-site-main .fl-contact__label {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
}

.c-site-main .fl-story__label::before,
.c-site-main .fl-product__label::before,
.c-site-main .fl-how-to-enjoy__label::before,
.c-site-main .fl-gallery__label::before,
.c-site-main .fl-shop__label::before,
.c-site-main .fl-news__label::before,
.c-site-main .fl-faq__label::before,
.c-site-main .fl-contact__label::before {
  content: "";
  flex: 0 0 auto;
  width: clamp(1.75rem, 3vw, 2.5rem);
  height: 1px;
  background: currentColor;
  opacity: 0.5;
}

/* ============================================================
   Natural Japanese line-breaking — site-wide
   Propagates the homepage typography quality to every page:
   headings balance across JA phrase boundaries (auto-phrase),
   body copy avoids orphans, kinsoku via line-break: strict.
   Does not set any max-width — each page keeps its own measure
   and art direction; only the break behaviour is unified.
   ============================================================ */
.c-site-main h1,
.c-site-main h2,
.c-site-main h3,
.c-site-main [class*="__title"],
.c-site-main [class*="__heading"],
.c-site-main [class*="__name"] {
  text-wrap: balance;
  line-break: strict;
  word-break: auto-phrase;
  overflow-wrap: normal;
}

.c-site-main p,
.c-site-main [class*="__description"],
.c-site-main [class*="__intro"],
.c-site-main [class*="__copy"],
.c-site-main [class*="__excerpt"] {
  text-wrap: pretty;
  line-break: strict;
  word-break: auto-phrase;
}

/* ---------------------------------------------------------------
   VIS-06 — light tonal break.
   Nine pages of unbroken #0b0b0b read as "dark", not as "luxury";
   the reference brands all break their rhythm somewhere. Scoping the
   colour tokens rather than restating rules means every child element
   follows automatically, and the dark default is untouched.
   Contrast on this surface: #1a1614 on #f3e9dc = 14.6:1.
   --------------------------------------------------------------- */
.fl-tone--light,
.fl-tone--champagne {
  --fl-bg: var(--fl-champagne);
  --fl-bg-secondary: #ffffff;
  --fl-surface: #ffffff;
  --fl-text: var(--fl-ink);
  --fl-text-secondary: #4a423c;
  --fl-text-muted: #6b615a;
  --fl-border: rgba(26, 22, 20, 0.12);
  --fl-glass: rgba(26, 22, 20, 0.04);
  --fl-glass-border: rgba(26, 22, 20, 0.10);
  /* The dark-surface gold reads 2.02:1 here — invisible. Section labels are
     set in it, so the light surface needs its own, deeper gold: 5.06:1. */
  --fl-gold: #805c0f;
  --fl-label: #805c0f;
  --fl-focus: #a01f57;

  background: var(--fl-champagne);
  color: var(--fl-ink);
}

.fl-tone--light .fl-btn--secondary,
.fl-tone--champagne .fl-btn--secondary {
  color: var(--fl-ink);
  border-color: rgba(26, 22, 20, 0.28);
}

.fl-tone--light .fl-btn--secondary:hover,
.fl-tone--champagne .fl-btn--secondary:hover {
  background: rgba(26, 22, 20, 0.05);
}


/* ==========================================================================
   Buttons — global (Sprint 20)

   Moved out of blocks/hero/style.css: pages without a hero block never load
   that file, so /privacy/, /terms/ and /tokushoho/ rendered their button
   with no styling at all.

   The gold underline is the only added ornament. Hover and :focus-visible
   are always declared together — a hover-only affordance is invisible to
   anyone using a keyboard.
   ========================================================================== */
.fl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 3.15rem;
  padding: 0.9rem 1.9rem;
  border-radius: 0;
  border: 1px solid transparent;
  font-family: var(--fl-font-body, "Manrope", sans-serif);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition:
    transform 0.45s var(--fl-ease),
    background-color 0.45s ease,
    color 0.45s ease,
    border-color 0.45s ease,
    box-shadow 0.45s ease;
}

.fl-btn--primary {
  background: var(--fl-pink, #d12b74);
  color: #fff;
}

.fl-btn--primary:hover,
.fl-btn--primary:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 12px 36px rgba(209, 43, 116, 0.30);
}

.fl-btn--secondary {
  background: transparent;
  color: #fff;
  border-color: rgba(255, 255, 255, 0.45);
}

.fl-btn--secondary:hover,
.fl-btn--secondary:focus-visible {
  background: rgba(255, 255, 255, 0.06);
  border-color: #fff;
  color: #fff;
}

.fl-btn {
  position: relative;
}

.fl-btn::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--fl-gold, #c9a14a);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--fl-dur-base) var(--fl-ease);
}

.fl-btn:hover::after,
.fl-btn:focus-visible::after {
  transform: scaleX(1);
}

/* Touch: there is no pointer to leave, so a hover state sticks until the
   next tap elsewhere. Give touch devices the resting state only. */
@media (hover: none) {
  .fl-btn::after,
  .fl-btn:hover::after {
    transform: scaleX(0);
    transition: none;
  }

  .fl-btn--primary:hover {
    transform: none;
    box-shadow: none;
  }

  .fl-story__media:hover .fl-story__img,
  .fl-gallery__figure:hover .fl-gallery__img {
    transform: scale(var(--fl-img-zoom, 1));
  }
}

/* ==========================================================================
   Reveal motion — stagger and mask (Sprint 20)

   Everything below is inert until reveal.js adds .fl-reveal to a section.
   That matters: if JS never runs, if IntersectionObserver is missing, or if
   the visitor asked for reduced motion, reveal.js returns early and none of
   these selectors ever match — the page is simply its finished state. The
   1800ms safety timeout in reveal.js covers the remaining case where the
   observer exists but never fires.

   Copy and media are declared separately on purpose. Media carries a third
   property (the clip-path wipe), and folding both into one selector list
   would mean declaring `transition` twice for the same element — the exact
   overwrite-instead-of-edit habit that FIX-13 cleaned out of this codebase.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  /* Copy: label → title → body → CTA, one 120ms step apart. That step is
     what reads as directed rather than as a single block fading in. */
  .fl-reveal [data-stagger] > * {
    opacity: 0;
    transform: translateY(16px);
    transition:
      opacity var(--fl-dur-base) var(--fl-ease),
      transform var(--fl-dur-base) var(--fl-ease);
    transition-delay: calc(var(--i, 0) * var(--fl-stagger));
  }

  .fl-reveal.is-visible [data-stagger] > * {
    opacity: 1;
    transform: none;
  }

  /* Capped at six: past that, the tail of a long section would still be
     waiting while the reader has already moved on. */
  [data-stagger] > :nth-child(1) { --i: 0; }
  [data-stagger] > :nth-child(2) { --i: 1; }
  [data-stagger] > :nth-child(3) { --i: 2; }
  [data-stagger] > :nth-child(4) { --i: 3; }
  [data-stagger] > :nth-child(5) { --i: 4; }
  [data-stagger] > :nth-child(n + 6) { --i: 5; }

  /* Media: same fade, plus an upward wipe. Deliberately NOT applied to
     .fl-hero — it holds the LCP element on every page and a clip-path there
     would delay the largest paint. reveal.js does not target .fl-hero
     either, so this is belt and braces. */
  .fl-reveal .fl-image-reveal,
  .fl-reveal .fl-story__media,
  .fl-reveal .fl-product__media,
  .fl-reveal .fl-gallery__figure {
    opacity: 0;
    transform: translateY(16px);
    clip-path: inset(0 0 100% 0);
    transition:
      opacity var(--fl-dur-base) var(--fl-ease),
      transform var(--fl-dur-base) var(--fl-ease),
      clip-path var(--fl-dur-slow) var(--fl-ease-mask);
    transition-delay: calc(var(--i, 0) * var(--fl-stagger));
  }

  .fl-reveal.is-visible .fl-image-reveal,
  .fl-reveal.is-visible .fl-story__media,
  .fl-reveal.is-visible .fl-product__media,
  .fl-reveal.is-visible .fl-gallery__figure {
    opacity: 1;
    transform: none;
    clip-path: inset(0 0 0 0);
  }

  /* A story image arrives after the copy it belongs to. */
  .fl-story__media,
  .fl-product__media {
    --i: 3;
  }

  /* Gallery tiles wipe in sequence, capped so a 30-image wall does not end
     with a four-second wait. */
  .fl-gallery__grid > :nth-child(1) .fl-gallery__figure { --i: 0; }
  .fl-gallery__grid > :nth-child(2) .fl-gallery__figure { --i: 1; }
  .fl-gallery__grid > :nth-child(3) .fl-gallery__figure { --i: 2; }
  .fl-gallery__grid > :nth-child(n + 4) .fl-gallery__figure { --i: 3; }
}

/* Explicit resting state. The query above already excludes these visitors,
   but declaring the end state costs nothing and removes any doubt about
   what a reduced-motion reader is served. */
@media (prefers-reduced-motion: reduce) {
  [data-stagger] > *,
  .fl-image-reveal,
  .fl-story__media,
  .fl-product__media,
  .fl-gallery__figure {
    opacity: 1;
    transform: none;
    clip-path: none;
    transition: none;
  }
}

/* ==========================================================================
   Japanese line breaking

   Japanese has no spaces, so a browser will break a line anywhere at all —
   including inside a word, which is how ﾊﾟｰﾄﾅｰｼｯﾌﾟで / も、 happens. Chrome
   can break at phrase boundaries instead when asked. Guarded by @supports:
   browsers without it fall back to today's behaviour, which is not worse.
   ========================================================================== */

@supports (word-break: auto-phrase) {
  :lang(ja) .fl-story__title,
  :lang(ja) .fl-story__description p,
  :lang(ja) .fl-gallery__intro p,
  :lang(ja) .fl-product__description p,
  :lang(ja) .fl-hero__subheading {
    word-break: auto-phrase;
  }
}

/* ==========================================================================
   Company profile (CNT-05)

   A definition list, not a table: these are label/value pairs, and a screen
   reader announces the pairing for free. Rows that have no value are never
   rendered at all, so the layout has to look finished at one row as well as
   at seven.
   ========================================================================== */

.fl-company {
  padding-block: clamp(5rem, 14vh, 9rem);
  background: var(--fl-bg, #0b0b0b);
  color: var(--fl-text, #fff);
}

.fl-company__label {
  margin: 0 0 1.1rem;
  color: var(--fl-label, rgba(201, 161, 74, 0.88));
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
}

.fl-company__title {
  margin: 0 0 clamp(2.25rem, 5vw, 3.5rem);
  font-family: var(--fl-font-display, serif);
  font-size: clamp(2.1rem, 4.4vw, 3.2rem);
  font-weight: 500;
  line-height: 1.15;
}

.fl-company__list {
  margin: 0;
  border-top: 1px solid var(--fl-border, rgba(255, 255, 255, 0.08));
}

.fl-company__row {
  display: grid;
  gap: 0.35rem 2rem;
  padding-block: clamp(1.1rem, 2.4vw, 1.5rem);
  border-bottom: 1px solid var(--fl-border, rgba(255, 255, 255, 0.08));
}

@media (min-width: 640px) {
  .fl-company__row {
    grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr);
  }
}

.fl-company__term {
  margin: 0;
  color: var(--fl-text-secondary, #c8c8c8);
  font-size: 0.86rem;
  letter-spacing: 0.06em;
}

.fl-company__desc {
  margin: 0;
  font-size: 1rem;
  line-height: 1.9;
}

.fl-company__pending {
  margin: 0;
  color: var(--fl-text-secondary, #c8c8c8);
  line-height: 1.95;
}

.fl-company__actions {
  margin: clamp(2.5rem, 6vw, 4rem) 0 0;
}

/* ==========================================================================
   Touch targets (A11Y-03)

   A finger is about 9mm across; a 24px control is a coin toss. WCAG 2.5.8
   asks for 24px, 2.5.5 for 44px, and Apple/Google both say 44.

   Scoped to (pointer: coarse) so a mouse keeps the tighter editorial rhythm,
   and inline text links are excluded — forcing 44px on a link inside a
   paragraph pushes the line box apart and breaks the type.
   ========================================================================== */

@media (pointer: coarse) {
  a,
  button,
  [role="button"],
  summary,
  input[type="checkbox"],
  input[type="radio"],
  input[type="submit"] {
    min-height: 44px;
    min-width: 44px;
  }

  /* Links set in running text keep their natural box. */
  p a,
  li a,
  dd a,
  .fl-prose a,
  .fl-story__description a,
  .fl-faq__answer a,
  .c-legal-nav a {
    min-height: 0;
    min-width: 0;
  }

  /* Checkboxes and radios are drawn small on purpose; the hit area belongs
     to the label wrapping them, which is already 44px by the rule above. */
  input[type="checkbox"],
  input[type="radio"] {
    min-height: 24px;
    min-width: 24px;
  }
}

/* Form field marks and help text (A11Y-04). */
.fl-contact-form__legend {
  margin: 0 0 1.5rem;
  color: var(--fl-text-muted, #9a9a9a);
  font-size: 0.82rem;
}

.fl-contact-form__req {
  margin-inline-start: 0.35em;
  color: var(--fl-pink, #d12b74);
}

.fl-contact-form__help {
  display: block;
  margin: 0.2rem 0 0.45rem;
  color: var(--fl-text-muted, #9a9a9a);
  font-size: 0.8rem;
  line-height: 1.7;
}

/* ==========================================================================
   NIGHT DEPTH (S23)

   The whole site was one flat #0b0b0b. Night is not a flat colour — it is a
   gradient away from a light source: warm and open near the light, cold and
   deep away from it. Nine identical black sections read as "dim"; the same
   nine with different light directions read as "night".

   This is why raising the overall brightness was the wrong fix twice over
   (FIX-06, FIX-15). The answer to dim is not bright, it is CONTRAST.

   Costs nothing: gradients, no images, no requests.
   ========================================================================== */

:root {
  /* Depth stops. Deep = furthest from any light, lit = the surface a lamp
     falls on. Both stay dark enough for white type at 11:1 or better. */
  --fl-night-deep: #050507;
  --fl-night: #0b0b0e;
  --fl-night-lit: #14101a;
  --fl-glow-pink: rgba(209, 43, 116, 0.16);
  --fl-glow-gold: rgba(201, 161, 74, 0.13);
  --fl-glow-cool: rgba(90, 120, 190, 0.10);
}

/* Every full-width section gets a light direction. Alternating the origin
   is what creates the sense of moving THROUGH a space rather than past a
   stack of panels. */
.fl-section {
  position: relative;
  /* overflow-x: clip — not hidden. The drifting glow layer below is inset
     past the section edge so it blends softly into its neighbours; without
     a horizontal clip that overflow becomes a scrollbar. Unlike hidden,
     clip stops the paint without creating a scroll container, so sticky
     children still work. Vertical bleed is left alone on purpose. */
  overflow-x: clip;
  background: var(--fl-night, #0b0b0e);
}

.fl-section:nth-of-type(odd) {
  background:
    radial-gradient(120% 90% at 12% 0%, var(--fl-glow-pink), transparent 55%),
    radial-gradient(90% 70% at 100% 100%, var(--fl-glow-cool), transparent 60%),
    linear-gradient(180deg, var(--fl-night) 0%, var(--fl-night-deep) 100%);
}

.fl-section:nth-of-type(even) {
  background:
    radial-gradient(110% 85% at 88% 8%, var(--fl-glow-gold), transparent 58%),
    radial-gradient(80% 60% at 0% 92%, var(--fl-glow-pink), transparent 62%),
    linear-gradient(180deg, var(--fl-night-deep) 0%, var(--fl-night) 100%);
}

/* A tonal section owns its own surface — do not paint night over it. */
.fl-section.fl-tone--light,
.fl-section.fl-tone--champagne {
  background: var(--fl-champagne, #f3e9dc);
}

/* Section seam: a one-pixel line of light where two nights meet. Reads as
   depth rather than as a border. */
.fl-section + .fl-section::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255, 255, 255, 0.09) 22%,
    rgba(255, 79, 163, 0.22) 50%,
    rgba(255, 255, 255, 0.09) 78%,
    transparent 100%
  );
  pointer-events: none;
}

/* ==========================================================================
   FULL-BLEED BREAKOUT (S23)

   Not one image on this site reached the edge of the screen, so every photo
   read as an illustration inside a frame rather than as something happening.
   .fl-bleed lets a child escape its container without touching the section's
   own padding — no negative margins guessed by hand, no horizontal scroll.
   ========================================================================== */

.fl-bleed {
  width: 100vw;
  max-width: 100vw;
  margin-inline: calc(50% - 50vw);
}

/* The viewport unit above ignores the scrollbar, which on Windows overflows
   the page by ~15px. Modern browsers can measure the real width. */
@supports (width: 100dvw) {
  .fl-bleed {
    width: 100dvw;
    max-width: 100dvw;
    margin-inline: calc(50% - 50dvw);
  }
}

/* ==========================================================================
   AMBIENT MOTION (S24)

   The site had eleven transitions and zero animations: nothing moved unless
   a person touched it. A nightlife brand cannot be a still photograph.

   Everything here is slow (14–26s), low-amplitude, and never on the critical
   render path. No scroll hijacking, no libraries, no JS. All of it stops
   dead under prefers-reduced-motion — a moving background is exactly what
   makes some people ill, so that is not a nicety.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  /* A room's light is never perfectly still. 24s is below the threshold at
     which the eye tracks it as movement — it reads as "alive", not "animated". */
  @keyframes fl-drift {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    50%  { transform: translate3d(2.5%, -1.5%, 0) scale(1.08); }
    100% { transform: translate3d(0, 0, 0) scale(1); }
  }

  /* Neon does not glow evenly — it breathes, and occasionally stutters. */
  @keyframes fl-neon {
    0%, 100%  { opacity: 1; }
    41%       { opacity: 1; }
    43%       { opacity: 0.72; }
    45%       { opacity: 1; }
    72%       { opacity: 1; }
    73.5%     { opacity: 0.84; }
    75%       { opacity: 1; }
  }

  @keyframes fl-marquee {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
  }

  /* The glow layer sits behind content and drifts. Painted on its own layer
     so the animation never triggers layout or repaints the text above it. */
  .fl-section::after {
    content: '';
    position: absolute;
    inset: -12%;
    z-index: 0;
    pointer-events: none;
    background: radial-gradient(
      42% 38% at 26% 22%,
      var(--fl-glow-pink, rgba(209, 43, 116, 0.16)),
      transparent 68%
    );
    will-change: transform;
    animation: fl-drift 26s ease-in-out infinite;
  }

  .fl-section:nth-of-type(even)::after {
    background: radial-gradient(
      38% 34% at 76% 30%,
      var(--fl-glow-gold, rgba(201, 161, 74, 0.13)),
      transparent 66%
    );
    animation-duration: 32s;
    animation-direction: reverse;
  }

  /* Content has to sit above the drifting layer. */
  .fl-section > * {
    position: relative;
    z-index: 1;
  }

  /* The hero is the LCP element — nothing animated goes behind it. */
  .fl-hero::after {
    content: none;
  }
}

/* ==========================================================================
   NEON TYPE + MARQUEE (S24)
   ========================================================================== */

.fl-neon {
  color: #fff;
  text-shadow:
    0 0 4px rgba(255, 255, 255, 0.55),
    0 0 14px rgba(255, 79, 163, 0.75),
    0 0 34px rgba(255, 79, 163, 0.42);
}

@media (prefers-reduced-motion: no-preference) {
  .fl-neon {
    animation: fl-neon 7s steps(1, end) infinite;
  }
}

/* Ticker. The track is duplicated in markup and translated by exactly -50%,
   so the loop is seamless without measuring anything at runtime. */
.fl-ticker {
  position: relative;
  overflow: hidden;
  padding-block: clamp(1.1rem, 2.4vh, 1.6rem);
  border-block: 1px solid var(--fl-border, rgba(255, 255, 255, 0.08));
  background: var(--fl-night-deep, #050507);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.fl-ticker__track {
  display: flex;
  width: max-content;
  gap: clamp(2.5rem, 6vw, 5rem);
  padding-inline-end: clamp(2.5rem, 6vw, 5rem);
}

@media (prefers-reduced-motion: no-preference) {
  .fl-ticker__track {
    will-change: transform;
    animation: fl-marquee 38s linear infinite;
  }

  /* Stop while a pointer rests on it, so the words can actually be read. */
  .fl-ticker:hover .fl-ticker__track {
    animation-play-state: paused;
  }
}

.fl-ticker__item {
  flex: none;
  color: var(--fl-text-muted, #9a9a9a);
  font-family: var(--fl-font-display, serif);
  font-size: clamp(0.95rem, 2vw, 1.35rem);
  letter-spacing: 0.16em;
  white-space: nowrap;
}

.fl-ticker__item::after {
  content: '·';
  margin-inline-start: clamp(2.5rem, 6vw, 5rem);
  color: var(--fl-pink, #d12b74);
}

/* ==========================================================================
   STATEMENT (S23)

   The one moment on the page that obeys none of the section rules. No
   container, no label, no image — the type IS the layout. Everything else
   on this site sits inside a 90rem container at a considered size; this
   deliberately does not, which is what makes it register as an event.

   Type scale tops out at 8rem. Bigger than that and Japanese punctuation
   starts to float away from the characters it belongs to.
   ========================================================================== */

.fl-statement {
  display: grid;
  place-items: center;
  min-height: min(78vh, 44rem);
  padding-block: clamp(6rem, 16vh, 11rem);
  padding-inline: clamp(1.25rem, 5vw, 4rem);
  text-align: center;
  overflow: hidden;
}

.fl-statement--pink {
  background:
    radial-gradient(58% 52% at 50% 42%, rgba(209, 43, 116, 0.24), transparent 66%),
    linear-gradient(180deg, #08080c 0%, #030305 100%);
}

.fl-statement--gold {
  background:
    radial-gradient(58% 52% at 50% 42%, rgba(201, 161, 74, 0.22), transparent 66%),
    linear-gradient(180deg, #0a0906 0%, #030303 100%);
}

.fl-statement__inner {
  max-width: 54rem;
}

.fl-statement__en {
  margin: 0 0 clamp(1.5rem, 4vh, 2.75rem);
  font-family: var(--fl-font-brand, "Cormorant Garamond", serif);
  font-size: clamp(0.78rem, 1.6vw, 1rem);
  font-weight: 500;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  /* The tracking above pushes the last letter off-centre; pull it back. */
  text-indent: 0.42em;
}

.fl-statement__ja {
  margin: 0;
  font-family: var(--fl-font-display, serif);
  font-size: clamp(2.6rem, 8vw, 5.5rem);
  font-weight: 500;
  line-height: 1.14;
  letter-spacing: 0.02em;
  overflow-wrap: break-word;
}

.fl-statement__sub {
  margin: clamp(2.25rem, 6vh, 3.5rem) auto 0;
  max-width: 34rem;
}

.fl-statement__sub p {
  margin: 0 0 0.65rem;
  color: var(--fl-text-secondary, #c8c8c8);
  font-size: clamp(0.95rem, 1.8vw, 1.1rem);
  line-height: 2;
}

.fl-statement__sub p:last-child {
  margin-bottom: 0;
  color: var(--fl-text, #fff);
}

/* Lines arrive one after another, like someone speaking. --i is set inline
   per line so the delay does not depend on a CSS nth-child ladder that
   breaks the moment the copy length changes. */
@media (prefers-reduced-motion: no-preference) {
  .fl-reveal .fl-statement__sub p,
  .fl-reveal .fl-statement__ja {
    opacity: 0;
    transform: translateY(14px);
    transition:
      opacity var(--fl-dur-base) var(--fl-ease),
      transform var(--fl-dur-base) var(--fl-ease);
    transition-delay: calc((var(--i, 0) + 1) * var(--fl-stagger));
  }

  .fl-reveal.is-visible .fl-statement__sub p,
  .fl-reveal.is-visible .fl-statement__ja {
    opacity: 1;
    transform: none;
  }
}

/* ==========================================================================
   REFINED MEDIA FRAME + SCROLL PARALLAX (S26)

   Borrowed, deliberately, from what the reference brands in the brief do:

   · Hermès / Clase Azul — an offset hairline that does NOT enclose the photo.
     A closed box says "picture". An open corner says "plate in a book".
   · Aesop / Le Labo    — a small, uneven radius. Enough to lose the office
     rectangle, too little to read as an app card.
   · Belvedere          — the edge dissolves into the dark rather than being
     cut against it, so the photo sits IN the page instead of ON it.
   · Apple              — the image drifts slower than the text while you
     scroll. Nothing is hijacked; the page still moves exactly as far as
     the wheel says.

   All of it is CSS. No library, no scroll listener, no layout thrash.
   ========================================================================== */

.fl-story__media,
.fl-product__media {
  /* Asymmetric, and small. 18px on the two outer corners, 3px on the inner
     two — it reads as a considered plate rather than a rounded button, and
     echoes the bottle's shoulder without imitating it. */
  border-radius: 18px 3px 18px 3px;
  overflow: hidden;
  /* Lift it off the background so the edge is a transition, not a cut. */
  box-shadow:
    0 24px 60px -28px rgba(0, 0, 0, 0.85),
    0 2px 0 0 rgba(255, 255, 255, 0.04) inset;
}

.fl-story--image-left .fl-story__media {
  border-radius: 3px 18px 3px 18px;
}

/* The hairline. Offset down and outward, drawn on two sides only, so the
   frame is open — the eye completes it and the photo keeps breathing. */
.fl-story__grid .fl-story__media {
  position: relative;
}

.fl-story__grid .fl-story__media::after {
  content: '';
  position: absolute;
  z-index: 2;
  inset: 18% -14px -14px 18%;
  border-right: 1px solid var(--fl-line, rgba(201, 161, 74, 0.42));
  border-bottom: 1px solid var(--fl-line, rgba(201, 161, 74, 0.42));
  border-radius: 0 0 18px 0;
  pointer-events: none;
}

.fl-story--image-left .fl-story__grid .fl-story__media::after,
.fl-story__grid.fl-layout-grid--media-start .fl-story__media::after {
  inset: 18% 18% -14px -14px;
  border-right: 0;
  border-bottom: 1px solid var(--fl-line, rgba(201, 161, 74, 0.42));
  border-left: 1px solid var(--fl-line, rgba(201, 161, 74, 0.42));
  border-radius: 0 0 0 18px;
}

/* The hairline must not create a scrollbar where it hangs past the column. */
.fl-story__grid {
  overflow-x: clip;
}

/* --------------------------------------------------------------------------
   Scroll-linked drift

   animation-timeline: view() ties the animation to the element's own passage
   through the viewport — the browser drives it, so it cannot fall behind the
   scroll the way a JS listener does, and it costs no main thread.

   Guarded twice: @supports for browsers without it (they simply get the
   static frame), and prefers-reduced-motion for people who asked for less.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    @keyframes fl-drift-media {
      from {
        transform: translate3d(0, 3.2%, 0) scale(1.045);
      }
      to {
        transform: translate3d(0, -3.2%, 0) scale(1.045);
      }
    }

    @keyframes fl-drift-copy {
      from {
        transform: translate3d(0, 1.4%, 0);
      }
      to {
        transform: translate3d(0, -1.4%, 0);
      }
    }

    /* The image is pre-scaled by 4.5% so the drift never exposes an edge. */
    .fl-story__grid .fl-story__img,
    .fl-story__grid .fl-story__media img {
      animation: fl-drift-media linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
      will-change: transform;
    }

    /* Copy moves too, less than half as far. Equal speeds read as a broken
       page; a clear difference reads as depth. */
    .fl-story__grid .fl-story__copy {
      animation: fl-drift-copy linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    /* The hairline stays put — it belongs to the page, not to the photo. */
  }
}

/* ==========================================================================
   REFINED FRAME — SITE-WIDE (S26b)

   The STORY treatment extended to the rest of the site, but NOT uniformly.
   Two different jobs need two different devices:

   · A single feature image is a plate. It carries the open hairline, the
     asymmetric radius and the parallax drift, because it is alone on its row.

   · A grid of tiles is a contact sheet. Twenty-one open hairlines is not
     twenty-one times as elegant — it is noise. Tiles get the radius and the
     lift and nothing more, so the grid stays a grid.

   Hooked on .fl-image-frame rather than per-block classes: every block puts
   its media through the same wrapper, so one rule reaches all of them and
   nothing is missed when a new block is added. The hero is excluded — it is
   a full-bleed background, and a rounded corner on it would show the page
   behind.
   ========================================================================== */

/* --------------------------------------------------------------- plates -- */

.fl-image-frame:not(.fl-hero__media) {
  border-radius: 18px 3px 18px 3px;
  overflow: hidden;
  box-shadow:
    0 24px 60px -28px rgba(0, 0, 0, 0.85),
    0 2px 0 0 rgba(255, 255, 255, 0.04) inset;
}

/* Mirror the corners when the image sits on the left, so the soft corners
   always face outward from the copy. */
.fl-story--image-left .fl-image-frame:not(.fl-hero__media),
.fl-layout-grid--media-start .fl-image-frame:not(.fl-hero__media) {
  border-radius: 3px 18px 3px 18px;
}

/* ---------------------------------------------------------------- tiles -- */

.fl-gallery__figure,
.fl-news__media-link {
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 18px 44px -26px rgba(0, 0, 0, 0.8);
}

/* A tile inside a grid never gets the plate treatment, even though it goes
   through the same frame wrapper. */
.fl-gallery__grid .fl-image-frame,
.fl-news__grid .fl-image-frame,
.fl-how-to-enjoy__grid .fl-image-frame,
.fl-shop__grid .fl-image-frame {
  border-radius: 10px;
  box-shadow: 0 18px 44px -26px rgba(0, 0, 0, 0.8);
}

.fl-gallery__grid .fl-image-frame::after,
.fl-news__grid .fl-image-frame::after,
.fl-how-to-enjoy__grid .fl-image-frame::after,
.fl-shop__grid .fl-image-frame::after {
  content: none;
}

/* --------------------------------------------------------------------------
   Scroll drift for tiles

   A grid where every tile drifts the same way looks like the page is sliding.
   Alternating direction by position turns the same motion into depth — the
   eye reads shallow parallax instead of a glitch.

   Half the amplitude of a feature image, because there are many of them.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    @keyframes fl-drift-tile-a {
      from { transform: translate3d(0, 1.8%, 0) scale(1.03); }
      to   { transform: translate3d(0, -1.8%, 0) scale(1.03); }
    }

    @keyframes fl-drift-tile-b {
      from { transform: translate3d(0, -1.2%, 0) scale(1.03); }
      to   { transform: translate3d(0, 1.2%, 0) scale(1.03); }
    }

    .fl-gallery__grid > :nth-child(odd) .fl-gallery__img,
    .fl-news__grid > :nth-child(odd) img {
      animation: fl-drift-tile-a linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    .fl-gallery__grid > :nth-child(even) .fl-gallery__img,
    .fl-news__grid > :nth-child(even) img {
      animation: fl-drift-tile-b linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    /* Hover wins — a drifting tile must not feel unclickable. */
    .fl-gallery__figure:hover .fl-gallery__img,
    .fl-news__media-link:hover img {
      animation-play-state: paused;
    }
  }
}


/* ==========================================================================
   SNS CTA — appears only when the URL is set in FLAMINGO settings

   Nothing here renders until an account URL exists, so the styles sit unused
   until then. That is deliberate: a dead link or a "coming soon" button costs
   more trust than an absent one.
   ========================================================================== */

.fl-sns-cta__lead {
  margin: 0 0 0.9rem;
  font-size: 0.8rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fl-label, rgba(255, 255, 255, 0.55));
}

.fl-sns-cta__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.fl-sns-cta__link {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
}

/* The arrow marks the link as leaving the site. It nudges on hover so the
   affordance is felt, not just seen. */
.fl-sns-cta__arrow {
  font-size: 0.85em;
  transition: transform 0.35s var(--fl-ease);
}

.fl-sns-cta__link:hover .fl-sns-cta__arrow,
.fl-sns-cta__link:focus-visible .fl-sns-cta__arrow {
  transform: translate(0.15em, -0.15em);
}

/* Inside a section, below the content it belongs to. */
.fl-sns-cta-wrap {
  margin-block-start: clamp(2rem, 4vw, 3.25rem);
}

.c-site-footer__sns {
  margin-block-start: 1.25rem;
}

@media (prefers-reduced-motion: reduce) {

  .fl-sns-cta__arrow {
    transition: none;
  }
}

/*
 * A channel that exists as a name but not yet as a destination.
 *
 * Deliberately not styled as a button: it must not look pressable. Same size
 * and rhythm as the linked ones so the row still reads as one list, at about
 * half the presence.
 */
.fl-sns-cta__pending {
  display: inline-flex;
  align-items: center;
  min-height: 2.7rem;
  padding-inline: 1.6rem;
  font-size: 0.68rem;
  letter-spacing: 0.24em;
  color: rgba(255, 255, 255, 0.34);
  border: 1px dashed rgba(255, 255, 255, 0.14);
  cursor: default;
  user-select: none;
}

/*
 * A channel name inside a sentence. Underlined so it is obviously a link
 * without breaking the line of the copy — a button in the middle of a
 * paragraph would stop the reading.
 */
.fl-sns-inline {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(201, 161, 74, 0.55);
  padding-bottom: 0.1em;
  transition: color 0.3s var(--fl-ease), border-color 0.3s var(--fl-ease);
}

.fl-sns-inline:hover,
.fl-sns-inline:focus-visible {
  color: var(--fl-gold, #c9a14a);
  border-color: var(--fl-gold, #c9a14a);
}

@media (prefers-reduced-motion: reduce) {

  .fl-sns-inline {
    transition: none;
  }
}


/* ==========================================================================
   fl-hero-page-motion - the still heroes, given a slow breath

   The home page runs ten slides. Every other page shows one photograph that
   finishes its entrance and then stops dead, so moving from home to any other
   page feels like the site switching off.

   -- The entrance and the loop must meet ------------------------------------

   Measured over the first six seconds, an earlier version of this file jumped
   127 pixels in a single frame at the 1.4s mark on PRODUCT, and 50-70 on three
   other pages. The shared entrance ended at scale(z) while each loop started
   somewhere else, so the difference was paid instantly.

   Three custom properties fix that by giving both halves one source of truth:

       --fl-hero-a    the scale multiplier the loop starts from
       --fl-hero-ax   the horizontal offset the loop starts from
       --fl-hero-ay   the vertical offset the loop starts from

   The entrance ENDS on these values; every loop BEGINS on them. Changing a
   page's opening frame now moves both, so they cannot drift apart again.

   -- Speed ------------------------------------------------------------------

   The reference is on this site already: the home ken-burns moves a corner of
   its image about 10px per second, and nobody has called it slow. These run at
   roughly two thirds of that.

   Just as important as the average is WHERE the slow part falls. These loops
   start at 1.4s, so an ease-in-out - which is motionless at both ends - puts
   its dead spot precisely on the seconds a visitor is actually watching. The
   drift curve is nearly linear for that reason.

   -- Everything is a MULTIPLE of --fl-img-zoom, never an absolute scale ------

   Each hero carries its own zoom, chosen per photograph in the editor: STORY
   sits at 0.98, CONTACT at 1.2, the rest at 1. Animating to a fixed scale
   throws that away - the framing someone chose for that particular image would
   be replaced by a number written here, which has never seen the image.

   -- Room to travel ---------------------------------------------------------

   With transform: scale(s) translate(t) the visible offset is s*t, while the
   image only overhangs its frame by (s-1)/2 on each side. Every pair below
   satisfies s*t <= (s-1)/2 at the SMALLER of its two scales, which is where an
   edge would show first.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {

  /*
   * The entrance, rewritten against the frame's own zoom and against the frame
   * each page's loop wants to start from.
   *
   * blocks/hero/style.css animates 1.12 -> 1.06 in absolute numbers and holds
   * the end state (`both`), so the moment it runs, --fl-img-zoom stops mattering
   * on every page. Below is the same 1.4s move expressed as a ratio.
   *
   * Scoped away from the home page: those ten slides carry their own ken-burns
   * and their own crossfade, and must not be touched.
   */
  body:not(.home) .fl-image-frame .fl-hero__image {
    --fl-hero-a: 1;
    --fl-hero-ax: 0%;
    --fl-hero-ay: 0%;
    animation: fl-hero-in-rel 1.4s var(--fl-ease) both;
  }

  @keyframes fl-hero-in-rel {
    from {
      opacity: 0;
      transform: scale(calc(var(--fl-img-zoom, 1) * var(--fl-hero-a) * 1.06))
                 translate3d(var(--fl-hero-ax), var(--fl-hero-ay), 0);
    }
    to {
      opacity: 1;
      transform: scale(calc(var(--fl-img-zoom, 1) * var(--fl-hero-a)))
                 translate3d(var(--fl-hero-ax), var(--fl-hero-ay), 0);
    }
  }

  /*
   * The loops. Each starts at 1.4s, on the frame the entrance just landed on,
   * and alternates, so it turns around at each end rather than snapping back.
   *
   * The page selectors carry `body` so they weigh the same as the entrance rule
   * above - without it the entrance is one element-selector heavier and
   * silently wins, which is exactly what happened the first time.
   */

  /* STORY - a quiet room. Move towards it.  15% / 19s */
  body.page-story .fl-image-frame .fl-hero__image {
    animation:
      fl-hero-in-rel 1.4s var(--fl-ease) both,
      fl-hero-push 15s var(--fl-ease-drift) 1.4s infinite alternate;
  }

  @keyframes fl-hero-push {
    from { transform: scale(var(--fl-img-zoom, 1)); }
    to   { transform: scale(calc(var(--fl-img-zoom, 1) * 1.15)); }
  }

  /* PRODUCT - the object. Start close, pull back, let it arrive.  12% / 18s
     The only hero that opens tighter than its editor framing rather than on
     it: you cannot pull back from a frame that is already the widest one. */
  body.page-product .fl-image-frame .fl-hero__image {
    --fl-hero-a: 1.12;
    animation:
      fl-hero-in-rel 1.4s var(--fl-ease) both,
      fl-hero-pull 13s var(--fl-ease-drift) 1.4s infinite alternate;
  }

  @keyframes fl-hero-pull {
    from { transform: scale(calc(var(--fl-img-zoom, 1) * 1.12)); }
    to   { transform: scale(var(--fl-img-zoom, 1)); }
  }

  /*
   * HOW TO ENJOY and NEWS both zoom while drifting the opposite way, and the
   * two partly cancel: measured over the four corners they moved at a third
   * of the rate of the other heroes. So the zoom carries most of the motion
   * here and the drift is small enough to read as direction rather than as
   * travel.
   */
  /* HOW TO ENJOY - a serve. Rise, closing in as it goes.  14% + 4% / 12s */
  body.page-how-to-enjoy .fl-image-frame .fl-hero__image {
    --fl-hero-a: 1.06;
    --fl-hero-ay: 2%;
    animation:
      fl-hero-in-rel 1.4s var(--fl-ease) both,
      fl-hero-rise-slow 12s var(--fl-ease-drift) 1.4s infinite alternate;
  }

  @keyframes fl-hero-rise-slow {
    from { transform: scale(calc(var(--fl-img-zoom, 1) * 1.06)) translate3d(0, 2%, 0); }
    to   { transform: scale(calc(var(--fl-img-zoom, 1) * 1.20)) translate3d(0, -2%, 0); }
  }

  /* GALLERY - the cover of an issue. Read across it.  8% of width / 18s */
  body.page-gallery .fl-image-frame .fl-hero__image {
    --fl-hero-a: 1.12;
    --fl-hero-ax: -4%;
    animation:
      fl-hero-in-rel 1.4s var(--fl-ease) both,
      fl-hero-pan 14s var(--fl-ease-drift) 1.4s infinite alternate;
  }

  /* Scale is held constant here - the zoom exists only to buy the overhang the
     sideways travel needs. */
  @keyframes fl-hero-pan {
    from { transform: scale(calc(var(--fl-img-zoom, 1) * 1.12)) translate3d(-4%, 0, 0); }
    to   { transform: scale(calc(var(--fl-img-zoom, 1) * 1.12)) translate3d(4%, 0, 0); }
  }

  /* SHOP - a doorway. Walk towards it.  16% / 19s */
  body.page-shop .fl-image-frame .fl-hero__image {
    animation:
      fl-hero-in-rel 1.4s var(--fl-ease) both,
      fl-hero-approach 15s var(--fl-ease-drift) 1.4s infinite alternate;
  }

  @keyframes fl-hero-approach {
    from { transform: scale(var(--fl-img-zoom, 1)); }
    to   { transform: scale(calc(var(--fl-img-zoom, 1) * 1.16)); }
  }

  /* NEWS - a record. The eye reads downward.  14% + 4% / 12s */
  body.page-news .fl-image-frame .fl-hero__image {
    --fl-hero-a: 1.06;
    --fl-hero-ay: -2%;
    animation:
      fl-hero-in-rel 1.4s var(--fl-ease) both,
      fl-hero-descend 12s var(--fl-ease-drift) 1.4s infinite alternate;
  }

  @keyframes fl-hero-descend {
    from { transform: scale(calc(var(--fl-img-zoom, 1) * 1.06)) translate3d(0, -2%, 0); }
    to   { transform: scale(calc(var(--fl-img-zoom, 1) * 1.20)) translate3d(0, 2%, 0); }
  }

  /*
   * FAQ and CONTACT - pages people open in order to read something or to ask
   * for something. The picture must not compete with the words, so this keeps
   * the gentlest rate on the site. Gentlest, not invisible: the first version
   * was slow enough to read as a still photograph, which is not restraint.
   */
  body.page-faq .fl-image-frame .fl-hero__image,
  body.page-contact .fl-image-frame .fl-hero__image {
    animation:
      fl-hero-in-rel 1.4s var(--fl-ease) both,
      fl-hero-settle 16s var(--fl-ease-drift) 1.4s infinite alternate;
  }

  @keyframes fl-hero-settle {
    from { transform: scale(var(--fl-img-zoom, 1)); }
    to   { transform: scale(calc(var(--fl-img-zoom, 1) * 1.12)); }
  }
}
