/* The Infinite Map (BIGMAP stage 2).
   Layout discipline — same rule as .explorer-viewport / .tiling-canvas in
   explorer.css / widgets.css: the viewport has a DETERMINISTIC height and the
   canvas lives OUT of flow (absolute, inset 0). The viewport must never size
   itself from its own canvas, or resize feedback loops the layout. */

.map-page {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
}

.map-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
}
.map-title h1 {
  margin: 0 0 4px;
  font-size: 24px;
}
.map-title p {
  margin: 0;
  max-width: 560px;
}

.map-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px;
}
.map-control {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
}
.map-control input,
.map-control select {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 8px;
  font: inherit;
  min-width: 90px;
}
.map-controls button {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 12px;
  font: inherit;
  cursor: pointer;
}
.map-controls button:hover {
  border-color: var(--accent);
}
.map-record-note {
  margin: 0;
  font-size: 13px;
}

.map-caution {
  margin: 0;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-left: 3px solid #d99a2b;
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--muted);
  font-size: 13px;
}

/* Strand-line controls. Collapsed by default so the header keeps a stable
   height — .map-viewport's height is a fixed calc(), so anything that grows
   above it must be bounded. */
.map-lines {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  padding: 6px 10px;
  font-size: 13px;
}
.map-lines > summary {
  cursor: pointer;
  user-select: none;
}
.map-lines-body {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px 18px;
  padding-top: 8px;
}
.map-lines-body .control-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
}
.map-lines-body .edge-subset-picker {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.map-lines-body .edge-subset-picker p {
  margin: 0;
}
.map-lines-body select,
.map-lines-body input[type='text'] {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 4px 8px;
  font: inherit;
}
.map-combo-input {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 1px;
  width: 13ch;
}
.map-lines-help {
  flex-basis: 100%;
  margin: 0;
  font-size: 12px;
}

.map-viewport {
  position: relative;
  /* Deterministic height (mirrors .explorer-viewport): never derived from
     the canvas bitmap. Constant allows for the header + the collapsed
     strand-line panel. */
  height: calc(100vh - 305px);
  min-height: 420px;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #0e1118;
  touch-action: none;
  cursor: grab;
}
.map-viewport:active {
  cursor: grabbing;
}
/* The bitmap lives OUT of flow (see widgets.css .tiling-canvas canvas). */
.map-viewport canvas {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* In-viewport tool cluster (top-right): stats toggle, record, fullscreen.
   Lives inside the host so it is still there when the viewport IS the screen
   — the page chrome (and its Record button) is not part of the fullscreen
   subtree. Absolutely positioned like .map-hud: never in the layout. */
.viewport-tools {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
  max-width: 60%;
}
.viewport-tools button {
  padding: 4px 9px;
  border: 1px solid rgba(244, 246, 251, 0.28);
  border-radius: var(--radius);
  background: rgba(8, 10, 16, 0.72);
  color: #e7ecf6;
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  cursor: pointer;
}
.viewport-tools button:hover {
  border-color: rgba(244, 246, 251, 0.6);
}
.viewport-tools button.is-recording {
  border-color: #e0524d;
  color: #ffb3b0;
}
/* Save/fail notes are echoed here while fullscreen — the page-level note
   (rendered in the chrome) is not on screen then. */
.viewport-tools-note {
  flex-basis: 100%;
  padding: 4px 8px;
  border-radius: var(--radius);
  background: rgba(8, 10, 16, 0.72);
  color: #d9c9a0;
  font-size: 12px;
  text-align: right;
}

/* Fullscreen: the viewport is the whole screen, so the deterministic-height
   calc() (sized around the page chrome) must give way, and the frame styling
   goes — there is no page for the border to separate it from. */
.map-viewport:fullscreen {
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 0;
}
.map-viewport:fullscreen::backdrop {
  background: #0e1118;
}
/* Safari before 16.4 knows only the prefixed form (kept as separate rules —
   one unknown selector in a list would invalidate the whole rule). */
.map-viewport:-webkit-full-screen {
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 0;
}

.map-hud {
  position: absolute;
  top: 8px;
  left: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border-radius: var(--radius);
  background: rgba(8, 10, 16, 0.72);
  color: #9fd6c8;
  font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  pointer-events: none;
  user-select: none;
  max-width: min(360px, 80%);
}

.map-note,
.map-warning {
  position: absolute;
  left: 8px;
  bottom: 8px;
  max-width: min(520px, 90%);
  padding: 8px 10px;
  border-radius: var(--radius);
  font-size: 13px;
  pointer-events: none;
}
.map-note {
  background: rgba(8, 10, 16, 0.72);
  color: #d9c9a0;
}
.map-note + .map-note {
  bottom: 52px;
}
.map-warning {
  background: rgba(64, 18, 22, 0.88);
  color: #ffd7d7;
  pointer-events: auto;
}

.map-help {
  margin: 0;
  font-size: 13px;
}

@media (max-width: 900px) {
  .map-viewport {
    height: 62vh;
  }
}

/* Trace ticker — the tiles the chase is crossing, along the bottom of the
   viewport. Absolutely positioned like .map-hud, so it never participates in
   the viewport's layout (which must stay deterministic). */
.trace-ticker {
  position: absolute;
  left: 8px;
  right: 8px;
  /* Clear of the viewport caption, which is absolutely positioned along the
     bottom edge of the Explorer's viewport at left: 12px. */
  bottom: 34px;
  display: flex;
  gap: 3px;
  align-items: center;
  justify-content: flex-end;
  padding: 5px 7px;
  border-radius: var(--radius);
  background: rgba(16, 19, 26, 0.72);
  /* Newest is at the right; older names run off the left edge rather than
     wrapping, so the strip keeps a fixed height however fast the chase runs. */
  overflow: hidden;
  white-space: nowrap;
  pointer-events: none;
}
.trace-tile {
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: 999px;
  color: #10131a;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 11px;
  line-height: 1.5;
  opacity: 0.75;
  /* Grow the arriving chip out of nothing instead of appearing at full width:
     the row is right-aligned, so a chip that pops into existence shoves
     everything left in one jump. Widening it over a frame or two turns that
     jump into a slide. Chips already on screen are untouched — they keep their
     identity across publishes (see `steps`), so this only ever runs on the
     newest one. */
  animation: trace-tile-in 150ms ease-out;
  overflow: hidden;
  white-space: nowrap;
}
@keyframes trace-tile-in {
  from {
    /* A cap, not a width: the chip stops growing once its own text fits. */
    max-width: 0;
    padding-left: 0;
    padding-right: 0;
    margin-left: -3px;
    opacity: 0;
  }
  to {
    max-width: 90px;
    padding-left: 6px;
    padding-right: 6px;
    margin-left: 0;
    opacity: 0.75;
  }
}
/* A chase at full speed crosses tiles far faster than the animation runs, and
   anyone who asked for less motion should not get a strip of it. */
@media (prefers-reduced-motion: reduce) {
  .trace-tile {
    animation: none;
  }
}
.trace-tile.is-newest {
  opacity: 1;
  box-shadow: 0 0 0 1.5px #f4f6fb;
}

/* Transition graph — the whole chase's type-to-type counts, parked above the
   ticker in the bottom-right corner. Same absolute-positioning rule as the
   ticker and .map-hud: it must not touch the viewport's own layout. */
.trace-graph {
  position: absolute;
  right: 8px;
  /* Clear of the ticker, which sits at bottom: 34px and is ~26px tall. */
  bottom: 66px;
  width: 208px;
  max-width: 46%;
  padding: 4px;
  border-radius: var(--radius);
  /* Nearly opaque: the tiling underneath is bright, and a hundred thin curves
     have no chance of reading through it. */
  background: rgba(12, 15, 21, 0.93);
  /* Edges are hover targets, so unlike the ticker this one takes the pointer.
     It sits over the tiling, so keep it to the panel itself. */
  pointer-events: auto;
}
/* With the ticker switched off there is nothing to sit above. */
.trace-graph.is-low {
  bottom: 34px;
}
/* Expanded: four times the area, for reading a hundred curves rather than
   just noticing them. Capped against the viewport so it can never grow past
   what there is room for. */
.trace-graph.is-big {
  width: 416px;
  max-width: 92%;
}
.tg-controls {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 1px 3px 3px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 10px;
  color: #c9d1e2;
}
.tg-controls label {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  cursor: pointer;
}
.tg-controls input {
  margin: 0;
  /* The panel is small; the default checkbox would dominate the row. */
  width: 11px;
  height: 11px;
}
.tg-expand {
  margin-left: auto;
  order: 9;
  padding: 0 5px;
  border: 1px solid rgba(244, 246, 251, 0.28);
  border-radius: 4px;
  background: transparent;
  color: #c9d1e2;
  font-size: 10px;
  line-height: 1.5;
  cursor: pointer;
}
.tg-expand:hover {
  border-color: rgba(244, 246, 251, 0.6);
}
.trace-graph svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}
.trace-graph text {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 9px;
  pointer-events: none;
}
.tg-line {
  fill: none;
  stroke-linecap: round;
}
/* Hover is resolved by nearest curve to the pointer (see TransitionGraph), so
   the edges themselves take no pointer events — overlapping hit shapes were
   the problem, not the solution. */
.tg-edge {
  pointer-events: none;
}
.trace-graph svg {
  cursor: crosshair;
}
.tg-readout {
  fill: #f4f6fb;
  font-size: 10px;
  paint-order: stroke;
  stroke: rgba(16, 19, 26, 0.9);
  stroke-width: 3px;
  stroke-linejoin: round;
}

/* --- the pinned pick ------------------------------------------------------ */

/* What is picked out, so it is legible without the pointer on it. */
.tg-pinned {
  padding: 2px 3px 3px;
  border-bottom: 1px solid rgba(244, 246, 251, 0.14);
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 10px;
  color: #e7ecf6;
}
.tg-pinned-top {
  display: flex;
  align-items: baseline;
  gap: 5px;
}
/* How much ink it actually put on the plane — including none, which is a
   fact about the rule or the chase rather than a broken panel. */
.tg-pinned-marks {
  color: #8f9bb3;
  font-size: 9px;
}
.tg-pinned-name {
  overflow: hidden;
  flex: 1 1 auto;
  color: #9aa6bd;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tg-pinned-count {
  color: #f4f6fb;
}
.tg-pinned-top button {
  padding: 0 4px;
  border: 1px solid rgba(244, 246, 251, 0.28);
  border-radius: 4px;
  background: transparent;
  color: #c9d1e2;
  font: inherit;
  cursor: pointer;
}
.tg-seq {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1px;
  font-size: 11px;
  line-height: 1.15;
}

/* --- ranked runs ---------------------------------------------------------- */

/* The ranked list is a list: it wants height, not the circle's square.
   Starts below the viewport tools, which own the top-right corner. */
.trace-graph.is-tall {
  top: 44px;
  display: flex;
  overflow: hidden;
  flex-direction: column;
}
.tg-ranked {
  display: flex;
  overflow: hidden;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}
/* 1 … 30, wrapped: every length is one click away rather than behind a stepper. */
.tg-lengths {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px 1px;
}
.tg-lengths button {
  min-width: 16px;
  padding: 0 2px;
  border: 1px solid rgba(244, 246, 251, 0.18);
  border-radius: 3px;
  background: transparent;
  color: #9aa6bd;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 9px;
  line-height: 1.5;
  cursor: pointer;
}
.tg-lengths button:hover {
  border-color: rgba(244, 246, 251, 0.5);
  color: #e7ecf6;
}
.tg-lengths button.is-on {
  border-color: #f4f6fb;
  background: rgba(244, 246, 251, 0.16);
  color: #f4f6fb;
}
.tg-rank-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 2px 2px 3px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 9px;
  color: #9aa6bd;
}
.tg-rank-head button {
  padding: 0 4px;
  border: 1px solid rgba(244, 246, 251, 0.18);
  border-radius: 3px;
  background: transparent;
  color: #c9d1e2;
  font: inherit;
  cursor: pointer;
}
.tg-rows {
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
.tg-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  padding: 1px 3px;
  border-radius: 3px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 10px;
  color: #c9d1e2;
  cursor: pointer;
}
.tg-row:hover {
  background: rgba(244, 246, 251, 0.1);
}
.tg-row.is-on {
  background: rgba(244, 246, 251, 0.2);
  color: #f4f6fb;
}
.tg-row-count {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
.tg-more,
.tg-empty {
  margin: 3px 0 0;
  padding: 2px;
  border: 0;
  background: transparent;
  color: #9aa6bd;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 9px;
  text-align: center;
  cursor: pointer;
}

@media (max-width: 780px) {
  .trace-ticker {
    font-size: 10px;
  }
  .trace-graph {
    width: 150px;
  }
}
