/* ==========================================================================
   NewLine Break — story
   Two scenes the reader scrolls through rather than past.

   1. The seam: what is inside the break the hero opens on.
   2. The build: one device assembled as the capability list is read.

   Both are complete, static sections first. The scroll-driven behaviour is
   layered on inside @supports and a motion query, so a browser that cannot
   run it, or a reader who has asked for less motion, gets finished drawings.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. The seam
   -------------------------------------------------------------------------- */

.seam {
  padding: clamp(4.5rem, 10vh, 7.5rem) var(--bleed);
  padding-left: calc(var(--bleed) + var(--gutter));
}

/* The column is exactly as wide as the figure, so the two statements and the
   drawing share one pair of edges. The figure's height is what is left of the
   screen once the copy is set, capped at a third, and the width follows from
   it: that is what keeps the whole scene inside one viewport when it pins. */
.seam__col {
  --fig-h: min(31dvh, 100dvh - 35rem);
  display: grid;
  gap: clamp(1.5rem, 4vh, 3rem);
  width: min(100%, max(30rem, calc(var(--fig-h) * 1200 / 320)));
}

/* Two tones in one sentence: the setup recedes, the claim stands forward.
   Rank is carried by colour and nothing else, so the weight stays light. */
.seam__say {
  max-width: 28ch;
  font-size: clamp(1.5rem, min(1rem + 2.4vw, 5.2vh), 3rem);
  font-variation-settings: 'wdth' 100, 'wght' 500;
  line-height: 1.12;
  letter-spacing: -0.028em;
  color: var(--fog);
  text-wrap: balance;
}

.seam__say em {
  font-style: normal;
  color: var(--text);
}

/* Problem top left, answer bottom right: the reading path steps down across
   the figure the same way the mark does. */
.seam__say--answer {
  justify-self: end;
  max-width: 24ch;
}

.seam__note {
  display: block;
  max-width: 42ch;
  margin-top: 1rem;
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.125rem);
  font-variation-settings: 'wdth' 100, 'wght' 400;
  line-height: 1.6;
  letter-spacing: 0;
  color: var(--fog);
  text-wrap: pretty;
}

.seam__fig { margin: 0; }

.fig--seam {
  aspect-ratio: 1200 / 320;
  overflow: hidden;
}

/* The moment of failure: two open ends and two excuses. It belongs to the
   middle of the story, so the finished drawing does not show it. */
.seam__blame { opacity: 0; }

@media (max-width: 820px) {
  .seam { padding-inline: var(--pad-x); }
  .seam__col { width: 100%; gap: clamp(1.25rem, 3vh, 2rem); }

  .seam__say { font-size: clamp(1.3125rem, 5.6vw, 1.75rem); }
  .seam__say--answer { justify-self: start; }

  /* The figure is sliced, not shrunk: the two panels run off the edges and
     the join between them fills the frame at a size that can be read. */
  .fig--seam { aspect-ratio: 2 / 1; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-height: 620px) {
    .seam {
      display: flex;
      flex-direction: column;
      /* safe: if it ever does not fit, the overflow goes below, never up
         under the nav. */
      justify-content: safe center;
      height: 100%;
      /* The top inset has to clear the fixed nav, which is 92px tall. */
      padding-block: clamp(6.75rem, 12.5vh, 8rem) clamp(2rem, 5vh, 3.5rem);
    }

    .seam__say--answer,
    .seam__wire,
    .seam__blame,
    .seam__step,
    .seam__node,
    .seam__pulse {
      animation: linear both;
      animation-timeline: --open;
    }

    /* The problem statement and the two panels are not animated. They are
       already there, and the parting strata uncover them. */

    .seam__wire {
      stroke-dasharray: 1 2;
      animation-name: fig-draw;
    }

    .seam__wire--a { animation-range: contain 30% contain 42%; }
    .seam__wire--b { animation-range: contain 32% contain 44%; }

    .seam__blame {
      animation-name: seam-blame;
      animation-range: contain 42% contain 68%;
    }

    .seam__step {
      stroke-dasharray: 1 2;
      animation-name: fig-draw;
      animation-range: contain 62% contain 70%;
    }

    .seam__node {
      transform-box: fill-box;
      transform-origin: center;
      animation-name: fig-pop;
      animation-range: contain 66% contain 72%;
    }

    .seam__pulse {
      stroke-dasharray: 0.08 2;
      animation-name: fig-run;
      animation-range: contain 72% contain 86%;
    }

    .seam__say--answer {
      animation-name: seam-rise;
      animation-range: contain 72% contain 86%;
    }

    @keyframes seam-rise {
      from { opacity: 0; transform: translateY(26px); }
    }

    @keyframes seam-blame {
      0%   { opacity: 0; }
      18%  { opacity: 1; }
      78%  { opacity: 1; }
      100% { opacity: 0; }
    }
  }
}

/* --------------------------------------------------------------------------
   2. The build
   -------------------------------------------------------------------------- */

.build {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  column-gap: clamp(2.5rem, 6vw, 6rem);
  align-items: start;
}

/* The figure comes first in the source so that on a narrow screen it can
   stick above the steps. On a wide one it takes the right-hand column. */
.build__fig {
  grid-column: 2;
  grid-row: 1;
  position: sticky;
  top: 6.5rem;
  margin: 0;
}

.build__steps {
  grid-column: 1;
  grid-row: 1;
  display: grid;
  gap: clamp(3rem, 7vw, 5rem);
}

.build__side {
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--signal-lit);
  margin-bottom: 1.5rem;
}

.build__title {
  font-size: clamp(1.625rem, 1.2rem + 1.6vw, 2.5rem);
  font-variation-settings: 'wdth' 100, 'wght' 600;
  margin-bottom: 1rem;
}

.build__body {
  max-width: 42ch;
  color: var(--fog);
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.125rem);
}

/* Hairline rows, not bullets. A list of what is in the box. */
.spec {
  max-width: 26rem;
  margin-top: 1.75rem;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--text);
}

.spec li {
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--line);
}

/* Below this the right-hand column is too narrow for the figure's labels to
   be read, so the figure goes above the steps at full width instead. */
@media (width <= 1100px) {
  .build { grid-template-columns: minmax(0, 1fr); }

  .build__fig,
  .build__steps {
    grid-column: 1;
    grid-row: auto;
  }

  .build__fig {
    position: static;
    margin-bottom: 2.5rem;
  }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-height: 620px) {
    /* One named timeline per step, lifted to the grid so the figure, which is
       a sibling of the steps and not a child, can be driven by them. Each
       stage of the drawing is tied to the paragraph that describes it, so the
       two cannot drift whatever height the steps end up. */
    .build { timeline-scope: --s1, --s2, --s3, --s4; }

    /* Longhands on purpose. The view-timeline shorthand carries the inset, so
       naming the timelines with it would put the inset back to auto, which
       means scroll-padding, and every stage would start 6rem early. */
    .build__step {
      view-timeline-axis: block;
      view-timeline-inset: 0;
    }

    .build__step:nth-child(1) { view-timeline-name: --s1; }
    .build__step:nth-child(2) { view-timeline-name: --s2; }
    .build__step:nth-child(3) { view-timeline-name: --s3; }
    .build__step:nth-child(4) { view-timeline-name: --s4; }

    .st1 { --tl: --s1; }
    .st2 { --tl: --s2; }
    .st3 { --tl: --s3; }
    .st4 { --tl: --s4; }

    /* --a and --b are set per part in the markup: where in its step's arrival
       a part starts, and where it is finished. --k rescales them for the
       narrow layout, where a step is read lower down the screen. */
    .fig--build :is(.d, .f, .p, .run) {
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: var(--tl);
      animation-range:
        entry calc(var(--a, 30%) * var(--k, 1))
        entry calc(var(--b, 90%) * var(--k, 1));
    }

    .fig--build .d {
      stroke-dasharray: 1 2;
      animation-name: fig-draw;
    }

    .fig--build .f { animation-name: fig-fade; }

    .fig--build .p {
      transform-box: fill-box;
      transform-origin: center;
      animation-name: fig-pop;
    }

    .fig--build .run {
      stroke-dasharray: 0.08 2;
      animation-name: fig-run;
    }

  }

  /* Wide: the figure holds the centre of the screen while four screens of
     steps pass beside it. */
  @media (prefers-reduced-motion: no-preference) and (min-height: 620px) and (width > 1100px) {
    /* Whichever step is being read stands forward; the rest recede. Only
       here: stacked, a step is read below the figure and off-centre, where
       this would dim it exactly when it is being read. */
    .build__step {
      animation: step-focus linear both;
      animation-timeline: view(block 0px);
      animation-range: cover 18% cover 82%;
    }

    @keyframes step-focus {
      0%   { opacity: 0.32; }
      34%  { opacity: 1; }
      66%  { opacity: 1; }
      100% { opacity: 0.32; }
    }

    .build__fig {
      top: 0;
      display: flex;
      flex-direction: column;
      justify-content: center;
      height: 100dvh;
      padding-top: 4.5rem;
    }

    /* Height-bound as well as width-bound, so the whole drawing is on screen
       on a short, wide window. */
    .build__cam {
      width: min(100%, calc((100dvh - 13rem) * 800 / 640));
    }

    .build__steps { gap: 0; }

    .build__step {
      display: flex;
      flex-direction: column;
      justify-content: center;
      min-height: 100dvh;
    }

    /* The grid is pulled up under the heading so the first step is not
       preceded by half a screen of nothing. */
    #capabilities .head { margin-bottom: 0; }
    .build { margin-top: -14dvh; }
  }

  /* Stacked: the figure sticks under the nav and the steps scroll beneath it.
     The drawing is too wide to read whole at this size, so it is shown at
     reading scale in a square frame and the frame moves to whichever part is
     being drawn. */
  @media (prefers-reduced-motion: no-preference) and (min-height: 620px) and (width <= 1100px) {
    .build {
      view-timeline-name: --build;
      view-timeline-axis: block;
      view-timeline-inset: 0;
    }

    .build__fig {
      position: sticky;
      top: 4.25rem;
      z-index: 3;
      margin-inline: calc(var(--pad-x) * -1);
      margin-bottom: 0;
      padding: 0.75rem var(--pad-x) 0.9rem;
      background: var(--ground);
      border-bottom: 1px solid var(--line);
    }

    /* Square, and sized from whichever of width and height is scarcer, so
       the frame's proportions, and with them the camera offsets below, are
       the same on a phone and on a tablet. */
    .build__cam {
      width: min(100%, 46dvh);
      aspect-ratio: 1;
      /* Without this the box grows to the height of the oversized drawing
         inside it: clip, unlike hidden, does not stop content from setting
         the automatic minimum. */
      min-height: 0;
      margin-inline: auto;
      overflow: clip;
    }

    .fig--build {
      width: 190%;
      max-width: none;
      animation: build-cam linear both;
      animation-timeline: --build;
      animation-range: contain 0% contain 100%;
    }

    /* Board, bench, break, service. Offsets are fractions of the drawing's
       own size, so they hold at any screen width. Each move is finished
       before its step starts drawing, so nothing is drawn off-frame. */
    @keyframes build-cam {
      0%, 10%   { transform: translate(-42%, -34%); }
      18%, 40%  { transform: translate(-1%, -34%); }
      47%, 69%  { transform: translate(-27%, 0%); }
      77%, 100% { transform: translate(-2%, 0%); }
    }

    .build .fig-cap { margin-top: 0.75rem; }

    .fig--build { --k: 0.62; }

    .build__steps { gap: 0; }

    .build__step {
      min-height: 82dvh;
      padding-top: 2.5rem;
    }
  }
}
