/* ============================================================================
   NewWebsite.builders : page-load intro sequence
   Self-contained module. All markup is built by assets/intro/intro.js.
   With JavaScript off this file paints nothing, because nothing exists to style.

   Token contract: reads the page tokens (--bg, --surface, --text, --text-2,
   --signal, --signal-ink, --signal-display, --hairline) and falls back to the
   brand hex values if the module is dropped into a page that lacks them.
   ============================================================================ */

.nwb-intro {
  /* Local aliases. Site tokens first, brand values as the fallback. */
  --i-bg:        var(--bg, #0A0A0B);
  --i-surface:   var(--surface, #141417);
  --i-text:      var(--text, #F5F2ED);
  --i-text-2:    var(--text-2, #8A8A93);
  --i-signal:    var(--signal, #FF6A1A);
  --i-signal-ink: var(--signal-ink, #FF6A1A);
  --i-signal-dsp: var(--signal-display, #FF6A1A);
  --i-hairline:  var(--hairline, rgba(245,242,237,.10));
  --i-hairline-2: var(--hairline-2, rgba(245,242,237,.18));
  --i-quiet:     cubic-bezier(0.22, 1, 0.36, 1);

  /* Depth. A glow on dark becomes a warm elevation shadow on light. */
  --i-tile-shadow: 0 1px 0 rgba(255,255,255,.04), 0 24px 50px -30px rgba(0,0,0,.85);
  --i-tile-shadow-hi: 0 1px 0 rgba(255,255,255,.06), 0 34px 70px -30px rgba(0,0,0,.95);
  --i-tile-fill: var(--surface, #141417);
  --i-aura-1: rgba(255,106,26,.20);
  --i-aura-2: rgba(255,178,122,.10);
  --i-vignette: rgba(0,0,0,.42);

  /* Layout knobs, overridden per breakpoint below. */
  --i-cols: 2;
  --i-rows: 4;
  --i-pad: 20px;
  --i-gap: 10px;
  --i-cap: 34px;
  --i-chrome: 104px;
  --i-tile-max: 300px;
  --i-radius: 12px;

  /* JS keeps --nwb-vh and --nwb-vw exact, so the scrollbar never skews the fit.
     The viewport units are the fallback before the first write. */
  --i-vh: var(--nwb-vh, 100vh);
  --i-vw: var(--nwb-vw, 100vw);

  position: fixed;
  inset: 0;
  z-index: 2147483000;
  overflow: hidden;
  contain: layout paint style;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;

  /* CSS failsafe. If the script stalls the overlay retires on its own at 8s,
     so the page underneath can never be left covered. Discrete steps, so this
     never fights the opacity the script animates on the sheet inside. */
  animation: nwbIntroFailsafe 8000ms steps(1, end) forwards;
}

@keyframes nwbIntroFailsafe {
  0%   { visibility: visible; pointer-events: auto; }
  100% { visibility: hidden;  pointer-events: none; }
}

/* Light theme. Glows become shadows, the aura drops to about a third. */
[data-theme="light"] .nwb-intro {
  --i-tile-shadow: 0 1px 2px rgba(62,34,14,.10), 0 18px 40px -20px rgba(10,10,11,.28);
  --i-tile-shadow-hi: 0 2px 4px rgba(62,34,14,.13), 0 26px 56px -22px rgba(10,10,11,.34);
  --i-tile-fill: var(--surface, #FFFFFF);
  --i-aura-1: rgba(255,106,26,.11);
  --i-aura-2: rgba(214,120,40,.06);
  --i-vignette: rgba(126,92,58,.10);
}

/* ---------- The sheet. Carries the ground and the exit lift. ---------- */
.nwb-intro__sheet {
  position: absolute;
  inset: 0;
  background: var(--i-bg);
  transform-origin: 50% 46%;
  will-change: opacity, transform;
}
.nwb-intro.is-lifting .nwb-intro__sheet {
  opacity: 0;
  transform: scale(1.035);
  transition: opacity 620ms var(--i-quiet), transform 620ms var(--i-quiet);
}

/* Ambient orange wash, same family as the page behind it. */
.nwb-intro__aura {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(62% 46% at 50% 8%,  var(--i-aura-1) 0%, transparent 72%),
    radial-gradient(70% 52% at 12% 96%, var(--i-aura-2) 0%, transparent 74%),
    radial-gradient(120% 100% at 50% 50%, transparent 52%, var(--i-vignette) 100%);
}

/* ---------- Skip control. Visible from the first frame, never animated in. ---------- */
.nwb-intro__skip {
  position: absolute;
  top: max(14px, env(safe-area-inset-top));
  right: max(14px, env(safe-area-inset-right));
  z-index: 4;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  gap: .5em;
  margin: 0;
  padding: 8px 13px;
  border: 1px solid var(--i-hairline-2);
  border-radius: 999px;
  background: var(--i-tile-fill);
  color: var(--i-text);
  font-family: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  font-size: 10px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: .14em;
  cursor: pointer;
  box-shadow: var(--i-tile-shadow);
  transition: border-color 400ms var(--i-quiet), color 400ms var(--i-quiet);
}
.nwb-intro__skip:hover { border-color: var(--i-signal); color: var(--i-signal-ink); }
.nwb-intro__skip svg { width: 11px; height: 11px; display: block; flex: none; }

/* ---------- Stage ---------- */
.nwb-intro__stage {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--i-pad);
  overflow: hidden;
}
.nwb-intro__phase {
  grid-area: 1 / 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(14px, 2.6vh, 26px);
  min-width: 0;
}

/* ---------- Phase two: the work grid ---------- */
.nwb-intro__eyebrow {
  margin: 0;
  opacity: 0;
  transform: translateY(6px);
  font-family: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  font-size: 10px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--i-text-2);
  transition: opacity 500ms var(--i-quiet), transform 500ms var(--i-quiet);
}
.nwb-intro__eyebrow.is-in { opacity: 1; transform: none; }

/* Tile width is the smaller of what the row allows and what the column allows,
   so seven tiles always fit the viewport with no scroll at any size. */
.nwb-intro__grid {
  --i-avail-w: calc(var(--i-vw) - (2 * var(--i-pad)) - ((var(--i-cols) - 1) * var(--i-gap)));
  --i-avail-h: calc(var(--i-vh) - var(--i-chrome) - ((var(--i-rows) - 1) * var(--i-gap)) - (var(--i-rows) * var(--i-cap)));
  --i-tile: min(
      var(--i-tile-max),
      calc(var(--i-avail-w) / var(--i-cols)),
      calc(var(--i-avail-h) / var(--i-rows) / 0.75)
    );
  /* Flex rather than grid, only so the short last row centres under the rest.
     max-width is what forces the wrap at --i-cols per row. */
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--i-gap);
  max-width: min(100%, calc((var(--i-cols) * var(--i-tile)) + ((var(--i-cols) - 1) * var(--i-gap))));
}

.nwb-intro__tile {
  flex: 0 0 auto;
  width: var(--i-tile);
  min-width: 0;
  opacity: 0;
  transform: translateY(14px) scale(.965);
  filter: blur(6px);
  will-change: opacity, transform, filter;
}
.nwb-intro__tile.is-in {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition: opacity 520ms var(--i-quiet), transform 520ms var(--i-quiet), filter 520ms var(--i-quiet);
}

.nwb-intro__shot {
  position: relative;
  display: flex;
  flex-direction: column;
  aspect-ratio: 4 / 3;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--i-hairline);
  border-radius: var(--i-radius);
  background: var(--i-tile-fill);
  box-shadow: var(--i-tile-shadow);
  transition: box-shadow 400ms var(--i-quiet), transform 400ms var(--i-quiet), border-color 400ms var(--i-quiet);
}
.nwb-intro__tile:hover .nwb-intro__shot {
  transform: translateY(-3px);
  border-color: var(--i-hairline-2);
  box-shadow: var(--i-tile-shadow-hi);
}
/* The screenshots are 1000x625 (16:10) and the tile is 4:3, so a full-width
   screenshot leaves exactly one eighth of the tile width spare at the top.
   That strip becomes browser chrome rather than dead space, and the shot
   below it stays uncropped. object-fit: cover would have cut a fifth of the
   page width off the left and right edges instead. */
.nwb-intro__chrome {
  flex: 0 0 16.667%;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
  background: var(--i-tile-fill);
  border-bottom: 1px solid var(--i-hairline);
}
.nwb-intro__dot {
  height: 30%;
  max-height: 6px;
  aspect-ratio: 1;
  border-radius: 999px;
  background: var(--i-text-2);
  opacity: .5;
  flex: none;
}
.nwb-intro__dot:first-child { background: var(--i-signal); opacity: .8; }
.nwb-intro__frame {
  position: relative;
  flex: 1 1 auto;
  overflow: hidden;
}
.nwb-intro__shot img {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  opacity: 0;
  transition: opacity 500ms var(--i-quiet);
}
.nwb-intro__shot img.is-loaded { opacity: 1; }

/* Fallback ground, and what stays on screen if a screenshot fails to load.
   The initial sits behind the image, so a broken path degrades to a mark
   rather than a broken-image icon. */
.nwb-intro__fallback {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background:
    linear-gradient(150deg, rgba(255,106,26,.16) 0%, transparent 58%),
    var(--i-tile-fill);
  font-family: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 400;
  font-size: clamp(20px, 4.4vw, 34px);
  letter-spacing: -0.03em;
  color: var(--i-signal-dsp);
}
.nwb-intro__shot img { z-index: 1; }

/* Under 640 the caption stacks, because a domain and a name side by side at
   137px of tile width truncates both. From 640 up they share one row. */
.nwb-intro__cap {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  height: var(--i-cap);
  padding: 0 3px;
  min-width: 0;
}
@media (min-width: 640px) {
  .nwb-intro__cap {
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
  }
}
.nwb-intro__name {
  font-family: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 400;
  font-size: clamp(11px, 1.1vw, 13px);
  letter-spacing: -0.02em;
  color: var(--i-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nwb-intro__host {
  font-family: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  font-size: 9px;
  letter-spacing: .08em;
  color: var(--i-text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 0 1 auto;
}

/* ---------- Phase one: the announcement ---------- */
.nwb-intro__phase--say {
  opacity: 0;
  pointer-events: none;
  gap: clamp(16px, 3vh, 30px);
  text-align: center;
}
.nwb-intro__phase--say.is-in { opacity: 1; }

/* The mark is 646x351, roughly 1.84:1. Constrained by width, height auto,
   so it is never forced into a square. */
.nwb-intro__mark {
  display: block;
  width: clamp(132px, 24vw, 226px);
  height: auto;
  opacity: 0;
  transform: translateY(10px) scale(.94);
  transition: opacity 640ms var(--i-quiet), transform 640ms var(--i-quiet);
}
.nwb-intro__phase--say.is-in .nwb-intro__mark { opacity: 1; transform: none; }
/* The mark is a fixed orange bitmap, so it cannot ride --signal-display the way
   the inline nav glyph does. On Bone, #FF6A1A measures 2.57:1 and misses the
   3:1 that non-text asks for. brightness(.85) lands it on 3.47:1, one hundredth
   off --signal-display's 3.48:1, without shipping a second PNG. Measured. */
[data-theme="light"] .nwb-intro__mark { filter: brightness(.85); }

.nwb-intro__title {
  margin: 0;
  max-width: 18ch;
  font-family: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: clamp(28px, 6.2vw, 58px);
  line-height: 1.04;
  letter-spacing: -0.035em;
  text-wrap: balance;
  color: var(--i-text);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 660ms var(--i-quiet) 90ms, transform 660ms var(--i-quiet) 90ms;
}
.nwb-intro__phase--say.is-in .nwb-intro__title { opacity: 1; transform: none; }

.nwb-intro__rule {
  width: 34px;
  height: 2px;
  border-radius: 2px;
  background: var(--i-signal);
  opacity: 0;
  transform: scaleX(.2);
  transition: opacity 620ms var(--i-quiet) 190ms, transform 620ms var(--i-quiet) 190ms;
}
.nwb-intro__phase--say.is-in .nwb-intro__rule { opacity: 1; transform: none; }

.nwb-intro__domain {
  margin: 0;
  font-family: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  font-size: 11px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: .14em;
  /* Quiet, but still announcement text, so it is mixed down from --text rather
     than set in --text-2. At 11px the antialiased stems of a mono face never
     reach the specified colour, and --text-2 measures 4.1:1 off the composited
     pixels even though the token itself is 5.8:1. Measured, not assumed. */
  color: var(--i-text-2);
  color: color-mix(in srgb, var(--i-text) 82%, var(--i-bg));
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 620ms var(--i-quiet) 240ms, transform 620ms var(--i-quiet) 240ms;
}
.nwb-intro__phase--say.is-in .nwb-intro__domain { opacity: 1; transform: none; }

/* The announcement recedes rather than cutting, and hands the stage to the
   grid. Same figures the grid used to leave on, so the two phases match. */
.nwb-intro__phase--say.is-out {
  opacity: 0;
  transform: scale(.972);
  filter: blur(7px);
  pointer-events: none;
  transition: opacity 480ms var(--i-quiet), transform 480ms var(--i-quiet), filter 480ms var(--i-quiet);
}

/* ---------- Breakpoints. Rows and columns move together so the fit maths hold. ---------- */
@media (min-width: 640px) {
  .nwb-intro {
    --i-cols: 3; --i-rows: 3;
    --i-pad: 32px; --i-gap: 14px; --i-cap: 32px; --i-chrome: 128px;
    --i-tile-max: 320px; --i-radius: 14px;
  }
}
@media (min-width: 1024px) {
  .nwb-intro {
    --i-cols: 4; --i-rows: 2;
    --i-pad: 40px; --i-gap: 16px; --i-cap: 34px; --i-chrome: 148px;
    --i-tile-max: 340px; --i-radius: 16px;
  }
  .nwb-intro__skip { top: 20px; right: 20px; }
}

/* Short landscape windows: keep the grid inside the viewport. */
@media (max-height: 520px) {
  .nwb-intro { --i-chrome: 86px; --i-cap: 26px; }
  .nwb-intro__eyebrow { display: none; }
}

/* ---------- Reduced motion. Nothing appears at all. ---------- */
@media (prefers-reduced-motion: reduce) {
  .nwb-intro { display: none !important; }
}
