{"$schema":"https://ui.artbloom.tech/schema/registry-item.json","name":"sillage","type":"registry:page","title":"Sillage","description":"A night-blooming fragrance house on a scroll-scrubbed film: a 30s take held cool and dark reads as a night garden, its beats — bergamot, night jasmine, oud — fading through the frame, with one silver-jade bloom swelling behind the type as jasmine opens at centre. Below the film, a real house: the composition read plainly, the bottle, where the jasmine is grown, the rest of the collection. Ships the hero film and six graded stills; reduced-motion gets a static fallback.","author":"@artbloom","dependencies":[],"registryDependencies":[],"files":[{"path":"app/sillage/page.tsx","target":"app/sillage/page.tsx","content":"import { SillageView } from \"./sillage-view\"\n\nimport \"./sillage.css\"\n\n/* -------------------------------------------------------------------------\n   SILLAGE — a night-blooming fragrance house on a scroll-scrubbed film.\n\n   The 30s take runs dusk → deep night; its native warm grade is held cool\n   and dark by fixed overlays so the whole thing reads as a night garden.\n   The one orchestrated moment is a silver-jade bloom that swells behind the\n   type as the night-jasmine beat reaches centre, then recedes. Below the\n   film the page becomes a real fragrance house: the composition, the bottle,\n   where the jasmine is grown, the rest of the house.\n\n   A whole page, not a component: `.lmRoot` is the single wrapper every rule\n   in sillage.css hangs off, so the sheet touches nothing else in the app.\n   The film and the six stills ship as real bytes under /sillage/.\n------------------------------------------------------------------------- */\n\nexport const metadata = {\n  title: \"Sillage — night-blooming eau de parfum\",\n  description:\n    \"A night-blooming botanical fragrance house on a scroll-scrubbed film: bergamot to night jasmine to oud, dusk into deep night.\",\n}\n\nexport default function SillagePage() {\n  return <SillageView />\n}\n","type":"registry:page"},{"path":"app/sillage/sillage-view.tsx","target":"app/sillage/sillage-view.tsx","content":"\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\n\n/* -------------------------------------------------------------------------\n   SILLAGE — LUMEN view. LUMINESCENCE direction: botanical night.\n\n   MECHANIC (reused from ../sillage/sillage-view.tsx, visual layer replaced):\n   dependency-free native `video.currentTime` scrubbing eased by a\n   requestAnimationFrame loop toward a scroll-derived target, CSS sticky\n   stage, duration captured with the cached-load readyState guard, and a\n   prefers-reduced-motion static fallback.\n\n   The film runs 30s / dusk → deep night. Its native grade drifts warm/amber\n   at the base beats; we hold it COOL and DARK with fixed overlays so the\n   whole thing reads as a night garden.\n\n   The signature is ONE orchestrated luminous moment: as the HEART beat\n   (night jasmine) reaches center, a soft silver-jade radial glow blooms\n   behind the type — a single reveal tied to the scrub position, peaking at\n   mid-scroll then receding. Everything else is kept quiet and dark.\n------------------------------------------------------------------------- */\n\ntype Beat = {\n  key: string\n  /* delicate italic descriptor, in place of a tracked-out caps eyebrow */\n  note: string\n  title: string\n  body: string\n  still: string\n}\n\n/* Copy is written, not lorem — the real arc of the fragrance, dusk into\n   deep night: bergamot (first light) → night jasmine (the bloom, the\n   emotional center) → oud & amber (the warm trail that lingers). */\nconst HERO = {\n  wordmark: \"sillage\",\n  tagline: \"the trail a scent keeps in a room after you have gone\",\n  meta: \"A night-blooming botanical. Eau de parfum, opened at dusk.\"\n}\n\nconst BEATS: Beat[] = [\n  {\n    key: \"top\",\n    note: \"the first light\",\n    title: \"Bergamot, cold-pressed\",\n    body: \"Green rind and a thread of oil, bright for a breath before the dark takes it back.\",\n    still: \"/sillage/still-2.jpg\"\n  },\n  {\n    key: \"heart\",\n    note: \"the bloom, opening in the dark\",\n    title: \"Night jasmine\",\n    body: \"Sambac unfurling in the warm hours, saffron drawn through it like a wire. The flower the night is named for.\",\n    still: \"/sillage/still-3.jpg\"\n  },\n  {\n    key: \"base\",\n    note: \"what lingers\",\n    title: \"Oud and amber\",\n    body: \"Aged wood and resin kept low against the skin, the warmth that stays long after the flowers have gone quiet.\",\n    still: \"/sillage/still-4.jpg\"\n  }\n]\n\nconst CLOSING = {\n  line: \"What remains is the sillage.\",\n  cta: \"read the composition\"\n}\n\n/* ---- content below the film: the complete site ------------------------- *\n   The scrub film is the ouverture. Once it releases, the page becomes a real\n   fragrance house: the composition read plainly, the bottle you can buy, where\n   the jasmine is grown, the rest of the house, and the ground matter. All of it\n   holds the same night — deep ink, one silver-jade light, serif voice. */\n\n/* the olfactory pyramid, stated as reference rather than re-narrated */\ntype Accord = { note: string; matter: string }\nconst COMPOSITION: { tier: string; accord: Accord[] }[] = [\n  {\n    tier: \"Top\",\n    accord: [\n      { note: \"Bergamot\", matter: \"cold-pressed, Calabria\" },\n      { note: \"Green mandarin\", matter: \"leaf and rind\" }\n    ]\n  },\n  {\n    tier: \"Heart\",\n    accord: [\n      { note: \"Sambac jasmine\", matter: \"night-picked, Grasse\" },\n      { note: \"Saffron\", matter: \"a single thread\" }\n    ]\n  },\n  {\n    tier: \"Base\",\n    accord: [\n      { note: \"Oud\", matter: \"aged twelve years\" },\n      { note: \"Amber\", matter: \"labdanum resin\" },\n      { note: \"Musk\", matter: \"kept close to the skin\" }\n    ]\n  }\n]\n\nconst PRODUCT = {\n  kicker: \"Eau de parfum\",\n  name: \"Sillage\",\n  subtitle: \"No. 1 — Night Jasmine\",\n  copy:\n    \"A night-blooming botanical, composed to be opened at dusk. It wears close for the first hour, then leaves the room slowly — the trail it keeps is the point.\",\n  concentration: \"24% · extrait strength\",\n  sizes: [\n    { ml: \"50 ml\", price: \"$220\" },\n    { ml: \"100 ml\", price: \"$340\" }\n  ],\n  cta: \"add to bag\",\n  still: \"/sillage/still-5.jpg\"\n}\n\nconst PROVENANCE = {\n  note: \"where it begins\",\n  title: \"Picked in the dark, in Grasse\",\n  body:\n    \"Sambac jasmine gives up the most of itself in the hours before dawn, so it is harvested by hand between three and sunrise, while the flower is still open. It takes roughly eight thousand blossoms to draw a single gram of the absolute at the heart of this scent.\",\n  figure: \"/sillage/still-6.jpg\",\n  stat: [\n    { value: \"8,000\", label: \"blossoms to the gram\" },\n    { value: \"03:00\", label: \"harvest, before first light\" },\n    { value: \"Grasse\", label: \"the only fields we use\" }\n  ]\n}\n\ntype Companion = { name: string; note: string; mood: string; still: string }\nconst COLLECTION: Companion[] = [\n  {\n    name: \"Brume\",\n    note: \"No. 2 — Cold Morning\",\n    mood: \"Iris and wet stone, the fog before the sun burns it off.\",\n    still: \"/sillage/still-1.jpg\"\n  },\n  {\n    name: \"Cendre\",\n    note: \"No. 3 — After the Fire\",\n    mood: \"Birch smoke and warm ash, the room once the flame is out.\",\n    still: \"/sillage/still-4.jpg\"\n  },\n  {\n    name: \"Sel\",\n    note: \"No. 4 — Low Tide\",\n    mood: \"Salt, driftwood and skin, the coast at the turn of the night.\",\n    still: \"/sillage/still-2.jpg\"\n  }\n]\n\nconst FOOTER = {\n  statement: \"A small house making night-blooming botanicals in Grasse.\",\n  columns: [\n    { head: \"Shop\", links: [\"The fragrance\", \"Discovery set\", \"Refills\", \"Gift cards\"] },\n    { head: \"House\", links: [\"Our story\", \"The atelier\", \"Sourcing\", \"Journal\"] },\n    { head: \"Care\", links: [\"Shipping\", \"Returns\", \"Contact\", \"Stockists\"] }\n  ],\n  newsletterNote: \"Word before each release. Nothing else.\",\n  fine: \"© Sillage Parfums — Grasse & New York\"\n}\n\n/** smoothstep — soft ends so the beats trade cleanly */\nfunction smooth(t: number) {\n  const x = Math.min(1, Math.max(0, t))\n  return x * x * (3 - 2 * x)\n}\n\nexport function SillageView() {\n  const [reduced, setReduced] = useState(false)\n  const [decided, setDecided] = useState(false)\n\n  // detect prefers-reduced-motion after mount so SSR stays deterministic\n  useEffect(() => {\n    const mq = window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n    const apply = () => setReduced(mq.matches)\n    apply()\n    setDecided(true)\n    mq.addEventListener?.(\"change\", apply)\n    return () => mq.removeEventListener?.(\"change\", apply)\n  }, [])\n\n  if (decided && reduced) return <LumenStatic />\n  return <LumenMotion />\n}\n\n/* ---- motion mode -------------------------------------------------------- */\n\nfunction LumenMotion() {\n  const stageRef = useRef<HTMLDivElement>(null)\n  const videoRef = useRef<HTMLVideoElement>(null)\n  const panelsRef = useRef<HTMLDivElement>(null)\n  const bloomRef = useRef<HTMLDivElement>(null)\n  const cueRef = useRef<HTMLDivElement>(null)\n\n  const durationRef = useRef(0)\n  const targetTimeRef = useRef(0)\n  const rafRef = useRef(0)\n  const runningRef = useRef(false)\n  const idleFramesRef = useRef(0)\n\n  useEffect(() => {\n    const video = videoRef.current\n    const stage = stageRef.current\n    if (!video || !stage) return\n\n    const captureDuration = () => {\n      const d = video.duration\n      if (Number.isFinite(d) && d > 0) durationRef.current = d\n    }\n    // GOTCHA: on a cached/fast load, loadedmetadata can fire before this\n    // effect runs. Read readyState immediately as well as listening.\n    if (video.readyState >= 1) captureDuration()\n    video.addEventListener(\"loadedmetadata\", captureDuration)\n\n    /* one frame: ease currentTime toward the scroll target, then write the\n       panel opacities and the single jasmine bloom from scroll position. */\n    const tick = () => {\n      const dur = durationRef.current\n      const vh = window.innerHeight\n      const rect = stage.getBoundingClientRect()\n      const travel = rect.height - vh\n      const progress = travel > 0 ? Math.min(1, Math.max(0, -rect.top / travel)) : 0\n\n      // ---- video scrub (eased) ----\n      if (dur > 0) {\n        const target = Math.min(dur - 0.05, progress * dur)\n        targetTimeRef.current = target\n        const cur = video.currentTime\n        const diff = target - cur\n        if (Math.abs(diff) > 0.012) {\n          const next = cur + diff * 0.18 // ease ~18% of the gap per frame\n          if (video.readyState >= 1 && !video.seeking) {\n            try {\n              video.currentTime = Math.min(dur - 0.05, Math.max(0, next))\n            } catch {\n              /* seeking can throw mid-load; the next tick retries */\n            }\n          }\n          idleFramesRef.current = 0\n        } else {\n          idleFramesRef.current += 1\n        }\n      }\n\n      // ---- panels: hero, 3 beats, closing — crossfade on their centers ----\n      // 5 panels across the scroll; the HEART panel sits dead center (0.5).\n      const panels = panelsRef.current?.children\n      if (panels) {\n        const n = panels.length\n        const seg = 1 / n\n        for (let i = 0; i < panels.length; i++) {\n          const el = panels[i] as HTMLElement\n          const center = (i + 0.5) * seg\n          const d = Math.abs(progress - center) / seg\n          let o = 1 - smooth((d - 0.34) / 0.16)\n          // hold the hero at the very top and the closing at the very bottom\n          if (i === 0 && progress < center) o = 1\n          if (i === n - 1 && progress > center) o = 1\n          el.style.opacity = o.toFixed(3)\n          el.style.transform = `translate3d(0, ${((1 - o) * 20).toFixed(1)}px, 0)`\n          el.style.visibility = o < 0.01 ? \"hidden\" : \"visible\"\n        }\n      }\n\n      // ---- THE signature: one luminous bloom, centered on the heart beat ----\n      // Panels are [hero, top, heart, base, closing] → heart center = 2.5/5 = 0.5.\n      // A single smooth hump peaks at 0.5 and falls away either side; the glow\n      // exists (dim) at rest and swells to full exactly as jasmine opens.\n      const bloom = bloomRef.current\n      if (bloom) {\n        const dist = Math.abs(progress - 0.5) / 0.24 // full within ~0.12 of center\n        const hump = 1 - smooth(dist) // 1 at center → 0 by ~0.24 away\n        const intensity = 0.14 + hump * 0.86 // never fully dark: a resting ember\n        bloom.style.opacity = intensity.toFixed(3)\n        bloom.style.transform = `translate(-50%, -50%) scale(${(0.82 + hump * 0.42).toFixed(3)})`\n      }\n\n      // ---- scroll cue fades once scrubbing begins ----\n      if (cueRef.current) {\n        cueRef.current.style.opacity = Math.max(0, 1 - progress * 9).toFixed(3)\n      }\n\n      if (idleFramesRef.current < 6) {\n        rafRef.current = requestAnimationFrame(tick)\n      } else {\n        runningRef.current = false\n      }\n    }\n\n    const kick = () => {\n      idleFramesRef.current = 0\n      if (!runningRef.current) {\n        runningRef.current = true\n        rafRef.current = requestAnimationFrame(tick)\n      }\n    }\n\n    // paint the initial frame (progress 0) even before duration is known\n    kick()\n    // once metadata/first frame land, sync the film to the loaded scroll pos\n    video.addEventListener(\"loadeddata\", kick)\n\n    window.addEventListener(\"scroll\", kick, { passive: true })\n    window.addEventListener(\"resize\", kick)\n\n    return () => {\n      cancelAnimationFrame(rafRef.current)\n      runningRef.current = false\n      window.removeEventListener(\"scroll\", kick)\n      window.removeEventListener(\"resize\", kick)\n      video.removeEventListener(\"loadedmetadata\", captureDuration)\n      video.removeEventListener(\"loadeddata\", kick)\n    }\n  }, [])\n\n  return (\n    <div className=\"lmRoot\" id=\"top\">\n      <div className=\"lmStage\" ref={stageRef}>\n        <div className=\"lmSticky\">\n          <video\n            className=\"lmVideo\"\n            ref={videoRef}\n            src=\"/sillage/journey.mp4\"\n            poster=\"/sillage/poster.jpg\"\n            muted\n            playsInline\n            preload=\"auto\"\n            aria-hidden=\"true\"\n          />\n          {/* grade the warm film COOL + DARK so it reads as night */}\n          <div className=\"lmTint\" aria-hidden=\"true\" />\n          <div className=\"lmDeepen\" aria-hidden=\"true\" />\n\n          {/* the one luminous moment — a soft silver-jade bloom behind the type */}\n          <div className=\"lmBloom\" ref={bloomRef} aria-hidden=\"true\" />\n\n          <div className=\"lmPanels\" ref={panelsRef}>\n            <section className=\"lmPanel lmPanel--hero\" aria-label=\"Sillage\">\n              <div className=\"lmPanelInner\">\n                <h1 className=\"lmWordmark\">{HERO.wordmark}</h1>\n                <p className=\"lmTagline\">{HERO.tagline}</p>\n                <p className=\"lmMeta\">{HERO.meta}</p>\n              </div>\n            </section>\n\n            {BEATS.map((b) => (\n              <section\n                className={`lmPanel lmPanel--${b.key}`}\n                key={b.key}\n                aria-label={b.title}\n              >\n                <div className=\"lmPanelInner\">\n                  <p className=\"lmNote\">{b.note}</p>\n                  <h2 className=\"lmTitle\">{b.title}</h2>\n                  <p className=\"lmBody\">{b.body}</p>\n                </div>\n              </section>\n            ))}\n\n            <section className=\"lmPanel lmPanel--closing\" aria-label=\"What remains\">\n              <div className=\"lmPanelInner\">\n                <p className=\"lmClosing\">{CLOSING.line}</p>\n                <a className=\"lmCta\" href=\"#top\">\n                  {CLOSING.cta}\n                </a>\n              </div>\n            </section>\n          </div>\n\n          <div className=\"lmCue\" ref={cueRef} aria-hidden=\"true\">\n            <span className=\"lmCueWord\">scroll into the dark</span>\n            <span className=\"lmCueLine\" />\n          </div>\n        </div>\n      </div>\n\n      <LumenSite />\n    </div>\n  )\n}\n\n/* ---- reduced-motion / static mode --------------------------------------\n   No scrubbing, no rAF. The poster carries the hero; each beat is a still.\n   The jasmine bloom is CSS-only here (does not depend on motion) so the\n   signature still reads. */\n\nfunction LumenStatic() {\n  return (\n    <div className=\"lmRoot lmRoot--static\" id=\"top\">\n      <header className=\"lmHeroStatic\">\n        {/* eslint-disable-next-line @next/next/no-img-element */}\n        <img className=\"lmHeroStaticImg\" src=\"/sillage/poster.jpg\" alt=\"\" />\n        <div className=\"lmTint\" aria-hidden=\"true\" />\n        <div className=\"lmDeepen\" aria-hidden=\"true\" />\n        <div className=\"lmPanelInner lmHeroStaticInner\">\n          <h1 className=\"lmWordmark\">{HERO.wordmark}</h1>\n          <p className=\"lmTagline\">{HERO.tagline}</p>\n          <p className=\"lmMeta\">{HERO.meta}</p>\n        </div>\n      </header>\n\n      <div className=\"lmSections\">\n        {BEATS.map((b) => (\n          <section\n            className={`lmSection lmSection--${b.key}`}\n            key={b.key}\n            aria-label={b.title}\n          >\n            <figure className=\"lmSectionFig\">\n              {/* eslint-disable-next-line @next/next/no-img-element */}\n              <img src={b.still} alt={`${b.title}, ${b.note}`} loading=\"lazy\" decoding=\"async\" />\n              <span className=\"lmSectionGrade\" aria-hidden=\"true\" />\n              {/* the heart keeps its bloom, CSS-only, so the signature survives */}\n              {b.key === \"heart\" ? <span className=\"lmBloom lmBloom--static\" aria-hidden=\"true\" /> : null}\n            </figure>\n            <div className=\"lmSectionText\">\n              <p className=\"lmNote\">{b.note}</p>\n              <h2 className=\"lmTitle\">{b.title}</h2>\n              <p className=\"lmBody\">{b.body}</p>\n            </div>\n          </section>\n        ))}\n      </div>\n\n      <section className=\"lmClosingStatic\" aria-label=\"What remains\">\n        <p className=\"lmClosing\">{CLOSING.line}</p>\n        <a className=\"lmCta\" href=\"#composition\">\n          {CLOSING.cta}\n        </a>\n      </section>\n\n      <LumenSite />\n    </div>\n  )\n}\n\n/* ---- the complete site, below the hero (shared by both modes) ----------- *\n   These sections scroll normally after the cinematic opening. They keep the\n   night — deep ink ground, one silver-jade accent, Fraunces/Instrument serifs —\n   and spend no second bold moment: no cards, no shadows, no numbered chrome,\n   just graded imagery, generous space, and the type doing the work. */\n\nfunction LumenSite() {\n  return (\n    <div className=\"lmSite\">\n      {/* THE COMPOSITION — the pyramid, read plainly, not re-narrated */}\n      <section className=\"lmComposition\" id=\"composition\" aria-label=\"The composition\">\n        <div className=\"lmCompHead\">\n          <p className=\"lmNote\">how it is built</p>\n          <h2 className=\"lmCompTitle\">The composition</h2>\n        </div>\n        <ol className=\"lmPyramid\">\n          {COMPOSITION.map((tier) => (\n            <li className=\"lmTier\" key={tier.tier}>\n              <span className=\"lmTierName\">{tier.tier}</span>\n              <div className=\"lmTierNotes\">\n                {tier.accord.map((a) => (\n                  <p className=\"lmAccord\" key={a.note}>\n                    <span className=\"lmAccordNote\">{a.note}</span>\n                    <span className=\"lmAccordMatter\">{a.matter}</span>\n                  </p>\n                ))}\n              </div>\n            </li>\n          ))}\n        </ol>\n      </section>\n\n      {/* THE BOTTLE — the object you can buy. Graded still, price as quiet\n          text, a hairline CTA — no e-commerce card. */}\n      <section className=\"lmProduct\" id=\"fragrance\" aria-label={PRODUCT.name}>\n        <figure className=\"lmProductFig\">\n          {/* eslint-disable-next-line @next/next/no-img-element */}\n          <img\n            src={PRODUCT.still}\n            alt=\"Sillage No. 1, Night Jasmine — the bottle at dusk\"\n            loading=\"lazy\"\n            decoding=\"async\"\n          />\n          <span className=\"lmSectionGrade\" aria-hidden=\"true\" />\n        </figure>\n        <div className=\"lmProductText\">\n          <p className=\"lmNote\">{PRODUCT.kicker}</p>\n          <h2 className=\"lmProductName\">{PRODUCT.name}</h2>\n          <p className=\"lmProductSub\">{PRODUCT.subtitle}</p>\n          <p className=\"lmProductCopy\">{PRODUCT.copy}</p>\n          <p className=\"lmProductConc\">{PRODUCT.concentration}</p>\n          <div className=\"lmPrices\">\n            {PRODUCT.sizes.map((s) => (\n              <p className=\"lmPrice\" key={s.ml}>\n                <span className=\"lmPriceMl\">{s.ml}</span>\n                <span className=\"lmPriceValue\">{s.price}</span>\n              </p>\n            ))}\n          </div>\n          <button className=\"lmBuy\" type=\"button\">\n            {PRODUCT.cta}\n          </button>\n        </div>\n      </section>\n\n      {/* PROVENANCE — where the jasmine comes from. Full-bleed graded still,\n          text over the deepened frame, three quiet figures. */}\n      <section className=\"lmProvenance\" aria-label={PROVENANCE.title}>\n        {/* eslint-disable-next-line @next/next/no-img-element */}\n        <img\n          className=\"lmProvImg\"\n          src={PROVENANCE.figure}\n          alt=\"\"\n          aria-hidden=\"true\"\n          loading=\"lazy\"\n          decoding=\"async\"\n        />\n        <div className=\"lmTint\" aria-hidden=\"true\" />\n        <div className=\"lmProvScrim\" aria-hidden=\"true\" />\n        <div className=\"lmProvInner\">\n          <p className=\"lmNote\">{PROVENANCE.note}</p>\n          <h2 className=\"lmProvTitle\">{PROVENANCE.title}</h2>\n          <p className=\"lmProvBody\">{PROVENANCE.body}</p>\n          <dl className=\"lmStats\">\n            {PROVENANCE.stat.map((s) => (\n              <div className=\"lmStat\" key={s.label}>\n                <dt className=\"lmStatValue\">{s.value}</dt>\n                <dd className=\"lmStatLabel\">{s.label}</dd>\n              </div>\n            ))}\n          </dl>\n        </div>\n      </section>\n\n      {/* THE HOUSE — companion scents, an editorial list, not a card grid.\n          Each is a full-width row: large graded still, name in serif. */}\n      <section className=\"lmCollection\" id=\"house\" aria-label=\"The house\">\n        <div className=\"lmCollHead\">\n          <p className=\"lmNote\">the rest of the house</p>\n          <h2 className=\"lmCompTitle\">Four scents, one night</h2>\n        </div>\n        <ul className=\"lmCompanions\">\n          {COLLECTION.map((c) => (\n            <li className=\"lmCompanion\" key={c.name}>\n              <a className=\"lmCompanionLink\" href=\"#fragrance\">\n                <figure className=\"lmCompanionFig\">\n                  {/* eslint-disable-next-line @next/next/no-img-element */}\n                  <img\n                    src={c.still}\n                    alt={`${c.name} — ${c.note}`}\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                  <span className=\"lmSectionGrade\" aria-hidden=\"true\" />\n                </figure>\n                <div className=\"lmCompanionText\">\n                  <p className=\"lmNote\">{c.note}</p>\n                  <h3 className=\"lmCompanionName\">{c.name}</h3>\n                  <p className=\"lmCompanionMood\">{c.mood}</p>\n                  <span className=\"lmCompanionCue\" aria-hidden=\"true\">\n                    smell it\n                  </span>\n                </div>\n              </a>\n            </li>\n          ))}\n        </ul>\n      </section>\n\n      {/* FOOTER — brand ground matter and the one form on the page */}\n      <footer className=\"lmFooter\" aria-label=\"Footer\">\n        <div className=\"lmFootTop\">\n          <div className=\"lmFootBrand\">\n            <span className=\"lmFootMark\">sillage</span>\n            <p className=\"lmFootStatement\">{FOOTER.statement}</p>\n          </div>\n          <nav className=\"lmFootNav\">\n            {FOOTER.columns.map((col) => (\n              <div className=\"lmFootCol\" key={col.head}>\n                <p className=\"lmFootHead\">{col.head}</p>\n                <ul>\n                  {col.links.map((l) => (\n                    <li key={l}>\n                      <a href=\"#fragrance\">{l}</a>\n                    </li>\n                  ))}\n                </ul>\n              </div>\n            ))}\n          </nav>\n        </div>\n\n        <form\n          className=\"lmSignup\"\n          aria-label=\"Release notices\"\n          onSubmit={(e) => e.preventDefault()}\n        >\n          <label className=\"lmSignupLabel\" htmlFor=\"lm-email\">\n            {FOOTER.newsletterNote}\n          </label>\n          <div className=\"lmSignupRow\">\n            <input\n              id=\"lm-email\"\n              className=\"lmSignupInput\"\n              type=\"email\"\n              inputMode=\"email\"\n              autoComplete=\"email\"\n              placeholder=\"your email\"\n            />\n            <button className=\"lmSignupBtn\" type=\"submit\">\n              join\n            </button>\n          </div>\n        </form>\n\n        <p className=\"lmFine\">{FOOTER.fine}</p>\n      </footer>\n    </div>\n  )\n}\n","type":"registry:component"},{"path":"app/sillage/sillage.css","target":"app/sillage/sillage.css","content":"/* -------------------------------------------------------------------------\n   SILLAGE — LUMEN. LUMINESCENCE direction: a botanical night, glowing.\n\n   Everything is prefixed `lm` and scoped under `.lmRoot`. No rule here\n   touches html, body, :root, or any bare element outside `.lmRoot`, and the\n   background is set on `.lmRoot` only — so this shares the bare origin with\n   its neighbours without either leaking.\n\n   Boldness is spent in exactly ONE place: the silver-jade bloom that opens\n   behind the jasmine heart. Everything else is deep, cool and still.\n------------------------------------------------------------------------- */\n\n.lmRoot {\n  /* --- palette: deep ink-blue night + a single luminous silver-jade --- */\n  --lm-void: #080d14; /* deep ink-blue black — the night */\n  --lm-void-2: #0b121c; /* a half-tone up, for section seams */\n  --lm-jade: #bfe3d0; /* the one accent — moonlit silver-jade */\n  --lm-jade-soft: rgba(191, 227, 208, 0.5);\n  --lm-ivory: #f2efe6; /* luminous ivory — the type */\n  --lm-ivory-dim: rgba(242, 239, 230, 0.72);\n  --lm-ivory-faint: rgba(242, 239, 230, 0.46);\n\n  /* grading layers used to hold the warm film cool + dark */\n  --lm-tint: #0a2230; /* cool teal-blue wash over the film */\n  --lm-sink: 4, 8, 14; /* rgb of near-void, for scrims */\n\n  --lm-gut: clamp(1.35rem, 6vw, 4rem);\n  --lm-measure: 40ch;\n\n  box-sizing: border-box;\n  min-height: 100vh;\n  background: var(--lm-void);\n  color: var(--lm-ivory);\n  color-scheme: dark;\n  font-family: var(--font-geist), \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: clamp(0.95rem, 1.5vw, 1.0625rem);\n  font-weight: 400;\n  line-height: 1.6;\n  letter-spacing: -0.003em;\n  -webkit-font-smoothing: antialiased;\n  text-rendering: optimizeLegibility;\n}\n\n.lmRoot *,\n.lmRoot *::before,\n.lmRoot *::after {\n  box-sizing: inherit;\n}\n\n.lmRoot :where(h1, h2, h3, p, figure, figcaption) {\n  margin: 0;\n}\n\n.lmRoot :where(a) {\n  color: inherit;\n  text-decoration: none;\n}\n\n.lmRoot :where(a):focus-visible,\n.lmRoot :where(video):focus-visible {\n  outline: 2px solid var(--lm-jade);\n  outline-offset: 4px;\n  border-radius: 2px;\n}\n\n/* --------------------------------------------------------------------- *\n   MOTION MODE — sticky stage, scroll-scrubbed film\n * --------------------------------------------------------------------- */\n\n/* 5 panels across the scroll → ~520vh, inside the 450–550vh brief */\n.lmStage {\n  position: relative;\n  height: 520vh;\n}\n\n.lmSticky {\n  position: sticky;\n  top: 0;\n  height: 100vh;\n  overflow: hidden;\n  background: var(--lm-void);\n}\n\n/* the film, full-bleed. Its native grade drifts warm/amber; we darken and\n   desaturate it here, then recolour it cool with the tint layer above. */\n.lmVideo {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  z-index: 0;\n  filter: brightness(0.66) saturate(0.5) contrast(1.06);\n}\n\n/* GRADE 1 — a cool teal-blue wash. `color` blend takes this layer's hue over\n   the (now desaturated) film, pulling the whole frame toward night. */\n.lmTint {\n  position: absolute;\n  inset: 0;\n  z-index: 1;\n  background: linear-gradient(180deg, #0c2b3a 0%, #0a1f30 48%, #081524 100%);\n  mix-blend-mode: color;\n  opacity: 0.62;\n  pointer-events: none;\n}\n\n/* GRADE 2 — sink the frame into the void: a soft central veil for type\n   legibility, deepening hard to near-black at the edges. */\n.lmDeepen {\n  position: absolute;\n  inset: 0;\n  z-index: 2;\n  background:\n    radial-gradient(\n      130% 100% at 50% 42%,\n      rgba(var(--lm-sink), 0.28) 0%,\n      rgba(var(--lm-sink), 0.52) 52%,\n      rgba(var(--lm-sink), 0.86) 100%\n    ),\n    linear-gradient(180deg, rgba(var(--lm-sink), 0.5) 0%, rgba(var(--lm-sink), 0) 26%);\n  pointer-events: none;\n}\n\n/* THE SIGNATURE — one luminous bloom. Silver-jade radial light that swells\n   behind the type as the jasmine heart opens. `screen` adds light to the\n   night; JS drives opacity + scale from the scrub position. */\n.lmBloom {\n  position: absolute;\n  left: 50%;\n  top: 44%;\n  z-index: 3;\n  width: min(150vh, 148vw);\n  height: min(150vh, 148vw);\n  transform: translate(-50%, -50%) scale(0.82);\n  border-radius: 50%;\n  background: radial-gradient(\n    circle at center,\n    rgba(191, 227, 208, 0.9) 0%,\n    rgba(160, 214, 196, 0.42) 24%,\n    rgba(120, 180, 180, 0.16) 46%,\n    rgba(80, 140, 160, 0.05) 62%,\n    rgba(8, 13, 20, 0) 74%\n  );\n  mix-blend-mode: screen;\n  filter: blur(14px);\n  opacity: 0.12;\n  pointer-events: none;\n  will-change: opacity, transform;\n}\n\n/* panels, stacked and centred; JS crossfades them on their scroll centres */\n.lmPanels {\n  position: absolute;\n  inset: 0;\n  z-index: 4;\n}\n\n.lmPanel {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 12vh var(--lm-gut) 14vh;\n  will-change: opacity, transform;\n  /* PRE-JS STATE — the fix for the pile-up. The scrub loop assigns each\n     panel's opacity from scroll position, but until it first runs (SSR'd\n     markup, the paint before hydration, a resting preview frame) every panel\n     would otherwise sit at its default opacity of 1 and stack on top of the\n     hero. Start them hidden; the hero below overrides itself back to visible,\n     so the resting state already matches scroll progress 0 — only \"sillage\"\n     shows, and JS takes it from there. */\n  opacity: 0;\n  visibility: hidden;\n}\n\n/* the hero is the one panel visible at rest (progress 0), so it is the one\n   panel that opts back in before the scrub loop touches anything */\n.lmPanel--hero {\n  opacity: 1;\n  visibility: visible;\n}\n\n.lmPanelInner {\n  width: 100%;\n  max-width: var(--lm-measure);\n  text-align: center;\n}\n\n/* --- hero --- */\n.lmWordmark {\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 144, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 330;\n  font-size: clamp(3.6rem, 17vw, 11rem);\n  line-height: 0.9;\n  letter-spacing: -0.02em;\n  color: var(--lm-ivory);\n  /* legibility over film — a faint luminous halo, not decoration */\n  text-shadow: 0 1px 60px rgba(191, 227, 208, 0.14), 0 2px 30px rgba(var(--lm-sink), 0.6);\n}\n\n.lmTagline {\n  margin-top: clamp(1.1rem, 2.4vw, 1.9rem);\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(1.15rem, 3vw, 1.7rem);\n  line-height: 1.4;\n  letter-spacing: 0.002em;\n  color: var(--lm-ivory);\n  text-wrap: balance;\n  text-shadow: 0 1px 24px rgba(var(--lm-sink), 0.7);\n}\n\n.lmMeta {\n  margin-top: clamp(1.6rem, 3.4vw, 2.6rem);\n  font-size: clamp(0.82rem, 1.5vw, 0.95rem);\n  line-height: 1.6;\n  color: var(--lm-ivory-faint);\n  text-shadow: 0 1px 18px rgba(var(--lm-sink), 0.7);\n}\n\n/* --- beats (top / heart / base) --- */\n.lmNote {\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(1rem, 2.3vw, 1.35rem);\n  line-height: 1.4;\n  color: var(--lm-jade);\n  letter-spacing: 0.004em;\n}\n\n.lmTitle {\n  margin-top: clamp(0.5rem, 1.4vw, 0.9rem);\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 72, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 360;\n  font-size: clamp(2.3rem, 8.5vw, 5rem);\n  line-height: 0.98;\n  letter-spacing: -0.015em;\n  color: var(--lm-ivory);\n  text-wrap: balance;\n  text-shadow: 0 2px 40px rgba(var(--lm-sink), 0.6);\n}\n\n.lmBody {\n  margin: clamp(1.1rem, 2.6vw, 1.7rem) auto 0;\n  max-width: 34ch;\n  font-size: clamp(1rem, 1.9vw, 1.15rem);\n  line-height: 1.65;\n  color: var(--lm-ivory-dim);\n  text-wrap: pretty;\n  text-shadow: 0 1px 22px rgba(var(--lm-sink), 0.72);\n}\n\n/* the heart title gets the light — a touch of the accent in its halo, in\n   concert with the bloom behind it (the one bold moment) */\n.lmPanel--heart .lmTitle {\n  text-shadow: 0 0 46px rgba(191, 227, 208, 0.3), 0 2px 40px rgba(var(--lm-sink), 0.5);\n}\n\n/* --- closing --- */\n.lmClosing {\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 96, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 340;\n  font-size: clamp(1.9rem, 6.5vw, 3.6rem);\n  line-height: 1.1;\n  letter-spacing: -0.012em;\n  color: var(--lm-ivory);\n  text-wrap: balance;\n  text-shadow: 0 2px 40px rgba(var(--lm-sink), 0.6);\n}\n\n.lmCta {\n  display: inline-block;\n  margin-top: clamp(1.8rem, 4vw, 2.8rem);\n  padding-bottom: 0.35rem;\n  font-size: clamp(0.9rem, 1.7vw, 1rem);\n  letter-spacing: 0.05em;\n  color: var(--lm-jade);\n  border-bottom: 1px solid var(--lm-jade-soft);\n  transition: border-color 0.4s ease, color 0.4s ease;\n}\n\n.lmCta:hover {\n  color: var(--lm-ivory);\n  border-color: var(--lm-ivory);\n}\n\n/* --- scroll cue --- */\n.lmCue {\n  position: absolute;\n  left: 50%;\n  bottom: clamp(1.6rem, 4vh, 3rem);\n  transform: translateX(-50%);\n  z-index: 5;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 0.9rem;\n  color: var(--lm-ivory-faint);\n  pointer-events: none;\n}\n\n.lmCueWord {\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(0.85rem, 1.6vw, 0.98rem);\n  letter-spacing: 0.01em;\n}\n\n.lmCueLine {\n  width: 1px;\n  height: clamp(2.2rem, 6vh, 3.4rem);\n  background: linear-gradient(180deg, var(--lm-jade-soft), rgba(191, 227, 208, 0));\n}\n\n/* --------------------------------------------------------------------- *\n   REDUCED-MOTION / STATIC MODE — poster hero + stills, bloom preserved\n * --------------------------------------------------------------------- */\n\n.lmHeroStatic {\n  position: relative;\n  min-height: 100vh;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 12vh var(--lm-gut);\n  overflow: hidden;\n}\n\n.lmHeroStaticImg {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  z-index: 0;\n  filter: brightness(0.66) saturate(0.5) contrast(1.06);\n}\n\n.lmHeroStaticInner {\n  position: relative;\n  z-index: 4;\n}\n\n.lmSections {\n  /* seams a half-tone up from the void so the sections breathe */\n  background: linear-gradient(180deg, var(--lm-void) 0%, var(--lm-void-2) 50%, var(--lm-void) 100%);\n}\n\n.lmSection {\n  max-width: 74rem;\n  margin: 0 auto;\n  padding: clamp(4rem, 12vh, 9rem) var(--lm-gut);\n  display: grid;\n  gap: clamp(1.8rem, 5vw, 4rem);\n  align-items: center;\n}\n\n@media (min-width: 820px) {\n  .lmSection {\n    grid-template-columns: 1fr 1fr;\n  }\n  .lmSection--heart {\n    /* keep the heart image on the left so its bloom leads the eye */\n    direction: ltr;\n  }\n}\n\n.lmSectionFig {\n  position: relative;\n  aspect-ratio: 4 / 5;\n  overflow: hidden;\n  border-radius: 2px;\n}\n\n.lmSectionFig img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  filter: brightness(0.72) saturate(0.55) contrast(1.05);\n}\n\n.lmSectionGrade {\n  position: absolute;\n  inset: 0;\n  background: linear-gradient(180deg, #0c2b3a 0%, #081524 100%);\n  mix-blend-mode: color;\n  opacity: 0.55;\n  pointer-events: none;\n}\n\n/* the signature survives without motion: a CSS-only bloom on the heart still */\n.lmBloom--static {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  width: 150%;\n  height: 150%;\n  transform: translate(-50%, -50%);\n  border-radius: 50%;\n  background: radial-gradient(\n    circle at center,\n    rgba(191, 227, 208, 0.8) 0%,\n    rgba(160, 214, 196, 0.36) 26%,\n    rgba(120, 180, 180, 0.12) 48%,\n    rgba(8, 13, 20, 0) 68%\n  );\n  mix-blend-mode: screen;\n  filter: blur(10px);\n  opacity: 1;\n  pointer-events: none;\n}\n\n.lmSectionText {\n  max-width: var(--lm-measure);\n}\n\n.lmSection .lmBody {\n  margin-left: 0;\n  margin-right: 0;\n  text-shadow: none;\n}\n\n.lmSection .lmNote,\n.lmSection .lmTitle,\n.lmClosingStatic .lmClosing {\n  text-shadow: none;\n}\n\n.lmClosingStatic {\n  min-height: 70vh;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  text-align: center;\n  padding: clamp(4rem, 14vh, 10rem) var(--lm-gut);\n}\n\n/* ===================================================================== *\n   THE COMPLETE SITE — sections below the hero, shared by both modes.\n   Deep ink ground, one silver-jade light, serif voice. No cards, no\n   shadows, no numbered chrome — graded imagery, space, and type.\n * ===================================================================== */\n\n.lmSite {\n  position: relative;\n  z-index: 1;\n  background: var(--lm-void);\n}\n\n/* shared section rhythm */\n.lmSite section,\n.lmSite footer {\n  padding-inline: var(--lm-gut);\n}\n\n/* --- THE COMPOSITION — the pyramid, read plainly --- */\n.lmComposition {\n  max-width: 60rem;\n  margin: 0 auto;\n  padding-block: clamp(5rem, 16vh, 11rem);\n  border-top: 1px solid rgba(191, 227, 208, 0.1);\n}\n\n.lmCompHead {\n  margin-bottom: clamp(2.4rem, 6vh, 4.5rem);\n}\n\n.lmCompTitle {\n  margin-top: clamp(0.5rem, 1.4vw, 0.9rem);\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 96, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 340;\n  font-size: clamp(2.1rem, 6.5vw, 3.9rem);\n  line-height: 1.02;\n  letter-spacing: -0.014em;\n  color: var(--lm-ivory);\n  text-wrap: balance;\n}\n\n.lmPyramid {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n.lmTier {\n  display: grid;\n  grid-template-columns: 1fr;\n  gap: 0.9rem 2rem;\n  padding-block: clamp(1.5rem, 4vh, 2.6rem);\n  border-top: 1px solid rgba(242, 239, 230, 0.09);\n}\n.lmTier:last-child {\n  border-bottom: 1px solid rgba(242, 239, 230, 0.09);\n}\n\n@media (min-width: 680px) {\n  .lmTier {\n    grid-template-columns: 12rem 1fr;\n    align-items: baseline;\n  }\n}\n\n.lmTierName {\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(1.3rem, 3vw, 1.9rem);\n  color: var(--lm-jade);\n  letter-spacing: 0.004em;\n}\n\n.lmTierNotes {\n  display: flex;\n  flex-direction: column;\n  gap: 0.7rem;\n}\n\n.lmAccord {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: baseline;\n  gap: 0.4rem 0.9rem;\n}\n\n.lmAccordNote {\n  font-family: var(--font-fraunces), Georgia, serif;\n  font-variation-settings: \"opsz\" 40;\n  font-weight: 400;\n  font-size: clamp(1.25rem, 2.6vw, 1.65rem);\n  color: var(--lm-ivory);\n  letter-spacing: -0.008em;\n}\n\n.lmAccordMatter {\n  font-size: clamp(0.86rem, 1.5vw, 0.98rem);\n  color: var(--lm-ivory-faint);\n  font-style: italic;\n  font-family: var(--font-instrument), Georgia, serif;\n}\n\n/* --- THE BOTTLE — object + price + one buy action --- */\n.lmProduct {\n  max-width: 78rem;\n  margin: 0 auto;\n  padding-block: clamp(4rem, 12vh, 8rem);\n  display: grid;\n  gap: clamp(2rem, 6vw, 5rem);\n  align-items: center;\n}\n\n@media (min-width: 860px) {\n  .lmProduct {\n    grid-template-columns: 1fr 1fr;\n  }\n}\n\n.lmProductFig {\n  position: relative;\n  aspect-ratio: 4 / 5;\n  overflow: hidden;\n  border-radius: 2px;\n}\n\n.lmProductFig img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  filter: brightness(0.7) saturate(0.55) contrast(1.05);\n}\n\n.lmProductName {\n  margin-top: clamp(0.5rem, 1.4vw, 0.9rem);\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 144, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 330;\n  font-size: clamp(3rem, 9vw, 5.6rem);\n  line-height: 0.92;\n  letter-spacing: -0.02em;\n  color: var(--lm-ivory);\n}\n\n.lmProductSub {\n  margin-top: 0.6rem;\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(1.05rem, 2.4vw, 1.4rem);\n  color: var(--lm-jade);\n}\n\n.lmProductCopy {\n  margin-top: clamp(1.3rem, 3vh, 2rem);\n  max-width: 42ch;\n  font-size: clamp(1rem, 1.9vw, 1.12rem);\n  line-height: 1.65;\n  color: var(--lm-ivory-dim);\n  text-wrap: pretty;\n}\n\n.lmProductConc {\n  margin-top: clamp(1rem, 2.4vh, 1.6rem);\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(0.9rem, 1.6vw, 1.02rem);\n  color: var(--lm-ivory-faint);\n}\n\n.lmPrices {\n  display: flex;\n  gap: clamp(1.6rem, 4vw, 2.8rem);\n  margin-top: clamp(1.6rem, 4vh, 2.4rem);\n}\n\n.lmPrice {\n  display: flex;\n  flex-direction: column;\n  gap: 0.25rem;\n}\n\n.lmPriceMl {\n  font-size: 0.82rem;\n  letter-spacing: 0.04em;\n  color: var(--lm-ivory-faint);\n}\n\n.lmPriceValue {\n  font-family: var(--font-fraunces), Georgia, serif;\n  font-variation-settings: \"opsz\" 40;\n  font-size: clamp(1.4rem, 3vw, 1.8rem);\n  color: var(--lm-ivory);\n}\n\n/* the one buy action: hairline at rest, fills with light on hover */\n.lmBuy {\n  margin-top: clamp(1.8rem, 4.5vh, 2.8rem);\n  padding: 0.85rem 2.2rem;\n  font: inherit;\n  font-size: clamp(0.9rem, 1.7vw, 1rem);\n  letter-spacing: 0.05em;\n  color: var(--lm-jade);\n  background: transparent;\n  border: 1px solid var(--lm-jade-soft);\n  border-radius: 2px;\n  cursor: pointer;\n  transition: background-color 0.4s ease, color 0.4s ease, border-color 0.4s ease;\n}\n\n.lmBuy:hover {\n  background: var(--lm-jade);\n  border-color: var(--lm-jade);\n  color: var(--lm-void);\n}\n\n.lmBuy:focus-visible {\n  outline: 2px solid var(--lm-jade);\n  outline-offset: 4px;\n}\n\n/* --- PROVENANCE — full-bleed graded still, text over the frame --- */\n.lmProvenance {\n  position: relative;\n  overflow: hidden;\n  padding-block: clamp(6rem, 20vh, 13rem);\n  padding-inline: var(--lm-gut);\n}\n\n.lmProvImg {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  z-index: 0;\n  filter: brightness(0.5) saturate(0.5) contrast(1.05);\n}\n\n/* reuse the cool tint; deepen for legibility */\n.lmProvScrim {\n  position: absolute;\n  inset: 0;\n  z-index: 2;\n  background:\n    radial-gradient(120% 120% at 30% 50%, rgba(var(--lm-sink), 0.55) 0%, rgba(var(--lm-sink), 0.82) 70%),\n    linear-gradient(90deg, rgba(var(--lm-sink), 0.7) 0%, rgba(var(--lm-sink), 0.1) 60%);\n  pointer-events: none;\n}\n\n.lmProvInner {\n  position: relative;\n  z-index: 3;\n  max-width: 46rem;\n}\n\n.lmProvTitle {\n  margin-top: clamp(0.5rem, 1.4vw, 0.9rem);\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 96, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 340;\n  font-size: clamp(2.1rem, 6vw, 4rem);\n  line-height: 1.02;\n  letter-spacing: -0.014em;\n  color: var(--lm-ivory);\n  text-wrap: balance;\n  text-shadow: 0 2px 40px rgba(var(--lm-sink), 0.6);\n}\n\n.lmProvBody {\n  margin-top: clamp(1.2rem, 3vh, 1.9rem);\n  max-width: 46ch;\n  font-size: clamp(1rem, 1.9vw, 1.15rem);\n  line-height: 1.7;\n  color: var(--lm-ivory-dim);\n  text-wrap: pretty;\n  text-shadow: 0 1px 22px rgba(var(--lm-sink), 0.7);\n}\n\n.lmStats {\n  display: flex;\n  flex-wrap: wrap;\n  gap: clamp(1.8rem, 5vw, 3.4rem);\n  margin: clamp(2.4rem, 6vh, 4rem) 0 0;\n}\n\n.lmStat {\n  display: flex;\n  flex-direction: column;\n  gap: 0.4rem;\n}\n\n.lmStatValue {\n  font-family: var(--font-fraunces), Georgia, serif;\n  font-variation-settings: \"opsz\" 72;\n  font-weight: 350;\n  font-size: clamp(1.8rem, 4.5vw, 2.8rem);\n  line-height: 1;\n  color: var(--lm-ivory);\n}\n\n.lmStatLabel {\n  margin: 0;\n  max-width: 18ch;\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(0.85rem, 1.5vw, 0.98rem);\n  color: var(--lm-jade);\n}\n\n/* --- THE HOUSE — companion scents, editorial rows --- */\n.lmCollection {\n  max-width: 82rem;\n  margin: 0 auto;\n  padding-block: clamp(5rem, 16vh, 11rem);\n}\n\n.lmCollHead {\n  margin-bottom: clamp(2.4rem, 7vh, 5rem);\n}\n\n.lmCompanions {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: clamp(1.5rem, 5vh, 3.2rem);\n}\n\n.lmCompanionLink {\n  display: grid;\n  gap: clamp(1.2rem, 4vw, 3rem);\n  align-items: center;\n  padding-block: clamp(1.5rem, 4vh, 2.4rem);\n  border-top: 1px solid rgba(242, 239, 230, 0.09);\n}\n.lmCompanion:last-child .lmCompanionLink {\n  border-bottom: 1px solid rgba(242, 239, 230, 0.09);\n}\n\n@media (min-width: 760px) {\n  .lmCompanionLink {\n    grid-template-columns: 26rem 1fr;\n  }\n  /* alternate the image side for rhythm */\n  .lmCompanion:nth-child(even) .lmCompanionFig {\n    order: 2;\n  }\n}\n\n.lmCompanionFig {\n  position: relative;\n  aspect-ratio: 16 / 10;\n  overflow: hidden;\n  border-radius: 2px;\n}\n\n.lmCompanionFig img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  filter: brightness(0.66) saturate(0.5) contrast(1.05);\n  transition: transform 0.9s cubic-bezier(0.2, 0.7, 0.2, 1), filter 0.9s ease;\n}\n\n.lmCompanionLink:hover .lmCompanionFig img,\n.lmCompanionLink:focus-visible .lmCompanionFig img {\n  transform: scale(1.03);\n  filter: brightness(0.78) saturate(0.62) contrast(1.05);\n}\n\n.lmCompanionName {\n  margin-top: clamp(0.4rem, 1.2vw, 0.8rem);\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 96, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 336;\n  font-size: clamp(2.2rem, 6vw, 3.6rem);\n  line-height: 0.98;\n  letter-spacing: -0.016em;\n  color: var(--lm-ivory);\n}\n\n.lmCompanionMood {\n  margin-top: clamp(0.7rem, 1.8vh, 1.1rem);\n  max-width: 40ch;\n  font-size: clamp(0.98rem, 1.8vw, 1.1rem);\n  line-height: 1.6;\n  color: var(--lm-ivory-dim);\n  text-wrap: pretty;\n}\n\n.lmCompanionCue {\n  display: inline-block;\n  margin-top: clamp(1rem, 2.4vh, 1.5rem);\n  padding-bottom: 0.25rem;\n  font-size: 0.9rem;\n  letter-spacing: 0.05em;\n  color: var(--lm-jade);\n  border-bottom: 1px solid var(--lm-jade-soft);\n  transition: border-color 0.4s ease, color 0.4s ease;\n}\n.lmCompanionLink:hover .lmCompanionCue,\n.lmCompanionLink:focus-visible .lmCompanionCue {\n  color: var(--lm-ivory);\n  border-color: var(--lm-ivory);\n}\n\n/* --- FOOTER --- */\n.lmFooter {\n  border-top: 1px solid rgba(191, 227, 208, 0.12);\n  padding-block: clamp(4rem, 12vh, 7rem);\n  background: var(--lm-void-2);\n}\n\n.lmFootTop {\n  max-width: 82rem;\n  margin: 0 auto;\n  display: grid;\n  gap: clamp(2.4rem, 6vw, 4rem);\n}\n\n@media (min-width: 780px) {\n  .lmFootTop {\n    grid-template-columns: 1.2fr 2fr;\n  }\n}\n\n.lmFootMark {\n  font-family: var(--font-fraunces), Georgia, \"Times New Roman\", serif;\n  font-variation-settings: \"opsz\" 96, \"SOFT\" 0, \"WONK\" 0;\n  font-weight: 330;\n  font-size: clamp(2rem, 5vw, 2.8rem);\n  letter-spacing: -0.02em;\n  color: var(--lm-ivory);\n}\n\n.lmFootStatement {\n  margin-top: 0.9rem;\n  max-width: 34ch;\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(1rem, 2vw, 1.2rem);\n  line-height: 1.5;\n  color: var(--lm-ivory-dim);\n}\n\n.lmFootNav {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: clamp(1.4rem, 4vw, 2.5rem);\n}\n\n@media (max-width: 520px) {\n  .lmFootNav {\n    grid-template-columns: repeat(2, 1fr);\n  }\n}\n\n.lmFootHead {\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: 1.05rem;\n  color: var(--lm-jade);\n  margin-bottom: 0.9rem;\n}\n\n.lmFootCol ul {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 0.6rem;\n}\n\n.lmFootCol a {\n  font-size: 0.95rem;\n  color: var(--lm-ivory-dim);\n  transition: color 0.3s ease;\n}\n.lmFootCol a:hover {\n  color: var(--lm-ivory);\n}\n\n.lmSignup {\n  max-width: 82rem;\n  margin: clamp(3rem, 8vh, 5rem) auto 0;\n  padding-top: clamp(2.4rem, 6vh, 3.6rem);\n  border-top: 1px solid rgba(242, 239, 230, 0.09);\n}\n\n.lmSignupLabel {\n  display: block;\n  font-family: var(--font-instrument), Georgia, serif;\n  font-style: italic;\n  font-size: clamp(1rem, 2vw, 1.25rem);\n  color: var(--lm-ivory-dim);\n  margin-bottom: 1.1rem;\n}\n\n.lmSignupRow {\n  display: flex;\n  gap: 0.75rem;\n  max-width: 30rem;\n  align-items: stretch;\n}\n\n.lmSignupInput {\n  flex: 1;\n  min-width: 0;\n  padding: 0.8rem 0;\n  font: inherit;\n  color: var(--lm-ivory);\n  background: transparent;\n  border: none;\n  border-bottom: 1px solid rgba(242, 239, 230, 0.24);\n  border-radius: 0;\n  transition: border-color 0.3s ease;\n}\n.lmSignupInput::placeholder {\n  color: var(--lm-ivory-faint);\n}\n.lmSignupInput:focus {\n  outline: none;\n  border-bottom-color: var(--lm-jade);\n}\n\n.lmSignupBtn {\n  padding: 0 1.6rem;\n  font: inherit;\n  font-size: 0.92rem;\n  letter-spacing: 0.05em;\n  color: var(--lm-jade);\n  background: transparent;\n  border: 1px solid var(--lm-jade-soft);\n  border-radius: 2px;\n  cursor: pointer;\n  transition: background-color 0.4s ease, color 0.4s ease, border-color 0.4s ease;\n}\n.lmSignupBtn:hover {\n  background: var(--lm-jade);\n  border-color: var(--lm-jade);\n  color: var(--lm-void);\n}\n.lmSignupBtn:focus-visible {\n  outline: 2px solid var(--lm-jade);\n  outline-offset: 3px;\n}\n\n.lmFine {\n  max-width: 82rem;\n  margin: clamp(3rem, 8vh, 4.5rem) auto 0;\n  font-size: 0.82rem;\n  letter-spacing: 0.02em;\n  color: var(--lm-ivory-faint);\n}\n\n/* --------------------------------------------------------------------- *\n   reduced motion: neutralise any transitions if this class tree is shown\n * --------------------------------------------------------------------- */\n@media (prefers-reduced-motion: reduce) {\n  .lmRoot * {\n    transition: none !important;\n    animation: none !important;\n  }\n}\n","type":"registry:style"}],"assets":[{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/journey.mp4","target":"public/sillage/journey.mp4","bytes":4213824},{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/poster.jpg","target":"public/sillage/poster.jpg","bytes":21198},{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/still-1.jpg","target":"public/sillage/still-1.jpg","bytes":21123},{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/still-2.jpg","target":"public/sillage/still-2.jpg","bytes":35538},{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/still-3.jpg","target":"public/sillage/still-3.jpg","bytes":39157},{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/still-4.jpg","target":"public/sillage/still-4.jpg","bytes":39292},{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/still-5.jpg","target":"public/sillage/still-5.jpg","bytes":42276},{"url":"https://ui.artbloom.tech/r/asset/templates/sillage/assets/still-6.jpg","target":"public/sillage/still-6.jpg","bytes":26681}],"meta":{"kind":"templates","categories":["landing","experiences"],"docs":"https://ui.artbloom.tech/artbloom/templates/sillage"}}