/* ============================================================
   Concept Clusters — styles
   Visual direction: a lab-notebook page. Graph-paper board,
   ink-blue text, classroom-marker cluster hues, purple bridges.
   ============================================================ */

:root {
  --paper: #fafaf7;
  --panel: #ffffff;
  --ink: #1e2433;
  --ink-soft: #5a6275;
  --rule: #e1e4ec;
  --grid: #eceef4;
  --select: #1e2433;

  --green: #23744a;      --green-bg: #e2f1e8;  --green-line: #9ecbb2;
  --blue: #2456c4;       --blue-bg: #e6edfb;   --blue-line: #a9bfec;
  --amber: #9a5b12;      --amber-bg: #f8ecd9;  --amber-line: #ddbd8f;
  --rose: #a8245b;       --rose-bg: #f8e7ee;   --rose-line: #e3abc2;
  --bridge: #6d33d6;     --bridge-bg: #efe8fb; --bridge-line: #c4abee;

  --radius: 12px;
  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Karla", "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
}

.wrap {
  max-width: 680px;
  margin: 0 auto;
  padding: 28px 20px 48px;
}

/* Wider layout for large puzzles — only has room to matter on large
   viewports; max-width is a ceiling, so small screens ignore it. */
.wrap.wide {
  max-width: 1000px;
}

/* ---------- rendering mode toggle ---------- */
.mode-bar {
  display: inline-flex;
  gap: 2px;
  margin-bottom: 14px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 2px;
  background: var(--panel);
}
.mode-btn {
  border: none;
  border-radius: 6px;
  padding: 5px 12px;
  font-size: 13px;
  font-family: var(--font-body);
  color: var(--ink-soft);
  background: transparent;
  cursor: pointer;
}
.mode-btn:hover { color: var(--ink); }
.mode-btn[aria-pressed="true"] { background: var(--select); color: #ffffff; }

/* ---------- header ---------- */
header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}

h1 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 26px;
  margin: 0;
  letter-spacing: -0.01em;
}

.controls { display: flex; gap: 8px; align-items: center; }

select, button {
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
}
button:hover, select:hover { border-color: var(--ink-soft); }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* A quiet hint that a bridge already landed on a valid-but-non-ideal
   term — deliberately just this, nothing on the board itself, since
   idealTerms are praise-when-earned, not a correction of a valid
   choice. Whether to go look is entirely up to the player. */
#show-solution.has-better {
  border-color: var(--bridge);
  box-shadow: 0 0 0 2px var(--bridge-bg);
}

.subhead {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  color: var(--ink-soft);
  font-size: 14px;
  margin-bottom: 10px;
}
.title-group { display: flex; align-items: baseline; gap: 8px; }
#puzzle-title { font-weight: 700; color: var(--ink); }
.badge-large {
  display: none;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--bridge);
  background: var(--bridge-bg);
  border-radius: 5px;
  padding: 2px 6px;
}
.badge-large.shown { display: inline-block; }

/* ---------- board: the graph-paper signature ---------- */
#board {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px),
    var(--panel);
  background-size: 24px 24px, 24px 24px, auto;
  /* Dragging a node/circle is a normal gesture here, not text selection —
     without this, the same mouse-down-and-move highlights whatever text
     the pointer crosses along the way, which fights every drag in both
     rendering modes. */
  user-select: none;
  -webkit-user-select: none;
}

/* ---------- links ---------- */
.link { stroke: #b9bfcd; stroke-width: 1.6; }
.bridge-link { stroke: var(--bridge-line); stroke-width: 2; }
.bridge-link.ideal { stroke: var(--bridge); stroke-width: 2.6; }
/* Mirrors .node.partial's dashed language, now on the line itself —
   a bridge still missing its other connection, in either mode. Combines
   fine with .ideal: a correctly-chosen side on an unfinished bridge is
   still worth showing as correct. */
.bridge-link.partial { stroke-dasharray: 4 3; }

/* ---------- nodes ---------- */
.node { cursor: pointer; }
.node rect { stroke-width: 1.6; transition: fill 120ms, stroke 120ms; }
.node text {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 700;
  text-anchor: middle;
  pointer-events: none;
}

.node.free rect    { fill: #f2f3f6; stroke: #c6cbd6; }
.node.free text    { fill: var(--ink); }

.node.partial rect { fill: #f2f3f6; stroke: var(--bridge); stroke-dasharray: 4 3; }
.node.partial text { fill: var(--ink); }

.node.selected rect { fill: var(--select); stroke: var(--select); }
.node.selected text { fill: #ffffff; }

.node.done.c-green rect { fill: var(--green-bg); stroke: var(--green-line); }
.node.done.c-green text { fill: var(--green); }
.node.done.c-blue rect  { fill: var(--blue-bg);  stroke: var(--blue-line); }
.node.done.c-blue text  { fill: var(--blue); }
.node.done.c-amber rect { fill: var(--amber-bg); stroke: var(--amber-line); }
.node.done.c-amber text { fill: var(--amber); }
.node.done.c-rose rect  { fill: var(--rose-bg);  stroke: var(--rose-line); }
.node.done.c-rose text  { fill: var(--rose); }
.node.done.bridge rect  { fill: var(--bridge-bg); stroke: var(--bridge-line); }
.node.done.bridge text  { fill: var(--bridge); }

/* A term that's the ideal anchor for some bridge, in the set-graph view.
   Same specificity as the c-* rules above (3 classes) so source order
   decides — this must stay below them to win the stroke override while
   keeping that rule's fill. */
.node.done.ideal-target rect { stroke: var(--bridge); stroke-width: 2.5; }

/* Names which bridge a highlighted term is ideal for — needed once two
   bridges each have their own (different) ideal term inside the same
   cluster, since both would otherwise render as an identical purple
   highlight with no way to tell them apart. Empty when not applicable,
   so it takes no space and needs no separate show/hide toggling. */
.node .ideal-tag {
  font-size: 9.5px;
  font-weight: 600;
  font-style: italic;
  fill: var(--bridge);
}

/* Marks a node as having extra info, shown in #term-info on hover/tap —
   the only cue that hovering (or, on touch, tapping) does anything extra
   here, since there's otherwise nothing to signal it. */
.node .info-dot {
  fill: var(--ink-soft);
  pointer-events: none;
}

/* ---------- set-graph view (Sets mode) ---------- */
.set-circle { stroke-width: 2; }
.set-circle.c-green { fill: var(--green-bg); stroke: var(--green-line); }
.set-circle.c-blue  { fill: var(--blue-bg);  stroke: var(--blue-line); }
.set-circle.c-amber { fill: var(--amber-bg); stroke: var(--amber-line); }
.set-circle.c-rose  { fill: var(--rose-bg);  stroke: var(--rose-line); }
.set-heading {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  text-anchor: middle;
  fill: var(--ink);
}

/* Position changes (a term joining its cluster, a bridge docking or
   completing, a drag committing) glide smoothly instead of popping to
   the new spot — scoped to sets-mode's own layers only. Applying this
   to .node generally would also catch the traditional board, where a
   force-simulation tick updates transform ~60 times a second; fighting
   a 400ms transition against that would look sluggish and wrong, not
   smooth. Disabled board-wide (not just on the grabbed element) while
   any drag is active: dragging a circle repositions its docked terms
   and any attached bridges too, every tick — if only the circle itself
   skipped the transition, those passengers would each tween toward a
   fresh target 400ms behind the live pointer, compounding into a
   visible chase lag instead of moving in lockstep. */
.set-clusters .set-cluster,
.set-pills .node {
  transition: transform 400ms ease;
}
#board.dragging .set-cluster,
#board.dragging .node {
  transition: none;
}
.set-cluster { cursor: grab; }
.set-cluster.dragging { cursor: grabbing; }

/* ---------- feedback ---------- */
#message {
  min-height: 24px;
  text-align: center;
  color: var(--ink-soft);
  margin-top: 12px;
}
#message[data-tone="good"] { color: var(--green); }

/* Reserves room for two lines always (same convention as #message
   above it, just taller) — authoring guidance asks for a one-line
   definition, but a bridge or a term with a longer name can still wrap,
   and this is the difference between that wrapping being invisible and
   it shifting the fact cards below on every hover. Flex-centers so a
   single line still sits centered in the two-line box rather than
   pinned to the top. */
#term-info {
  min-height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-style: italic;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--ink-soft);
  margin-top: 4px;
  opacity: 0;
  transition: opacity 150ms ease;
}
#term-info.visible { opacity: 1; }
#term-info a {
  color: var(--blue);
  font-style: normal;
  white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) {
  #term-info { transition: none; }
}

/* ---------- fact cards ---------- */
#facts { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }

.fact-card {
  border-radius: var(--radius);
  padding: 10px 14px;
  font-size: 14px;
}
.fact-card strong { display: block; font-weight: 700; }
.fact-card.c-green { background: var(--green-bg); color: var(--green); }
.fact-card.c-blue  { background: var(--blue-bg);  color: var(--blue); }
.fact-card.c-amber { background: var(--amber-bg); color: var(--amber); }
.fact-card.c-rose  { background: var(--rose-bg);  color: var(--rose); }
.fact-card.bridge  { background: var(--bridge-bg); color: var(--bridge); }

/* ---------- footer ---------- */
footer {
  margin-top: 18px;
  font-size: 13px;
  color: var(--ink-soft);
  border-top: 1px solid var(--rule);
  padding-top: 10px;
}

@media (prefers-reduced-motion: reduce) {
  .node rect { transition: none; }
}
