/* ============================================================
   SECTION 04 — "BUILT FOR THE JOBS A TRADE LOSES MONEY ON", as a pinned
   VERTICAL slide. Founder-directed: lock the viewport and move the cards
   vertically rather than horizontally.

   It is 01's construction with one axis changed, and deliberately so — the page
   already has a pinned slide and a second one that behaved differently would
   read as a bug rather than a rhyme.

   ⚠️ A PINNED BAND, NOT A PINNED SCREEN. `.vsl__pin` is 560px, not 100vh, for
   the reason 01 had to learn twice: what fills the viewport during a pin is the
   pin's own tall container, so a sticky child the height of the screen OWNS the
   screen, and shrinking it while leaving the container tall changes nothing.
   A band leaves ~340px of page visible at 900px tall, and the container is only
   200vh, so the section above is still in frame when the band locks and the one
   below is in frame before it lets go.

   ⚠️ THE TRANSLATE IS IN PIXELS, NOT PERCENT. A percentage translateY is a
   share of the ELEMENT'S OWN height, and the track is four panels tall — so
   `-300%` would move it twelve panels, not three. `--vp` is declared once and
   both the track height and the travel are computed from it; two places
   agreeing on a number by coincidence is how this kind of layout drifts.

   IT SLIDES, IT DOES NOT SCROLL. Each panel holds across a flat band of the
   range and the moves happen in 10% slivers with a strong ease-in-out, which
   spends its time at the ENDS of each band and crosses the ambiguous
   two-half-panels frame fastest. Holds are 70% of the pin. Same shape the
   founder chose for 01.

   ⚠️ THE HOLDS ALSO ABSORB THE ONE IMPRECISION HERE. `contain` is the span in
   which the CONTAINER covers the viewport; the sticky period of a band SHORTER
   than the viewport is not exactly that span. They differ a little at each end
   — and because both ends are flat keyframes, the difference lands inside a
   hold and cannot be seen. It could be, if the track moved linearly.

   ⚠️ NO `data-rise` ANYWHERE INSIDE. The reveal sets `transform: none` on `.in`
   (site.css:304) and every moving part here IS a transform. trio.css:60,
   s01hz.css and the thread it replaces all record the same collision.

   ⚠️ FALLBACK IS NOT OPTIONAL. The panels are stacked in a clipped track, so
   without it Safari, Firefox and reduced-motion readers see ONE panel and never
   the other three. Run `node scripts/webcheck/check.js <url> --sweep --still`
   after touching this.

   CONTENT: every line traces to context/levrg-service-catalog.md (services 3,
   8, 9, 10) or to statute. No number without a source; no result, case study or
   testimonial until a trade customer is live.
   ============================================================ */
.sec--vslide { background: var(--paper-warm); padding-block: var(--s7); }

/* ⚠️ --vp IS THE PANEL HEIGHT AND THE WINDOW CLIPS TO IT. Measured, the tallest
   panel is 286px at 1440 and 242px at 1024; 340 leaves real headroom rather
   than the 14px that 300 left. This is the same shape as the hero's
   `max-height` bug, which silently ate the primary CTA on every phone: a fixed
   height over hidden overflow does not warn you, it just removes the bottom of
   whatever grew. ANY copy edit to these four panels must be re-measured —
   scripts/webcheck has no check for it because a clipped panel still reports
   perfect contrast and no overflow. */
.vsl { --vp: 340px; height: 200vh; position: relative;
  view-timeline-name: --vsl; view-timeline-axis: block; }
/* ⚠️ `min-height`, NOT `height`. A DECLARED SIZE IS A REQUEST — the band asked
   for 560px and its own content measured 579 (heading 175 + 32 + window 340 +
   48 + the progress rule), and a flex column with `justify-content: center`
   pushes overflow off its START edge, so the missing 19px came off the TOP of
   the heading. "Systems built for the" lost its caps and ascenders on every
   desktop, and it sat there through seven clean sweep widths because fault 10
   only measured the bottom edge; the founder found it by eye. → #264

   The number is not raised to 580 or 600, because that is the same bug with a
   later trigger: one extra line of heading is 36px and it returns. An auto
   height cannot clip its own content whatever the copy does, and the min keeps
   the band from collapsing below the size the composition was solved at.
   `top` is now offset by half of ~580 rather than half of 560 — the centring is
   approximate either way, and it is cosmetic; the clip was not. */
.vsl__pin { position: sticky; top: max(var(--s3), calc(50vh - 290px));
  height: auto; min-height: 560px;
  display: flex; flex-direction: column; justify-content: center;
  overflow: hidden; }
/* the heading does not travel with the panels: it is the claim they are
   evidence for, and a pinned section that carries its question away with the
   first panel leaves three unlabelled cards behind */
.vsl__head { margin-bottom: var(--s4); }
.vsl__head h2 { font-family: var(--display); font-weight: var(--w-display);
  font-size: clamp(24px, 2.7vw, 36px); line-height: 1.06; letter-spacing: -0.025em;
  color: var(--ink); max-width: 20ch; margin: 0; }
.vsl__head p { font-size: var(--t-sm); line-height: 1.5; color: var(--ink-3);
  margin-top: var(--s2); max-width: 56ch; }

/* ⚠️ `flex: 0 0 auto` IS LOad-BEARING, NOT TIDINESS. The window is a flex item
   in a 560px pin that also holds a 175px heading, so with the default
   `flex-shrink: 1` the browser resolved `height: var(--vp)` (340px) down to
   303px — while the track went on translating by the full 340px per step and
   every `.vsl__p` stayed 340px tall. The bottom 37px of EVERY panel was being
   clipped, silently and at every width; panel 04 was simply the first whose
   copy reached that far. A shrinkable window and a fixed translate distance
   cannot both be right, and the flex algorithm resolves the disagreement
   without saying anything. */
.vsl__win { height: var(--vp); flex: 0 0 auto; overflow: hidden; position: relative; }
.vsl__track { height: calc(4 * var(--vp)); will-change: transform;
  animation: vsl-slide linear both;
  animation-timeline: --vsl; animation-range: contain 0% contain 100%; }
@keyframes vsl-slide {
  0%,  20% { transform: translate3d(0,0,0);                    animation-timing-function: cubic-bezier(0.76,0,0.24,1); }
  30%, 45% { transform: translate3d(0, calc(-1 * var(--vp)),0); animation-timing-function: cubic-bezier(0.76,0,0.24,1); }
  55%, 70% { transform: translate3d(0, calc(-2 * var(--vp)),0); animation-timing-function: cubic-bezier(0.76,0,0.24,1); }
  80%,100% { transform: translate3d(0, calc(-3 * var(--vp)),0); }
}
.vsl__p { height: var(--vp); display: grid; grid-template-columns: 46px minmax(0,1fr);
  gap: var(--s4); align-content: center; }
.vsl__n { font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em;
  color: var(--accent-ink); padding-top: 8px; }
.vsl__p b { display: block; font-family: var(--display); font-weight: var(--w-display);
  font-size: clamp(23px, 2.9vw, 40px); line-height: 1.06; letter-spacing: -0.035em;
  color: var(--ink); max-width: 18ch; }
.vsl__p p { font-size: var(--t-md); line-height: 1.5; color: var(--ink-2);
  margin-top: var(--s3); max-width: 54ch; }
.vsl__p i { font-style: normal; display: block; font-size: var(--t-sm); line-height: 1.5;
  color: var(--ink-3); margin-top: var(--s3); max-width: 52ch; }

/* four ticks. A pinned section is a page that has stopped scrolling, and with
   no mark of how far through you are that reads as the page having frozen. */
.vsl__prog { display: flex; gap: var(--s2); margin-top: var(--s5); }
.vsl__tick { width: 54px; height: 2px; background: var(--line-2); overflow: hidden; }
.vsl__tick i { display: block; height: 100%; background: var(--accent-ink);
  transform-origin: left; transform: scaleX(0);
  animation: vsl-fill linear both; animation-timeline: --vsl; }
.vsl__tick:nth-child(1) i { animation: none; transform: scaleX(1); }
.vsl__tick:nth-child(2) i { animation-range: contain 20% contain 30%; }
.vsl__tick:nth-child(3) i { animation-range: contain 45% contain 55%; }
.vsl__tick:nth-child(4) i { animation-range: contain 70% contain 80%; }
@keyframes vsl-fill { to { transform: scaleX(1); } }

/* ---- the finished state: ALL FOUR PANELS, not the first one ------------
   The track is clipped to one panel's height, so "no animation" means three of
   the four are simply gone. The fallback has to unclip it, not just stop it. */
@supports not (animation-timeline: view()) {
  .vsl { height: auto; }
  .vsl__pin { position: static; height: auto; overflow: visible; }
  .vsl__win { height: auto; overflow: visible; }
  .vsl__track { height: auto; animation: none; transform: none; }
  .vsl__p { height: auto; padding-block: var(--s4); border-top: 1px solid var(--line); }
  .vsl__prog { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .vsl { height: auto; }
  .vsl__pin { position: static; height: auto; overflow: visible; }
  .vsl__win { height: auto; overflow: visible; }
  .vsl__track { height: auto; animation: none; transform: none; }
  .vsl__p { height: auto; padding-block: var(--s4); border-top: 1px solid var(--line); }
  .vsl__prog { display: none; }
}
@media (max-width: 900px) {
  /* 560px of pinned band on a handset is most of the screen, and the panels are
     taller here because the copy wraps — it becomes a plain stack */
  .vsl { height: auto; }
  .vsl__pin { position: static; height: auto; overflow: visible; }
  .vsl__win { height: auto; overflow: visible; }
  .vsl__track { height: auto; animation: none; transform: none; }
  /* ⚠️ TWO COLUMNS ON A PHONE: the service on the left, what it saves on the
     right. Founder-chosen, design 06 off /s04m.html. Section 1878px -> ~940.

     ⚠️ THE EXPLANATION IS NOT SHOWN HERE, AND RIGHT NOW IT IS NOWHERE ELSE
     EITHER. Each item is a name, an explanation and a payoff; this design keeps
     the first and the third. The usual answer — "the detail is on the page the
     link goes to" — does NOT hold yet, because /trades/ is a 404. So on a phone
     these four services currently have no long-form description anywhere on the
     site. That is a real gap, not a tidy trade, and it closes the moment
     /trades/ is built. Flagged rather than discovered later.

     `display: contents` on the inner wrapper is what lets the <b> and the <i>
     become grid items directly; the <p> between them is hidden, so the two that
     remain fall into the two columns without the markup being restructured. */
  .vsl__p { height: auto; padding-block: var(--s4); border-top: 1px solid var(--line);
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
  /* ⚠️ `:not(.vsl__n)`, BECAUSE THE NUMBER IS A DIV TOO. `.vsl__p > div` matched
     both children — the wrapper AND the number — so the number's text became a
     grid item, took column one, and pushed the service name into column two
     with its payoff underneath. The columns came out reversed and the number
     was still there after a rule that said `display: none`, because the rule it
     was fighting was my own. */
  .vsl__p > div:not(.vsl__n) { display: contents; }
  .vsl__p > .vsl__n { display: none; }
  .vsl__p b { font-family: var(--display); font-weight: var(--w-display);
    font-size: var(--t-sm); letter-spacing: -0.02em; color: var(--ink); line-height: 1.3; }
  .vsl__p p { display: none; }
  /* ⚠️ `margin-top: 0` AND A SHARED FIRST BASELINE. The desktop rule gives this
     line `margin-top: var(--s3)`, which is right when it sits UNDER a
     paragraph and wrong when it sits BESIDE a heading: the two columns started
     24px apart and every row read as a step down. That is the "stairs".
     `align-items: start` is not enough on its own either — two columns at
     different font sizes start at the same box top and different baselines, so
     the text still looks misaligned by a few pixels. `first baseline` aligns
     what the eye actually reads. */
  /* ⚠️ THE HEADING GOES BACK TO THE PAGE'S OWN SCALE. `clamp(24px, 2.7vw, 36px)`
     exists because on desktop this heading shares a 560px pinned band with four
     panels and a window — it is sized to FIT something. On a phone the pin is
     gone and the section is a plain two-column stack, so that constraint came
     with it and nothing replaced it: the heading rendered at 24px where 02 and
     03 render at 32. A number solved for a container that no longer exists is
     not a type choice. */
  .vsl__head h2 { font-size: var(--t-xl); line-height: 1.02;
    letter-spacing: var(--track); max-width: 18ch; }

  .vsl__p { align-items: first baseline; }
  .vsl__p i { font-style: normal; font-size: var(--t-xs); line-height: 1.5;
    color: var(--ink-3); margin-top: 0; }
  .vsl__prog { display: none; }
}
