/* ==========================================================
   neur0doku-threadmap.css

   The paginated level-select "thread-map" — replaces a flat level
   list with a winding path of level nodes, grouped into "scenes" (one
   per grid-tier block from story.js's STORY_RANGES: Simple 1-15, Easy
   16-35, Medium 36-70, Hard 71-110, Expert 111-160), then an
   effectively-infinite cycling tail past that — see threadmap.js's
   own header. Levels are shown PAGINATED, exactly 5 at a time
   (LEVELS_PER_PAGE in threadmap.js).

   NAV BUTTONS FLOAT OVER THE STAGE'S TOP-RIGHT CORNER (2026-09,
   reverted back from a brief "own row" pass): giving the buttons
   their own row above the level area cost real vertical space that
   should go to showing levels — same "give levels the space back"
   logic as the original mobile-optimization pass, just applied here.
   Floating them only over a small top-right rectangle (rather than a
   full-height gutter down the right edge, which an even earlier pass
   tried) means nodes get to use almost the entire width — threadmap.js's
   computePageMetrics() only nudges a node's x position away from that
   one small corner when a node's y actually falls within it, instead
   of permanently reserving a whole side of the screen nothing else
   ever needs to avoid.

   SHARED BACKDROP: the scene background lives on ONE element
   (.nd-threadmap-backdrop), positioned behind the topbar, stage, AND
   group bar — see threadmap.js's updateBackdrop(). Everything else
   (topbar, stage, group bar) is transparent so it shows straight
   through; only individual controls (back button, scene-label pill,
   nav buttons, group chips) keep their own opaque backgrounds.

   PHONE PASS (2026-09-21):
   1. SHELL FIX. This page is exactly one screen tall (100dvh) and
      must fill <main class="container site-main"> edge to edge. It
      didn't: the website's styles.css still loads on the web build
      and its `main.container { padding-top: calc(var(--site-header-
      height) + 1rem) }` (specificity 0,1,1) out-ranks neur0doku-
      theme.css's plain `.site-main { padding: 0 }` (0,1,0). Result: a
      1rem strip of body background (the blue band) above the map,
      and 100dvh + 1rem of content, so the page scrolled by 1rem and
      the group bar sat partly below the fold. The `main.container.
      site-main` rule below is 0,2,1, so it wins no matter what
      loads first. It is scoped to this stylesheet (this page only).
      neur0doku-theme.css's `.site-main` has the same latent problem
      on every other page; changing that selector to `main.site-main`
      would fix them all (see the hand-off note).
   2. FLUID CONTROL SIZES. The section sets ONE base font-size that
      grows with the phone's width, and every control (back button,
      scene label, Up/Down buttons, group chips, level-number badges)
      is sized in EM against it. Change the clamp() line and the
      whole page scales together. threadmap.js reads this font-size
      (and the real nav-stack box) so node placement and the level-
      badge reserve follow the controls automatically.
        360px wide -> 16px   (identical to the old fixed rem sizes)
        393px wide -> 17.3px
        430px wide -> 18.9px
        cap 22px   -> reached at 500px wide (beyond phone range)
      GOTCHA: an em width resolves against the element's OWN
      font-size. .nd-icon-btn sets its own rem font-size, so the back
      button needs `font-size: inherit`; elements that set their own
      font-size (scene label, badges, chips' text) have their other
      em values pre-divided by that size.

   LOAD ORDER: neur0doku-theme.css, neur0doku-components.css (for
   .nd-icon-btn), then this file.
   ========================================================== */

/* ---------- 1. shell fix (see note above) ---------- */
main.container.site-main {
  padding: 0;
  margin: 0 auto;
  min-height: 0;
}

.neur0doku-threadmap {
  position: relative;
  max-width: 460px;
  margin: 0 auto;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* ---------- 2. the one fluid size knob ---------- */
  font-size: clamp(16px, 6vw, 25px);
}

/* ---------- shared backdrop ---------- */
.nd-threadmap-backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-repeat: repeat-y;
  background-position: top center;
  background-size: 100% auto;
  background-color: var(--nd-threadmap-fallback-bg, var(--nd-bg));
  pointer-events: none;
}

/* ---------- topbar ---------- */
.nd-threadmap-topbar {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  padding: 0.6em 0.9em 0.3em;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* Back button: the shared .nd-icon-btn--sm (2.5 x 2 at 16px), scaled.
   Two extra classes so it beats components.css regardless of order. */
.neur0doku-threadmap .nd-threadmap-topbar .nd-icon-btn.nd-icon-btn--sm {
  font-size: inherit;
  width: 2.5em;
  height: 2em;
  border-radius: 0.8em;
}

/* font-size 0.85em => 0.4rem = 0.47em, 0.9rem = 1.06em of its own size */
.nd-threadmap-scene-label {
  padding: 0.47em 1.06em;
  border-radius: 999px;
  background: var(--nd-surface);
  border: 1px solid var(--nd-border);
  box-shadow: 0 3px 8px var(--nd-shadow);
  font-family: ui-rounded, 'SF Pro Rounded', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-weight: 800;
  font-size: 0.85em;
  color: var(--nd-accent);
}

/* ---------- the stage ----------
   Everything between the topbar and the group bar. Nav buttons live
   INSIDE this as a floating overlay (see .nd-threadmap-navstack
   below) rather than in their own row, specifically to give the level
   area back that row's vertical space. Transparent — the backdrop
   shows through it. */
.nd-threadmap-stage {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.nd-threadmap-viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.nd-threadmap-loading {
  text-align: center;
  padding-top: 4rem;
  color: var(--nd-text-soft);
}

/* ---------- Up/Down nav — floats over the stage's top-right corner ----------
   threadmap.js's computePageMetrics() MEASURES this element's real
   box (offsetTop/offsetHeight/offsetWidth + right inset) and reserves
   a matching corner in its node-position math, so a node is never
   placed underneath these at any screen size — see that function's
   own comment. Kept OUT of #nd-threadmap-viewport on purpose: that
   element gets its innerHTML replaced on every page turn (see
   showPage()), which would wipe these buttons out if they lived
   inside it — same "DOM surgery" lesson already learned once on this
   page's earlier scroll-based build. */
.nd-threadmap-navstack {
  position: absolute;
  top: 0.5em;
  right: 0.5em;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4em;
}

.nd-threadmap-nav-btn {
  width: 2.4em;
  height: 2.4em;
  border-radius: 0.8em;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--nd-surface);
  border: 1px solid var(--nd-border);
  box-shadow: 0 3px 8px var(--nd-shadow);
  color: var(--nd-accent);
  font-size: 1em;
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;
}
.nd-threadmap-nav-btn:hover:not(:disabled) { transform: translateY(-1px); }
.nd-threadmap-nav-btn:focus-visible { outline: 2px solid var(--nd-focus); outline-offset: 2px; }
.nd-threadmap-nav-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  box-shadow: none;
}

/* ---------- one page (5 level slots) ---------- */
.nd-threadmap-page {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* ---------- the squiggly thread path ---------- */
.nd-threadmap-path {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}
.nd-threadmap-path path {
  fill: none;
  stroke: var(--nd-surface);
  stroke-width: 5;
  stroke-linecap: round;
  opacity: 0.85;
}

/* ---------- decorative props (rocks, bushes, ...) ---------- */
.nd-threadmap-prop {
  position: absolute;
  z-index: 1;
  pointer-events: none;
  object-fit: contain;
}

/* ---------- level nodes ---------- */
.nd-threadmap-node {
  position: absolute;
  z-index: 2;
  width: var(--nd-threadmap-node-size, 4.5rem);
  height: var(--nd-threadmap-node-size, 4.5rem);
  transform: translate(-50%, -50%);
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.nd-threadmap-node img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}
.nd-threadmap-node.is-locked {
  cursor: not-allowed;
}
.nd-threadmap-node:not(.is-locked):hover {
  transform: translate(-50%, -50%) translateY(-2px);
}
.nd-threadmap-node:focus-visible {
  outline: 2px solid var(--nd-focus);
  outline-offset: 3px;
  border-radius: 50%;
}
.nd-threadmap-node.is-current {
  animation: nd-threadmap-current-pulse 1.8s ease-in-out infinite;
}
@keyframes nd-threadmap-current-pulse {
  0%, 100% { filter: drop-shadow(0 0 0 rgba(0,0,0,0)); }
  50% { filter: drop-shadow(0 0 8px var(--nd-accent)); }
}

/* Level-number badge. Own font-size 0.78em, so the old rem values are
   divided by 0.78: bottom -1.3rem = -1.67em, 0.15rem/0.5rem = 0.19em/
   0.64em, shadow stays px. The node button's font-size is inherited
   from the section, so this scales with the page. */
.nd-threadmap-node-num {
  position: absolute;
  z-index: 3;
  bottom: -1.67em;
  left: 50%;
  transform: translateX(-50%);
  font-family: ui-rounded, 'SF Pro Rounded', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-weight: 800;
  font-size: 0.78em;
  color: var(--nd-text);
  background: var(--nd-surface);
  border: 1px solid var(--nd-border);
  border-radius: 999px;
  padding: 0.19em 0.64em;
  white-space: nowrap;
  box-shadow: 0 2px 5px var(--nd-shadow);
}

@media (prefers-reduced-motion: reduce) {
  .nd-threadmap-node.is-current { animation: none; }
  .nd-threadmap-node:not(.is-locked):hover { transform: translate(-50%, -50%); }
  .nd-threadmap-nav-btn:hover:not(:disabled) { transform: none; }
}

/* ---------- bottom group bar ---------- */
.nd-threadmap-groupbar {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  display: flex;
  gap: 0.5em;
  overflow-x: auto;
  padding: 0.5em 0.9em calc(0.5em + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--nd-border);
  background: transparent;
  scrollbar-width: none;
}
.nd-threadmap-groupbar::-webkit-scrollbar { display: none; }

.nd-threadmap-group-chip {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1em;
  padding: 0.4em 0.8em;
  border-radius: 0.8em;
  background: var(--nd-surface);
  border: 1px solid var(--nd-border);
  box-shadow: 0 3px 8px var(--nd-shadow);
  color: var(--nd-text);
  font: inherit;
  cursor: pointer;
}
.nd-threadmap-group-chip.is-active {
  border-color: var(--nd-accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--nd-accent) 35%, transparent 65%);
}
.nd-threadmap-group-chip:focus-visible { outline: 2px solid var(--nd-focus); outline-offset: 2px; }
.nd-threadmap-group-chip.is-locked,
.nd-threadmap-group-chip:disabled {
  cursor: not-allowed;
}

.nd-threadmap-group-range {
  font-family: ui-rounded, 'SF Pro Rounded', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-weight: 800;
  font-size: 0.8em;
}
.nd-threadmap-group-stars {
  font-size: 0.65em;
  color: var(--nd-text-soft);
  white-space: nowrap;
}
/* Emoji placeholder (see threadmap.js's buildGroupbar) — swap to a
   real lock icon/SVG later by just styling this class, no JS change
   needed. Slightly dimmer than the normal star readout so a locked
   chip doesn't compete visually with ones showing real progress. */
.nd-threadmap-group-stars.is-locked {
  opacity: 0.7;
}