/* =========================================================
   SPIRAL STAIRCASE — the main visual of the knowledge map
   (index.html, exam.html, konsept.html — all the same component)
   ========================================================= */
.sp{ --sp-w:13px; --sp-hit:26px; --sp-bg:var(--panel); position:relative; }
.sp-compact{ --sp-w:10px; --sp-hit:22px; }

.sp-stage{ position:relative; width:100%; max-width:460px; margin-inline:auto; }
.sp-svg{ display:block; width:100%; height:auto; overflow:visible; }

/* ---- guide spiral: draws itself on reveal ---- */
.sp-guide{
  fill:none; stroke:var(--line); stroke-width:1.1; opacity:.6;
  stroke-dasharray:100; stroke-dashoffset:100;
  transition:stroke-dashoffset 1.5s linear;    /* duration is set in JS from the number of steps */
}
.sp-live .sp-guide{ stroke-dashoffset:0; }

/* ---- step edges ---- */
.sp-tread{
  stroke:var(--line); stroke-width:1.4; stroke-linecap:round;
  opacity:0; transition:opacity .4s ease;
}
.sp-live .sp-tread{ opacity:.75; }
.sp-reveal .sp-tread{ transition-delay:var(--d,0s); }

/* ---- the track of each topic (the empty part) ---- */
.sp-track{
  fill:none; stroke:var(--line); stroke-width:var(--sp-w); stroke-linecap:round;
  opacity:0; transition:opacity .45s ease, stroke-width .25s ease;
}
.sp-live .sp-track{ opacity:1; }
.sp-reveal .sp-track{ transition-delay:var(--d,0s); }
/* An unlocked topic's empty part is a faded tone of its own colour; a locked one
   stays grey. The tones are literal values in styles.css (--track-*): color-mix()
   does not exist before Chrome 111 and the spiral came out all grey on Android 5/6. */
.sp-track.weak  { stroke:var(--track-weak); }
.sp-track.mid   { stroke:var(--track-mid); }
.sp-track.strong{ stroke:var(--track-strong); }
.sp-track.locked{ stroke:var(--line); stroke-width:3.5; }

/* ---- the mastered part: length = score ---- */
.sp-step{
  fill:none; stroke:var(--line); stroke-width:var(--sp-w); stroke-linecap:round;
  stroke-dasharray:100; stroke-dashoffset:100;
  transition:stroke-dashoffset .55s cubic-bezier(.2,.85,.25,1),
             stroke-width .25s ease, opacity .25s ease, stroke .4s ease;
}
/* on reveal each step "pops" in its turn */
.sp-reveal .sp-step{ transition-delay:var(--d,0s); }
.sp-reveal.sp-live .sp-step:not(.locked){
  animation:spPop .5s cubic-bezier(.3,1.45,.45,1) var(--d,0s) backwards;
}
@keyframes spPop{
  0%  { stroke-width:0 }
  55% { stroke-width:calc(var(--sp-w) + 6px) }
  100%{ stroke-width:var(--sp-w) }
}
.sp-step.weak  { stroke:var(--m-weak); }
.sp-step.mid   { stroke:var(--m-mid); }
.sp-step.strong{ stroke:var(--m-strong); }
.sp-step.locked{ stroke:var(--line); stroke-width:3; }

/* ---- the climbing light ----
   One dash travelling the whole spiral, over the steps. It never restarts and
   nothing interrupts it, so the map always looks alive — the old per-step wave
   jumped every time a score changed and was dropped.
   The dash is drawn on the SAME curve as the steps, so it reads as a light
   running up the staircase rather than a separate ring. */
.sp-flow{
  fill:none; stroke:var(--brand); stroke-width:3.4; stroke-linecap:round;
  stroke-dasharray:5 95; stroke-dashoffset:100;
  opacity:0; pointer-events:none; transition:opacity .6s ease;
}
.sp-live .sp-flow{ opacity:.9; animation:spFlow var(--flow,6s) linear infinite; }
@keyframes spFlow{ from{ stroke-dashoffset:100 } to{ stroke-dashoffset:0 } }
/* while a topic is focused the light steps back — the focus is the message then */
.sp-focus .sp-flow{ opacity:.25; }

/* ---- short bump when the score changes ----
   A transition, not a keyframe: it cannot collide with anything else that is
   running and it always ends on the step's own width. */
.sp .sp-step.bump{ stroke-width:calc(var(--sp-w) + 7px); }
.sp-lg.bump{ animation:spLgBump 1.1s ease; }
@keyframes spLgBump{
  0%,100%{ background:transparent }
  25%    { background:var(--tint) }
}

/* ---- invisible touch band ---- */
.sp-hit{ fill:none; stroke:transparent; stroke-width:var(--sp-hit);
  stroke-linecap:round; pointer-events:stroke; cursor:pointer; }

/* ---- focus: related topics stay, the rest fade ---- */
.sp-focus .sp-step, .sp-focus .sp-track{ opacity:.2; }
.sp-focus .sp-step.kin, .sp-focus .sp-track.kin{ opacity:1; }
.sp-focus .sp-step.self{ stroke-width:calc(var(--sp-w) + 5px); }
.sp-focus .sp-tread{ opacity:.25; }

/* ---- marker of the next topic ---- */
.sp-next{ opacity:0; transition:opacity .5s ease 1.1s; pointer-events:none; }
.sp-next-dot{ fill:var(--ai); animation:spNextBeat 2.6s ease-in-out infinite; }
.sp-next-ring{ fill:none; stroke:var(--ai); stroke-width:1.8; transform-box:fill-box;
  transform-origin:center; animation:spPing 2.6s ease-out infinite; }
@keyframes spPing{
  0%   { transform:scale(.4); opacity:.95 }
  70%  { transform:scale(1.6); opacity:0 }
  100% { transform:scale(1.6); opacity:0 }
}
@keyframes spNextBeat{
  0%,100%{ transform:scale(1) }
  50%    { transform:scale(1.22) }
}

/* ---- centre ---- */
.sp-core{ fill:var(--sp-bg); stroke:var(--line); stroke-width:1.4; }
.sp-core-arc{
  fill:none; stroke:var(--m-strong); stroke-width:2.6; stroke-linecap:round;
  stroke-dasharray:100; stroke-dashoffset:100;
  transform:rotate(-90deg); transform-origin:230px 230px; transform-box:view-box;
  transition:stroke-dashoffset 1.2s cubic-bezier(.2,.8,.25,1) .5s;
  opacity:.85;
}
.sp-hub{
  position:absolute; inset:0; display:grid; place-content:center; justify-items:center;
  gap:3px; text-align:center; pointer-events:none; padding:0 26%;
}
/* soft fade behind the text — a long name stays readable over the arcs */
.sp-hub::before{
  content:""; position:absolute; inset:22%; border-radius:50%;
  background:radial-gradient(closest-side, var(--sp-bg) 62%, transparent);
}
.sp-hub > *{ position:relative; }
.sp-hub-v{
  font-family:var(--font-display); font-weight:800; letter-spacing:-.02em;
  font-size:clamp(20px,4.6vw,30px); line-height:1; color:var(--text);
  font-variant-numeric:tabular-nums; transition:color .3s;
}
.sp-compact .sp-hub-v{ font-size:clamp(18px,4vw,24px); }
.sp-hub-v.weak{ color:var(--m-weak); } .sp-hub-v.mid{ color:var(--m-mid); }
.sp-hub-v.strong{ color:var(--m-strong); } .sp-hub-v.locked{ color:var(--text-3); }
.sp-hub-k{
  font-size:10.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--text-3);
  line-height:1.35; max-width:15ch;
}
.sp-hub.on .sp-hub-k{ text-transform:none; letter-spacing:0; font-size:11.5px; color:var(--text-2); }

/* ---- calm rotation (decorative placements only) ---- */
.sp-rot{ transform-origin:230px 230px; transform-box:view-box; }
.sp-spin.sp-live .sp-rot{ animation:spSpin 150s linear infinite; }
.sp-spin:hover .sp-rot{ animation-play-state:paused; }
@keyframes spSpin{ to{ transform:rotate(360deg) } }

/* ---- legend ---- */
.sp-legend{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fit,minmax(320px,1fr)); gap:1px 34px;
}
.sp-lg{
  display:flex; align-items:center; gap:10px; padding:6px 2px; font-size:13px;
  border-bottom:1px solid var(--line-soft); transition:opacity .2s, color .2s;
}
.sp-lg i{ width:9px; height:9px; border-radius:50%; flex:none; background:var(--line);
  transition:transform .2s, background .3s; }
.sp-lg.weak i{ background:var(--m-weak); } .sp-lg.mid i{ background:var(--m-mid); }
.sp-lg.strong i{ background:var(--m-strong); }
.sp-lg.locked i{ background:transparent; border:1.5px dotted var(--text-3); }
.sp-lg span{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sp-lg b{ font-size:11px; color:var(--text-3); font-weight:500; font-variant-numeric:tabular-nums; }
.sp-lg.locked span{ color:var(--text-3); }
.sp-focus .sp-lg{ opacity:.32; }
.sp-focus .sp-lg.kin{ opacity:1; }
.sp-focus .sp-lg.self{ opacity:1; }
.sp-focus .sp-lg.self span{ color:var(--ai); font-weight:500; }
.sp-focus .sp-lg.self i{ transform:scale(1.5); }

/* ---- two-column layout: spiral + legend ---- */
.sp-split{ display:grid; grid-template-columns:minmax(0,520px) minmax(0,1fr);
  gap:52px; align-items:center; }
.sp-split > .sp .sp-stage{ max-width:520px; }
@media (max-width:900px){
  .sp-split{ grid-template-columns:1fr; gap:26px; }
  .sp-legend{ grid-template-columns:1fr; }
}

@media (prefers-reduced-motion: reduce){
  .sp-live .sp-flow{ animation:none; opacity:0; }
  .sp-next-dot{ animation:none; }
  .sp-lg.bump{ animation:none; }
  .sp-reveal.sp-live .sp-step:not(.locked){ animation:none; }
  .sp-reveal .sp-step, .sp-reveal .sp-track, .sp-reveal .sp-tread{ transition-delay:0s !important; }
  .sp-next-ring{ animation:none; opacity:.5; }
  .sp-spin.sp-live .sp-rot{ animation:none; }
  .sp-guide, .sp-step, .sp-track, .sp-tread, .sp-core-arc{ transition-duration:.01ms !important; }
}

/* =========================================================
   Where it is placed
   ========================================================= */
/* hero panel — decorative, rotates slowly */
.kmap-spiral{ padding:6px 0 2px; --sp-bg:var(--canvas); }
.kmap-spiral .sp-stage{ max-width:min(520px, 100%); }

/* side panel of the mock-exam screen — comes alive as questions are answered */
.sp-side{ flex:none; padding:14px 20px 12px; border-bottom:1px solid var(--line-soft); }
.sp-side .sp-stage{ max-width:250px; }
/* On a short screen the spiral ate the space — but only where the layout is
   locked, i.e. on a wide screen. On a phone the page scrolls and it should show. */
@media (max-height:720px) and (min-width:1081px){ .sp-side{ display:none; } }

/* the note in the graph section */
.sp-hint{ margin:18px 0 0; font-size:13.5px; color:var(--text-3); line-height:1.6; }
.sp-hint-next{ display:inline-block; width:9px; height:9px; border-radius:50%;
  background:var(--ai); vertical-align:middle; margin:0 2px; }
