/* ============================================================
   ck-anim-fragments — "Nothing connected it" -> "Now you see it"
   Fully scoped under .szaf-root. Obsidian & Ember tokens.
   Signal green (--szaf-signal) is used ONLY on the resolved node —
   the brand's reserved "genuine match" semantic, never decorative.

   Renders fully resolved (threads drawn, node visible, fragments
   faded) by DEFAULT. The JS-only ".szaf--anim" modifier class hides
   /stages everything for the scroll-triggered reveal; without JS,
   or with prefers-reduced-motion, that class is never added and the
   resolved state above simply stands (see anim-base.css for the
   shared reduced-motion kill-switch on [data-sz-anim]).
   No <a> elements are rendered by this module, so no theme link
   bleed-guard is required.
   LIGHT CARD (2026-07-22): this card sits on a warm cream ground, not the
   obsidian it launched with. Notes that matter if you touch the colours:
     - The ground is cream (#F4F0E8), NOT #FFFFFF — a white card on a white
       article page stops reading as a figure at all.
     - Prominence inverts on a light ground: DARKNESS now carries emphasis,
       so former-#fff labels are ink (#17140F) and former-#B0A49E secondary
       text is #4A443B. --ink-dim was already legible on light and is unchanged.
     - Signal green is split in two: --*-signal (#157A44) for TEXT, and
       --*-signal-ring (#1C9350) for rings/strokes. Neon #00FC88 fails
       contrast badly as text on light.
     - The old wide neon glow is gone: a 20-30px coloured halo is a dark-UI
       device and reads as a smudge on light. Emphasis is now a crisp ring
       plus a tight offset shadow (lift, not radiance).
     - "Unresolved" chips use a translucent INK fill on light, never a
       translucent white. Soften the CONTAINER, keep the label text legible.

   ============================================================ */

.szaf-root {
	--szaf-ground:  #F4F0E8;
	--szaf-cream:   #F4F0E8;
	--szaf-paper:   #FFFFFF;
	--szaf-ink:     #17140F;
	--szaf-ink-2:   #4A443B;
	--szaf-ink-dim: #6B6257;
	--szaf-flame:       #E5471D;
	--szaf-flame-bright:#FD4D1F;
	--szaf-ember:       #F7A823;
	--szaf-flame-text:  #C8400F;
	--szaf-line:    #E2DACB;
	--szaf-signal:      #157A44;   /* text on cream: ~5.6:1 */
	--szaf-signal-ring: #1C9350;   /* ring / stroke only */

	position: relative;
	box-sizing: border-box;
	max-width: 900px;
	margin: 0 auto;
	padding: clamp(20px, 3vw, 32px) clamp(16px, 3vw, 32px) clamp(28px, 4vw, 40px);
	background: var(--szaf-ground);
	border: 1px solid #E2DACB;
	border-radius: 14px;
	box-shadow: 0 1px 2px rgba(23,20,15,.05), 0 14px 28px -14px rgba(23,20,15,.12);
	font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
	overflow: hidden;
}
.szaf-root *, .szaf-root *::before, .szaf-root *::after { box-sizing: border-box; }

.szaf-caption {
	text-align: center;
	font-family: 'Roboto Mono', monospace;
	text-transform: uppercase;
	letter-spacing: .12em;
	font-size: 11px;
	color: var(--szaf-ink-dim);
	margin-bottom: 18px;
}

.szaf-stage {
	position: relative;
	width: 100%;
	aspect-ratio: 5 / 3;
}

.szaf-threads {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	z-index: 0;
}
.szaf-thread {
	fill: none;
	stroke: var(--szaf-ember);
	stroke-width: 2;
	stroke-dasharray: 800;
	stroke-dashoffset: 0; /* drawn by default (resolved end-state) */
	opacity: .45;
}

.szaf-frag {
	position: absolute;
	z-index: 1;
	width: 108px;
	height: 108px;
	margin-left: -54px;
	margin-top: -54px;
	border: 1.5px dashed rgba(226, 218, 203, .35);
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	padding: 10px;
	opacity: .55; /* faintly visible behind the resolved node, by default */
}
.szaf-frag-txt {
	font-size: 11px;
	line-height: 1.3;
	color: #4A443B;
	font-weight: 600;
}

.szaf-node {
	position: absolute;
	z-index: 2;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	width: 168px;
	height: 168px;
	border-radius: 50%;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 4px;
	text-align: center;
	padding: 12px;
	/* Opaque centre so the converging threads don't show through the resolved
	   node (2026-08-06, matching the ck-anim-ubo fix). #DEE4D8 = the old
	   rgba(21,122,68,.10) green tint baked opaque over the cream ground. */
	background: radial-gradient(circle, #DEE4D8, var(--szaf-ground) 72%);
	border: 1.5px solid var(--szaf-signal-ring);
	box-shadow: 0 0 0 1px rgba(28,147,80,.16), 0 8px 20px -10px rgba(21,122,68,.28), 0 2px 6px rgba(23,20,15,.06);
}
.szaf-node-ring { display: none; } /* only used during the animated one-shot pulse */
.szaf-node-eyebrow {
	font-family: 'Roboto Mono', monospace;
	text-transform: uppercase;
	letter-spacing: .12em;
	font-size: 10px;
	color: var(--szaf-signal);
}
.szaf-node-label {
	font-size: 14px;
	font-weight: 700;
	color: #17140F;
	line-height: 1.25;
}

.szaf-resolved-caption {
	text-align: center;
	margin-top: 18px;
	font-size: 15px;
	font-weight: 700;
	color: #17140F;
}

@media (max-width: 640px) {
	.szaf-frag { width: 84px; height: 84px; margin-left: -42px; margin-top: -42px; }
	.szaf-frag-txt { font-size: 9.5px; }
	.szaf-node { width: 132px; height: 132px; }
	.szaf-node-label { font-size: 12.5px; }
}

/* ============================================================
   ANIMATED MODE — JS adds .szaf--anim, then stages the reveal.
   Without JS (or with reduced motion), the resolved end-state
   above is what's shown, unconditionally.
   ============================================================ */
.szaf--anim .szaf-frag {
	opacity: 0;
	transform: translateY(10px);
	transition: opacity .55s ease, transform .55s ease;
}
.szaf--anim .szaf-frag.is-in { opacity: .85; transform: none; }
.szaf--anim .szaf-frag.is-settled { opacity: .35; transition: opacity .8s ease; }

.szaf--anim .szaf-thread {
	stroke-dashoffset: 800;
	transition: stroke-dashoffset .9s ease;
}
.szaf--anim .szaf-thread.is-drawn { stroke-dashoffset: 0; }

.szaf--anim .szaf-node {
	opacity: 0;
	transform: translate(-50%, -50%) scale(.88);
	transition: opacity .6s ease, transform .6s ease;
}
.szaf--anim .szaf-node.is-in { opacity: 1; transform: translate(-50%, -50%) scale(1); }

.szaf--anim .szaf-node.is-pulse .szaf-node-ring {
	display: block;
	position: absolute;
	inset: -1.5px;
	border-radius: 50%;
	border: 2px solid var(--szaf-signal-ring);
	animation: szaf-pulse .8s ease-out 1;
}
@keyframes szaf-pulse {
	0%   { opacity: .9; transform: scale(1); }
	100% { opacity: 0;  transform: scale(1.45); }
}

.szaf--anim .szaf-resolved-caption {
	opacity: 0;
	transition: opacity .5s ease;
}
.szaf--anim .szaf-resolved-caption.is-in { opacity: 1; }
