/* ==========================================================================
   INDEX — SIGNATURE LAYER
   Loaded last, only on the landing page.

   The rest of the site is deliberately quiet. The front door is allowed
   to speak up, but in the same voice: same palette, same hairlines, same
   two weights, same mono. What changes is scale, composition and how much
   the page responds to you.

   The organising idea is the page's own claim — one person carrying every
   layer, diagram to production. So the page is built as a system diagram
   that assembles while you read it: a pipeline rail down the left edge
   that fills as you descend, nodes you can actually interrogate, and
   plates that tilt under the cursor.

   Everything here degrades to the quiet version when a reader asks for
   reduced motion.
   ========================================================================== */

:root {
  --sig-rail-w: 4.5rem;   /* the left pipeline rail        */
  --sig-ghost: .55;       /* opacity of the outline numeral */
}

/* ── 1. SCROLL PROGRESS ────────────────────────────────────────────────
   A hairline across the top of the nav that fills with the read. The
   whole page is a pipeline; this is its completion bar.                */

#sig-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 100%;
  z-index: 1000;
  transform: scaleX(var(--sig-p, 0));
  transform-origin: left;
  background: var(--blue);
  opacity: .9;
  pointer-events: none;
  will-change: transform;
}

/* ── 2. THE PIPELINE RAIL ──────────────────────────────────────────────
   Five stops down the left edge, one per section, wired together with a
   hairline that fills as you pass each one. It reads as a station map,
   which is exactly what the page is.                                    */

#sig-rail {
  position: fixed;
  top: 50%;
  left: 1.5rem;
  transform: translateY(-50%);
  z-index: 880;
  display: none;                       /* only where there is room */
  flex-direction: column;
  align-items: flex-start;
  gap: 1.5rem;
  padding: 0;
  margin: 0;
  list-style: none;
}

@media (min-width: 1320px) {
  #sig-rail { display: flex; }
}

#sig-rail::before {
  content: '';
  position: absolute;
  left: 5px;
  top: .55rem;
  bottom: .55rem;
  width: 1px;
  background: var(--rule);
}

/* the travelled part of the line */
#sig-rail::after {
  content: '';
  position: absolute;
  left: 5px;
  top: .55rem;
  width: 1px;
  height: calc((100% - 1.1rem) * var(--sig-rail-p, 0));
  background: var(--blue);
  transition: height .25s linear;
}

.sig-stop {
  position: relative;
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--mono);
  font-size: .58rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  line-height: 1;
  transition: color .2s ease;
}

.sig-stop i {
  position: relative;
  z-index: 1;
  display: block;
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  border: 1px solid var(--rule-strong);
  border-radius: 50%;
  background: var(--bg);
  transition: background .2s ease, border-color .2s ease, transform .2s ease;
}

.sig-stop span {
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity .2s ease, transform .2s ease;
  white-space: nowrap;
}

.sig-stop:hover,
.sig-stop:focus-visible { color: var(--ink); }
.sig-stop:hover span,
.sig-stop:focus-visible span { opacity: 1; transform: none; }
.sig-stop:hover i { border-color: var(--blue); }

.sig-stop.on { color: var(--ink); }
.sig-stop.on i { background: var(--blue); border-color: var(--blue); transform: scale(1.18); }
.sig-stop.on span { opacity: 1; transform: none; }

/* ── 3. THE OUTLINE NUMERAL ────────────────────────────────────────────
   One oversized hairline numeral per section, set in the margin. It is
   texture rather than hierarchy — drawn as an outline so it never
   competes with the heading it sits behind.                             */

section[data-sig] { position: relative; }

section[data-sig]::before {
  content: attr(data-sig);
  position: absolute;
  top: 2rem;
  right: 2.5rem;
  z-index: 0;
  font-family: var(--mono);
  font-size: clamp(7rem, 15vw, 15rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -.04em;
  color: transparent;
  -webkit-text-stroke: 1px var(--rule);
  opacity: var(--sig-ghost);
  pointer-events: none;
  user-select: none;
}

section[data-sig] > * { position: relative; z-index: 1; }

@media (max-width: 900px) {
  section[data-sig]::before { display: none; }
}

/* ── 4. HERO ───────────────────────────────────────────────────────────
   The one place the capped type scale is broken on purpose. Everything
   else on the site tops out at 2.5rem; the front door goes to five, at
   weight 300, tracked tight. Scale is doing the work that weight is not
   allowed to.                                                           */

.hero-h1 {
  font-size: clamp(2.6rem, 7.4vw, 5.25rem) !important;
  line-height: .9 !important;
  letter-spacing: -.055em !important;
}

/* each line rises out of its own clip, one after the other */
.hero-h1 .ln {
  display: block;
  overflow: hidden;
  /* the clip has to clear the descenders on "diagram" and "production",
     so pad both edges and pull the box back to the original metrics */
  padding-top: .08em;
  margin-top: -.08em;
  padding-bottom: .16em;
  margin-bottom: -.16em;
}
.hero-h1 .ln > span {
  display: block;
  transform: translateY(105%);
  animation: sigLine .9s cubic-bezier(.16, .84, .3, 1) forwards;
}
.hero-h1 .ln:nth-child(1) > span { animation-delay: .18s; }
.hero-h1 .ln:nth-child(2) > span { animation-delay: .30s; }
.hero-h1 .ln:nth-child(3) > span { animation-delay: .42s; }
@keyframes sigLine { to { transform: none; } }

.hero-lead { max-width: 44ch; }

/* a spotlight on the grid bed that tracks the pointer */
#hero {
  --sig-mx: 42%;
  --sig-my: 44%;
}
#hero::before {
  mask-image:
    radial-gradient(38rem 30rem at var(--sig-mx) var(--sig-my),
                    #000 12%, rgba(0, 0, 0, .35) 46%, transparent 78%);
  -webkit-mask-image:
    radial-gradient(38rem 30rem at var(--sig-mx) var(--sig-my),
                    #000 12%, rgba(0, 0, 0, .35) 46%, transparent 78%);
  opacity: 1;
}

/* ── 5. THE TOPOLOGY, MADE INTERROGABLE ────────────────────────────────
   The diagram was decorative. Now each node is a control: take one and
   the matching confidence bar below lights up, and the rest recede. No
   new labels — it just wires two things the card already showed.        */

.topo .node { cursor: pointer; }
.topo .node .node-box,
.topo .node .node-txt { transition: fill .2s ease, stroke .2s ease, opacity .2s ease; }

.topo .node .hitpad { fill: transparent; }

.sys-card.probing .topo .node { opacity: .38; }
.sys-card.probing .topo .node.sel { opacity: 1; }
.sys-card.probing .topo .node.sel .node-box { fill: var(--blue-pale); stroke: var(--blue); }
.sys-card.probing .topo .node.sel .node-txt { fill: var(--ink); }

.topo .node:hover .node-box  { stroke: var(--blue); }
.topo .node:focus-visible    { outline: none; }
.topo .node:focus-visible .node-box { stroke: var(--blue); stroke-width: 2; }

.tc-skill { transition: opacity .2s ease; }
.sys-card.probing .tc-skill { opacity: .32; }
.sys-card.probing .tc-skill.lit { opacity: 1; }
.tc-skill.lit .tc-skill-head span:first-child { color: var(--ink); }
.tc-skill.lit .tc-skill-track { box-shadow: 0 0 0 1px var(--blue-border); }

/* the card stops bobbing while it is being read */
.sys-card:hover, .sys-card.probing { animation-play-state: paused; }

/* ── 7. FIGURES ────────────────────────────────────────────────────────
   The stats band is the page's only pure-number moment, so let it be
   properly large. Mono, tabular, one weight.                            */

.stat-num {
  font-size: clamp(2.4rem, 5.6vw, 4rem) !important;
  letter-spacing: -.055em;
}
.stat-cell { padding-block: 64px; }

/* ── 8. CLOSING ────────────────────────────────────────────────────────
   The sign-off matches the hero, so the page opens and closes on the
   same note.                                                            */

.cta-h2 {
  font-size: clamp(2.2rem, 6vw, 4rem) !important;
  line-height: .94 !important;
  letter-spacing: -.05em !important;
}

/* ── 9. REDUCED MOTION ─────────────────────────────────────────────────
   Everything above is decoration on top of a page that already worked.
   Strip the movement and leave the composition.                         */

@media (prefers-reduced-motion: reduce) {
  #sig-progress { display: none; }
  #sig-rail::after { transition: none; }
  .hero-h1 .ln > span { transform: none; animation: none; }
  #hero::before { animation: none; mask-image: var(--grid-mask); -webkit-mask-image: var(--grid-mask); }
  .sys-card { animation: none; }
}

/* ── 6. THE STACK ──────────────────────────────────────────────────────
   "Six layers, one pair of hands", and the lead says they run from the
   first diagram to the thing that renders. A marquee said none of that;
   it only moved. So the layers are drawn as what they are — a stack you
   take apart.

   Six plates, offset into a staircase so the edge of every one is
   visible at once, like a cross-section. Take a plate and it pulls out
   of the stack; the seam beside it opens on what that layer actually
   involves. It assembles itself as the section comes into view.        */

.mc-sd  { --mc: var(--cat-2); }
.mc-cl  { --mc: var(--cat-3); }
.mc-dp  { --mc: var(--cat-6); }
.mc-be  { --mc: var(--cat-5); }
.mc-ai  { --mc: var(--cat-1); }
.mc-app { --mc: var(--cat-4); }

#expertise { overflow: visible; }

.stack {
  display: grid;
  grid-template-columns: minmax(0, 27rem) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}

/* ── the plates ── */

.stack-col {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.plate {
  --i: 0;
  position: relative;
  display: grid;
  grid-template-columns: 3.1rem minmax(0, 1fr);
  align-items: center;
  gap: .9rem;
  width: 100%;
  min-height: 3.9rem;
  padding: 0 1.15rem 0 1rem;
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-left: 3px solid color-mix(in oklab, var(--mc) 55%, var(--rule));
  border-radius: var(--r);
  /* the staircase: each plate sits a little further in than the last,
     so you see the edge of every layer at once */
  margin-left: calc(var(--i) * 13px);
  transition: transform .34s cubic-bezier(.2, .8, .25, 1),
              margin-left .34s cubic-bezier(.2, .8, .25, 1),
              border-color .22s ease, background .22s ease,
              box-shadow .28s ease;
}

.plate-n {
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .1em;
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
  transition: color .22s ease;
}

.plate-t {
  font-size: 1rem;
  color: var(--ink-3);
  letter-spacing: -.005em;
  transition: color .22s ease;
}

.plate:hover {
  border-color: color-mix(in oklab, var(--mc) 34%, var(--rule));
  background: var(--surface-raised);
}
.plate:hover .plate-t { color: var(--ink); }
.plate:focus-visible { outline: 2px solid var(--mc); outline-offset: 3px; }

/* taken: the plate leaves the stack */
.plate.is-on {
  margin-left: 0;
  background: var(--surface);
  border-color: color-mix(in oklab, var(--mc) 46%, var(--rule));
  border-left-color: var(--mc);
  box-shadow: 0 14px 34px -18px color-mix(in oklab, var(--mc) 70%, transparent),
              var(--sh-sm);
  transform: translateX(-6px);
}
.plate.is-on .plate-n { color: var(--mc); }
.plate.is-on .plate-t { color: var(--ink); }

/* ── the seam ── */

.stack-seams { position: relative; }

.seam {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    'icon desc'
    '.    tags';
  gap: 1.15rem 1.4rem;
  align-content: start;
  position: relative;
  padding: 1.9rem 2rem 2rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--mc);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-sm);
}
.seam[hidden] { display: none; }

/* a hairline reaching back across the gap to the plate that opened it,
   so the pair reads as one wired thing rather than two boxes */
.seam::before {
  content: '';
  position: absolute;
  right: 100%;
  /* script points this at the open plate; the fallback is the panel's
     own first line, which is where the eye starts anyway */
  top: var(--seam-y, 2.9rem);
  width: clamp(1rem, 4vw, 3.4rem);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--mc));
  transition: top .34s cubic-bezier(.2, .8, .25, 1);
}
@media (max-width: 900px) { .seam::before { display: none; } }

.seam .mc-icon { grid-area: icon; margin-bottom: 0; }
.seam-desc {
  grid-area: desc;
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.7;
  color: var(--ink-3);
  text-wrap: pretty;
}
.seam .mc-tags { grid-area: tags; }

/* the panel arrives from the plate that opened it */
.seam.is-on { animation: sigSeam .42s cubic-bezier(.16, .84, .3, 1) both; }
@keyframes sigSeam {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ── assembly on scroll-in ──
   Only armed once script is running, so a failed observer or no script
   at all leaves the plates plainly visible rather than blank. */

.stack.arming .plate {
  opacity: 0;
  transform: translateX(26px);
  transition: opacity .5s ease calc(var(--i) * 70ms),
              transform .5s cubic-bezier(.16, .84, .3, 1) calc(var(--i) * 70ms),
              border-color .22s ease, background .22s ease, box-shadow .28s ease;
}
.stack.arming.built .plate { opacity: 1; transform: translateX(0); }
.stack.arming.built .plate.is-on { transform: translateX(-6px); }

/* ── narrow: the stack lies flat and the seam follows it ── */

@media (max-width: 900px) {
  .stack { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
  .plate { margin-left: 0 !important; }
  .plate.is-on { transform: none; }
  .stack-seams { min-height: 0; }
  .seam { padding: 1.5rem 1.35rem 1.6rem; }
}

@media (prefers-reduced-motion: reduce) {
  .plate, .seam.is-on, .stack.arming .plate { transition: none; animation: none; }
  .stack.arming .plate { opacity: 1; transform: none; }
  .stack.arming.built .plate.is-on { transform: none; }
}

/* ── 7. THE TRAJECTORY, AS AN INSTRUMENT ───────────────────────────────
   The copy says "Pick a stop", so the chart behaves like something you
   scrub rather than a picture with three dots on it.

     · the scope fills only as far as the era you have taken
     · a lead line carries the accent up to that point; the curve beyond
       it stays as a faint trace of where the story is going
     · a packet runs the whole path on a loop, so the chart reads as
       live even when nobody has touched it
     · the taken stop pulses; its metrics count up as the panel changes

   The clip and the lead offset are driven from script, because both
   depend on where along the path a given year actually falls.          */

.trace-svg { overflow: visible; }

/* the base curve is now the ghost of the whole story */
.trace {
  stroke: color-mix(in oklab, var(--blue) 30%, transparent);
  stroke-width: 1.75;
}

/* and this one is the part you have travelled */
.traceLead {
  fill: none;
  stroke: var(--blue);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: var(--lead-len, 1400);
  stroke-dashoffset: var(--lead-len, 1400);
  transition: stroke-dashoffset .95s cubic-bezier(.22, .8, .25, 1);
  filter: drop-shadow(0 1px 5px color-mix(in oklab, var(--blue) 35%, transparent));
}

/* the scope fills up to the same point. Width, not transform: a
   transform on a clipPath child is not honoured, and the fill silently
   keeps its full extent. */
.traceClipRect {
  transition: width .95s cubic-bezier(.22, .8, .25, 1);
}

/* a packet running the length of the path, forever */
.tracePacket {
  fill: var(--blue);
  offset-distance: 0%;
  offset-rotate: 0deg;
  opacity: 0;
  filter: drop-shadow(0 0 6px color-mix(in oklab, var(--blue) 70%, transparent));
}
.trace-box.running .tracePacket {
  animation: sigPacket 7.5s cubic-bezier(.5, 0, .5, 1) infinite;
}
@keyframes sigPacket {
  0%        { offset-distance: 0%;   opacity: 0; }
  6%, 88%   { opacity: .95; }
  100%      { offset-distance: 100%; opacity: 0; }
}

/* the taken stop keeps a ring going out of it */
.stopRing {
  fill: none;
  stroke: var(--blue);
  stroke-width: 1.4;
  opacity: 0;
  transform-origin: center;
  transform-box: fill-box;
}
.stop.active .stopRing { animation: sigStopRing 2.6s ease-out infinite; }
@keyframes sigStopRing {
  0%        { transform: scale(1);   opacity: .6; }
  70%, 100% { transform: scale(2.9); opacity: 0; }
}

.stop .hit { r: 6; transition: r .25s cubic-bezier(.2, .9, .3, 1.4), fill .2s ease; }
.stop.active .hit { r: 7.5; }
.stop:hover .hit { r: 8; }

/* The stops are revealed by a transition carrying up to a 2.3s delay,
   which means a stalled transition clock leaves the years invisible.
   Script lights them unconditionally once the entrance has had its
   time, so the labels can never be lost to a dropped animation. */
.trace-box.stops-lit .stop {
  opacity: 1;
  transform: none;
  /* the delays live on .stop:nth-of-type(), so the backstop has to
     opt out of transitioning or it inherits the same stall */
  transition: none;
}

/* the year on the taken stop steps forward with it */
.stop .lbl { transition: fill .2s ease, font-size .25s ease; }
.stop.active .lbl { fill: var(--ink); }

/* the panel's figures roll rather than swap */
.era-mv { font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .traceLead, .traceClipRect { transition: none; }
  .trace-box.running .tracePacket { animation: none; opacity: 0; }
  .stop.active .stopRing { animation: none; opacity: 0; }
}
