/* PROVI homepage — 3D scroll journey layout. */

body.home{overflow-x:hidden}
/* no nav on the homepage, so the shared nav-height offset on <main> would
   just add dead space to the top of the scroll track */
body.home main{padding-top:0}

#sceneCanvas{
  position:fixed;inset:0;z-index:0;
  display:block;width:100vw;height:100vh;
}

/* the scroll track: real page height so the browser gives us real scroll
   distance to drive the camera with. Each content block is fixed to the
   viewport and only its opacity/transform change — nothing here scrolls
   in the traditional sense, the world moves instead. */
.scene-track{
  position:relative;
  /* Length is dictated by the speed table in scene.js, not chosen. Dwell
     costs 1/0.30 of normal scroll, features cost 1, cruise 1/1.7, transit
     1/3. Integrating over the journey gives 1.2110 cost units; sizing so a
     reading window still spans ~355vh puts the track here. Change any rate
     or any window and this has to move with it. */
  height:2345vh;
  z-index:1;
}

.scene-content{
  position:fixed;
  inset:0;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  text-align:center;
  padding-inline:var(--pad);
  opacity:0;
  pointer-events:none;
  transition:none; /* driven by JS, not CSS transitions */
}
.scene-content.is-active{pointer-events:auto}

/* The scene behind the copy is busy grain at full contrast, so every text
   block sits on its own pool of shade. It fades with the section, because
   opacity on the parent takes the pseudo-element with it. */
.scene-content::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse 62% 46% at 50% 50%,
    rgba(10,10,12,.78), rgba(10,10,12,0) 72%);
}
/* A positioned pseudo-element paints above ordinary block children, so the
   shade would cover the copy it exists to support. Lifting the children is
   safer than pushing the shade to z-index -1, which would drop it behind the
   track's own stacking context and onto the wrong side of the canvas. */
.scene-content > *{position:relative;z-index:1}

.scene-content .kicker{opacity:.9}

/* The display voice: Bruno Ace, caps, tracked open. No weight is set
   because there is only one, and asking for a heavier one would synthesise
   it and clog the square counters that give the face its shape.

   Sizes are well below the Archivo settings these replaced: Bruno Ace runs
   far wider per character, so the same clamp would have overflowed. Leading
   sits just under 1 because capitals have no descenders and the default line
   box otherwise opens a gap that reads as two separate lines. */
.scene-h{
  font-family:var(--font-display);
  font-weight:400;
  text-transform:uppercase;
  font-size:clamp(1.85rem,5.2vw,4.5rem);
  letter-spacing:.02em;
  line-height:1.0;
  max-width:15ch;
  margin-bottom:1.4rem;
  /* Not white. #F2F1EC against this much black is a hole punched in the
     frame; the type should sit in the same tonal range as the brightest
     grain behind it. */
  color:#B4B2AB;
  /* NO text-shadow here. text-shadow paints ABOVE a background-clip:text
     fill: the glyph is transparent so it contributes nothing, but the shadow
     still renders, and a wide dark blur at zero offset lands as a grey cloud
     straight through the middle of every letter. (On ordinary opaque text
     the glyph hides it, which is why the subtitle can still use one.)
     The halo is a drop-shadow instead, inside the keyframes below, because
     filter operates on the rendered element and composites behind it. */
}

/* The title used to be filled with the same fractal noise as the page
   overlay, clipped to the letterforms. It went with the rest of the CSS
   grain: at this size the speckle read as a dirty print rather than as
   film, and against the point-grain scene behind it the two textures
   fought. The glyphs are a flat paper tone now; the grain in the scene
   is the only grain on the page. */

.scene-p{
  color:#8A8882;
  font-size:clamp(.98rem,1.15vw,1.12rem);
  line-height:1.65;
  max-width:32rem;
  letter-spacing:.005em;
  text-shadow:0 0 10px rgba(242,241,236,.07), 0 0 26px rgba(10,10,12,.92);
}
/* THE PUNCHLINE OF EACH SECTION. Every scene block ends on one emphasised
   line — the tab they stop on, the same standard at every size — and they
   were a slightly brighter grey among greys. They are the sentences the whole
   section exists to deliver, so they are the right place to spend colour on a
   page that otherwise has none.

   IT HAS TO BE THE WHOLE LINE, AND THAT NEEDS A CLASS. <em> does two jobs in
   this copy: a closing statement that is its own paragraph, and a foreign
   word inside a sentence (providentia, provisions on the About page).
   Colouring both puts the highlight on two words mid-paragraph, which on
   the web reads as a hyperlink. :only-child cannot separate them, because it ignores
   text nodes and so matches an <em> surrounded by prose just as happily. */
.scene-p em{color:#C6C4BD;font-style:normal}
.scene-p--close em{color:var(--accent-text)}

/* ---- alignment variants ------------------------------------------------
   The flight banks the canyon to one side and leaves the other empty, so
   copy is placed into the dark half rather than centred over the grain. The
   shade moves with it: a pool still centred at 50% would sit on the wall and
   miss the text. */
.scene-block{width:min(100%,40rem)}
.scene-block .scene-p{max-width:none}
.scene-block .scene-p + .scene-p{margin-top:1.05rem}

.scene-content--right{align-items:flex-end;text-align:right}
.scene-content--right::before{
  background:radial-gradient(ellipse 54% 52% at 74% 50%,
    rgba(10,10,12,.84), rgba(10,10,12,0) 72%);
}
.scene-content--left{align-items:flex-start;text-align:left}
.scene-block--center{text-align:center;margin-inline:auto}
.scene-content--left::before{
  background:radial-gradient(ellipse 54% 52% at 26% 50%,
    rgba(10,10,12,.84), rgba(10,10,12,0) 72%);
}

/* Secondary heading: same grain fill and caps, sized to sit under a body of
   text instead of carrying a frame on its own. */
.scene-h--sm{
  font-size:clamp(1.3rem,2.7vw,2.15rem);
  letter-spacing:.025em;
  line-height:1.14;
  max-width:19ch;
  margin-bottom:1.5rem;
}
.scene-content--right .scene-h--sm{margin-left:auto}
/* A centred block centres its TEXT, but .scene-h--sm is capped at 19ch and
   its box stays flush left inside a 40rem block. The words then sit centred
   within a narrow box that is itself off to one side, which reads as a
   heading nudged left of everything under it. The auto margins centre the
   box, not just the text. */
.scene-block--center .scene-h{margin-inline:auto}

/* The closing call to action. Two routes out: commit, or keep looking. Both
   outlined rather than filled, because a solid button would be the only
   opaque object on a page made entirely of grain. The lead one is separated
   by weight of line and brightness instead. */
.scene-cta-row{
  display:flex;gap:.85rem;flex-wrap:wrap;
  justify-content:center;margin-top:1.9rem;
}
.scene-content--right .scene-cta-row{justify-content:flex-end}
.scene-content--left  .scene-cta-row{justify-content:flex-start}
.scene-cta{
  display:inline-block;
  font-family:var(--font-display);font-weight:400;
  font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;
  color:#9C9A94;
  padding:.9rem 1.7rem;
  border:1px solid rgba(242,241,236,.16);
  transition:border-color .3s,color .3s,background-color .3s;
}
/* The lead route out of the closing frame. It was separated from its
   neighbour by line weight alone, which on a page made entirely of grey grain
   is a very quiet way to say "this is the one". Still outlined rather than
   filled, because a solid button would be the only opaque object on the
   page, but the outline and the type now carry the highlight. */
.scene-cta--lead{
  color:var(--accent-text);
  border-color:var(--accent-dim);
}
.scene-cta--lead:hover{
  color:var(--accent-text);
  border-color:var(--accent);
  background-color:var(--accent-soft);
}
.scene-cta:hover{
  border-color:var(--accent-dim);
  color:var(--accent-text);
  background-color:var(--accent-soft);
}

/* The hero's halo lives in its keyframes. Sections that fade on scroll have
   no entrance animation, so they carry it statically. */
.scene-content:not(#s-hero) .scene-h{
  filter:drop-shadow(0 0 12px rgba(242,241,236,.09))
         drop-shadow(0 0 28px rgba(10,10,12,.88));
}

/* ---- arrival ----------------------------------------------------------
   Blur resolving to nothing, so the copy assembles out of the grain rather
   than switching on over it. Staggered, and applied to the CHILDREN because
   scene.js writes opacity onto the section element every frame and would
   overwrite any animation set there. */
@keyframes hero-in{
  from{opacity:0;transform:translateY(13px);filter:blur(8px)}
  to  {opacity:1;transform:none;filter:blur(0)}
}
/* The title's own keyframe carries its halo. It cannot live in a static
   filter rule: hero-in animates filter, and with fill-mode both the settled
   frame would overwrite it. */
@keyframes hero-title-in{
  from{opacity:0;transform:translateY(13px);
       filter:blur(8px)
              drop-shadow(0 0 15px rgba(242,241,236,.10))
              drop-shadow(0 0 32px rgba(10,10,12,.88))}
  to  {opacity:1;transform:none;
       filter:blur(0)
              drop-shadow(0 0 15px rgba(242,241,236,.10))
              drop-shadow(0 0 32px rgba(10,10,12,.88))}
}
@keyframes hero-grain-drift{
  from{background-position:0 0}
  to  {background-position:120px 72px}
}
#s-hero .scene-h{
  animation:hero-title-in .85s cubic-bezier(.22,.61,.36,1) .12s both,
            hero-grain-drift 26s linear infinite;
}
#s-hero .scene-p{animation:hero-in .85s cubic-bezier(.22,.61,.36,1) .28s both}
#s-hero .scroll-cue{animation:hero-in .85s cubic-bezier(.22,.61,.36,1) .46s both}

@media(prefers-reduced-motion:reduce){
  #s-hero .scene-h,#s-hero .scene-p,#s-hero .scroll-cue{animation:none}
  /* the halo lives in the keyframes, so it needs restating once they are off */
  #s-hero .scene-h{
    filter:drop-shadow(0 0 15px rgba(242,241,236,.10))
           drop-shadow(0 0 32px rgba(10,10,12,.88));
  }
}

/* CENTRED WITHOUT A TRANSFORM. It used to centre with translateX(-50%), and
   hero-in animates `transform` and settles on `transform:none` with fill
   both — so the entrance wiped the centring and left the cue sitting half its
   own width to the right, measured at 27px off on every screen size. Setting
   both insets and letting text-align do the work leaves transform free for
   the animation that owns it. */
.scroll-cue{
  position:absolute;bottom:8vh;left:0;right:0;
  text-align:center;
  font-size:.72rem;letter-spacing:.18em;text-transform:uppercase;
  /* The first instruction on the site, and the only thing asking the visitor
     to do something. It reads as an instruction rather than as furniture now. */
  color:var(--accent-text);
}
/* On a phone, vh measures the viewport with the browser chrome hidden, so an
   8vh offset can land under the toolbar once it slides back in. svh is that
   short state, and the floor keeps it clear of the home indicator. */
@media(max-width:640px){
  .scroll-cue{bottom:max(12svh, 88px)}
}

/* Sits at the same bottom offset the scroll marker uses. They never overlap:
   the marker reaches 0 at exactly the scroll position where this section
   starts opening. */
.scene-foot{
  position:absolute;left:50%;transform:translateX(-50%);
  bottom:clamp(18px,4vh,40px);
  width:max-content;max-width:calc(100% - var(--pad) * 2);
  pointer-events:auto;
  font-family:var(--font-display);font-weight:400;
  font-size:.58rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--paper-faint);
  text-shadow:0 0 18px rgba(10,10,12,.9);
}
.scene-foot a{color:inherit;transition:color .3s}
.scene-foot a:hover{color:#DEDCD4}

.path-grid{
  display:grid;grid-template-columns:1fr;gap:1.5rem;
  margin-top:2rem;width:min(100%,720px);
}
@media(min-width:680px){.path-grid{grid-template-columns:1fr 1fr}}
.path-card{
  border:1px solid var(--line);
  background:rgba(20,20,22,.55);
  backdrop-filter:blur(6px);
  padding:2rem 1.75rem;
  text-align:left;
}
.path-name{font-family:var(--font-display);font-weight:400;font-size:1rem;letter-spacing:.04em;text-transform:uppercase}
.path-price{color:var(--accent-text);font-weight:600;margin-top:.35rem}
.path-desc{color:var(--paper-dim);font-size:.92rem;margin-top:.75rem;line-height:1.6}
.path-card .btn{margin-top:1.5rem}

.process-list{
  margin-top:2rem;display:grid;grid-template-columns:1fr;gap:.9rem;
  width:min(100%,420px);text-align:left;
}
.process-list li{
  display:flex;align-items:baseline;gap:1rem;
  font-family:var(--font-body);font-size:1.02rem;font-weight:500;
  border-top:1px solid var(--line);padding-top:.9rem;
}
.process-list li span{color:var(--accent-text);font-size:.82rem;font-weight:700}

.cta-row{display:flex;gap:1rem;flex-wrap:wrap;justify-content:center;margin-top:2rem}

@media(prefers-reduced-motion:reduce){
  .scene-track{height:auto}
  .scene-content{
    position:relative;opacity:1;pointer-events:auto;
    min-height:60vh;
  }
  #sceneCanvas{position:fixed}
}

/* ---------- the standing waitlist link ----------
   Bottom left for the whole scroll. The scroll cue and the section feet own
   the bottom centre and the marker owns the bottom right, so the fourth
   corner is the one place a permanent link can sit without ever colliding
   with the scene's own furniture.

   It borrows hero-in rather than defining its own entrance: the brief is
   that it arrives WITH the content, not before it, and reusing the keyframe
   means it can never drift out of step with the hero if that timing is
   retuned. Last in the stagger, just behind the scroll cue at .46s. */
.wl-corner{
  position:fixed;
  left:var(--pad);
  bottom:clamp(18px,4vh,40px);
  z-index:100;                    /* the nav's layer: above both canvases */
  font-family:var(--font-display);font-weight:400;
  font-size:.58rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--paper-faint);
  padding:.35rem 0;
  border-bottom:1px solid transparent;
  text-shadow:0 0 20px rgba(10,10,12,.9);
  transition:color .3s,border-color .3s;
  animation:hero-in .85s cubic-bezier(.22,.61,.36,1) .58s both;
}
.wl-corner:hover{color:var(--accent-text);border-bottom-color:var(--paper-faint)}
.wl-corner:focus-visible{outline:2px solid var(--accent);outline-offset:4px}

/* On a phone the bottom centre is crowded and the side padding is small, so
   it steps down a size rather than crowding the cue beside it. */
@media(max-width:560px){
  .wl-corner{font-size:.52rem;letter-spacing:.16em}
}
@media(prefers-reduced-motion:reduce){
  .wl-corner{animation:none}
}
