/* ============================================================
   CLIPSCOUT — GROUND & SHEET

   A different architecture, not another adjustment.

   Until now the film was a set of separate widgets: a rail at each
   viewport edge, a channel down the contact panel, a row across the
   principles block. Each carried its own background, so none of them
   could belong to the same piece of stock — which is why the side rails
   read as slapped on while the form's perforations read as film.

   Three layers instead:

     GROUND   a lit, grained, abstract dark field. Fixed, so the page
              moves over it the way a strip moves over a light box.
     SHEET    ONE continuous piece of celluloid running the whole
              document, perforated down both its own edges. Translucent,
              so the ground shows through it as tint.
     CONTENT  sits on the sheet.

   The holes are gaps in the sheet, so the ground arrives through them
   undimmed. Nothing is painted anywhere.
   ============================================================ */

:root {
  --cs-sheet-w: 100%;   /* the sheet is the page, edge to edge */
  /* GAUGE.

     A perforation is the same physical object on every format. Kodak
     Standard is 2.79 x 1.98mm with a 0.51mm corner radius at a 4.75mm
     pitch, and 35mm, 65mm and 70mm all use it — what changes between
     them is the width of the STOCK around the hole. 16mm is the one
     real exception: a smaller hole (1.83 x 1.27mm) at a much longer
     7.62mm pitch, one per frame instead of four.

     So a wider gauge does not mean bigger holes. It means the same
     holes with more film between them, and proportionally more image.
     65/70 is the large format — Lawrence of Arabia, 2001, IMAX.

     perf-mask.js builds the real thing, with the corners actually
     curved, and publishes --cs-perf-mask / --cs-perf-pitch /
     --cs-perf-channel. The variables below are the no-JS fallback:
     square holes, roughly the right size. */
  --cs-gauge: 70;
  --cs-perf-ratio: calc(35 / var(--cs-gauge));

  --cs-perf-w: calc(30px * var(--cs-perf-ratio));
  --cs-perf-h: calc(22px * var(--cs-perf-ratio));
  --cs-perf-gap: calc(32px * var(--cs-perf-ratio));
  --cs-perf-inset: calc(20px * var(--cs-perf-ratio));
}

/* ---------- GROUND ----------
   Broad soft blooms over a deep base, the way light falls across a dark
   surface — no hard stops, no visible gradient seams. */
.cs-ground {
  position: fixed;
  inset: -14% -8%;          /* overscan, so parallax never exposes an edge */
  z-index: -3;
  pointer-events: none;

  /* The field is generated (ground-field.js) rather than assembled from
     gradients. Stacked radial-gradients can only make blobs — each one
     is a circle with a soft edge, and six circles read as six circles.
     Domain-warped fBm gives structure at every scale instead, which is
     what a hole needs to have something to look at.

     This declaration is the fallback for when the script has not run. */
  background:
    radial-gradient(ellipse 80% 60% at 46% -8%, rgba(150, 196, 230, 0.42), transparent 60%),
    linear-gradient(180deg, #16293a 0%, #0d1d2b 40%, #08111b 74%, #05090f 100%);
  background-size: cover;
  background-position: 50% 50%;

  /* The sky is viewport-fixed. Translating it by a percentage of the full
     document scroll eventually pulled the canvas above the viewport on long
     legal pages, exposing the static html background at the bottom. The
     celestial renderer already supplies continuous motion, so the ground
     must remain pinned here from the header through the footer. */
  transform: none;
  will-change: transform;
}

/* The celestial renderer owns this layer. Keep the source canvas in the
   overscanned fixed ground so it covers every viewport shape without
   introducing the circular bokeh forms from the previous information shell. */
.cs-mesh-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}

.cs-ground:has(.cs-mesh-canvas) { background: none; }

/* the generated field, once it exists */
.cs-ground-ready .cs-ground {
  background-image: var(--cs-ground-img);
  background-size: cover;
  background-repeat: no-repeat;
}

/* grain, so the field has a surface instead of being a smooth print */
.cs-ground::after {
  position: absolute;
  inset: 0;
  content: "";
  opacity: 0.20;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='180' height='180' filter='url(%23n)'/></svg>");
  background-size: 180px 180px;
  /* the grain travels at its own rate, which is what sells a hole as a
     window: two things at different depths moving past it */
  background-position: 0 calc(var(--cs-scroll, 0px) * 0.06);
}

/* ---------- SHEET ----------
   One strip, the height of the document, perforated down both edges.
   The mask keeps stock everywhere except where a hole column and a hole
   row agree to drop out — the default `add` composite means opaque wins
   wherever either layer keeps it. */
.cs-sheet {
  position: absolute;
  top: 0;
  left: 50%;
  z-index: -1;
  width: var(--cs-sheet-w);
  transform: translateX(-50%);
  pointer-events: none;
  /* The sheet is also the reading surface. Its density must leave the sky
     visible without ever letting a noon highlight erase copy. The holes are
     still completely open, so this contrast floor does not weaken the
     background-through-film effect at either edge. */
  background:
    linear-gradient(180deg, rgba(170, 208, 226, 0.05), rgba(158, 146, 214, 0.055) 52%, rgba(170, 208, 226, 0.03)),
    linear-gradient(180deg, rgba(5, 10, 15, 0.72), rgba(6, 12, 18, 0.78));

  -webkit-mask-image:
    linear-gradient(to right,
      #000 0 var(--cs-perf-inset),
      transparent var(--cs-perf-inset) calc(var(--cs-perf-inset) + var(--cs-perf-w)),
      #000 calc(var(--cs-perf-inset) + var(--cs-perf-w)) calc(100% - var(--cs-perf-inset) - var(--cs-perf-w)),
      transparent calc(100% - var(--cs-perf-inset) - var(--cs-perf-w)) calc(100% - var(--cs-perf-inset)),
      #000 calc(100% - var(--cs-perf-inset)) 100%),
    repeating-linear-gradient(to bottom,
      transparent 0 var(--cs-perf-h),
      #000 var(--cs-perf-h) calc(var(--cs-perf-h) + var(--cs-perf-gap)));
  mask-image:
    linear-gradient(to right,
      #000 0 var(--cs-perf-inset),
      transparent var(--cs-perf-inset) calc(var(--cs-perf-inset) + var(--cs-perf-w)),
      #000 calc(var(--cs-perf-inset) + var(--cs-perf-w)) calc(100% - var(--cs-perf-inset) - var(--cs-perf-w)),
      transparent calc(100% - var(--cs-perf-inset) - var(--cs-perf-w)) calc(100% - var(--cs-perf-inset)),
      #000 calc(100% - var(--cs-perf-inset)) 100%),
    repeating-linear-gradient(to bottom,
      transparent 0 var(--cs-perf-h),
      #000 var(--cs-perf-h) calc(var(--cs-perf-h) + var(--cs-perf-gap)));
}

/* ---------- REAL PERFORATIONS ----------
   The gradient mask above can only make rectangles. A perforation has
   radiused corners, so the real one is an SVG cut with an even-odd path
   and handed over by perf-mask.js.

   It is built at the sheet's actual pixel width, which is why `watch()`
   rebuilds it on resize: at `mask-size: 100% <pitch>` a tile authored at
   some other width would be stretched horizontally, and the rounded
   rectangles would smear into ovals. */
.cs-perf-ready .cs-sheet {
  -webkit-mask-image: var(--cs-perf-mask);
  mask-image: var(--cs-perf-mask);
  -webkit-mask-size: 100% var(--cs-perf-pitch);
  mask-size: 100% var(--cs-perf-pitch);
  -webkit-mask-repeat: repeat-y;
  mask-repeat: repeat-y;
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
  /* the path carries its own transparency, so alpha is correct here and
     we are not relying on luminance support */
  -webkit-mask-composite: source-over;
  mask-composite: add;
  mask-mode: alpha;
}

/* ---------- the widgets the sheet replaces ----------
   Every one of these carried its own background and its own holes, which
   is precisely why they could never read as the same piece of stock. */
.information-film-rail,
.rail-film,
.information-film-perforations,
.perf-v, .perf-h, .perf-row, .perf-fill, .rm-perf { display: none !important; }

/* panels stop being film and go back to being panels ON film */
.contact-form-panel,
.legal-toc,
.privacy-principles,
.strip-stage {
  -webkit-mask-image: none !important;
  mask-image: none !important;
}

.contact-form-panel::before,
.contact-form-panel::after,
.legal-toc::before { display: none !important; }

/* the page's own scrim would sit between ground and sheet and flatten
   both; the sheet is the only thing dimming the ground now */
body.information-page::before { display: none !important; }
body.about-page .scrim { opacity: 0.35; }
body.about-page .bokeh { display: none !important; }
.information-atmosphere { display: none !important; }

body.information-page,
body.about-page { background: transparent !important; }

html { background: var(--info-void, var(--void, #06090d)); }

/* The continuous sheet supersedes the old panel-by-panel film masks and
   wrapper glows. Small reel markers retain their five supplied cut variants. */
.film-surface-wrap::before { display: none !important; }
.film-surface {
  clip-path: none !important;
  -webkit-mask-image: none !important;
  mask-image: none !important;
}

.bokeh,
.beam,
.beam-core,
.information-atmosphere { display: none !important; }

/* Punchwords use a lifted exposure of the current sky family—not a clipped
   photograph and not its complementary hue. Controlled saturation and
   luminance prevent white hotspots while keeping the words distinct from the
   darker centre of the sheet. */
@property --cs-punch-a {
  syntax: "<color>";
  inherits: true;
  initial-value: rgb(133 171 245);
}

@property --cs-punch-b {
  syntax: "<color>";
  inherits: true;
  initial-value: rgb(177 154 248);
}

@property --cs-punch-c {
  syntax: "<color>";
  inherits: true;
  initial-value: rgb(213 151 236);
}

html {
  transition:
    --cs-punch-a 360ms linear,
    --cs-punch-b 360ms linear,
    --cs-punch-c 360ms linear;
}

body.information-page :is(
  .legal-hero h1 span,
  .contact-intro h1 span,
  .contact-supplemental-heading h2 span
),
body.about-page :is(
  .h-sec em,
  .hero-title .l2,
  .hero-deck .accent,
  .prose .pull,
  .creed-value,
  .cel h3
) {
  color: var(--cs-punch-a);
  background-image: linear-gradient(105deg,
    var(--cs-punch-a) 0%,
    var(--cs-punch-b) 48%,
    var(--cs-punch-c) 100%);
  background-size: 180% 100%;
  background-position: 0% 50%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: .35px rgba(2, 7, 12, .58);
  text-stroke: .35px rgba(2, 7, 12, .58);
  filter:
    drop-shadow(0 1px 0 rgba(2, 7, 12, .72))
    drop-shadow(0 0 18px rgba(2, 7, 12, .2));
  transition:
    --cs-punch-a 360ms linear,
    --cs-punch-b 360ms linear,
    --cs-punch-c 360ms linear;
  animation: cs-punch-spectrum 12s ease-in-out infinite alternate;
}

body.about-page .prose .pull {
  border-left-color: var(--cs-punch-b);
}

/* Background-clipped italic glyphs can extend below their element's painted
   box. Preserve that ink—especially f, g, j, p, q and y—without moving the
   hero composition or tightening the following content. */
body.about-page .hero-title .l2 {
  box-sizing: border-box;
  padding: 0 .06em .16em;
  margin-inline: -.06em;
  margin-bottom: -.16em;
}

body.about-page :is(.h-sec em, .hero-deck .accent) {
  padding: 0 .04em .12em;
  margin-inline: -.04em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

body.about-page :is(.prose .pull, .creed-value, .cel h3) {
  padding-bottom: .13em;
}

@keyframes cs-punch-spectrum {
  from { background-position: 0% 50%; }
  to { background-position: 100% 50%; }
}

@media (prefers-reduced-motion: reduce) {
  body.information-page :is(
    .legal-hero h1 span,
    .contact-intro h1 span,
    .contact-supplemental-heading h2 span
  ),
  body.about-page :is(
    .h-sec em,
    .hero-title .l2,
    .hero-deck .accent,
    .prose .pull,
    .creed-value,
    .cel h3
  ) {
    animation: none;
    background-position: 50% 50%;
  }
}

/* Contact's slate is the companion to About's Academy countdown. */
#slateIntro {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: grid;
  place-items: center;
  background: var(--info-void, #06090d);
  transition: opacity .45s ease, visibility .45s ease;
}

#slateIntro.done { opacity: 0; visibility: hidden; }
.slate-stage { width: min(74vmin, 460px); }
.slate {
  position: relative;
  width: 100%;
  --rule: rgba(236, 234, 227, .55);
  clip-path: inset(-45% -25% -25% 0);
}

.slate-body {
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(236, 234, 227, .16);
  border-radius: 5px 5px 9px 9px;
  background:
    linear-gradient(168deg, rgba(255,255,255,.055), rgba(255,255,255,.012)),
    linear-gradient(180deg, #2c3339, #1d2226);
  box-shadow: 0 30px 70px -34px rgba(0,0,0,.9), inset 0 1px 0 rgba(255,255,255,.14);
}

.clap-arm,
.clap-base {
  position: relative;
  overflow: hidden;
  aspect-ratio: 13 / 1;
  background-image: repeating-linear-gradient(66deg,
    var(--paper, #eceae3) 0 7.2%, rgba(10,13,18,.94) 7.2% 14.4%);
}

.clap-arm {
  z-index: 2;
  border-radius: 6px 6px 0 0;
  box-shadow: 0 6px 18px -6px rgba(0,0,0,.8);
  transform: rotate(-26deg);
  transform-origin: 0 100%;
}

#slateIntro.snap .clap-arm { animation: celestial-slate-snap .46s linear forwards; }
@keyframes celestial-slate-snap {
  0%   { transform: rotate(-26deg); animation-timing-function: cubic-bezier(.5,0,.85,.45); }
  60%  { transform: rotate(2.8deg); animation-timing-function: cubic-bezier(.3,0,.35,1); }
  72%  { transform: rotate(-1.9deg); }
  82%  { transform: rotate(.9deg); }
  91%  { transform: rotate(-.35deg); }
  100% { transform: rotate(0deg); }
}

#slateIntro.snap .slate { animation: celestial-slate-recoil .46s linear forwards; }
@keyframes celestial-slate-recoil {
  0%, 54% { transform: translateY(0); }
  61% { transform: translateY(2.5px); }
  71% { transform: translateY(-1.2px); }
  82% { transform: translateY(.5px); }
  100% { transform: translateY(0); }
}

.clap-base { aspect-ratio: 19 / 1; border-radius: 0 0 2px 2px; }
.slate-fields { display: grid; grid-template-columns: 1fr 1fr; }
.sf {
  display: flex;
  min-width: 0;
  padding: .95em 1.1em;
  flex-direction: column;
  gap: .34em;
  border-top: 1px solid var(--rule);
  border-right: 1px solid var(--rule);
}
.sf:nth-child(2n) { border-right: 0; }
.sf span {
  color: rgba(236,234,227,.62);
  font: 500 .58rem/1 var(--mono, var(--info-mono, monospace));
  letter-spacing: .22em;
  text-transform: uppercase;
}
.sf b {
  color: var(--paper, var(--info-paper, #eceae3));
  font: 500 .9rem/1.2 var(--mono, var(--info-mono, monospace));
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
}
.leader-flash {
  position: absolute;
  inset: 0;
  z-index: 5;
  border-radius: 6px 6px 10px 10px;
  background: var(--paper, var(--info-paper, #eceae3));
  opacity: 0;
  pointer-events: none;
}
#slateIntro.snap .leader-flash { animation: celestial-slate-flash .34s steps(1) forwards; }
@keyframes celestial-slate-flash {
  0%, 56% { opacity: 0; }
  61% { opacity: .32; }
  71%, 100% { opacity: 0; }
}
.slate-caption {
  margin-top: 18px;
  color: var(--paper-dim, var(--info-paper-dim, #cbd3d7));
  font: 500 .72rem/1.4 var(--mono, var(--info-mono, monospace));
  letter-spacing: .26em;
  text-align: center;
  white-space: nowrap;
}

.information-header, .information-main, .contact-main, .information-footer,
.about-page main, .about-page header, .about-page footer { position: relative; z-index: 1; }

/* the perforation channel is real estate: content has to clear it */
.information-main, .contact-main, .about-page main > *,
.information-header, .information-footer {
  padding-left: max(var(--cs-gutter, 0px),
    calc(var(--cs-perf-channel, calc(var(--cs-perf-inset) + var(--cs-perf-w))) + 22px));
  padding-right: max(var(--cs-gutter, 0px),
    calc(var(--cs-perf-channel, calc(var(--cs-perf-inset) + var(--cs-perf-w))) + 22px));
}

@media (max-width: 900px) {
  :root {
    --cs-perf-w: calc(18px * var(--cs-perf-ratio));
    --cs-perf-h: calc(13px * var(--cs-perf-ratio));
    --cs-perf-gap: calc(19px * var(--cs-perf-ratio));
    --cs-perf-inset: calc(10px * var(--cs-perf-ratio));
  }

  .slate-caption { font-size: .58rem; letter-spacing: .16em; }
}

@media (prefers-reduced-motion: reduce) {
  #slateIntro { display: none !important; }
}
