/* ============================================================
   Work — section 03 (desktop, >=820px braided thread)
   Ported from refer/work-thread.html.

   RECONCILED: the source file had two duplicate, conflicting
   "LEAVING" blocks (an opacity-based draft and a colour-based
   final). system_2.md §5 settles it explicitly: "colour, never
   opacity... Wash at 520ms, navigate at 820ms" — matches the
   colour/--fill-dim block's timing exactly. Kept that one, fixed
   its broken `.card__hit` selector (a stray comment had merged it
   into `.card__hit .work__wash`, silently killing the wash's
   position:fixed), and deleted the opacity-based duplicate.
   ============================================================ */

/* Owner request: no top padding above the thread's start — flush with
   whatever precedes it. Scoped to this instance only, not .section
   itself, so every other section's padding is untouched. */
@media (min-width:900px){ .section--work-top-flush{ padding-top:0; } }

.work{ position:relative; }

.work__thread{
  position:absolute; inset:0; width:100%; height:100%;
  pointer-events:none; z-index:0; overflow:visible;
}
/* Filled outlines, not strokes — a stroke is one width for its whole
   length, so a converging bundle always ends blunt; these taper to zero. */
.work__thread path{ fill:var(--thread); stroke:none; }
.work__thread circle{ fill:var(--thread); transition:opacity var(--dur-1) var(--ease-move); }

/* Invisible position anchors — work-thread.js reads their
   getBoundingClientRect exactly like it reads a card's, so the braid
   gets a real first node (below the Cover intro, dead centre — see
   work-cover.js) and a real last node (the wordless convergence point
   after card 5), with no special-casing in the thread math itself. */
.work__origin{ display:block; height:1px; margin-bottom:var(--sp-12); }
.work__terminus{ display:block; height:1px; margin-top:var(--sp-12); }

/* Cards sit ~660px apart horizontally; if the vertical gap is much
   smaller than that the braid has nowhere to go and flattens out. */
.work__list{
  position:relative; z-index:1;
  display:flex; flex-direction:column; gap:var(--sp-12);
}

/* Owner request: cards read as 3D objects, not flat outlined boxes —
   a soft shadow cast below. Uses --shadow (tokens.css), not a flat
   rgba(0,0,0,..): a pure black shadow is invisible against dark
   theme's near-black field (blending black into black shows nothing,
   at any alpha), so --shadow flips to a faint light glow in dark
   theme instead — the standard substitute for a drop shadow once the
   field itself is already near-black. Bumped padding (sp-6->sp-7) and
   width (43%->45%) for a slightly larger footprint; 45% still leaves
   enough of the centred channel for the thread/loader
   (work-loader.css).

   background:var(--surface), not --bg — owner report: in dark theme
   the card was nearly invisible, since --bg IS the page field there
   too (near-black on near-black), leaving only the shadow to read the
   card at all. --surface is the token every other card/panel on the
   page (Stack's tiles, Skill cards, Method's panel) already uses for
   exactly this "lifts off the field" contrast; .card was the one
   component still on --bg. */
.card{
  position:relative; width:100%;
  background:var(--surface);
  border-radius:var(--r-md); padding:var(--sp-7);
  box-shadow:0 28px 44px -28px var(--shadow);
  transition:box-shadow var(--dur-1) var(--ease-move), transform var(--dur-1) var(--ease-move);
}
/* Owner request: cards lost their border (site-wide pass) — hover
   feedback moves from border-color to the same shadow/lift language
   the card's own depth already relies on, instead of a now-nonexistent
   border darkening. */
.card:hover{
  box-shadow:0 34px 54px -26px var(--shadow);
  transform:translateY(-2px);
}
.card > *:not(.card__hit){ position:relative; z-index:1; }

/* The whole card is the target, not just the link — a stretched anchor
   keeps it one real link for keyboard and screen readers. */
.card__hit{ position:absolute; inset:0; z-index:2; border-radius:inherit; }

/* Thread mode only ever renders >=900px (system_2.md's one breakpoint
   that matters), so this query aligns with that gate rather than the
   source prototype's arbitrary 820px. */
@media (min-width:900px){
  .card{ width:45%; }
  .card--right{ margin-left:auto; }
}

#work{ display:none; }
@media (min-width:900px){ #work{ display:block; } }

.card__meta{
  display:flex; justify-content:space-between;
  font-size:var(--fs-t4); letter-spacing:var(--ls-caps); text-transform:uppercase;
  color:var(--text-muted); margin-bottom:var(--sp-5);
}
.card__name{
  font-family:var(--font-display); font-weight:400;
  font-size:clamp(26px, 2.6vw, 32px); line-height:1.1; margin:0;
  view-transition-name:none;
}
.is-chosen .card__name{ view-transition-name:project-name; }

/* Owner request: card text a little smaller everywhere except the
   project name itself (.card__name) and the metric numbers (.card__metrics
   dt, a stat callout, not body copy) — calc(var(--fs-tN) * 0.9), not a
   full token step down, since "just a little" is closer to a ~10% trim
   than the ~20%+ jump between adjacent --fs-t tokens. */
.card__claim{ margin:var(--sp-3) 0 0; font-size:calc(var(--fs-t2) * 0.9); color:var(--text-muted); max-width:40ch; }

/* THE TRIAD — three numbers, three different jobs: scale, quality,
   guarantee, each with its unit underneath. A bare number is noise. */
.card__metrics{
  display:grid; grid-template-columns:repeat(3,1fr); gap:var(--sp-4);
  margin:var(--sp-5) 0; padding:var(--sp-4) 0; border-block:1px solid var(--border);
}
.card__metrics dt{ font-size:clamp(22px, 2.2vw, 26px); font-weight:500; line-height:1.1; font-variant-numeric:tabular-nums; }
.card__metrics dd{ margin:6px 0 0; font-size:calc(var(--fs-t3) * 0.9); line-height:1.3; color:var(--text-muted); }

.card__ba{ margin:0; display:grid; gap:var(--sp-3); }
.card__ba > div{ display:grid; grid-template-columns:56px 1fr; gap:var(--sp-4); align-items:baseline; }
.card__ba dt{ font-size:var(--fs-t4); letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--text-muted); }
.card__ba dd{ margin:0; font-size:calc(var(--fs-t2) * 0.9); max-width:42ch; }

.card__foot{
  display:flex; justify-content:space-between; align-items:baseline; gap:var(--sp-4); flex-wrap:wrap;
  margin-top:var(--sp-6); padding-top:var(--sp-4); border-top:1px solid var(--border);
  font-size:var(--fs-t4); color:var(--text-muted);
}
.card__go{ color:var(--text); white-space:nowrap; }
.card__go::after{ content:' \2192'; display:inline-block; transition:transform var(--dur-3) var(--ease-move); }
.card:hover .card__go::after{ transform:translateX(3px); }

/* NO-JS GUARD — desktop thread only. Both the box-open and the
   child-rise are scrubbed frame by frame from scroll position (see
   work-thread.js), not CSS transitions, so scrolling back up unwinds
   them exactly. clip-path, not transform: getBoundingClientRect returns
   the TRANSFORMED box, so a hidden card carrying scale/translate would
   be measured at the wrong height and every thread anchor would attach
   off-target. Scoped to .work__list — the mobile track shares the same
   .card class but has no reveal script to ever remove this clip. */
.js .work__list .card{ clip-path:inset(0 0 100% 0); }
.js .work__list .card > *{ opacity:0; transform:translateY(7px); }

/* ============================================================
   LEAVING — the withdrawal, on card click
   The page declines everything you did not choose. Four unchosen
   cards settle to --fill-dim (the same tokens the scroll-fill runs
   between — navigating performs the site's own thesis), the chosen
   one stays lit and lifts, then the wash rises and the page navigates.

     0ms    threads retract toward the chosen card (JS)
     0ms    the other four desaturate toward --fill-dim
     300ms  they clip upward and are gone
     260ms  the chosen card lifts
     520ms  the wash rises
     820ms  navigate
   ============================================================ */
.work__wash{
  position:fixed; inset:0; z-index:55; background:var(--bg);
  opacity:0; pointer-events:none;
}
.work.is-leaving .work__wash{ opacity:1; transition:opacity var(--dur-2) linear 520ms; }

.work.is-leaving .card{ pointer-events:none; }

.work.is-leaving .card:not(.is-chosen){
  color:var(--fill-dim);
  border-color:var(--fill-dim);
  clip-path:inset(0 0 100% 0);
  transition:color 360ms var(--ease-move),
             border-color 360ms var(--ease-move),
             clip-path 420ms var(--ease-move) 300ms;
}
.work.is-leaving .card:not(.is-chosen) .card__name,
.work.is-leaving .card:not(.is-chosen) .card__meta,
.work.is-leaving .card:not(.is-chosen) .card__claim,
.work.is-leaving .card:not(.is-chosen) .card__metrics dd,
.work.is-leaving .card:not(.is-chosen) .card__ba dt,
.work.is-leaving .card:not(.is-chosen) .card__foot{
  color:var(--fill-dim); transition:color 360ms var(--ease-move);
}

.work.is-leaving .is-chosen{
  z-index:58; transform:translateY(-10px);
  transition:transform 700ms var(--ease-move) 260ms;
}

@media (prefers-reduced-motion:reduce){
  .work__wash{ display:none; }
  .work.is-leaving .card{ transition:none !important; }
  .js .work__list .card{ clip-path:none; }
  .js .work__list .card > *{ opacity:1; transform:none; }
}
