/* =========================================================
   PANTRY principle section
   One sticky stage and a long scroll. A Pantry visual comes apart into
   four layers (its decisions), each layer is focused in turn, then the same layers make
   many outputs. Caption bar on top, the visual centred on the grid below.
   Motion lives in scripts/principle.js and only changes transform and
   opacity. Every value comes from tokens.css (the --principle-* and
   --poster-* ones are for this section).

   The poster is drawn once at --poster-width and scaled by JS to whole
   grid columns (.l-poster), so the real button and the type tokens fit
   inside it. Anything that must keep its real size while the poster is
   scaled uses --inv (1 / scale), set by JS.
   ========================================================= */

.section.principle {
  padding: 0;
}

.principle-track {
  position: relative;
  height: var(--principle-scroll);
}

.principle-stage {
  position: sticky;
  top: 0;
  height: var(--stage-height);
  display: flex;
  flex-direction: column;
  padding-block: max(var(--space-24), env(safe-area-inset-top)) var(--space-16);
  overflow: clip;
}

/* ---------- Caption bar ----------
   Label and headline on the left, counter on the right, the hairline
   under them with a red progress line on it. */

.principle-head {
  position: relative;
  padding-bottom: var(--space-16);
  border-bottom: var(--line-width) solid var(--color-rule);
}

.grid-12.principle-head-grid {
  row-gap: var(--space-8);
}

/* The label stays on one line; the counter beside it is empty until the
   outputs come. */
.principle-head .label {
  white-space: nowrap;
}

/* One line on desktop, two below it, so the poster never jumps when a
   caption wraps. */
.principle-title {
  min-height: calc(var(--text-section-lh) * 1em);
  text-wrap: balance;
}

@media (max-width: 1023px) {
  .principle-title {
    min-height: calc(var(--text-section-lh) * 2em);
  }
}

/* Phones: a size down, so every caption fits in two lines. */
@media (max-width: 639px) {
  .principle-title {
    font-size: var(--principle-title-size-mobile);
  }
}

.principle-title.is-swap {
  animation: principle-swap var(--principle-swap-duration) var(--ease);
}

@keyframes principle-swap {
  from {
    opacity: 0;
    transform: translateY(var(--principle-swap-shift));
  }

  to {
    opacity: 1;
    transform: none;
  }
}

.principle-accent {
  color: var(--color-red-signal);
}

.principle-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--line-width) * -1);
  height: calc(var(--line-width) * 2);
  background: var(--color-red-signal);
  transform: scaleX(0);
  transform-origin: left center;
  will-change: transform;
}

/* Counter: a big number on tablet and up, one mono line on phones. */
.principle-count {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: var(--space-8);
  opacity: 0;
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  will-change: opacity;
}

.principle-count-num {
  font-family: var(--font-mono);
  font-size: var(--text-label-size);
  font-weight: var(--text-label-weight);
  line-height: var(--text-label-lh);
}

@media (min-width: 640px) {
  .principle-head .l-caption-count {
    grid-row: span 2;
    align-self: end;
  }

  .principle-count {
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-4);
  }

  .principle-count-num {
    font-family: var(--font-sans);
    font-size: var(--text-section-size);
    font-weight: var(--text-display-weight);
    line-height: 1;
    letter-spacing: var(--text-display-ls);
  }
}

/* ---------- Stage box ----------
   The same 12 / 8 / 4 grid as the page. The poster takes the middle
   columns; wires and outputs are placed on the same columns. */

/* Its own stacking layer, so the wipe and the statement cover it whole. */
.principle-area {
  position: relative;
  z-index: 0;
  flex: 1;
  min-height: 0;
}

.grid-12.principle-box {
  position: relative;
  height: 100%;
  grid-template-rows: minmax(0, 1fr);
  row-gap: 0;
  perspective: var(--principle-perspective);
}

.principle-slot {
  position: relative;
  pointer-events: none;
}

/* ---------- Poster ---------- */

.principle-stack {
  --poster-hair: calc(var(--line-width) * var(--inv, 1));
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--poster-width);
  aspect-ratio: var(--poster-ratio);
  translate: -50% -50%;
  transform-style: preserve-3d;
  will-change: transform;
  z-index: 3;
}

.principle-layer {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  will-change: transform, opacity;
}

.layer-edge {
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 calc(var(--line-width) * var(--edge-inv, var(--inv, 1))) var(--color-rule);
  opacity: 0;
}

/* Where the layer in front came from: a dashed outline left in the stack. */
.layer-ghost {
  position: absolute;
  inset: 0;
  border: var(--poster-hair) dashed var(--color-rule);
  opacity: 0;
  pointer-events: none;
}

/* Label beside a layer. It counter-scales, so it keeps its real size. */
.layer-tag {
  position: absolute;
  left: 100%;
  top: 0;
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin-left: calc(var(--space-16) * var(--inv, 1));
  font-family: var(--font-mono);
  font-size: var(--text-label-size);
  font-weight: var(--text-label-weight);
  line-height: var(--text-label-lh);
  letter-spacing: var(--text-label-ls);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-ink);
  opacity: 0;
  transform-origin: left center;
  transform: scale(var(--inv, 1));
}

.layer-tag::before {
  content: "";
  width: var(--space-24);
  height: var(--line-width);
  background: currentColor;
}

.layer-tag em {
  font-style: normal;
}

.principle-layer.is-focus .layer-tag {
  color: var(--color-red-text);
}

@media (max-width: 639px) {
  .layer-tag span {
    display: none;
  }

  .layer-tag::before {
    width: var(--space-12);
  }
}

/* Narrow stages: no room beside the layer in front, so its label sits
   under it, with the plain detail ("who it's from"). */
.principle-stage.is-narrow .principle-layer.is-focus .layer-tag {
  top: 100%;
  left: 0;
  /* As wide as the card on screen: the label counter-scales by --edge-inv. */
  width: calc(100% / var(--edge-inv, var(--inv, 1)));
  margin-left: 0;
  margin-top: calc(var(--space-12) * var(--edge-inv, var(--inv, 1)));
  transform-origin: left top;
  flex-wrap: wrap;
  white-space: normal;
}

.principle-stage.is-narrow .principle-layer.is-focus .layer-tag::before {
  display: none;
}

.principle-stage.is-narrow .principle-layer.is-focus .layer-tag span {
  display: inline;
}

/* The white card behind a layer while it is in front, and the faint sheet
   outline that shows each layer as a pane while the stack is open. */
.layer-card {
  position: absolute;
  inset: 0;
  background: var(--color-canvas);
  box-shadow: 0 calc(var(--space-16) * var(--inv, 1)) calc(var(--space-48) * var(--inv, 1)) color-mix(in srgb, var(--color-ink) 8%, transparent);
  opacity: 0;
}

.layer-sheet {
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 var(--poster-hair) var(--color-rule);
  opacity: 0;
}

/* Meaningful colors: a white sheet, one quiet block, the red button, and
   the palette with what each color is for. */
.layer-color .poster-fill {
  position: absolute;
  inset: 0;
  background: var(--color-canvas);
  box-shadow: 0 0 0 var(--poster-hair) var(--color-guide);
}

.layer-color .poster-quiet {
  position: absolute;
  left: 50%;
  right: 0;
  top: 0;
  height: 56%;
  background: var(--color-surface-subtle);
}

/* The real button component, only placed. */
.layer-color .btn {
  position: absolute;
  right: var(--space-16);
  bottom: var(--space-16);
}

.layer-color .poster-chips {
  position: absolute;
  left: var(--space-16);
  right: var(--space-16);
  top: 61%;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-12);
  opacity: 0;
}

.poster-chip {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  font-family: var(--font-mono);
  font-size: var(--text-label-size);
  font-weight: var(--text-label-weight);
  line-height: var(--text-label-lh);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.poster-chip i {
  height: var(--space-64);
  box-shadow: inset 0 0 0 var(--poster-hair) var(--color-rule);
}

/* 3D elements: objects crossfade in one box. */
.layer-element .poster-obj {
  position: absolute;
  left: 54%;
  top: 5%;
  width: 42%;
  aspect-ratio: 1;
  transform-origin: right top;
  filter: drop-shadow(0 calc(var(--poster-width) * 0.03) calc(var(--poster-width) * 0.03) color-mix(in srgb, var(--color-red-text) 22%, transparent));
}

.layer-element .poster-obj img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity var(--principle-swap-duration) var(--ease);
}

.layer-element .poster-obj img.is-on {
  opacity: 1;
}

/* Visual identity: the logo. */
.layer-identity .poster-logo {
  position: absolute;
  left: var(--space-16);
  top: var(--space-16);
  width: 25%;
  height: auto;
  transform-origin: left top;
}

/* Font: label, headline (set word by word in front) and the short line. */
.layer-font .poster-label {
  position: absolute;
  left: var(--space-16);
  top: 61%;
  font-family: var(--font-mono);
  font-size: var(--text-label-size);
  font-weight: var(--text-label-weight);
  line-height: var(--text-label-lh);
  letter-spacing: var(--text-label-ls);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.layer-font .poster-headline {
  position: absolute;
  left: var(--space-16);
  right: 12.5%;
  top: 65%;
  font-size: var(--poster-headline-size);
  font-weight: var(--text-display-weight);
  line-height: var(--text-display-lh);
  letter-spacing: var(--text-display-ls);
}

.layer-font .poster-voice {
  position: absolute;
  left: var(--space-16);
  bottom: var(--space-16);
  width: 44%;
  font-size: var(--text-nav-size);
  line-height: var(--text-body-lh);
  color: var(--color-text-secondary);
  text-wrap: balance;
}

/* ---------- Outputs ----------
   Cards sit on grid columns (left and width in columns of the set's
   grid) and fly out of the poster along dashed red lines. */

.principle-wires {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 1;
}

.principle-wires line {
  stroke: var(--color-red-signal);
  stroke-width: var(--line-width);
  stroke-dasharray: 3 4;
  opacity: 0;
}

.principle-outs {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

/* --u, --ox and --oy (one column of the set, and where the set starts) are
   set by JS on .principle-outs; --x, --y and --w by each card, in columns.
   A card starts on its column line and stops one gutter short of the next. */
.principle-out {
  position: absolute;
  left: calc(var(--ox) + var(--x) * var(--u));
  top: calc(var(--oy) + var(--y) * var(--u));
  width: calc(var(--w) * var(--u) - var(--space-16));
  overflow: hidden;
  opacity: 0;
  transform: scale(0.35);
  will-change: transform, opacity;
  container-type: inline-size;
  box-shadow: 0 0 0 var(--line-width) var(--color-rule);
}

.principle-out.tone-w {
  background: var(--color-canvas);
}

.principle-out.tone-s {
  background: var(--color-surface-subtle);
}

.principle-out.tone-k {
  background: var(--color-ink);
  color: var(--color-text-on-ink);
}

.principle-out .out-mark {
  position: absolute;
  left: 7%;
  top: 7%;
  width: 13%;
  height: auto;
}

.principle-out .out-obj {
  position: absolute;
  right: 6%;
  top: 6%;
  width: 42%;
  aspect-ratio: 1;
  object-fit: contain;
}

.principle-out .out-line {
  position: absolute;
  left: 7%;
  right: 7%;
  bottom: 22%;
  font-size: 11cqi;
  font-weight: var(--text-display-weight);
  line-height: var(--text-display-lh);
  letter-spacing: var(--text-display-ls);
  text-wrap: balance;
}

.principle-out .out-bar {
  position: absolute;
  left: 7%;
  bottom: 7%;
  width: 36%;
  height: 7%;
  min-height: var(--space-4);
  background: var(--color-red-signal);
}

.principle-out .out-format {
  position: absolute;
  right: 7%;
  bottom: 7%;
  font-family: var(--font-mono);
  font-size: max(var(--space-8), 4.2cqi);
  font-weight: var(--text-label-weight);
  text-transform: uppercase;
  opacity: 0.6;
}

/* Narrow cards have no room for the format name beside the bar. */
@container (max-width: 130px) {
  .out-format {
    display: none;
  }
}

/* Wide cards put the object small on the right; tall cards give it room. */
.principle-out.is-wide .out-obj {
  width: 22%;
  top: 8%;
  right: 5%;
}

.principle-out.is-wide .out-line {
  right: 34%;
  bottom: 26%;
  font-size: 6.5cqi;
}

.principle-out.is-wide .out-mark {
  width: 8%;
}

.principle-out.is-wide .out-bar {
  width: 20%;
  height: 10%;
}

.principle-out.is-tall .out-obj {
  width: 62%;
  top: 16%;
  right: 19%;
}

.principle-out.is-tall .out-line {
  font-size: 14cqi;
  bottom: 16%;
}

.principle-out.is-tall .out-bar {
  height: 4%;
}

/* ---------- The four layers as a list ----------
   Always in the page for screen readers. Seen only with reduced motion,
   where it replaces the labels beside the layers. */

.principle-labels {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.principle-labels-title {
  margin-bottom: var(--space-16);
}

.principle-labels li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-16);
  padding-block: var(--space-12);
  border-top: var(--line-width) solid var(--color-rule);
}

/* ---------- After the teardown: wipe to dark and the statement ----------
   scripts/statement.js builds these on the same pinned stage. */

/* Once the wipe is done, the whole track is ink: nothing light shows
   between the bars or below the stage. */
.principle-track.is-dark {
  background: var(--color-ink);
}

/* Invisible boxes JS reads to turn the scroll lengths into px. */
.distance {
  position: absolute;
  top: 0;
  width: 0;
  visibility: hidden;
}

.distance--teardown {
  height: calc(var(--principle-scroll) - var(--stage-height));
}

.distance--wipe {
  height: var(--wipe-scroll);
}

.distance--hold {
  height: var(--statement-hold);
}

/* ---------- Structure lines on dark ---------- */

.ink-lines {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.ink-lines .container,
.wipe .container {
  height: 100%;
}

.ink-lines .vline {
  background: var(--color-guide-on-ink);
}

/* ---------- Part 2: wipe ----------
   One bar per bay, rising from the bottom. The outer bars bleed past the
   container to the screen edges so the whole screen ends up dark. */

.wipe {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: clip;
  pointer-events: none;
  visibility: hidden;
}

.wipe-bar {
  --bleed-left: 0px;
  --bleed-right: 0px;
  position: absolute;
  bottom: 0;
  height: 100%;
  left: calc(var(--from) - var(--bleed-left));
  width: calc(var(--to) - var(--from) + var(--bleed-left) + var(--bleed-right));
  background: var(--color-ink);
  transform: scaleY(0);
  transform-origin: bottom;
}

.bay-1 { --from: 0%; --to: 25%; --bleed-left: 100vw; }
.bay-2 { --from: 25%; --to: 50%; }
.bay-3 { --from: 50%; --to: 75%; }
.bay-4 { --from: 75%; --to: 100%; --bleed-right: 100vw; }

/* Tablet: 2 bays, like the structure lines. Mobile: 1. */
@media (max-width: 1023px) {
  .bay-2,
  .bay-4 {
    display: none;
  }

  .bay-1 { --to: 50%; }
  .bay-3 { --from: 50%; --to: 100%; --bleed-right: 100vw; }
}

@media (max-width: 639px) {
  .bay-3 {
    display: none;
  }

  .bay-1 { --to: 100%; --bleed-right: 100vw; }
}

/* ---------- Part 3: statement ----------
   Sits on top of the stage and shows once the wipe is done. */

.statement-layer {
  position: absolute;
  inset: 0;
  z-index: 2;
  visibility: hidden;
  color: var(--color-canvas);
}

/* The wipe already draws the lines while pinned; these are for reduced motion. */
.statement-layer > .ink-lines {
  display: none;
}

/* Positioned against the stage, not the container, so it can travel
   from edge to edge. */
.statement {
  position: absolute;
  top: 50%;
  left: 0;
  width: max-content;
  white-space: nowrap;
  transform: translate3d(0, -50%, 0);
  font-size: var(--statement-size, var(--text-statement-size));
  font-weight: var(--text-display-weight);
  line-height: var(--text-statement-lh);
  letter-spacing: var(--text-statement-ls);
  word-spacing: var(--text-statement-ws);
}

/* "That's why:" holds still in the first bay while the line passes under
   it. Letters are always on the baseline by the time they reach the first
   bay, so it sits one step above the line. It fades in as the screen turns
   dark. --statement-size is the fitted size principle.js sets. */
.statement-bridge {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(50% + var(--statement-size, var(--text-statement-size)) * var(--text-statement-lh) / 2 + var(--space-32));
  opacity: 0;
  transition: opacity var(--duration) var(--ease);
}

.statement-layer.is-on .statement-bridge {
  opacity: 1;
}

.statement-word {
  white-space: nowrap;
}

.statement-char {
  display: inline-block;
  will-change: transform;
}

/* ---------- Reduced motion ----------
   No pinning and no motion: the visual flat, the four layers as a
   list, the last caption. */

@media (prefers-reduced-motion: reduce) {
  .principle-track {
    height: auto;
  }

  .principle-stage {
    position: static;
    height: auto;
    padding-block: var(--section-pad);
    overflow: visible;
  }

  .principle-head {
    margin-bottom: var(--space-48);
  }

  .principle-title.is-swap {
    animation: none;
  }

  .principle-progress,
  .principle-count,
  .principle-wires,
  .principle-outs,
  .layer-tag,
  .poster-chips,
  .layer-card,
  .layer-sheet,
  .layer-ghost,
  .layer-edge {
    display: none;
  }

  .grid-12.principle-box {
    height: auto;
    perspective: none;
    row-gap: var(--space-32);
  }

  .principle-layer,
  .principle-stack {
    transform-style: flat;
    will-change: auto;
  }

  .col.centered.principle-slot {
    grid-column: 1 / span 4;
    aspect-ratio: var(--poster-ratio);
  }

  .principle-labels {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
    white-space: normal;
    grid-column: 7 / -1;
  }

  .layer-element .poster-obj img {
    transition: none;
  }

  /* The statement: no wipe, a still dark block after the visual. */
  .principle-stage {
    padding-bottom: 0;
  }

  .wipe,
  .distance {
    display: none;
  }

  .statement-layer {
    position: relative;
    inset: auto;
    margin-top: var(--section-pad);
    visibility: visible;
    padding: var(--section-pad) 0;
    background: var(--color-ink);
  }

  .statement-layer > .ink-lines {
    display: block;
  }

  .statement-bridge {
    position: relative;
    bottom: auto;
    margin-bottom: var(--space-24);
    opacity: 1;
    transition: none;
  }

  /* One clause per line ("Humans must decide," / "AI only builds."). */
  .statement-sentence {
    display: block;
  }

  .statement {
    position: relative;
    width: auto;
    white-space: normal;
    transform: none;
    font-size: var(--text-statement-still-size);
    line-height: var(--text-display-lh);
    /* Keep clear of the outer lines. The lines run behind it. */
    padding-inline: var(--cell-pad);
  }

  .statement-char {
    will-change: auto;
  }
}

@media (prefers-reduced-motion: reduce) and (max-width: 1023px) {
  .principle-labels {
    grid-column: 5 / -1;
  }
}

@media (prefers-reduced-motion: reduce) and (max-width: 639px) {
  .principle-labels {
    grid-column: 1 / -1;
  }
}
