/* ==========================================================================
   uplift.v1.css  (Phase 2 foundation)
   The premium finish layer: grain, the optical security sweep, calibration
   chrome, the proof band, kinetic headings and the spotlight surface.
   Sources: docs/phase2-design-research-2026-07-17.md. Everything here is
   token-driven (tokens.v1.css), gated by html.no-motion where it moves, and
   safe over every existing surface. Link AFTER borderglow.v1.css.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. GRAIN - the tactile finish. One fixed, non-interactive noise layer over
   the whole viewport; kills the flat-vector "template" read on dark slabs.
   body::after is owned by surveyed.v1.css - grain lives on body::before.
   -------------------------------------------------------------------------- */
body::before{
  content:"";
  position:fixed;
  inset:0;
  z-index:2147483000;
  pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  background-size:160px 160px;
  mix-blend-mode:overlay;
  opacity:.035;
}
html[data-theme="light"] body::before{opacity:.02}
@media print{ body::before{display:none} }
/* TOUCH DEVICES GET NO GRAIN. A full-viewport position:fixed layer with
   mix-blend-mode forces iOS Safari to composite the whole root through the
   blend, which disables its opaque status-bar strip fill: the strip falls
   back to a translucent top-edge sample and scrolled page text ghosts
   through above the chrome bar (owner screenshot, 5 August 2026). Isolated
   by bisecting the live page on the iPhone 17 Pro simulator - with this one
   layer off and the theme-color meta armed, the strip is opaque. At .035/.02
   opacity the grain is imperceptible on a phone screen anyway, and the blend
   layer taxes compositing on exactly the devices that can least afford it.
   Desktop pointers keep the finish. */
@media (pointer:coarse){ body::before{display:none} }

/* --------------------------------------------------------------------------
   2. OPTICAL SECURITY SWEEP (.osl) - the domain-native shimmer. A single
   diagonal light pass, the digital cousin of the DOVID/kinegram foil on a
   physical credential. One shot on hover/focus-visible; never looping,
   never decorative on unverified content. Applied automatically to primary
   CTAs and fact plates; opt in elsewhere with .osl.
   -------------------------------------------------------------------------- */
.osl,
.pp-cta,
.cta-band .cta,
.fact-plate{
  position:relative;
  overflow:hidden;
  isolation:isolate;
}
.osl::after,
.pp-cta::after,
.cta-band .cta::after,
.fact-plate::after{
  content:"";
  position:absolute;
  inset:0;
  z-index:1;
  pointer-events:none;
  background:linear-gradient(115deg,
    transparent 38%,
    rgba(255,255,255,.28) 49%,
    rgba(245,156,0,.14) 52%,
    transparent 62%);
  background-size:260% 100%;
  background-position:120% 0;
  background-repeat:no-repeat;
  opacity:0;
}
.osl:hover::after,
.osl:focus-visible::after,
.pp-cta:hover::after,
.pp-cta:focus-visible::after,
.cta-band .cta:hover::after,
.cta-band .cta:focus-visible::after,
.fact-plate:hover::after,
.fact-plate:focus-within::after{
  opacity:1;
  animation:osl-sweep var(--t-slab) var(--ease-monument) 1 forwards;
}
@keyframes osl-sweep{
  from{background-position:120% 0}
  to{background-position:-40% 0}
}
html.no-motion .osl::after,
html.no-motion .pp-cta::after,
html.no-motion .cta-band .cta::after,
html.no-motion .fact-plate::after{
  display:none;
}

/* --------------------------------------------------------------------------
   3. CALIBRATION CHROME (.cal) - the demo page's registration language,
   promoted site-wide. Static by design: corner registration ticks and a
   datum baseline that make a section read as an instrument, not a card.
   -------------------------------------------------------------------------- */
.cal{position:relative}
.cal::before,
.cal::after{
  content:"";
  position:absolute;
  width:18px;height:18px;
  pointer-events:none;
  opacity:.55;
}
.cal::before{
  top:0;left:0;
  border-top:var(--stroke-w) solid var(--datum);
  border-left:var(--stroke-w) solid var(--datum);
}
.cal::after{
  bottom:0;right:0;
  border-bottom:var(--stroke-w) solid var(--datum);
  border-right:var(--stroke-w) solid var(--datum);
}
.cal-datum{
  position:absolute;
  left:0;right:0;
  height:0;
  border-top:1px dashed var(--datum);
  opacity:.35;
  pointer-events:none;
}

/* --------------------------------------------------------------------------
   4. PROOF BAND (.proof-band) - one considered near-black band per page for
   the strongest evidence moment. Sits BELOW the ground floor (bg-footer),
   amber datum accents only: navy fails WCAG on dark (ADR-0006).
   -------------------------------------------------------------------------- */
.proof-band{
  position:relative;
  background:var(--bg-footer);
  border-top:1px solid var(--hairline-strong);
  border-bottom:1px solid var(--hairline-strong);
}
/* The band owns its own internal padding, the way .cta-band owns
   --space-cta-pad in primitives.v1.css. Without this the join model in
   surveyed.v1.css still deducted --space-proof-pad from the preceding
   section's tail, so a page that never wrote a local literal (visitor
   management) got a band with no padding AND a pre-shortened neighbour.
   The exclusion skips home's band, which is both classes and is deliberately
   governed by the plain stacked-section rhythm instead - the same exclusion
   the join model uses. It MUST be :where(:not(...)) and not :not(...):
   :not() contributes its argument's specificity, which took this rule to
   (0,2,0) and made it outrank every page-local single-class literal
   (.tr-proof went 60px -> 52px, .hc-proof 56px -> 52px). :where() contributes
   zero, so this stays (0,1,0): it supplies the curve where a page has none and
   yields to a page that states its own. */
.proof-band:where(:not(.sheet-section)){padding-block:var(--space-proof-pad)}
.proof-band::before{
  content:"";
  position:absolute;
  top:-1px;
  /* start the accent at the content-wrap edge (aligned with the section
     heading), not the full-bleed band's left edge. Mirrors .wrap: a 1280px
     column centred with margin auto, then its padding-inline
     (var(--frame) + 16px). At narrow widths max() collapses to 0 and the rule
     sits exactly on the wrap gutter. Fixes the "short line flush to x=0 that
     stops for no reason" (POLISH-12). */
  left:calc(max((100% - 1280px) / 2, 0px) + var(--frame) + 16px);
  width:120px;height:0;
  border-top:2px solid var(--stamp);
  pointer-events:none;
}
.proof-band .k{color:var(--amber-ink)}

/* --------------------------------------------------------------------------
   5. KINETIC HEADINGS (.kin) - line-mask reveals riding the EXISTING reveal
   engine (.r -> .in from reveal.v1.js). No new observers, no variable-font
   axis (Poppins ships static weights only - verified). Wrap each line in
   .kin-line > .kin-inner; stagger with --i.
   -------------------------------------------------------------------------- */
.kin .kin-line{display:block;overflow:hidden;padding-top:.16em;margin-top:-.16em;padding-bottom:.16em;margin-bottom:-.16em}
.kin .kin-inner{
  display:block;
  transform:translateY(112%);
  opacity:.001;
}
.r.in .kin .kin-inner,
.kin.r.in .kin-inner{
  transform:translateY(0);
  opacity:1;
  transition:
    transform var(--t-slab) var(--ease-expo) calc(var(--i,0)*var(--stag-base)),
    opacity var(--t-mid) linear calc(var(--i,0)*var(--stag-base));
}
html.no-motion .kin .kin-inner{
  transform:none;
  opacity:1;
  transition:none;
}

/* --------------------------------------------------------------------------
   6. SCROLL-DRIVEN ACCENTS - compositor-thread progress, decorative only,
   progressive enhancement. The .r/.in arming contract is untouched.
   -------------------------------------------------------------------------- */
@supports (animation-timeline: view()){
  .flow-progress{
    position:relative;
  }
  .flow-progress::after{
    content:"";
    position:absolute;
    left:0;top:0;bottom:0;
    width:2px;
    background:linear-gradient(to bottom, var(--stamp), var(--blue));
    transform-origin:top;
    transform:scaleY(0);
    animation:flow-fill linear both;
    animation-timeline:view();
    animation-range:entry 20% cover 70%;
    pointer-events:none;
  }
  @keyframes flow-fill{
    from{transform:scaleY(0)}
    to{transform:scaleY(1)}
  }
  html.no-motion .flow-progress::after{
    animation:none;
    transform:scaleY(1);
  }
}

/* --------------------------------------------------------------------------
   7. SPOTLIGHT SURFACE (.spot) - pointer-lit panel. magnetic.v1.js feeds
   --mx/--my; keyboard and touch users always see the fully lit state, the
   spotlight only ever ADDS light for pointer users.
   -------------------------------------------------------------------------- */
.spot{position:relative}
.spot::before{
  content:"";
  position:absolute;
  inset:0;
  z-index:0;
  pointer-events:none;
  opacity:0;
  background:radial-gradient(340px circle at var(--mx,50%) var(--my,50%),
    rgba(96,165,250,.10), transparent 65%);
  transition:opacity var(--t-base) var(--ease-out-soft);
}
.spot:hover::before{opacity:1}
html.no-motion .spot::before{display:none}

/* --------------------------------------------------------------------------
   8. MAGNETIC TARGETS ([data-magnetic]) - transform written by
   magnetic.v1.js; CSS owns only the release spring. Pointer-fine only.
   -------------------------------------------------------------------------- */
@media (pointer:fine){
  [data-magnetic]{
    transition:transform var(--t-base) var(--ease-spring);
    will-change:transform;
  }
}
