/* ==========================================================================
   SomsoriumThread v0.1 — the line that runs through a page.

   One grammar everywhere: line, node, branch.

     past    ────────────   a path already walked
     now     ──────●──────  where we are
     future  - - - - - -    intended, not yet real

   Restrained on purpose. It uses the existing mint accent — no new brand
   colour, no glow — and it never competes with the content it runs beside.
   ========================================================================== */

/* --- stacking (§11): background 0 / thread 1 / content 2 ------------------- */
/* The article is the positioning context. An absolutely positioned child
   cannot change its parent's height, so measuring can never feed back into
   the height just measured. */
.has-thread { position: relative; }

.thread {
  position: absolute;
  inset: 0;
  z-index: 1;                 /* above the sections' own backgrounds … */
  pointer-events: none;       /* … and never in the way of a click */
}
.thread.is-hidden { display: none; }
.thread__svg { display: block; overflow: visible; }

/* Every section's inner column paints above the thread, so the line can cross
   a white card section and still never sit on top of a word. Applied only when
   a thread is actually present, so nothing else in the theme is disturbed. */
.thread-active .has-thread .wrap,
.thread-active .has-thread .produkt-single__body { position: relative; z-index: 2; }

/* The marquee is a full-bleed band with its own track instead of a .wrap, so the
   rule above misses it and the line would run across its scrolling text. It gets
   the content level too: the thread passes behind the band and comes out the
   other side, which is quieter than a line threading through moving type. */
.thread-active .has-thread .marquee { position: relative; z-index: 2; }

/* The story timeline draws its own connector in CSS. Where the thread runs down
   that column it becomes the connector, so the static one steps aside rather
   than doubling it (§25: integrate, do not add a second line). The timeline
   keeps its dots — they mark all thirteen entries, while the thread carries the
   line and the eras.

   Only where the thread actually runs through it. In compact mode the thread
   collapses to the gutter instead, so the timeline has to keep its own line —
   otherwise a phone shows a column of dots with nothing joining them.

   The journey rail used to be listed here too, from when the front page drew one
   and the thread ran along it. It does not any more — the algae journey is a
   sequence of stations with the thread beside them — and the rails that remain,
   on /ausblick/ and /forschung-bildung/, are never travelled by the thread, so
   hiding their connector only left them as dots with nothing joining them. */
.thread-active:not(.thread-compact) .timeline li:not(:last-child)::after { opacity: 0; }

/* --- the line ------------------------------------------------------------- */
.thread__segment {
  fill: none;
  stroke: var(--mint-dark);    /* light grounds: the accessible teal */
  stroke-width: 1.75;          /* inside the 1.5–2px band */
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .46;                /* already-read line is calm … */
  transition: opacity .45s ease;
}
/* … the stretch being read is a shade stronger, then settles back. */
.thread__segment.is-current { opacity: .74; }
.thread__segment.is-drawn { opacity: .46; }

/* Dark grounds need the light accent or the line vanishes into the navy.
   Same accent system, one step brighter — not a new colour. */
.thread__segment.is-dark { stroke: var(--mint); opacity: .5; }
.thread__segment.is-dark.is-current { opacity: .78; }

/* Intended but not yet real. The dash says it without a word. */
.thread__segment--future { stroke-dasharray: 5 8 !important; opacity: .3; }
.thread__segment--future.is-current,
.thread__segment--future.is-drawn { opacity: .34; }

/* The replication fan: several strands, each thinner than the main line, but
   not so faint that they blur into one shape — at .26 the three strands read as
   a single smudge rather than as several runs of the same experiment. */
.thread__segment--branch { stroke-width: 1.3; opacity: .42; }
.thread__segment--branch.is-current { opacity: .6; }
.thread__segment--branch.is-drawn { opacity: .42; }

/* The discussion aside is the same grammar said more quietly: short stubs that
   leave the line and stop. Thinner and fainter than the replication fan on
   purpose — a remark beside the path, not a second path. */
.thread__segment--aside { stroke-width: 1.1; opacity: .34; }
.thread__segment--aside.is-current { opacity: .5; }
.thread__segment--aside.is-drawn { opacity: .34; }

/* --- strands (v1.1) ------------------------------------------------------- */
/* A fan through named waypoints numbers its strands, so a scene can say which
   one the page is talking about. Used once: the front page's analysis view
   names the middle run in its annotation, and a reader has to be able to find
   it among three otherwise identical lines. Everywhere else the strands stay
   equal, because "more than one" is the whole claim.

   These run inside illustrations rather than down the page margin, so they are
   a shade stronger than a branch out in the gutter — at .42 they read as a
   smudge behind the drawing instead of part of it. */
[class*="thread__segment--strand-"] { stroke-width: 1.45; opacity: .5; }
[class*="thread__segment--strand-"].is-current { opacity: .68; }
[class*="thread__segment--strand-"].is-drawn { opacity: .5; }
/* The one run the page names in words. Scoped to the fan that carries the
   annotation, so the middle strand of every other fan stays equal to its
   neighbours — in replication and in discussion, singling one out would be a
   claim the copy does not make. Not a different colour and not a highlight:
   the same line, held a little stronger. */
.thread__segment--fan-compare.thread__segment--strand-2,
.thread__segment--fan-compare.thread__segment--strand-2.is-drawn { stroke-width: 1.85; opacity: .74; }

/* --- nodes ---------------------------------------------------------------- */
/* Small and quiet. A node marks a place; it is not a button and not a light. */
.thread__node {
  fill: var(--bg);
  stroke: var(--mint-dark);
  stroke-width: 1.5;
  opacity: .8;
  transition: opacity .35s ease;
}
/* A node waits for the line to reach it. Without this every dot was present
   from the first frame while the line was still travelling, so the page opened
   on a column of markers with nothing joining them.
   JS adds .is-dim to nodes whose segment has not started — the dim state is the
   exception, so if the loop never runs the nodes are simply all visible. */
.thread-active .thread__node.is-dim { opacity: 0; }
.thread__node.is-dark { fill: var(--navy); stroke: var(--mint); }
.thread__node--now { fill: var(--mint-dark); stroke: var(--mint-dark); opacity: 1; }
.thread__node--now.is-dark { fill: var(--mint); stroke: var(--mint); }
.thread__node--future { opacity: .45; }
/* The hand-off point at the top of the front page. Same shape as every other
   node (§14) — only its meaning differs: this is where the line starts. */
.thread__node--origin { opacity: .9; }

/* --- compact (§15) -------------------------------------------------------- */
/* On a phone the scene collapses to one vertical line inside the existing page
   padding. It never narrows the text column. */
.thread.is-compact .thread__segment { stroke-width: 1.5; }
.thread.is-compact .thread__node { stroke-width: 1.25; }

/* --- reduced motion (§14) ------------------------------------------------- */
/* The line is simply there, at rest, fully drawn. No scroll drawing, no fades.
   JS forces progress to 1; this removes the transitions with it. */
@media (prefers-reduced-motion: reduce) {
  .thread__segment { transition: none; stroke-dashoffset: 0 !important; }
  /* Every node simply present, no waiting and no fade. */
  .thread__node { transition: none; }
  .thread-active .thread__node.is-dim { opacity: .8; }
}

/* --- print ---------------------------------------------------------------- */
@media print { .thread { display: none; } }
