/* ===========================================================================
   VELVET EMBRACE — PHASE 2 / "NEURO"
   ---------------------------------------------------------------------------
   The fifth sheet, and the second optional one. motion.css gives the site its
   finish; this gives it a nervous system — a cursor that reacts, surfaces that
   remember being touched, stagger chains that move like a stadium wave, and a
   background that behaves like something alive rather than something looped.

   SAME CONTRACT AS EVERY OTHER LAYER HERE: delete the two tags that load this
   and the site is complete, correct and standing still. Nothing below defines
   layout, invents a colour, or gates content behind an animation.

   ⚠  WHAT IT DELIBERATELY DOES NOT DO, AND WHY.
      Every effect here answers a real element on this page. Four of the
      requested ones had nothing on this site to attach to and are NOT faked:

        · add-to-cart          there is no cart. Orders are placed on ESSENS's
                               own shop or written as a message. The collapse /
                               tunnel / shockwave sequence is attached instead
                               to the one moment that IS a commitment here —
                               the scent finder handing its sentence to the
                               order form.
        · category filters     there is no product grid to re-filter. The
                               melt-and-assemble transition is attached to the
                               scent finder's questions, which is the one place
                               a set of cards is genuinely replaced by another.
        · price scramble       there is not a single price on this site, and a
                               test fails the build if one appears. The digit
                               roll runs on the question counter instead.
        · a page-wide canvas   the sections have opaque grounds by design (the
                               contrast audit depends on them), so a canvas
                               behind the whole document would be invisible
                               except in the hero. It runs full-bleed in the
                               hero, where it can actually be seen.

   ⚠  AND IT ALL STOPS FOR prefers-reduced-motion. Not "runs slower" — stops.
      styles.css flattens durations globally; the JavaScript half never
      attaches its listeners at all, and the real mouse cursor comes back.
   ========================================================================= */

/* =========================================================================
   THE EASING SYSTEM
   -------------------------------------------------------------------------
   Use these. Nothing in this file uses `linear`, `ease`, or `ease-in-out`,
   because none of those describe how anything physical moves. Each curve
   below is named for the material it imitates, and the name is the choosing
   instruction: if the thing should feel like it is on a spring, it gets
   --ease-spring, and the number is not up for debate at the call site.
   ========================================================================= */

:root {
  /* Overshoots past its target and settles. Presses, pops, anything that
     should feel like it has mass on a spring. */
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  /* A harder overshoot. Impacts only — it visibly bounces. */
  --ease-rubber: cubic-bezier(.18, .89, .32, 1.28);
  /* No overshoot, very fast out of the gate, long tail. The luxury curve:
     things arrive quickly and come to rest slowly. */
  --ease-smooth: cubic-bezier(.23, 1, .32, 1);
  /* Water: slow to start, slow to stop, no bounce. Reveals, dissolves. */
  --ease-water: cubic-bezier(.65, 0, .35, 1);
  /* Magnetic: accelerates towards the target as it gets closer. */
  --ease-magnet: cubic-bezier(.4, 0, .2, 1.1);
  /* Heavy suspension. For anything large that has to settle without wobble. */
  --ease-suspend: cubic-bezier(.19, 1, .22, 1);

  /* Durations, so a change of feel is one edit and not forty. */
  --t-tap: .42s;      /* a press and its rebound */
  --t-react: .55s;    /* a hover reaction */
  --t-settle: .9s;    /* something coming to rest */
  --t-drift: 1.4s;    /* something large moving */

  /* How far apart the links in a stagger chain fire. */
  --chain: 55ms;
}

/* =========================================================================
   1. THE NEURAL CURSOR
   -------------------------------------------------------------------------
   Two layers. A hard bright core that tracks the pointer almost exactly, and
   a soft halo lagging about 100ms behind on a lerp, so it reads as something
   being dragged through air rather than drawn at coordinates.

   Over a control the halo stops trailing and SNAPS TO THE CONTROL'S BOX —
   it becomes a containment field around the thing you are about to press.
   Over text it shrinks and sharpens to a reading dot.

   ⚠  THE REAL CURSOR IS ONLY HIDDEN WHEN THE REPLACEMENT IS ACTUALLY
      RUNNING. `cursor: none` is applied by a class that neuro.js adds, never
      by this sheet on its own — so a browser that cannot run the script, a
      visitor with reduced motion, and anybody on a touch screen all keep the
      pointer their operating system gave them. A site that hides the cursor
      and then fails to draw one is unusable, and it is unusable in a way
      nobody reports: they just leave.
   ========================================================================= */

html.nc-on, html.nc-on * { cursor: none !important; }
/* ...except where a cursor carries meaning that a dot cannot. */
html.nc-on input, html.nc-on textarea, html.nc-on select { cursor: auto !important; }

.nc-core, .nc-halo {
  position: fixed;
  left: 0; top: 0;
  z-index: 9000;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s var(--ease-smooth);
}
.nc-on .nc-core, .nc-on .nc-halo { opacity: 1; }
.nc-hidden .nc-core, .nc-hidden .nc-halo { opacity: 0 !important; }

/* The laser. Small, hard, and it does not lag — the eye uses this one to
   know where it is pointing, so any delay on it reads as broken input. */
.nc-core {
  width: 6px; height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: var(--sage-deep);
  box-shadow: 0 0 6px color-mix(in srgb, var(--sage-deep) 60%, transparent);
  transition: opacity .3s var(--ease-smooth), width .3s var(--ease-spring),
              height .3s var(--ease-spring), margin .3s var(--ease-spring);
}

/* The halo. Trails, blurs, inverts what is behind it, and changes shape
   according to what it is over. Width/height/radius are written by neuro.js;
   the TRANSITION on them is what makes the snap feel magnetic rather than
   instantaneous. */
.nc-halo {
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1.5px solid var(--sage-mid);
  border-radius: 999px;
  backdrop-filter: blur(1.5px) saturate(1.25);
  -webkit-backdrop-filter: blur(1.5px) saturate(1.25);
  background: color-mix(in srgb, var(--sage) 10%, transparent);
  transition:
    opacity .3s var(--ease-smooth),
    width var(--t-react) var(--ease-magnet),
    height var(--t-react) var(--ease-magnet),
    margin var(--t-react) var(--ease-magnet),
    border-radius var(--t-react) var(--ease-magnet),
    border-color var(--t-react) var(--ease-smooth),
    background-color var(--t-react) var(--ease-smooth);
}

/* Over a control: the halo has been sized and positioned onto the control by
   the script, so it stops being a circle and becomes an outline of the thing. */
.nc-halo.is-latched {
  border-color: var(--sage-deep);
  border-width: 1.5px;
  background: color-mix(in srgb, var(--sage) 14%, transparent);
  backdrop-filter: blur(0px) saturate(1.35);
  -webkit-backdrop-filter: blur(0px) saturate(1.35);
}
/* Over running text: small, sharp, out of the way of the words. */
.nc-halo.is-reading {
  border-color: color-mix(in srgb, var(--sage-mid) 55%, transparent);
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* Pressed. */
.nc-halo.is-down { border-color: var(--sage-deep); scale: .92; }
.nc-core.is-down { scale: 1.9; }

/* =========================================================================
   2. SOUNDLESS HAPTICS
   -------------------------------------------------------------------------
   Every pressable thing squashes under the finger and springs back past its
   own size before settling. That overshoot is the entire effect: without it
   a press feels like a state change, with it a press feels like contact.

   ⚠  IT IS ON `scale:`, THE INDEPENDENT PROPERTY, NEVER INSIDE `transform`.
      motion.css already builds these elements' transforms out of custom
      properties for the magnetic pull and the tilt; writing a scale into the
      same transform would silently delete both. The two properties compose.

   ⚠  AND NOTHING PRESSABLE MAY SQUASH WHILE IT IS BEING MEASURED. The browser
      suite proves every scent-finder option is a 44px thumb target, and a
      scaled element reports a scaled box. These only scale on :active, which
      no measurement is ever taken during.
   ========================================================================= */

.btn, .chip, .faq__q, .nav-toggle, .to-top, .opt, .join__card {
  transition: scale var(--t-tap) var(--ease-spring);
}
.btn:active   { scale: .94; }
.chip:active  { scale: .965; }
.faq__q:active, .nav-toggle:active, .to-top:active { scale: .9; }
.opt:active   { scale: .98; }

/* =========================================================================
   3. CONTEXTUAL AWARENESS — the page reads how fast you are moving
   -------------------------------------------------------------------------
   neuro.js measures scroll velocity and puts one of two classes on <html>.
   Scrolling fast, things snap into place: somebody skimming does not want to
   wait for elegance, and a slow reveal during a fast scroll is just a page
   that seems to be lagging. Scrolling slowly, everything lingers.
   ========================================================================= */

html.is-scrubbing .reveal.is-in {
  transition-duration: .28s, .34s, .28s;
  transition-timing-function: var(--ease-smooth);
}
html.is-drifting .reveal.is-in {
  transition-duration: 1.15s, 1.35s, 1.15s;
  transition-timing-function: var(--ease-suspend);
}

/* =========================================================================
   4. STAGGER CHAINS
   -------------------------------------------------------------------------
   Hovering one card in a row does not move only that card. Its neighbours
   answer, later and less, in a wave outwards from the one under the pointer —
   the way a row of anything physical responds to being touched at one point.

   --wave is the distance in cards from the one being hovered, written by
   neuro.js. Everything else is arithmetic on it.
   ========================================================================= */

.cat, .route {
  transition:
    transform var(--t-settle) var(--ease-suspend),
    box-shadow var(--t-settle) var(--ease-smooth),
    border-color var(--t-react) var(--ease-smooth),
    filter var(--t-settle) var(--ease-smooth);
  transition-delay: calc(var(--wave, 0) * var(--chain));
}

/* The neighbours lift a fraction of what the hovered card does, and the
   fraction falls away with distance. Two cards out it is barely a shrug. */
.range__grid:hover .cat,
.routes:hover .route {
  transform: translateY(calc(-4px / max(var(--wave, 0), 1)));
}
.range__grid:hover .cat:hover,
.routes:hover .route:hover {
  transform: translateY(-7px) scale(1.012);
}

/* =========================================================================
   5. PHYSICAL MEMORY
   -------------------------------------------------------------------------
   A surface that has been touched does not return instantly to how it was.
   --mem is set to 1 by neuro.js when the pointer LEAVES a card, and then
   released, so the residue decays over a couple of seconds. It is a very
   small thing and it is the difference between a page that responds and a
   page that remembers.
   ========================================================================= */

.cat, .route, .finder, .join__card, .order__panel {
  --mem: 0;
  box-shadow:
    var(--shadow-sm),
    0 0 0 calc(var(--mem) * 1px) color-mix(in srgb, var(--sage-mid) calc(var(--mem) * 45%), transparent);
}
.cat, .route, .finder, .join__card, .order__panel {
  transition: --mem 2.4s var(--ease-water);
}
@property --mem {
  syntax: '<number>';
  inherits: false;
  initial-value: 0;
}

/* =========================================================================
   6. "4D" SURFACES — light, shimmer and depth
   -------------------------------------------------------------------------
   Three separate illusions stacked, and each one is doing a different job:

     the LIGHT   a broad specular gradient that moves OPPOSITE the pointer.
                 Opposite is the whole trick — light sources do not follow
                 your hand, and a highlight that tracks the cursor reads as a
                 torch rather than as a sheen.
     the SHIMMER a narrow diagonal streak crossing on a long loop, which
                 speeds up while the surface is being hovered.
     the DEPTH   the contents sit forward of the card in Z, so tilting the
                 card slides them across it. That parallax is what makes a
                 flat rectangle read as a pane of glass with something under
                 it, rather than as a rectangle that rotates.
   ========================================================================= */

.route, .cat, .hero__figure, .join__card {
  transform-style: preserve-3d;
}

.route, .cat {
  transform:
    perspective(900px)
    rotateX(var(--tilt-x, 0deg))
    rotateY(var(--tilt-y, 0deg))
    translateY(var(--lift, 0px));
}

/* ---- the studio light --------------------------------------------------- */
.route::before, .cat::before, .join__card::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 0;
  background: radial-gradient(60% 60% at var(--light-x, 50%) var(--light-y, 0%),
    color-mix(in srgb, var(--white) 70%, transparent),
    transparent 70%);
  opacity: 0;
  transition: opacity var(--t-react) var(--ease-smooth);
}
.route:hover::before, .cat:hover::before, .join__card:hover::before { opacity: .55; }

/* ---- the shimmer -------------------------------------------------------- */
.hero__figure::after {
  animation-duration: 12s;
}
.hero__figure:hover::after {
  animation-duration: 3.4s;
}

/* ---- depth: the contents ride above the card ---------------------------- */
.route > *, .cat > * { transform: translateZ(18px); position: relative; z-index: 1; }
.route__cta { transform: translateZ(34px); }
.cat__title { transform: translateZ(26px); }

/* The hero illustration parallaxes INSIDE its own frame, against the frame's
   tilt. Small numbers: this is depth, not a diorama. */
.hero__panel {
  transition: translate var(--t-drift) var(--ease-suspend);
  translate: calc(var(--ry, 0deg) * .9px) calc(var(--rx, 0deg) * -.9px);
}

/* =========================================================================
   7. LIQUID TRANSITIONS — the scent finder's questions
   -------------------------------------------------------------------------
   The answered question does not vanish, it MELTS: a clip-path edge runs down
   through it while it drops and blurs. The next one ASSEMBLES: each option
   flies in from a scattered position to its place in the grid, in a chain.

   ⚠  TRANSLATE AND OPACITY ONLY, NEVER SCALE AND NEVER ROTATE. The browser
      suite measures each option's height to prove it is a 44px target, and it
      measures shortly after the question is drawn. A scaled or rotated element
      reports a scaled or rotated bounding box, and the test would fail on an
      option that is, in fact, exactly the right size.
   ========================================================================= */

.finder__stage { position: relative; }

.nc-melt {
  position: absolute;
  pointer-events: none;
  z-index: 2;
}
.nc-melt > * {
  animation: nc-melt .62s var(--ease-water) both;
  animation-delay: calc(var(--i, 0) * 32ms);
}
@keyframes nc-melt {
  from { opacity: 1; clip-path: inset(0 0 0 0);     transform: translateY(0);    filter: blur(0); }
  to   { opacity: 0; clip-path: inset(100% 0 0 0);  transform: translateY(22px); filter: blur(6px); }
}

/* ⚠ :nth-child(n) IS NOT DECORATION HERE. motion.css sets a per-position
     animation-delay on these chips so that IT works on its own, and
     `.finder__opts .chip:nth-child(1)` outweighs `.finder__opts .chip`. The
     matching pseudo-class ties the specificity, and this sheet loads later,
     so the chain wins — without motion.css losing its own standalone
     behaviour. */
.finder__opts .chip:nth-child(n) {
  animation: nc-assemble .78s var(--ease-suspend) both;
  animation-delay: calc(var(--i, 0) * var(--chain));
}
@keyframes nc-assemble {
  from {
    opacity: 0;
    transform: translate(var(--from-x, 0px), var(--from-y, -40px));
    filter: blur(9px);
  }
  to { opacity: 1; transform: none; filter: blur(0); }
}

/* =========================================================================
   8. THE QUANTUM HAND-OFF
   -------------------------------------------------------------------------
   What "add to cart" would have been, on the one action here that is
   equivalent: the scent finder handing its sentence to the order form.

   The sentence collapses to a point, a ring tunnels outwards from where the
   click landed, and the order form takes the impact — a shockwave on the
   panel and a roll on the counter it lands in.
   ========================================================================= */

.nc-collapse { animation: nc-collapse .5s var(--ease-water) both; }
@keyframes nc-collapse {
  from { opacity: 1; transform: scale(1);   filter: blur(0); }
  60%  { opacity: .5; transform: scale(.55); filter: blur(5px); }
  to   { opacity: 0; transform: scale(.06); filter: blur(14px); }
}

.nc-ring {
  position: fixed;
  z-index: 8000;
  pointer-events: none;
  border-radius: 50%;
  border: 2px solid var(--sage-mid);
  animation: nc-tunnel .95s var(--ease-smooth) both;
}
@keyframes nc-tunnel {
  from { width: 0; height: 0; margin: 0; opacity: .9; border-width: 14px; }
  to   { width: 46rem; height: 46rem; margin: -23rem 0 0 -23rem; opacity: 0; border-width: 1px; }
}

.nc-shock {
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--sage) 70%, transparent);
  animation: nc-shock 1.1s var(--ease-smooth) both;
}
@keyframes nc-shock {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sage) 75%, transparent); }
  to   { box-shadow: 0 0 0 2.5rem color-mix(in srgb, var(--sage) 0%, transparent); }
}

/* The digit roll. Each glyph is its own element so they can land in sequence
   like a slot machine coming to rest. */
.nc-roll { display: inline-block; overflow: hidden; vertical-align: bottom; }
.nc-roll > span {
  display: block;
  animation: nc-roll .55s var(--ease-spring) both;
  animation-delay: calc(var(--i, 0) * 60ms);
}
@keyframes nc-roll {
  from { transform: translateY(-1.15em); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* =========================================================================
   9. THE DRAWN EMPHASIS
   -------------------------------------------------------------------------
   As a heading arrives, a stroke draws itself under its last word — by hand,
   left to right, the way somebody underlines something they mean. It is an
   SVG path with its own dash offset animated, so it is a LINE BEING DRAWN
   and not a box growing.
   ========================================================================= */

.nc-mark { position: relative; white-space: nowrap; }
.nc-mark svg {
  position: absolute;
  left: -4%; bottom: -.22em;
  width: 108%; height: .5em;
  overflow: visible;
  pointer-events: none;
}
.nc-mark path {
  fill: none;
  stroke: color-mix(in srgb, var(--sage) 85%, transparent);
  stroke-width: 7;
  stroke-linecap: round;
  stroke-dasharray: var(--len, 300);
  stroke-dashoffset: var(--len, 300);
}
.nc-mark.is-drawn path {
  animation: nc-draw 1.1s var(--ease-water) .15s both;
}
@keyframes nc-draw { to { stroke-dashoffset: 0; } }

/* On the deep green panel the stroke has to be the pale tone or it vanishes. */
.join .nc-mark path { stroke: color-mix(in srgb, var(--sage-soft) 70%, transparent); }

/* =========================================================================
   10. ATMOSPHERIC BREATHING
   -------------------------------------------------------------------------
   The whole page rises and falls on a twenty-second cycle. It is under
   everything else and it is the reason the site feels inhabited when nothing
   at all is happening.

   ⚠  IT BREATHES ON THE GROUND, NOT THROUGH AN OVERLAY, and that is a
      deliberate retreat from the obvious build. A fixed soft-light pane over
      the whole document is the usual way to do this and it lightens every
      piece of text underneath it — silently, by an amount no audit measures,
      because the contrast checker reads the computed colours and never sees
      the pane. This site's palette clears 4.5:1 with very little to spare
      (see the notes on --ink-faint and --sage-deep in tokens.css), and
      spending that margin on an ambient effect would be spending the wrong
      budget.

      Moving the GROUND instead gives the same sensation — the page is very
      slightly breathing — and cannot touch the type at all.
   ========================================================================= */

body { animation: nc-ground 20s var(--ease-water) infinite alternate; }
@keyframes nc-ground {
  from { background-color: var(--paper); }
  to   { background-color: color-mix(in srgb, var(--paper) 88%, var(--white)); }
}

/* The hero's own atmosphere breathes harder, because there is nothing over it
   to protect — the aura already lives inside the clipped effects box. */
.hero__aura { animation-duration: 26s, 20s; }

/* =========================================================================
   11. THE LATCH, AND THE SCROLLBAR THAT KNOWS WHERE IT IS
   -------------------------------------------------------------------------
   ⚠  THIS IS PROXIMITY SNAP, NOT SCROLLJACKING, AND THE DIFFERENCE IS THE
      WHOLE POINT.

      A real scrolljack takes the wheel event and animates the page itself.
      It also: breaks the keyboard, fights trackpad momentum, strands screen
      readers, makes the browser's own find-in-page land in the wrong place,
      and on a phone it feels like the page is arguing with your thumb. On a
      shop belonging to one person selling perfume to people who found her on
      Facebook, it is the single fastest way to lose somebody.

      `scroll-snap-type: proximity` does what was actually wanted: come to
      rest NEAR a section boundary and the browser eases you onto it, using
      its own compositor, with the suspension curve below. Come to rest
      anywhere else and it leaves you exactly where you asked to be. Nothing
      is ever taken away from the person scrolling.
   ========================================================================= */

@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  html { scroll-snap-type: y proximity; }
  main > section { scroll-snap-align: start; scroll-snap-stop: normal; }
  /* The hero snaps to the very top, not to its own padding. */
  .hero { scroll-snap-align: start; }
}

/* The scrollbar travels through the palette as the page does — sage at the
   top, silver through the middle, deep sage at the end. --p is written by
   neuro.js and is the same number the header's progress line uses. */
html {
  scrollbar-color: color-mix(in srgb, var(--sage-mid) calc(40% + var(--p, 0) * 60%), var(--silver))
                   transparent;
}
::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--sage-mid) calc(40% + var(--p, 0) * 60%), var(--silver));
}

/* =========================================================================
   12. GHOST NOTIFICATIONS
   -------------------------------------------------------------------------
   The two confirmations on this site — "copied your order", "copied the link"
   — used to be a line of small text that appeared. They are now a pane of
   frosted glass that COALESCES: it arrives as a blur with no legible edge and
   resolves into type, then drains a hairline of progress and leaves. Hovering
   it pauses the drain, because the one time somebody wants to read a toast
   twice is the moment it starts to go.

   ⚠ The original live-region text is left in the DOM and only visually
     hidden. A screen reader announces the status; it must not be told about
     a decorative pane instead.
   ========================================================================= */

/* ⚠ IT HAS TO CLEAR THE FURNITURE ALREADY IN THAT CORNER. Back-to-top lives
     bottom-right, and on a phone the action bar runs along the bottom. A
     toast at the usual offset lands on top of both — and the first version
     did, half-covering the button it was reporting on. */
.nc-toast {
  position: fixed;
  right: clamp(1rem, 3vw, 2rem);
  bottom: calc(clamp(1rem, 3vw, 2rem) + 3.6rem);
  z-index: 8500;
  max-width: min(22rem, calc(100vw - 2rem));
  padding: .9rem 1.1rem 1rem;
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--white) 55%, var(--sage-soft));
  background: color-mix(in srgb, var(--white) 72%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  box-shadow: var(--shadow-lg);
  color: var(--ink);
  font-size: .9rem;
  line-height: 1.45;
  overflow: hidden;
  animation: nc-coalesce .75s var(--ease-spring) both;
}
.nc-toast.is-going { animation: nc-dissolve .45s var(--ease-water) both; }
body.has-action-bar .nc-toast { bottom: calc(clamp(1rem, 3vw, 2rem) + 8.4rem); }

@keyframes nc-coalesce {
  from { opacity: 0; transform: translate(14px, 18px); filter: blur(16px); letter-spacing: .22em; }
  60%  { opacity: 1; filter: blur(2px); }
  to   { opacity: 1; transform: none; filter: blur(0); letter-spacing: normal; }
}
@keyframes nc-dissolve {
  from { opacity: 1; filter: blur(0); }
  to   { opacity: 0; filter: blur(12px); transform: translateY(10px); }
}

/* The original status line stays in the DOM for a screen reader and is taken
   out of sight, because a toast is a picture of a message and not the
   message. Same geometry as .sr-only in styles.css. */
.nc-muted {
  position: absolute !important; width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important; overflow: hidden !important;
  clip-path: inset(50%) !important; white-space: nowrap !important;
}

.nc-toast__bar {
  position: absolute; left: 0; bottom: 0;
  height: 2px; width: 100%;
  transform-origin: left;
  background: linear-gradient(90deg, var(--sage-mid), var(--silver));
  animation: nc-drain var(--life, 4s) linear both;
}
.nc-toast:hover /* The original status line stays in the DOM for a screen reader and is taken
   out of sight, because a toast is a picture of a message and not the
   message. Same geometry as .sr-only in styles.css. */
.nc-muted {
  position: absolute !important; width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important; overflow: hidden !important;
  clip-path: inset(50%) !important; white-space: nowrap !important;
}

.nc-toast__bar { animation-play-state: paused; }
@keyframes nc-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* =========================================================================
   13. HER OWN LOGO
   -------------------------------------------------------------------------
   The redrawn vector mark is the site's identity — it is crisp at any size
   and it is what the header, the favicon and the share card use. But the logo
   SHE made is the one she has been posting for months, and it is what her
   customers recognise. It appears three times: pressed onto the hero panel
   like a seal, on the registration card, and in the footer.

   ⚠ It is a 561x540 screenshot with the marble worktop cut out of it by
     tools/prepare-supplied-logo.py, so it is only ever shown SMALL. It must
     not be stretched across a hero. See QUESTIONS-FOR-VIKKI.md §8.
   ========================================================================= */

/* ⚠ `.hero__figure .hero__seal`, NOT `.hero__seal`. styles.css carries
     `.hero__figure img { width: 100%; height: 100%; object-fit: cover }` so
     that a photograph from her fills the frame — and that selector, with one
     class and one type, outweighs a bare class. The seal was being sized as
     though it were the photograph: her logo blown up to 500px, covering the
     illustration it was meant to be pressed onto, and soft as a result. */
.hero__figure .hero__seal {
  position: absolute;
  /* Top-left. It was bottom-left first and sat across the shelf line and the
     first bottle — a sticker over the drawing rather than beside it. The top
     corner of the panel is the one piece of empty ground in the composition,
     and it is where the eye enters the picture. */
  left: clamp(.9rem, 2.6vw, 1.6rem);
  top: clamp(.9rem, 2.6vw, 1.6rem);
  width: clamp(4.8rem, 11.5vw, 7.4rem);
  height: auto;
  z-index: 2;
  filter: drop-shadow(0 10px 22px rgba(74, 98, 71, .28));
  animation: nc-seal 11s var(--ease-water) infinite alternate;
  transition: scale var(--t-settle) var(--ease-spring), rotate var(--t-settle) var(--ease-spring);
}
.hero__figure:hover .hero__seal { scale: 1.06; rotate: -3deg; }
.hero__figure .hero__seal { object-fit: contain; }
@keyframes nc-seal {
  from { translate: 0 0; rotate: -1.2deg; }
  to   { translate: 0 -5px; rotate: 1.2deg; }
}

.footer__seal {
  width: 3.4rem; height: auto;
  margin-bottom: .9rem;
  opacity: .9;
  transition: opacity var(--t-react) var(--ease-smooth), scale var(--t-react) var(--ease-spring);
}
.footer__seal:hover { opacity: 1; scale: 1.05; }

/* =========================================================================
   PRINT, NO-JS, AND REDUCED MOTION
   ========================================================================= */

@media print {
  .nc-toast, .nc-core, .nc-halo, .nc-ring, .nc-melt { display: none !important; }
  .nc-mark path { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-snap-type: none; }
  body { animation: none; }
  .hero__seal, .footer__seal { animation: none; }
  .finder__opts .chip, .nc-melt > *, .nc-toast { animation: none !important; }
  .nc-mark path { stroke-dashoffset: 0; }
}

.no-js .nc-mark path { stroke-dashoffset: 0; }
