/* ============================================================
   timeline.css — the numbered "switch" timeline, shared.

   3SC's HeritageTimeline as this site builds it: a centre spine with the
   entries ALTERNATING either side of it, each entry a two-column card of art
   and copy with the art on the side away from the line, and a square numbered
   badge in the step's own colour. Below 820px it becomes one centred column
   with the number lifted onto the line and the art stacked above the copy.

   Used by school.html (the switch) and schoolrun.html (how it works) — one
   source, so a fix lands on both. timeline.js drives the entrance.

   Markup:
     <ol class="sl-steps">
       <div class="sl-spine" aria-hidden="true"></div>
       <li class="sl-step" style="--rc: var(--pink)">
         <div class="sl-card">
           <div class="sl-art"><object data="visuals/x.svg"></object></div>
           <div class="sl-copy">
             <div class="sl-head-row"><div class="sl-num">01</div><p class="sl-title">…</p></div>
             <p class="sl-text">…</p>
           </div>
         </div>
       </li>
     </ol>

   Set --rc per step for the badge colour; add .sl-yellow / .sl-green when that
   colour needs black text. Self-contained and token-independent (fallbacks),
   so it renders on any ground.
   ============================================================ */

/* the timeline itself */
.sl-steps { position: relative; margin: clamp(46px, 6.5vh, 82px) auto 0; max-width: 1000px; }
/* their spine: 4px black, centred, stopping short of the last card's foot,
   with a white-ringed dot capping it */
.sl-spine { position: absolute; left: 50%; top: 0; bottom: clamp(40px, 7vh, 84px); width: 4px;
  margin-left: -2px; background: var(--cube-black, var(--black, #000)); }
.sl-spine::after { content: ""; position: absolute; left: 50%; bottom: -9px; width: 18px; height: 18px;
  margin-left: -9px; border-radius: 50%; border: 2px solid #fff; background: var(--cube-black, var(--black, #000));
  box-shadow: 3px 3px 0 0 rgba(0,0,0,.18); }
/* well apart: at clamp(30,4.5vh,56) three of the four looping visuals were on
   screen at once and they competed with each other */
.sl-steps > .sl-step + .sl-step { margin-top: clamp(64px, 11vh, 150px); }
/* Each entry hangs off one side of the spine and OVERLAPS it, which is what
   HeritageTimeline does (max-w-xl cards in an 840px column). The cards have
   to be this wide because the card itself is two columns — see .sl-card. */
.sl-step { position: relative; display: flex; }
.sl-step:nth-child(even) { justify-content: flex-start; }
.sl-step:nth-child(odd) { justify-content: flex-end; }
/* HeritageTimeline's card: grid-cols-2, art one side and copy the other,
   and the art takes the side AWAY from the spine (their md:order-1 /
   md:order-2 swap). The art is one of the animated visuals from visuals/ —
   self-contained SVG, no JS — sitting beside the copy the way the audit
   page pairs them (visual-blocks.css .vb-beside). */
.sl-card { width: 76%; background: #fff;
  border: 3px solid var(--cube-black, var(--black, #000)); box-shadow: 6px 6px 0 0 var(--cube-black, var(--black, #000));
  display: grid; grid-template-columns: 1fr 1fr; align-items: stretch; }
.sl-art { display: block; background: #eaeaea; overflow: hidden; }
/* The cell's ground MUST match the artwork's own (#eaeaea in every visual this
   component uses). object-fit leaves a letterbox whenever the cell's ratio is
   not the art's 480/340, and a mismatched ground turns that into a visible band
   across the top and bottom. Matching it lets the background read full height
   without the artwork being stretched to fill. */
.sl-art object, .sl-art img { display: block; width: 100%; height: 100%;
  object-fit: cover; aspect-ratio: 480 / 340; }
/* art outside, copy inside — mirrored either side of the line */
.sl-step:nth-child(even) .sl-art { order: 1; border-right: 3px solid var(--cube-black, var(--black, #000)); }
.sl-step:nth-child(even) .sl-copy { order: 2; }
.sl-step:nth-child(odd) .sl-art { order: 2; border-left: 3px solid var(--cube-black, var(--black, #000)); }
.sl-step:nth-child(odd) .sl-copy { order: 1; }
.sl-copy { padding: clamp(18px, 2vw, 26px); display: flex; flex-direction: column; justify-content: center; }
.sl-head-row { display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }
.sl-num { flex: none; display: grid; place-items: center; width: 54px; height: 54px;
  border: 3px solid var(--cube-black, var(--black, #000)); box-shadow: 3px 3px 0 0 var(--cube-black, var(--black, #000));
  background: var(--rc, var(--cube-pink, var(--pink, #f70087))); color: #fff;
  font: 800 clamp(18px, 1.8vw, 22px)/1 var(--cube-font-primary, var(--font, 'Poppins', system-ui, sans-serif)); letter-spacing: -.02em; }
.sl-step.sl-yellow .sl-num, .sl-step.sl-green .sl-num { color: #000; }
.sl-title { font: 700 clamp(17px, 1.5vw, 21px)/1.25 var(--cube-font-primary, var(--font, 'Poppins', system-ui, sans-serif)); color: var(--cube-ink, var(--ink, #0a0a0a)); }
.sl-text { font: 400 var(--t-body)/var(--lh-body) var(--cube-font-secondary, var(--mono, 'Anonymous Pro', ui-monospace, monospace)); color: var(--cube-ink, var(--ink, #0a0a0a)); }
/* .rv = the script is driving: cards start off their own side and slide in.
   The armed state is scoped :not(.on) rather than left to be overridden —
   the two selectors weigh the same, so the mobile override below would
   otherwise simply come last and pin every card at its offset for good. */
.sl-steps.rv .sl-step:not(.on) .sl-card { opacity: 0; }
.sl-steps.rv .sl-step:nth-child(even):not(.on) .sl-card { transform: translateX(-50px); }
.sl-steps.rv .sl-step:nth-child(odd):not(.on) .sl-card { transform: translateX(50px); }
.sl-steps.rv .sl-step.on .sl-card { opacity: 1; transform: none;
  transition: opacity .4s ease var(--sd, 0s), transform .6s var(--e-s100-15, cubic-bezier(.22,1,.36,1)) var(--sd, 0s); }
/* their spine is static; it only needs to not be there before the first
   card has arrived, or a bare line hangs in the section */
.sl-steps.rv .sl-spine { opacity: 0; transition: opacity .45s ease; }
.sl-steps.rv.lit .sl-spine { opacity: 1; }
/* below their md breakpoint the entries go full width and the spine moves
   to the left edge, so the cards still read as hung off one line */
@media (max-width: 820px) {
  /* A single centred column on a phone: the line runs down the middle
     BEHIND everything, each step's number sits ON the line, and its card
     hangs under it —
         (1)
        [card]
         (2)
        [card]
     — so the four read as one sequence top to bottom. Hung off a left-hand
     line they read as four separate cards with a rule beside them. */
  .sl-spine { left: 50%; margin-left: -2px; top: 0; bottom: 0; }
  .sl-spine::after { display: none; }        /* the last card is the end, not a dot */
  .sl-step { flex-direction: column; align-items: center; padding-top: 74px; }
  .sl-step:nth-child(odd), .sl-step:nth-child(even) { justify-content: flex-start; }
  /* the number lifts out of the card and onto the line */
  .sl-num { position: absolute; top: 0; left: 50%; margin-left: -27px; z-index: 1; }
  /* their below-md card: one column, art stacked above the copy */
  .sl-card { width: 100%; grid-template-columns: 1fr; }   /* NOT position:relative — the number anchors to .sl-step */
  .sl-step:nth-child(even) .sl-art, .sl-step:nth-child(odd) .sl-art {
    order: 1; border: 0; border-bottom: 3px solid var(--cube-black, var(--black, #000)); }
  .sl-step:nth-child(even) .sl-copy, .sl-step:nth-child(odd) .sl-copy { order: 2; }
  /* the title has the head row to itself now */
  .sl-head-row { gap: 0; margin-bottom: 10px; }
  /* The badge is position:absolute against .sl-step, but it LIVES inside
     .sl-card — and a transform on an ancestor makes that ancestor the
     containing block for its absolutely-positioned descendants. So the moment
     the entrance put translateY() on the card, the badge stopped measuring
     from .sl-step's padding box and started measuring from the card's: it
     dropped ~107px onto the card (clearance went 20px -> -57px), sat there
     clashing for the length of the transition, then snapped back up the
     instant the transform resolved to none. Moving the entrance onto .sl-step
     fixes it at the root: the step is the badge's containing block either way,
     so badge and card travel together and the badge never leaves the line.
     Both properties have to move — the badge was fully opaque while its card
     was still invisible, which is what made the clash so loud. */
  .sl-steps.rv .sl-step:nth-child(odd):not(.on) .sl-card,
  .sl-steps.rv .sl-step:nth-child(even):not(.on) .sl-card { transform: none; opacity: 1; }
  .sl-steps.rv .sl-step:nth-child(odd):not(.on),
  .sl-steps.rv .sl-step:nth-child(even):not(.on) { opacity: 0; transform: translateY(30px); }
  .sl-steps.rv .sl-step:nth-child(odd).on,
  .sl-steps.rv .sl-step:nth-child(even).on { opacity: 1; transform: none;
    transition: opacity .4s ease var(--sd, 0s), transform .6s var(--e-s100-15, cubic-bezier(.22,1,.36,1)) var(--sd, 0s); }
  .sl-num { width: 54px; height: 54px; }
  .sl-head-row { gap: 13px; }
}
