/* ============================================================================
   nōesis · motion.css
   The path, the trail rail, the gates, the prologue, the cursor trail.
   Everything here is scroll-linked or intent-driven. Nothing loops on its own.
   ========================================================================== */

/* ---- 1. the wandering line ---------------------------------------------- */
/* One SVG path threading the whole document, drawn against scroll progress.
   Gallery plates are positioned ON it via getPointAtLength(). */
/* The spine sits in the gutter between the menu rail and the text. It is only
   as wide as the band the wave swings through, so it never overlaps content. */
/* z-index 3, above the chapters. A section background paints inside its own
   chapter, and the chapter sits at z-index 1, so at 0 the line was buried
   underneath every background image. It can safely sit above the content
   because it only ever occupies the gutter, which no text reaches. */
#wander{
  position:absolute;
  top:0; left:0;
  pointer-events:none;
  z-index:3;
  overflow:visible;
}
#wander path{
  fill:none;
  stroke:var(--accent);
  stroke-width:2.5;
  stroke-linecap:round;
  stroke-linejoin:round;
  vector-effect:non-scaling-stroke;
  opacity:.85;
}
/* the route ahead of the reader, faint but present, so the line reads as a
   path being walked rather than one being invented */
#wander path.ghost{
  stroke:var(--rule-strong);
  stroke-width:2;
  opacity:.3;
}
#wander path.strand{stroke-width:1.25;opacity:.22}

/* milestones: one bead where each section begins. Hollow while the section is
   still ahead of you, filled once the drawn head has passed it. */
#wander circle.bead{
  fill:var(--ground);
  stroke:var(--rule-strong);
  stroke-width:1.5;
  vector-effect:non-scaling-stroke;
  transition:fill .45s ease,stroke .45s ease,r .45s cubic-bezier(.22,1,.36,1);
}
#wander circle.bead[data-reached="true"]{
  fill:var(--accent);
  stroke:var(--accent);
  r:5.5;
}

@media (max-width:1080px){
  #wander path{stroke-width:1.5}
  #wander path.ghost{stroke-width:1.25}
  /* the gutter is only ~20px on a phone, so the beads shrink with the line */
  #wander circle.bead{r:3;stroke-width:1.25}
  #wander circle.bead[data-reached="true"]{r:3.5}
}
.chapter,.hero,.foot{position:relative;z-index:1}

/* ---- 2. the trail rail  ·  the route map that is also the menu ---------- */
/* Every label is visible at rest. A menu you have to hover to read is not a
   menu, it is a puzzle. The line and the waypoints stay, because they show
   where you are on the journey, which a normal nav bar cannot do. */
.trail{
  position:fixed;
  left:0; top:0; bottom:0;
  width:var(--rail);
  z-index:40;
  display:flex;
  flex-direction:column;
  padding:var(--s3) 0 var(--s3) var(--s3);
  border-right:1px solid var(--rule);
  background:var(--ground);
}
.trail .brand{display:block;width:104px;margin-bottom:var(--s4)}
.trail .brand svg{width:100%;height:auto}

.rail-label{
  font-size:.74rem;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--ink-mute);
  font-weight:500;
  margin-bottom:var(--s3);
}

/* the line runs behind the waypoints, through the centre of every dot */
.trail .track{
  position:relative;
  display:flex;
  flex-direction:column;
  justify-content:flex-start;
  gap:clamp(1.1rem,3.2vh,2.4rem);
  padding-top:.2rem;
  padding-bottom:.2rem;
}
.trail .track::before{
  content:'';
  position:absolute;
  left:5px; top:6px; bottom:0;
  width:1px;
  background:var(--rule);
}
/* The bar is drawn at full length and scaled down, rather than grown by
   height. It is on screen for the whole visit and it changes on every scroll
   frame, so `height` meant a layout pass per frame for a 1px rule. --p comes
   from updateTrail. */
.trail .fill{
  position:absolute;
  left:5px; top:6px; bottom:0;
  width:1px;
  background:var(--accent);
  transform-origin:top;
  transform:scaleY(var(--p,0));
  transition:transform .25s linear;
}

.trail .tick{
  position:relative;
  display:flex;
  align-items:center;
  gap:.85rem;
  padding:.2rem 0;
}
.trail .tick .dot{
  flex:0 0 11px;
  width:11px; height:11px;
  display:grid; place-items:center;
  position:relative; z-index:1;
}
.trail .tick .dot::before{
  content:'';
  width:5px; height:5px;
  border-radius:50%;
  background:var(--rule-strong);
  /* a ring of the page colour so the line does not run through the dot */
  box-shadow:0 0 0 3px var(--ground);
  transition:background .3s ease,transform .3s ease;
}
.trail .tick .txt{display:flex;align-items:baseline;gap:.5rem;min-width:0}
.trail .tick .num{
  font-family:var(--font-display);
  font-size:.72rem;
  color:var(--ink-mute);
  letter-spacing:.04em;
  flex:0 0 auto;
  transition:color .3s ease;
}
.trail .tick .lbl{
  font-size:.82rem;
  letter-spacing:.11em;
  text-transform:uppercase;
  color:var(--ink-dim);
  white-space:nowrap;
  /* scale, not font-size: changing font-size reflows the whole rail and the
     beads and fill would jump with it. Origin left so it grows away from the
     line rather than across it. */
  transform-origin:left center;
  transition:color .3s ease,transform .3s cubic-bezier(.22,1,.36,1);
}

.trail .tick:hover .lbl{color:var(--ink);transform:scale(1.16)}
.trail .tick:focus-visible .lbl{color:var(--ink);transform:scale(1.16)}
.trail .tick:hover .dot::before{background:var(--ink);transform:scale(1.3)}

/* legs already walked */
.trail .tick[data-passed="true"] .dot::before{background:var(--accent);opacity:.65}

/* the page you are on */
.trail .tick[aria-current="page"] .lbl{color:var(--ink);font-weight:500;transform:scale(1.08)}
.trail .tick[aria-current="page"] .num{color:var(--accent-ink)}
.trail .tick[aria-current="page"] .dot::before{
  background:var(--accent);
  transform:scale(1.7);
}

/* the foot of the rail: the tagline, the one real action, socials, language */
.rail-foot{
  margin-top:auto;
  padding-top:var(--s4);
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:var(--s2);
}
/* Quicksand Light, matching the tagline set inside lockup.svg */
.rail-tagline{
  font-family:var(--font-tagline);
  font-weight:300;
  font-size:.9rem;
  line-height:1.25;
  color:var(--ink-dim);
  letter-spacing:.015em;
  padding-bottom:var(--s2);
  border-bottom:1px solid var(--rule);
  width:calc(100% - var(--s3));
}
/* The one real action in the rail. It spans the rail's whole column, from the
   left padding to the gutter the tagline above already reserves, so it reads
   as the width of the menu rather than as a chip parked in it.

   On hover it EXTRUDES. The offset is a hard box-shadow, no blur, in the same
   hairline the border is drawn in, so what appears is the outline given depth
   rather than a shadow cast onto the page: at rest the shadow is folded flat
   underneath the face, and on hover the face travels up and left by exactly
   the distance the shadow travels down and right. The far corner of the slab
   therefore stays pinned where the button was sitting, which is what makes it
   read as a solid block lifting rather than as the whole control sliding.

   Pressing it puts the face back down on the slab, quickly.

   The experience bar is still here, underneath all that: the bottom border is
   a track, and a rosewood segment fills it as the face lifts. The bar only
   ever travels LEFT TO RIGHT. Animating `right: 100% -> 0` filled correctly
   but ran backwards on mouse-out, which reads as losing progress. Swapping
   transform-origin instead fixes that: growing from the left edge on hover,
   shrinking toward the right edge on leave, so the motion is rightward in
   both directions. The accent is a moving rule here, not a resting fill: at
   rest the control is a quiet outline.

   THIS IS THE SITE'S ONLY PRIMARY-ACTION TREATMENT. It used to be the rail's
   alone, while the hero CTA, the Range+ CTA and the contact submit were plain
   outlined boxes that flooded solid stone on hover: one site speaking two
   languages for the same job, and the plain three were the least designed
   thing on a very designed page. They share the mechanic now. The stone flood
   is gone with them, which does not contradict "primary action is STONE, never
   accent-filled": the rosewood here is three pixels of moving rule along the
   bottom edge, never a fill of the face. */
.rail-cta,
.hero .begin,
.range-cta,
.send,
.consent-btn{
  position:relative;
  isolation:isolate;
  box-shadow:0 0 0 var(--rule-strong);
  transition:transform .3s cubic-bezier(.22,1,.36,1),
             box-shadow .3s cubic-bezier(.22,1,.36,1),
             color .3s ease;
}
/* the rail's own column layout, which the page CTAs do not share */
.rail-cta{
  display:block;
  width:calc(100% - var(--s3));
  text-align:center;
  font-size:.78rem;
  letter-spacing:.16em;
  text-transform:uppercase;
  font-weight:500;
  color:var(--ink);
  border:1px solid var(--rule-strong);
  padding:.7rem 1rem .75rem;
}
/* the empty track, so it reads as a bar before anything happens */
.rail-cta::after,
.hero .begin::after,
.range-cta::after,
.send::after,
.consent-btn::after{
  content:'';
  position:absolute;
  left:0; right:0; bottom:0;
  height:3px;
  background:var(--rule);
}
/* the fill */
.rail-cta::before,
.hero .begin::before,
.range-cta::before,
.send::before,
.consent-btn::before{
  content:'';
  position:absolute;
  left:0; right:0; bottom:0;
  height:3px;
  background:var(--accent);
  z-index:1;
  transform:scaleX(0);
  transform-origin:right;          /* leaving: the left edge travels right */
  transition:transform .55s cubic-bezier(.22,1,.36,1);
}
.rail-cta:hover::before,      .rail-cta:focus-visible::before,
.hero .begin:hover::before,   .hero .begin:focus-visible::before,
.range-cta:hover::before,     .range-cta:focus-visible::before,
.send:hover::before,          .send:focus-visible::before,
.consent-btn:hover::before,   .consent-btn:focus-visible::before{
  transform:scaleX(1);
  transform-origin:left;           /* arriving: the right edge travels right */
}
.rail-cta:hover,    .rail-cta:focus-visible,
.hero .begin:hover, .hero .begin:focus-visible,
.range-cta:hover,   .range-cta:focus-visible,
.send:hover,        .send:focus-visible,
.consent-btn:hover, .consent-btn:focus-visible{
  color:var(--stone);
  transform:translate(-4px,-4px);
  box-shadow:4px 4px 0 var(--rule-strong);
}
.rail-cta:active,
.hero .begin:active,
.range-cta:active,
.send:active,
.consent-btn:active{
  transform:translate(0,0);
  box-shadow:0 0 0 var(--rule-strong);
  transition-duration:.09s;
}

/* The rail has no right padding, so anything set here can run all the way to
   the border. The tagline above reserves that gutter for itself; the socials
   need it too now that they are readable, otherwise at the 190px rail the two
   labels stop 3px short of the rule. With the gutter they wrap instead. */
.rail-social{
  display:flex;
  gap:var(--s2);
  flex-wrap:wrap;
  max-width:calc(100% - var(--s3));
}
/* Drawn marks rather than the names. Three names would have wrapped to two
   lines in a 163px column and outweighed the button they sit under; three
   hairline marks read at a glance and leave the column quiet. The name is
   still there for a screen reader and on hover, via aria-label and title. */
.rail-social a{
  display:block;
  color:var(--ink-mute);
  transition:color .3s ease,transform .3s cubic-bezier(.22,1,.36,1);
}
.social-mark{width:20px;height:20px}
.rail-social a:hover,
.rail-social a:focus-visible{color:var(--ink);transform:translateY(-2px)}

.trail .lang{
  align-self:flex-start;
  font-size:.72rem;
  letter-spacing:.16em;
  color:var(--ink-mute);
  border:1px solid var(--rule);
  padding:.35rem .6rem;
  transition:color .3s ease,border-color .3s ease;
}
.trail .lang:hover{color:var(--ink);border-color:var(--rule-strong)}

/* A laptop open at 1100x600 is a real thing, and the rail is a fixed column
   with no scroll: at that height the foot used to run past the bottom edge.
   The waypoint gap above already shrinks with the viewport (3.2vh), so this
   only has to do the same for the block underneath it. */
@media (max-height:780px){
  .trail .brand{margin-bottom:var(--s3)}
  .rail-label{margin-bottom:var(--s2)}
  .rail-foot{gap:.7rem;padding-top:var(--s3)}
  .rail-tagline{padding-bottom:.7rem}
  .rail-cta{padding:.5rem .9rem .55rem}
}
/* below that there is simply not enough column left, at any type size, so the
   rail carries its own scroll rather than clipping the language toggle off the
   bottom edge. It is fixed and out of the document flow, so this cannot touch
   the page scroll the wandering line depends on. */
@media (max-height:560px){
  .trail{overflow-y:auto;scrollbar-width:thin}
}

/* ---- 3. the bar  ·  tablet and phone ------------------------------------ */
.topbar{
  display:none;
  position:fixed; top:0; left:0; right:0; z-index:45;
  padding:var(--s2) var(--pad-x);
  background:color-mix(in srgb,var(--ground) 94%,transparent);
  backdrop-filter:blur(12px);
  border-bottom:1px solid var(--rule);
  align-items:center;
  gap:var(--s3);
}
.topbar .wm{width:88px;flex:0 0 auto}
/* the bar says where you are, so the current chapter reads without opening
   anything */
.bar-where{
  flex:1;
  min-width:0;
  font-size:.76rem;
  letter-spacing:.15em;
  text-transform:uppercase;
  color:var(--ink-dim);
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.bar-where .n{color:var(--accent-ink);margin-right:.5rem}

.menu-btn{
  flex:0 0 auto;
  display:flex;
  align-items:center;
  gap:.6rem;
  font-size:.76rem;
  letter-spacing:.15em;
  text-transform:uppercase;
  color:var(--ink);
  font-weight:500;
  border:1px solid var(--rule-strong);
  padding:.55rem .85rem;
}
.menu-btn .bars{display:grid;gap:3px;width:14px}
.menu-btn .bars i{display:block;height:1px;background:currentColor;transition:transform .3s ease,opacity .3s ease}
.menu-btn[aria-expanded="true"] .bars i:nth-child(1){transform:translateY(4px) rotate(45deg)}
.menu-btn[aria-expanded="true"] .bars i:nth-child(2){opacity:0}
.menu-btn[aria-expanded="true"] .bars i:nth-child(3){transform:translateY(-4px) rotate(-45deg)}

.sheet{
  position:fixed; inset:0; z-index:44;
  background:var(--ground);
  padding:calc(66px + var(--s4)) var(--pad-x) var(--s5);
  transform:translateY(-100%);
  /* visibility, not just transform: moved offscreen alone, the links stay in
     the tab order and a keyboard user falls into an invisible menu */
  visibility:hidden;
  transition:transform .5s cubic-bezier(.4,0,.2,1),visibility 0s linear .5s;
  overflow-y:auto;
}
.sheet[data-open="true"]{
  transform:translateY(0);
  visibility:visible;
  transition:transform .5s cubic-bezier(.4,0,.2,1),visibility 0s;
}
.sheet-label{
  font-size:.74rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-mute);font-weight:500;margin-bottom:var(--s3);
}
.sheet a{
  display:flex;align-items:baseline;gap:var(--s3);
  padding:var(--s3) 0;border-bottom:1px solid var(--rule);
  font-family:var(--font-display);font-weight:200;
  font-size:clamp(1.6rem,7vw,2.4rem);line-height:1.1;
}
.sheet a .n{
  font-size:.76rem;letter-spacing:.14em;color:var(--accent-ink);
  font-family:var(--font-text);font-weight:500;flex:0 0 2ch;
}
.sheet a[aria-current="page"] .w{color:var(--accent-ink)}
.sheet .lang{
  margin-top:var(--s4);display:inline-block;
  font-size:.76rem;letter-spacing:.16em;
  border:1px solid var(--rule);padding:.55rem .95rem;color:var(--ink-mute);
}

/* The rail needs room for its longest Greek label, ΕΠΙΚΟΙΝΩΝΙΑ. Below this
   it would crowd the content, so the bar takes over. */
@media (max-width:1080px){
  .trail{display:none}
  .topbar{display:flex}
}
/* above the breakpoint the bar and its sheet do not exist at all, so neither
   can be reached by keyboard */
@media (min-width:1081px){
  .topbar,.sheet{display:none}
}

/* ---- 4. the gate  ·  page transitions ----------------------------------- */
/* On a five-page site the wipe that used to happen between sections happens
   between pages. Leaving, the gate closes over the screen; arriving, it opens
   away, carrying the numeral and name of the chapter you are entering. */
.gate-overlay{
  position:fixed; inset:0; z-index:85;
  background:var(--charcoal);
  display:grid; place-items:center;
  pointer-events:none;
  clip-path:inset(0 0 100% 0);
}
/* Two states only. There were four: the arriving page also covered itself and
   wiped away, which could not work, because the gate is injected by JS and the
   document has already painted by then. See gate() in app.js. */
.gate-overlay[data-state="closing"]{
  clip-path:inset(0 0 0 0);
  transition:clip-path .5s cubic-bezier(.76,0,.24,1);
}
.gate-overlay[data-state="idle"]{clip-path:inset(0 0 100% 0);transition:none}

/* The arriving half. The gate used to have only a departing one: the panel
   swept over the page you were leaving, and then the page you arrived at
   simply appeared, finished, with nothing to uncover it. Half a transition.

   It cannot be injected by chrome(). That was the original bug, and the
   comment in gate() records what it looked like: the modules do not land until
   ~950ms, so the new page painted first and the panel dropped over it
   afterwards, a curtain falling after the show had started. So this is built
   at markup time by the handoff script at the top of every page, before the
   first paint, exactly the way index.html handles the prologue. The element
   only exists when the visitor actually came through a closing gate, so a
   direct visit or a bookmark never sees it.

   Direction matters as much as it does on the rail CTA: the departing panel's
   BOTTOM edge sweeps down over the old page, and this one's TOP edge carries
   on down off the new one. One continuous move rather than a rewind. */
.gate-arrive{
  position:fixed; inset:0; z-index:86;
  background:var(--charcoal);
  display:grid; place-items:center;
  pointer-events:none;
  clip-path:inset(0 0 0 0);
  /* it lifts itself: nothing has to come back for it, so a module that never
     lands can never leave a visitor under a charcoal panel */
  animation:gate-part .55s cubic-bezier(.76,0,.24,1) .12s forwards;
}
@keyframes gate-part{to{clip-path:inset(100% 0 0 0)}}

.gate-overlay .gate-inner,
.gate-arrive .gate-inner{
  display:flex;
  align-items:baseline;
  gap:var(--s4);
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.6rem,5vw,3.6rem);
  letter-spacing:-.02em;
  color:var(--stone);
}
.gate-overlay .g-num,
.gate-arrive .g-num{
  font-size:var(--t-micro);
  letter-spacing:.24em;
  font-family:var(--font-text);
  font-weight:500;
  color:var(--rose);
  text-transform:uppercase;
}

/* ---- 5. content entrances ----------------------------------------------- */
/* Clip-mask wipes, never opacity fade-ups.
   Every hidden starting state is scoped to html.motion, which only JS adds.
   If the script fails, nothing is ever clipped and the page reads normally. */
.motion .wipe,
.motion .page-head{clip-path:inset(0 0 100% 0)}
.wipe,.page-head{transition:clip-path .95s cubic-bezier(.22,1,.36,1)}
.wipe[data-lit="true"],
.page-head[data-lit="true"]{clip-path:inset(0 0 0 0)}

/* The stagger. --i is the element's place in the batch that crossed the line
   together, set in observe(); see the note there for why it is not its place in
   the list. Capped at six so a long section cannot make its last row wait two
   thirds of a second, which stops reading as sequence and starts reading as
   lag. Only on elements whose transition is clip-path alone: `transition-delay`
   applies to every property in the list, and delaying .plate here would delay
   its hover lift too. */
.wipe,.page-head,.guide .portrait,.expedition .plate-lg{
  transition-delay:calc(min(var(--i,0),6) * 55ms);
}

/* plates wipe up as they enter */
.motion .plate,
.motion .guide .portrait,
.motion .expedition .plate-lg{clip-path:inset(0 0 100% 0)}
.plate,.guide .portrait,.expedition .plate-lg{
  transition:clip-path 1s cubic-bezier(.22,1,.36,1);
}
.plate[data-lit="true"],
.guide .portrait[data-lit="true"],
.expedition .plate-lg[data-lit="true"]{clip-path:inset(0 0 0 0)}

/* gallery hover: the plate lifts.
   The parallax and the hover are on DIFFERENT properties on purpose. `translate`
   carries the scroll offset that drift() publishes as --dy, and is deliberately
   left out of the transition list so it tracks the scroll exactly. `transform`
   carries the hover and is the only one that eases. Putting both on `transform`
   is what broke this before: whichever wrote last won, and drift wrote every
   frame. Two properties, two jobs, and they compose. */
.plate{
  translate:0 var(--dy,0px);
  transition:clip-path 1s cubic-bezier(.22,1,.36,1),transform .5s ease;
}
.plate:hover{transform:translateY(-8px)}
.plate-caption{
  opacity:0;
  translate:0 var(--dy,0px);
  transform:translateY(6px);
  transition:opacity .4s ease,transform .4s ease;
}
.plate:hover+.plate-caption,
.plate:focus-visible+.plate-caption{opacity:1;transform:translateY(0)}

/* ---- the parallax, off the main thread ----------------------------------
   drift() in gallery.js does this by hand on every scroll frame: eight
   getBoundingClientRect-driven writes a frame. A scroll timeline does the same
   arithmetic in the compositor and never wakes the main thread, so when the
   browser has one, gallery.js stands down and this takes over.

   The timeline is declared on the CONSTELLATION, not on each plate, so every
   plate shares one progress value and they travel in lockstep at different
   rates, which is what the hand-written version did. Per-plate view() timelines
   would each run through their own range and the composition would come apart.

   --rate is the plate's own drift rate, published once when it is built. The
   ±210px matches the hand-written amplitude of `off * rate * 420`. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion:no-preference) and (min-width:861px) {
    .constellation{view-timeline:--constellation block}
    .plate,.plate-caption{
      animation:plate-drift linear both;
      animation-timeline:--constellation;
      animation-range:cover;
    }
    @keyframes plate-drift{
      from{translate:0 calc(var(--rate,.05) * -210px)}
      to  {translate:0 calc(var(--rate,.05) *  210px)}
    }
  }
}

/* ---- 6. the prologue ---------------------------------------------------- */
/* Present in the markup and covering from the first paint. The inline guard in
   index.html suppresses it before anything is drawn when it should not play. */
.prologue{
  position:fixed; inset:0; z-index:90;
  background:var(--charcoal);
  display:grid;
  place-items:center;
  clip-path:inset(0 0 0 0);
  transition:clip-path 1.05s cubic-bezier(.76,0,.24,1);
  /* the CSS fallback: it lifts on its own even if the script never runs, so a
     failed module can never leave a visitor staring at a blank panel */
  animation:pro-auto-lift 1.05s cubic-bezier(.76,0,.24,1) 2.2s forwards;
}
.no-prologue .prologue{display:none}
.prologue[data-lift="true"]{clip-path:inset(0 0 100% 0)}
@keyframes pro-auto-lift{to{clip-path:inset(0 0 100% 0)}}
.prologue .pro-inner{display:grid;place-items:center;gap:var(--s4);text-align:center}

/* the mark: the full lockup, wordmark and Elevated Wellness in one piece.
   It used to arrive in two beats, a rosewood wave drawing itself and the
   letterforms resolving under it. One piece, in stone, is quieter and puts the
   tagline in front of the visitor on the very first screen. */
.prologue .pro-mark{
  width:min(56vw,420px);
  color:var(--stone);
}
.prologue .pro-mark svg{width:100%;height:auto;display:block}

/* the wipe only starts once the SVG has actually landed, otherwise it plays
   against an empty div and the mark just pops in */
.prologue .pro-mark{clip-path:inset(0 100% 0 0)}
.prologue[data-ready="true"] .pro-mark{
  animation:wipe-in 1.05s cubic-bezier(.22,1,.36,1) forwards;
}
@keyframes wipe-in{to{clip-path:inset(0 0 0 0)}}
.prologue .pro-line{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.05rem,2.4vw,1.5rem);
  color:var(--taupe);
  letter-spacing:.01em;
  clip-path:inset(0 100% 0 0);
}
.prologue[data-ready="true"] .pro-line{
  animation:wipe-in .7s cubic-bezier(.22,1,.36,1) .8s forwards;
}
.prologue .skip{
  position:absolute; bottom:var(--s4); right:var(--s4);
  font-size:var(--t-micro); letter-spacing:.16em; text-transform:uppercase;
  color:var(--grey);
  opacity:0;
}
.prologue[data-ready="true"] .skip{
  animation:show .4s ease .7s forwards;
}
@keyframes show{to{opacity:1}}
.prologue .skip:hover{color:var(--stone)}

/* ---- 7. cursor trail ---------------------------------------------------- */
.cursor-trail{
  position:fixed; inset:0; z-index:55;
  pointer-events:none;
  display:none;
}
@media (hover:hover) and (pointer:fine){
  .cursor-trail{display:block}
}
.cursor-trail path{
  fill:none;
  stroke:var(--rose);
  stroke-width:1;
  stroke-linecap:round;
  opacity:.4;
}

/* ---- 7b. the consent notice --------------------------------------------
   It wipes up from the bottom edge and back down again, the same clip-path
   move the gate and every section on this site use, rather than sliding or
   fading in the way a bolted-on cookie bar does. `visibility` is stepped so
   the band is not a focus target before it has arrived or after it has left. */
.consent{
  clip-path:inset(100% 0 0 0);
  visibility:hidden;
  transition:clip-path .75s cubic-bezier(.22,1,.36,1),visibility 0s linear .75s;
}
.consent[data-open="true"]{
  clip-path:inset(0 0 0 0);
  visibility:visible;
  transition:clip-path .75s cubic-bezier(.22,1,.36,1),visibility 0s;
}
/* The branch you took holds in rosewood on the way out; the other one goes.
   The decision is the last thing you see of it. */
.consent[data-chosen] .stem{stroke:var(--accent)}
.consent[data-chosen="essential"] .branch-r,
.consent[data-chosen="essential"] .dot-r,
.consent[data-chosen="stats"] .branch-l,
.consent[data-chosen="stats"] .dot-l{opacity:0}
.consent[data-chosen="essential"] .branch-l,
.consent[data-chosen="essential"] .dot-l,
.consent[data-chosen="stats"] .branch-r,
.consent[data-chosen="stats"] .dot-r{stroke:var(--accent)}

/* ---- 8. lightbox entrance ----------------------------------------------- */
.lightbox{
  clip-path:inset(0 0 100% 0);
  transition:clip-path .6s cubic-bezier(.22,1,.36,1),visibility 0s linear .6s;
}
.lightbox[data-open="true"]{
  clip-path:inset(0 0 0 0);
  transition:clip-path .6s cubic-bezier(.22,1,.36,1),visibility 0s;
}

/* ============================================================================
   9. REDUCED MOTION
   Everything renders complete and static. Full content parity, no exceptions.
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms!important;
    animation-iteration-count:1!important;
    transition-duration:.001ms!important;
    /* Belt and braces on the stagger. observe() never sets --i on this path, so
       the delay already computes to zero, but a near-zero DURATION with a live
       DELAY would still hold content back by a third of a second, which is the
       one failure mode that would matter to somebody who asked for less motion
       rather than more. */
    transition-delay:0s!important;
    animation-delay:0s!important;
    scroll-behavior:auto!important;
  }
  .wipe,.page-head,.plate,.guide .portrait,.expedition .plate-lg,
  .lightbox[data-open="true"]{
    clip-path:none!important;
  }
  /* the gate never covers the screen at all when motion is reduced */
  .gate-overlay,.gate-arrive{display:none!important}
  /* the notice arrives and leaves without the wipe, but it still arrives: a
     consent choice is content, not decoration, and is never suppressed here */
  .consent{clip-path:none!important;visibility:hidden}
  .consent[data-open="true"]{visibility:visible}
  .prologue{display:none!important}
  .cursor-trail{display:none!important}
  .plate:hover{transform:none}
  .trail .tick:hover .lbl,.trail .tick:focus-visible .lbl{transform:none}
  /* the button still marks itself on hover, it just does not travel to do it,
     and the bar arrives filled rather than filling */
  .rail-cta:hover,.rail-cta:focus-visible{transform:none}
  .rail-cta::before{transition:none!important}
  .rail-cta:hover::before,.rail-cta:focus-visible::before{
    transform:scaleX(1);transition:none!important;
  }
  .plate-caption{opacity:1;transform:none}
  #wander path{opacity:.4}
}
