/* TreeView: chart canvas, zoom controls, details panel, search highlighting. */

.header-field { min-width: 150px; }
.header-field select { min-width: 150px; }
.search-field { min-width: 210px; }

/* The "from this computer" field is a div, not a label, so it needs the
   caption styling that common.css only applies to label.field. */
.header-field > span {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 3px;
}

.search-wrap { position: relative; display: flex; align-items: center; }
.search-wrap input { padding-right: 52px; }

.search-count {
  position: absolute;
  right: 7px;
  font-size: 11px;
  font-weight: 650;
  color: var(--text-muted);
  background: var(--surface-2);
  border-radius: 99px;
  padding: 1px 7px;
  pointer-events: none;
}

.search-count.none { color: var(--danger); }

/* Divides whole-file controls (left) from current-chart controls (right). */
.header-sep {
  align-self: stretch;
  width: 1px;
  margin: 2px 4px;
  background: var(--border-strong);
  flex: none;
}

/* Whole-file matches, listed in two groups: those drawn on the current chart
   and those that are not. Selecting one opens its details without moving the
   chart; re-centring happens only via "Centre chart here". */
.search-results {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  right: 0;
  min-width: 260px;
  max-height: 300px;
  overflow-y: auto;
  z-index: 20;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 5px;
}

.search-results[hidden] { display: none !important; }

.search-results .sr-head {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 5px 8px 6px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}

.search-results button {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: none;
  border-radius: 5px;
  padding: 5px 8px;
  font-size: 13px;
  font-weight: 400;
  color: var(--text);
  cursor: pointer;
}

.search-results button:hover { background: var(--accent-soft); }
.search-results button .sr-meta { display: block; font-size: 11.5px; color: var(--text-muted); }
.search-results .sr-none { padding: 6px 8px; font-size: 12.5px; color: var(--text-muted); }

/* ------------------------------------------------------------ chart canvas */

.chart-area {
  position: relative;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  background:
    radial-gradient(circle at 1px 1px, var(--border) 1px, transparent 0) 0 0 / 22px 22px,
    var(--bg);
}

#chart {
  width: 100%;
  height: 100%;
  display: block;
  cursor: grab;
  touch-action: none;
}

#chart.panning { cursor: grabbing; }
#chart.is-empty { display: none; }

.chart-loading {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: color-mix(in srgb, var(--bg) 75%, transparent);
  font-weight: 600;
  color: var(--text-muted);
  z-index: 3;
}

.chart-loading__stage {
  font-variant-numeric: tabular-nums;   /* counts do not jitter as they change */
  min-width: 20ch;
}

.chart-loading__spinner {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent);
  animation: chart-spin .7s linear infinite;
  flex: none;
}

@keyframes chart-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .chart-loading__spinner { animation-duration: 2.4s; }
}

/* The overlays set display:flex, which would otherwise beat [hidden]. */
#chart-placeholder { position: absolute; inset: 0; z-index: 2; }
#chart-placeholder[hidden], .chart-loading[hidden], .details-panel[hidden] { display: none !important; }

.chart-hint {
  margin: 0;
  /* Shrinks ten times faster than the status bar, so the counts survive a
     narrow chart area and the hint - which is only a reminder - gives way. */
  flex: 0 10 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 11.5px;
  color: var(--text-muted);
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  border: 1px solid var(--border);
  border-radius: 99px;
  padding: 3px 11px;
  pointer-events: none;
}

/* The bottom strip lays the two readouts out side by side. They used to be
   positioned independently and collided once the details panel narrowed the
   chart area; as flex items they simply share the width instead. */
.chart-strip {
  position: absolute;
  left: 12px;
  right: 14px;
  bottom: 12px;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  pointer-events: none;          /* the gap between them must not eat drags */
}

/* Only the controls take pointer events back; the hint stays transparent to
   drags, as it always was. */
.chart-strip > .status-bar { pointer-events: auto; }

/* One box holds the zoom controls and, after the zoom percentage, the
   coverage readout. */
.status-bar {
  display: flex;
  align-items: stretch;
  flex: 0 1 auto;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.zoom-controls {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px;
  flex: none;
}

.zoom-controls button {
  border: none;
  background: transparent;
  font-size: 15px;
  line-height: 1;
  width: 28px;
  height: 26px;
  justify-content: center;
  padding: 0;
}

.zoom-controls button:hover { background: var(--surface-2); }

.zoom-level {
  font-size: 11px;
  font-weight: 650;
  color: var(--text-muted);
  min-width: 42px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------- overrides for Topola's own CSS
   Topola injects a <style> into the SVG that hard-codes black text and pale
   fills. These rules are more specific, so they win in both themes.        */

#chart .detailed text { font-family: system-ui, "Segoe UI", Roboto, sans-serif; fill: var(--text); }
#chart .detailed .name { font-size: 12px; font-weight: 650; }
#chart .detailed .details { font-size: 10px; fill: var(--text-muted); }
#chart .detailed .id { font-size: 9.5px; fill: var(--text-muted); font-style: normal; }
#chart .detailed rect { stroke: var(--border-strong); }
#chart .link { stroke: var(--border-strong); fill: none; stroke-width: 1.2px; }

#chart .detailed rect.nocolor { fill: var(--surface); }
#chart .detailed rect.bysex { fill: var(--surface-2); }
#chart .detailed rect.bysex.male { fill: color-mix(in srgb, var(--male) 16%, var(--surface)); }
#chart .detailed rect.bysex.female { fill: color-mix(in srgb, var(--female) 16%, var(--surface)); }
#chart .detailed rect.bygeneration { fill: var(--surface-2); }

#chart g.indi { cursor: pointer; }
#chart g.indi rect.border { stroke: transparent; stroke-width: 2px; transition: stroke .12s; }
#chart g.indi:hover rect.border { stroke: var(--accent); }

/* Search hits and the currently selected person.
   The two searches get distinct colours so it is clear which one matched. */
#chart g.indi.hit-all rect.border { stroke: #e8a317; stroke-width: 3.5px; }   /* Search all  */
#chart g.indi.hit-view rect.border { stroke: #16a3a3; stroke-width: 3.5px; }  /* Search view */
#chart g.indi.hit-all.hit-view rect.border { stroke: #7c9c1f; stroke-width: 4px; } /* both */
#chart g.indi.selected rect.border { stroke: var(--accent); stroke-width: 3.5px; }

/* When either search is active, anything matching neither recedes. */
#chart.searching g.indi:not(.hit-all):not(.hit-view) { opacity: .32; }

/* ---------------------------------------------------------- details panel */

.details-panel {
  position: relative;
  flex: none;
  width: 330px;
  background: var(--surface);
  border-left: 1px solid var(--border);
  overflow-y: auto;
  padding: 0 18px 24px;      /* top padding is supplied by .details-actions */
  scrollbar-width: thin;
}

.details-close {
  position: absolute;
  top: 10px;
  right: 12px;
  z-index: 3;                 /* above the sticky action bar */
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 13px;
}

.details-close:hover { background: var(--surface-2); }

.details-name {
  font-size: 17px;
  font-weight: 650;
  letter-spacing: -.01em;
  /* No longer needs to dodge the close button: the action bar is above it. */
  margin: 0 0 2px;
  line-height: 1.25;
}

.details-sub {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 16px;
  font-size: 12px;
  color: var(--text-muted);
}

.sex-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.sex-dot.M { background: var(--male); }
.sex-dot.F { background: var(--female); }
.sex-dot.U { background: var(--unknown); }

.details-photo {
  margin: 0 0 18px;
}

.details-photo a {
  display: block;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
  line-height: 0;
}

.details-photo a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.details-photo img {
  width: 100%;
  max-height: 320px;
  object-fit: cover;
  display: block;
}

.details-photo figcaption {
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 6px;
  line-height: 1.4;
}

.details-section { margin-bottom: 18px; }

.details-section h3 {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 7px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--border);
}

.details-row { display: flex; gap: 10px; padding: 3px 0; font-size: 13px; }
.details-row dt { flex: none; width: 62px; color: var(--text-muted); font-size: 12px; }
.details-row dd { margin: 0; flex: 1; min-width: 0; word-wrap: break-word; }

.details-note {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-muted);
  background: var(--surface-2);
  border-radius: 5px;
  padding: 7px 9px;
  margin-bottom: 6px;
  white-space: pre-wrap;
}

.person-link {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: 5px;
  padding: 5px 7px;
  font-size: 13px;
  color: var(--accent);
  cursor: pointer;
}

.person-link:hover { background: var(--accent-soft); }
.person-link .rel { color: var(--text-muted); font-size: 11.5px; }

/* Pinned to the top of the panel so it stays reachable however far the reader
   scrolls. The right padding keeps it clear of the close button, which is
   absolutely positioned in the same corner. */
.details-actions {
  display: flex;
  gap: 6px;
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
  /* Bleeds to the panel edges and carries the panel's top padding, so content
     scrolling underneath is fully covered rather than peeking above it. */
  margin: 0 -18px 14px;
  padding: 16px 34px 10px 18px;
  box-shadow: 0 6px 10px -8px rgba(0, 0, 0, .55);
}

/* Re-rooting the chart is the one action in this panel, and since search
   results deliberately no longer move the view, it needs to be findable.
   Brass, to match the company mark in the header. */
#center-here {
  width: 100%;
  justify-content: center;
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .01em;
  color: #23180a;
  background: linear-gradient(180deg, #dcc079, #c0a050);
  border: 1px solid #8a6e2f;
  border-radius: 6px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .30), inset 0 1px 0 rgba(255, 244, 214, .45);
  transition: background .15s ease, transform .1s ease;
}

#center-here:hover {
  background: linear-gradient(180deg, #efdba4, #dcc079);
  color: #23180a;
}

#center-here:active { transform: translateY(1px); }
#center-here:focus-visible { outline: 2px solid #efdba4; outline-offset: 2px; }

@media (max-width: 860px) {
  .details-panel {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    z-index: 6;
    box-shadow: -4px 0 16px rgba(0, 0, 0, .12);
  }
  .search-field, .header-field { min-width: 130px; }
}

/* Search-all results carry two groups. Matches already drawn on the chart get
   the same amber marker used to highlight them, so the list and the chart
   read as one thing. */
.search-results button.sr-here {
  border-left: 3px solid #e8a317;
  padding-left: 5px;
}

.search-results button.sr-here .sr-meta { color: #b07d10; }

.search-results .sr-head + button.sr-here { margin-top: 1px; }

/* Names can be long, so this one gets more room than a stock header field. */
/* The two save buttons are a pair: a tighter gap than the header's own, so
   they wrap together rather than drifting apart. */
.save-group {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.root-field { min-width: 210px; }
.root-field select { min-width: 210px; max-width: 260px; }

/* Coverage readout, in the same box as the zoom controls and after the zoom
   percentage. Deliberately NOT a flex container: flex layout drops the
   whitespace-only runs between the <strong> counts, which ran the numbers
   into the words around them. */
.view-status {
  display: block;
  align-self: center;
  border-left: 1px solid var(--border);
  margin: 5px 0;
  padding: 0 12px 0 11px;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 24px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;   /* the numbers change constantly */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.view-status[hidden] { display: none !important; }
.view-status strong { color: var(--text); font-weight: 700; }

/* Too narrow for the full sentence: keep the numbers, drop the preamble. */
@media (max-width: 720px) {
  .view-status .vs-prefix { display: none; }
}

/* The hint sits in the same strip; below this width the status bar reaches it.
   It describes mouse gestures anyway, so it is the one to go. */
@media (max-width: 900px) {
  .chart-hint { display: none; }
}
