/* Mapa de conocimiento: estados/formas de nodos, aristas, regiones, tooltip.
   Las clases de estado son EXACTAMENTE los strings del backend:
   locked | available | read | passed | planned  ·  content | checkpoint | milestone */

#map-wrap {
  position: relative;
  flex: 1;
  overflow: hidden;
  background:
    radial-gradient(ellipse at 30% 20%, #101828 0%, transparent 55%),
    radial-gradient(ellipse at 75% 80%, #0d1522 0%, transparent 60%),
    var(--bg-deep);
}

#map {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: grab;
  /* Sin esto el navegador se queda con los gestos táctiles: el arrastre de un
     dedo compite con el desplazamiento de la página y el pellizco hace zoom a
     todo el documento en vez de al mapa. */
  touch-action: none;
}

#map:active {
  cursor: grabbing;
}

/* ---------- regiones conceptuales ---------- */

.region-glow {
  pointer-events: none;
}

.region-label {
  font-family: var(--font-display);
  font-size: 25px;
  font-variant: small-caps;
  letter-spacing: 0.3em;
  fill: color-mix(in srgb, var(--region-color, var(--ink)) 55%, var(--ink));
  opacity: 0.7;
}

.chalk-eq {
  font-family: var(--font-display);
  font-style: italic;
  fill: var(--ink);
  opacity: 0.04;
}


/* ---------- aristas ---------- */

.edge {
  fill: none;
  stroke: color-mix(in srgb, var(--region-color, var(--locked)) 45%, var(--locked));
  stroke-width: 1.4;
  opacity: 0.55;
}

.edge.lit {
  stroke: var(--spec-cyan);
  opacity: 1;
  stroke-width: 2.2;
}

/* tramo con ambos extremos completados: circuito recorrido, dorado-cian */
.edge.circuit {
  stroke: color-mix(in srgb, var(--spec-amber) 35%, var(--spec-cyan));
}

/* resplandor de las aristas iluminadas: trazo gemelo ancho, sin filtros */
.edge-under {
  fill: none;
  stroke: var(--spec-cyan);
  stroke-width: 8;
  opacity: 0.16;
  stroke-linecap: round;
}

.edge.into-recommended {
  stroke: var(--spec-amber);
  stroke-dasharray: 6 5;
  animation: edge-flow 1.6s linear infinite;
}

@keyframes edge-flow {
  to { stroke-dashoffset: -11; }
}

@media (prefers-reduced-motion: reduce) {
  .edge.into-recommended { animation: none; }
}

/* ---------- nodos ---------- */

g.node {
  cursor: pointer;
}

g.node .shape {
  fill: var(--bg-raised);
  stroke: var(--ink-dim);
  stroke-width: 1.6;
  transition: stroke var(--dur) var(--ease), fill var(--dur) var(--ease);
}

g.node .inner-ring {
  fill: none;
  stroke: var(--ink-dim);
  stroke-width: 1;
  opacity: 0.7;
}

g.node .halo {
  fill: none;
  stroke: transparent;
  stroke-width: 2;
  /* sin esto, scale() en SVG escala desde el origen del canvas */
  transform-box: fill-box;
  transform-origin: center;
}

.node-label {
  font-family: var(--font-ui);
  font-size: 12px;
  fill: color-mix(in srgb, var(--ink) 78%, var(--ink-dim));
  pointer-events: none;
  paint-order: stroke;
  stroke: var(--bg-deep);
  stroke-width: 2.5px;
}

.node-glyph {
  font-size: 13px;
  fill: var(--ink);
  pointer-events: none;
}

/* estados */

g.node.locked {
  cursor: not-allowed;
}

g.node.locked .shape {
  stroke: color-mix(in srgb, var(--region-color, var(--ink-dim)) 38%, var(--locked));
  fill: color-mix(in srgb, var(--region-color, var(--bg-raised)) 5%, var(--bg-deep));
  opacity: 0.85;
}

g.node.locked .node-label { opacity: 0.62; }

.lock-shackle {
  fill: none;
  stroke: var(--ink-dim);
  stroke-width: 1.3;
  opacity: 0.8;
}

.lock-body {
  fill: var(--ink-dim);
  opacity: 0.8;
}

g.node.planned { cursor: default; }

g.node.planned .shape {
  stroke: var(--locked);
  stroke-dasharray: 4 4;
  fill: transparent;
  opacity: 0.6;
}

g.node.available .shape {
  stroke: color-mix(in srgb, var(--region-color, var(--ink)) 55%, var(--ink));
  fill: color-mix(in srgb, var(--region-color, #141d2e) 10%, #141d2e);
}

g.node.available .node-label {
  fill: var(--ink);
  opacity: 0.85;
}

g.node.available .halo {
  stroke: color-mix(in srgb, var(--region-color, var(--spec-cyan)) 60%, transparent);
  animation: halo-pulse 2.6s var(--ease) infinite;
}

g.node.read .shape {
  stroke: var(--spec-cyan);
  stroke-dasharray: 5 3;
}

g.node.passed .shape {
  stroke: var(--spec-cyan);
  stroke-width: 2.2;
  fill: color-mix(in srgb, var(--spec-cyan) 30%, var(--bg-raised));
  filter: url(#glow-cyan);
}

g.node.passed .node-glyph { fill: var(--spec-cyan); }
g.node.passed .node-label { fill: var(--ink); }

/* tipos */

g.node.checkpoint .shape {
  stroke-width: 2.2;
  stroke: color-mix(in srgb, var(--region-color, var(--spec-violet)) 70%, var(--locked));
  fill: color-mix(in srgb, var(--region-color, var(--spec-violet)) 8%, var(--bg-raised));
}

g.node.checkpoint .node-glyph {
  fill: color-mix(in srgb, var(--region-color, var(--spec-violet)) 85%, var(--ink));
  font-size: 15px;
}

g.node.checkpoint.available .shape,
g.node.checkpoint.read .shape {
  stroke: var(--region-color, var(--spec-violet));
}

g.node.checkpoint.passed .shape {
  stroke: var(--spec-cyan);
  fill: color-mix(in srgb, var(--spec-cyan) 22%, var(--bg-raised));
  filter: url(#glow-cyan);
}

g.node.checkpoint.passed .node-glyph { fill: var(--spec-cyan); }

g.node.milestone .shape {
  stroke: var(--spec-amber);
  stroke-width: 2;
}

/* Los hitos mantienen presencia incluso bloqueados: son las puertas del mapa. */
g.node.milestone.locked .shape {
  stroke: color-mix(in srgb, var(--spec-amber) 45%, var(--locked));
  opacity: 0.9;
}

g.node.milestone.passed .shape {
  fill: color-mix(in srgb, var(--spec-amber) 22%, var(--bg-raised));
  filter: url(#glow-amber);
}

g.node.milestone .node-glyph { fill: var(--spec-amber); }

/* recomendado y seleccionado */

g.node.recommended .shape {
  stroke: var(--spec-amber);
  stroke-width: 3.2;
  filter: url(#glow-amber);
}

g.node.recommended .node-label {
  fill: var(--spec-amber);
  opacity: 1;
}

g.node.recommended .halo {
  stroke: var(--spec-amber);
  animation: halo-pulse 1.8s var(--ease) infinite;
}

g.node.selected .shape {
  stroke-width: 3;
}

/* con selección activa, los nodos no vecinos se atenúan */
#map.has-selection g.node.dimmed {
  opacity: 0.35;
  transition: opacity var(--dur) var(--ease);
}

g.node.fallback .shape {
  stroke-dasharray: 2 3;
}

@keyframes halo-pulse {
  0%, 100% { opacity: 0.15; transform: scale(1); }
  50% { opacity: 0.8; transform: scale(1.12); }
}

@media (prefers-reduced-motion: reduce) {
  g.node .halo { animation: none !important; opacity: 0.5; }
}

/* ---------- controles y tooltip ---------- */

#map-controls {
  position: absolute;
  right: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: right var(--dur) var(--ease);
}

/* con el panel lateral abierto, los controles se corren para no quedar tapados */
#map-wrap:has(#side-panel.open) #map-controls {
  right: calc(min(380px, 92vw) + 14px);
}

#map-controls button {
  width: 38px;
  height: 38px;
  padding: 0;
  font-size: 1.1em;
  background: color-mix(in srgb, var(--bg-raised) 85%, transparent);
  backdrop-filter: blur(4px);
}

#map-hint {
  position: absolute;
  left: 16px;
  bottom: 12px;
  max-width: 46ch;
  opacity: 0.7;
}

#map-tooltip {
  position: absolute;
  transform: translate(-50%, -100%);
  background: var(--bg-overlay);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 9px 12px;
  max-width: 300px;
  pointer-events: none;
  z-index: 30;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
  font-size: 0.9em;
}

#map-tooltip .tooltip-meta {
  font-family: var(--font-mono);
  font-size: 0.8em;
  color: var(--ink-dim);
  margin-top: 2px;
}

#map-tooltip .tooltip-requires {
  color: var(--spec-amber);
  font-size: 0.86em;
  margin-top: 5px;
}

/* ---------- móvil: mapa táctil, con la lista como alternativa ----------

   El mapa es la identidad de la plataforma y la mayoría de los estudiantes
   entra desde el teléfono, así que en móvil se muestra igual que en escritorio.
   La lista vertical sigue disponible con el botón ☰ del encabezado, para quien
   prefiera leer en vertical; la elección se guarda en localStorage.           */

#mobile-tree {
  display: none;
}

body.vista-lista #mobile-tree {
  display: block;
  position: absolute;
  inset: 0;
  overflow-y: auto;
  padding: 14px 14px 40vh;
}

body.vista-lista #map,
body.vista-lista #map-controls,
body.vista-lista #map-hint,
body.vista-lista #map-tooltip,
body.vista-lista #minimap,
body.vista-lista #map-legend {
  display: none;
}

/* En pantallas táctiles el tooltip depende de un hover que no existe: la
   información ya la da el panel al tocar el nodo. */
@media (pointer: coarse) {
  #map-tooltip { display: none; }
}

@media (max-width: 720px) {
  /* Los controles de zoom se agrandan al mínimo táctil recomendado (44 px) y
     se separan del borde inferior, donde los navegadores móviles ponen su
     propia barra. */
  #map-controls {
    bottom: 84px;
    right: 10px;
    gap: 8px;
  }

  #map-controls button {
    width: 44px;
    height: 44px;
    font-size: 1.1rem;
  }

  /* En 390 px el texto suelto se encabalgaba con las etiquetas de los nodos y
     ninguno de los dos se leía. Se le da fondo propio y deja de competir. */
  #map-hint {
    left: 12px;
    right: 12px;
    bottom: 10px;
    max-width: none;
    font-size: 0.78rem;
    text-align: center;
    background: var(--bg-overlay);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 7px 10px;
  }
}

/* La pista es una ayuda de arranque: en cuanto el estudiante toca el mapa ya
   sabe que responde, y estorba. Se desvanece con la clase que pone main.js. */
#map-hint {
  transition: opacity 0.4s ease;
}

#map-hint.oculta {
  opacity: 0;
  pointer-events: none;
}

.ml-banner {
  width: 100%;
  text-align: left;
  margin-bottom: 14px;
  background: color-mix(in srgb, var(--spec-amber) 12%, var(--bg-raised));
  border-color: color-mix(in srgb, var(--spec-amber) 45%, var(--border));
}

.ml-unit {
  color: var(--ink-dim);
  font-size: 0.72em;
  letter-spacing: 0.14em;
  margin: 18px 0 6px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 4px;
}

.ml-node {
  display: flex;
  width: 100%;
  gap: 10px;
  align-items: center;
  text-align: left;
  padding: 10px 12px;
  margin: 4px 0;
  background: var(--bg-raised);
}

.ml-node .ml-title {
  flex: 1;
  min-width: 0;
}

.ml-node .ml-status {
  color: var(--ink-dim);
  font-size: 0.72em;
  flex: none;
}

.ml-node.locked {
  opacity: 0.55;
}

.ml-node.passed .ml-dot {
  color: var(--spec-cyan);
}

.ml-node.checkpoint .ml-dot {
  color: var(--spec-violet);
}

.ml-node.milestone .ml-dot {
  color: var(--spec-amber);
}

.ml-node.available {
  border-color: color-mix(in srgb, var(--spec-cyan) 40%, var(--border));
}

.ml-node.planned {
  border-style: dashed;
  opacity: 0.6;
}

/* ---------- minimapa ---------- */

#minimap {
  position: absolute;
  left: 16px;
  bottom: 44px;
  width: 230px;
  height: 62px;
  background: color-mix(in srgb, var(--bg-overlay) 88%, transparent);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  backdrop-filter: blur(4px);
}

/* en el minimapa los nodos se agrandan para ser visibles a esa escala */
#minimap g.node use,
.mini-map g.node use {
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(3.5);
}

#minimap .edge,
.mini-map .edge {
  stroke-width: 6;
}

#minimap .edge-under,
#minimap .node-label,
#minimap .region-label,
#minimap .region-glow,
#minimap .chalk-eq,
#minimap .lock,
#minimap .halo,
#minimap .node-glyph {
  display: none;
}

#minimap g.node .shape {
  filter: none !important;
}

.minimap-viewport {
  fill: rgba(72, 201, 229, 0.08);
  stroke: var(--spec-cyan);
  stroke-width: 14;
  pointer-events: none;
}

/* ---------- leyenda ---------- */

#map-legend {
  position: absolute;
  left: 16px;
  bottom: 112px;
  background: color-mix(in srgb, var(--bg-overlay) 88%, transparent);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 4px 12px;
  font-size: 0.82em;
  backdrop-filter: blur(4px);
  max-width: 240px;
}

#map-legend summary {
  cursor: pointer;
  color: var(--ink-dim);
  font-size: 0.85em;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 0;
}

#map-legend .legend-body {
  padding: 6px 0 8px;
  display: grid;
  gap: 5px;
  color: var(--ink-dim);
}

.lg-dot, .lg-hex, .lg-mile, .lg-planned {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  margin-right: 6px;
  font-size: 0.9em;
}

.lg-dot { border-radius: 50%; height: 14px; width: 14px; border: 1.5px solid var(--ink-dim); }
.lg-passed { border-color: var(--spec-cyan); background: color-mix(in srgb, var(--spec-cyan) 30%, transparent); color: var(--spec-cyan); font-size: 9px; }
.lg-available { border-color: var(--ink); }
.lg-current { border-color: var(--spec-amber); box-shadow: 0 0 6px var(--spec-amber); }
.lg-locked { border-color: var(--locked); opacity: 0.8; font-size: 8px; }
.lg-hex { color: var(--spec-violet); }
.lg-mile { color: var(--spec-amber); }
.lg-planned { color: var(--ink-dim); }

/* la pista de uso se corre a la derecha del minimapa */
#map-hint {
  left: 260px;
  max-width: 40ch;
}

@media (max-width: 900px) {
  #minimap, #map-legend { display: none; }
  #map-hint { left: 16px; }
}

.lg-note {
  border-top: 1px solid var(--border);
  padding-top: 6px;
  margin-top: 3px;
  font-size: 0.88em;
  opacity: 0.85;
}

/* nodos voluntarios: presentes pero atenuados, con borde punteado suave */
g.node.voluntary .shape {
  stroke-dasharray: 3 3;
  opacity: 0.72;
}
g.node.voluntary .node-label {
  font-style: italic;
  opacity: 0.7;
}

.ml-node.voluntary { opacity: 0.72; border-style: dashed; }
.ml-node.voluntary .ml-title { font-style: italic; }
