/* ---- Design tokens ---------------------------------------------------- */
:root {
  --paper: #f7f4ec;
  --paper-panel: #f1ecdf;
  --paper-line: #ddd5c0;
  --ink: #262220;
  --ink-soft: #7a7266;
  --seal-wine: #7a2e33;
  --seal-slate: #35586b;
  --accent: #7a2e33;

  --card-bg: #ffffff;
  --male-color: #7fa3b8;
  --male-border: #c3d5de;
  --female-color: #cf90a3;
  --female-border: #e6c6d0;
  --card-radius: 14px;
  --link-color: #8a8272;

  --font-display: 'Spectral', Georgia, serif;
  --font-body: 'IBM Plex Sans', -apple-system, Segoe UI, sans-serif;
  --font-mono: 'IBM Plex Mono', 'Courier New', monospace;

  --radius: 6px;
  --sidebar-width: 280px;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
}

.app {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ---- Top bar ------------------------------------------------------------ */
.topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--paper-line);
  background: var(--paper-panel);
}

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

.status-line {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-soft);
}

.icon-btn {
  border: 1px solid var(--paper-line);
  background: var(--paper);
  color: var(--ink);
  border-radius: var(--radius);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.icon-btn:hover { background: var(--paper-panel); }

.account-menu {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 10px;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--paper);
}
.account-email {
  font-size: 12.5px;
  color: var(--ink);
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.account-role-badge {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  background: var(--paper-panel);
  border: 1px solid var(--paper-line);
  border-radius: 4px;
  padding: 2px 6px;
  flex-shrink: 0;
}
.account-menu .icon-btn {
  width: 26px;
  height: 26px;
  border: none;
  background: none;
  flex-shrink: 0;
}
.account-menu .icon-btn:hover { background: var(--paper-panel); }

/* ---- Body / layout -------------------------------------------------- */
.body {
  flex: 1;
  display: flex;
  min-height: 0;
}

.sidebar {
  width: var(--sidebar-width);
  flex-shrink: 0;
  overflow-y: auto;
  border-right: 1px solid var(--paper-line);
  background: var(--paper-panel);
  padding: 16px;
  transition: margin-left 0.2s ease;
}

.app.collapsed .sidebar {
  margin-left: calc(-1 * var(--sidebar-width));
}

.canvas-wrap {
  position: relative;
  flex: 1;
  min-width: 0;
  min-height: 0;
}

/* ---- Panels / controls ------------------------------------------------ */
.panel {
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--paper-line);
}
.panel:last-child { border-bottom: none; }

.panel-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin-bottom: 8px;
}

#searchInput {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 13px;
}
#searchInput:focus {
  outline: 2px solid var(--seal-slate);
  outline-offset: 1px;
}

.search-results {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--paper);
  max-height: 220px;
  overflow-y: auto;
}
.search-results li {
  padding: 7px 9px;
  font-size: 13px;
  cursor: pointer;
  border-bottom: 1px solid var(--paper-line);
}
.search-results li:last-child { border-bottom: none; }
.search-results li:hover { background: var(--paper-panel); }
.search-results li .meta {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-soft);
}

.selected-person {
  margin-top: 10px;
  font-size: 13px;
}
.focus-indicator {
  margin-top: 10px;
  padding: 6px 8px;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--paper);
  font-size: 12px;
}
.link-btn {
  border: none;
  background: none;
  color: var(--seal-wine);
  text-decoration: underline;
  cursor: pointer;
  font-size: 12px;
  padding: 0 0 0 6px;
}

.radio-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  padding: 5px 0;
  cursor: pointer;
}
.radio-row:has(input:disabled) {
  opacity: 0.45;
  cursor: default;
}
.picture-mode-btn:disabled {
  opacity: 0.45;
  cursor: default;
}
.scope-sub-controls {
  margin-top: 4px;
  padding-top: 8px;
  border-top: 0.5px solid var(--paper-line);
}
.scope-sub-controls[hidden] { display: none; }

/* Sits first in the section, before the scope radio group - orientation
   applies to the "no focus" overview too, not just a focused tree, so it
   needs to stay visible regardless of scope, and is placed first as the
   more fundamental "how does the whole tree lay out" choice. Separator
   goes below rather than above (unlike .scope-sub-controls) since nothing
   precedes it except the section header. */
#orientationRow {
  padding-bottom: 10px;
  margin-bottom: 12px;
  border-bottom: 0.5px solid var(--paper-line);
}

.hint {
  font-size: 12px;
  color: var(--seal-wine);
  margin: 6px 0 0;
}

.switch-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
.switch {
  position: relative;
  display: inline-flex;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 34px;
  height: 18px;
  margin: 0;
  cursor: pointer;
}
.switch-track {
  width: 34px;
  height: 18px;
  border-radius: 20px;
  background: var(--paper-line);
  display: inline-block;
  position: relative;
  transition: background 0.15s ease;
}
.switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--paper);
  transition: left 0.15s ease;
}
.switch input:checked + .switch-track { background: var(--seal-slate); }
.switch input:checked + .switch-track .switch-thumb { left: 18px; }

.secondary-btn {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 13px;
  cursor: pointer;
}
.secondary-btn:hover { background: var(--paper-panel); }
.secondary-btn:disabled {
  color: var(--ink-soft);
  cursor: not-allowed;
}

/* ---- Canvas / loading -------------------------------------------------- */
#FamilyChart {
  width: 100%;
  height: 100%;
}
#FamilyChart[hidden] { display: none; }

/* Overview (no focus): every connected group rendered at native, matching
   scale on one shared pannable/zoomable canvas - #FamilyChartOverview is
   the fixed viewport (this is where our own single d3.zoom listener
   attaches, mirroring how family-chart wires its own per-tree zoom), and
   .overview-canvas is the one element the resulting transform is applied
   to, so everything scales and pans together. Each cluster's own
   family-chart instance has its built-in zoom stripped and is sized to
   its exact computed content - see renderOverview() in app.js. */
#FamilyChartOverview {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
  cursor: grab;
}
#FamilyChartOverview[hidden] { display: none; }
#FamilyChartOverview.grabbing { cursor: grabbing; }

.overview-canvas {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
}

/* Large (multi-generation) clusters. Vertical orientation: stacked as full-
   width rows (column flow) - the default. Horizontal orientation: placed
   side by side instead (row flow), matching each cluster's own tree now
   running left-to-right rather than top-to-bottom - see renderOverview() in
   app.js, which also sets .orientation-horizontal to match the current
   toggle. `gap` (rather than a margin on the children) means the same
   value works for both directions without swapping which margin side is
   used. */
.overview-large-clusters {
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.overview-large-clusters.orientation-horizontal {
  flex-direction: row;
  align-items: flex-start;
}

/* Individuals, couples, and other small groups don't each deserve a full
   row - they flow together so several sit side by side. */
.overview-small-clusters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 40px;
}

.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-soft);
}
.loading-overlay[hidden] { display: none; }

/* ---- Pan / zoom controls ------------------------------------------------ */
.view-controls {
  position: absolute;
  right: 16px;
  bottom: 16px;
  display: flex;
  align-items: flex-end;
  gap: 10px;
  z-index: 5;
}
.view-controls[hidden] { display: none; }

.pan-pad {
  display: grid;
  grid-template-columns: repeat(3, 30px);
  grid-template-rows: repeat(3, 30px);
  gap: 2px;
  background: var(--paper-panel);
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  padding: 4px;
}

.pan-btn {
  border: 1px solid transparent;
  background: var(--paper);
  color: var(--ink);
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
}
.pan-btn:hover { background: var(--paper-line); }
.pan-btn:active { background: var(--seal-slate); color: var(--paper); }

.pan-up { grid-column: 2; grid-row: 1; }
.pan-left { grid-column: 1; grid-row: 2; }
.pan-center { grid-column: 2; grid-row: 2; }
.pan-right { grid-column: 3; grid-row: 2; }
.pan-down { grid-column: 2; grid-row: 3; }

.zoom-stack {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  overflow: hidden;
}

.overview-btn {
  width: 32px;
  height: 32px;
  background: var(--paper-panel);
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
}
#overviewBtn[hidden] { display: none; }

.zoom-btn {
  width: 32px;
  height: 32px;
  border: none;
  border-bottom: 1px solid var(--paper-line);
  background: var(--paper-panel);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.zoom-btn:last-child { border-bottom: none; }
.zoom-btn:hover { background: var(--paper-line); }
.zoom-btn:active { background: var(--seal-slate); color: var(--paper); }

/* ---- family-chart theme overrides -------------------------------------
   family-chart exposes these as CSS custom properties on .f3; we retheme
   them to match the registry-ledger palette instead of its dark default. */
.f3 {
  --genderless-color: var(--ink-soft);
  --background-color: var(--paper);
  --text-color: var(--ink);
  font-family: var(--font-body);
}

.f3 div.card {
  font-family: var(--font-body);
}

/* Scope display: the focused person always gets a highlight ring,
   regardless of scope. "Grey out others" dims everyone outside the
   current scope's relevant set rather than removing them - transition is
   the card's own existing one, so this settles in smoothly alongside a
   tree redraw rather than popping. */
.f3 div.card.is-focus-person .card-inner {
  outline: 2px solid var(--seal-slate);
  outline-offset: 1px;
}
.f3 div.card.is-dimmed {
  --male-color: var(--paper-line);
  --female-color: var(--paper-line);
  --male-border: var(--paper-line);
  --female-border: var(--paper-line);
}
.f3 div.card.is-dimmed .card-label,
.f3 div.card.is-dimmed .card-label * {
  color: var(--ink-soft);
}

/* Card fill: family-chart's default puts a solid gender-colored background
   on .card-inner. We override that with a white card, a thin light outline,
   and a wider colored band on the left only. */
.f3 div.card-male .card-inner,
.f3 div.card-female .card-inner,
.f3 div.card-genderless .card-inner {
  background-color: var(--card-bg);
  border-radius: var(--card-radius);
  border: 1px solid var(--paper-line);
  border-left-width: 6px;
  padding: 2px 4px;
  position: relative;
}
.f3 div.card-male .card-inner {
  border-color: var(--male-border);
  border-left-color: var(--male-color);
}
.f3 div.card-female .card-inner {
  border-color: var(--female-border);
  border-left-color: var(--female-color);
}

/* Redundant, higher-specificity restatement of family-chart's own
   .person-icon svg background rule. That rule already ties the avatar
   square to --male-color/--female-color the same way the border above
   does, and should update live the same way - but in practice it was
   observed staying frozen regardless of color changes. This explicit
   override (one class more specific, and loaded after family-chart's own
   stylesheet) guarantees it wins the cascade regardless of what was
   suppressing the original rule. */
.f3 div.card.card-male .person-icon svg { background-color: var(--male-color); }
.f3 div.card.card-female .person-icon svg { background-color: var(--female-color); }
.f3 div.card.card-genderless .person-icon svg { background-color: var(--genderless-color); }

/* Must come after the three rules above - same specificity, so source
   order decides, and this needs to win for dimmed cards. */
.f3 div.card.is-dimmed .person-icon svg {
  background-color: var(--card-bg);
  color: var(--paper-line);
}

/* Dimmed card, actual photo: greys the photo out the same way the
   sex-icon fallback above is muted, so a person with a photo dims just
   as clearly as one without. Option D from the mockup round - sepia +
   desaturate + reduced opacity, read as an "archival" fade rather than
   a flat B&W. Targets the real family-chart markup directly: the photo
   renders as a bare <img> that is a *direct child* of .card-inner (see
   the corrected note above the picture-mode rules further down for why
   this distinction matters) - this selector can never accidentally
   catch the fallback icon, which is an <svg> nested one level deeper
   inside .person-icon. */
.f3 div.card.is-dimmed .card-inner > img {
  filter: sepia(0.4) grayscale(0.6) opacity(0.6);
}

/* Sex mark: back to inline, right before the surname - baked directly
   into the display_name string in app.js (toChartNodes), so it flows
   naturally with the text and stays correctly placed even when the name
   wraps, rather than needing a fixed corner position. */
.f3 .card-label > div:first-child {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13px;
}
.f3 .inline-sex-mark {
  font-weight: 700;
}
/* Sex-icon toggle is pure CSS visibility - see app.js toChartNodes for why
   (avoids a chart data rebuild, which would reset pan/zoom). The trailing
   space is embedded inside the span itself so hiding it removes the gap
   before the name too, rather than leaving a stray leading space. */
body.hide-sex-icon .f3 .inline-sex-mark {
  display: none;
}
.f3 div.card-male .inline-sex-mark { color: var(--male-color); }
.f3 div.card-female .inline-sex-mark { color: var(--female-color); }

.f3 .card-label > div:not(:first-child) {
  font-family: var(--font-mono);
  font-size: 10.5px;
  opacity: 0.9;
}
/* Narrow layout's row 2 is the family-name line, not a date/meta row like
   wide's row 2 (lifespan) - it should follow the selected Name Font the
   same as row 1, not the monospace "ledger" styling :not(:first-child)
   gives every other row. Rows 3+ (DOB/DOD/email/social) are unaffected
   and stay mono. */
body.card-layout-narrow .f3 .card-label > div:nth-child(2) {
  font-family: var(--font-display);
  font-size: 13px;
  opacity: 1;
}
/* strong's default weight is the relative "bolder" keyword, which jumps
   by a different amount depending on the row's own base weight (600 for
   Arial/Spectral, 400 for Handwritten) and even varies by browser - an
   unpredictable jump that can read as a different typeface entirely
   rather than a controlled bold. Pinning it to a fixed, real weight makes
   the family-name bolding consistent across every Name Font choice. */
.f3 .card-label > div strong {
  font-weight: 700;
}

/* Marriage date now renders on the spouse connector line itself (see
   setLinkSpouseText in app.js) rather than inside either person's card.
   Matches the darker link color below, rather than family-chart's
   hardcoded white (meant for its own dark default background). */
.f3 .link-text text {
  fill: var(--link-color) !important;
  font-family: var(--font-mono) !important;
  font-size: 10.5px !important;
  font-weight: 500;
}

/* family-chart hardcodes a 4px light-gray outline plus a heavy dark glow
   on whichever card is currently centered ("main"), designed to read as a
   glow against its own dark default background. On our light paper it
   showed up as a mismatched gray halo around the card's own colored
   border, so we replace both with a subtle paper-toned lift instead. */
.f3 div.card-main .card-inner {
  outline: none;
}
.f3 div.card-main .card-inner,
.f3 div.card:hover .card-inner {
  box-shadow: 0 2px 6px rgba(38, 34, 32, 0.14);
}
.f3 .link {
  stroke: var(--link-color) !important;
}
.f3 div.card-inner.f3-path-to-main {
  outline: none;
}

/* ---- Edit mode ------------------------------------------------------- */
.edit-mode-badge {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--seal-wine);
  border: 1px solid var(--female-color);
  border-radius: 20px;
  padding: 2px 9px;
}

.app.editing .canvas-wrap {
  outline: 2px solid var(--female-color);
  outline-offset: -2px;
}

/* ---- Editor form ------------------------------------------------------ */
.editor-field-row {
  display: flex;
  gap: 10px;
}
.editor-field-row > div { flex: 1; }

.photo-field-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
}
.photo-preview {
  width: 60px;
  height: 60px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--paper-line);
  flex-shrink: 0;
}
.photo-preview[hidden] { display: none; }
.photo-remove-btn {
  border: none;
  background: none;
  font-size: 11px;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
  color: var(--seal-wine);
  margin-top: 4px;
  display: block;
}
.photo-remove-btn[hidden] { display: none; }

#editModalBody input[type="text"],
#editModalBody select,
#editModalBody textarea {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--card-bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 13px;
}
#editModalBody textarea { resize: vertical; min-height: 50px; }

.relation-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  padding: 7px 9px;
  font-size: 13px;
  background: var(--card-bg);
  margin-bottom: 6px;
}
.relation-row .muted { color: var(--ink-soft); }
/* #rp_selected in the relation picker reuses this class but starts
   hidden - .relation-row sets display:flex explicitly, and an author
   rule that sets display always wins over the browser's built-in
   [hidden] { display: none }, regardless of selector specificity. Every
   other hideable element in this file has its own [hidden] override for
   exactly this reason; this one was missed, so the empty "Selected: ..."
   row rendered as an empty bordered box before anything was chosen. */
.relation-row[hidden] { display: none; }

.relation-action {
  border: none;
  background: none;
  font-size: 11px;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
}
.relation-action.change { color: var(--seal-slate); }
.relation-action.remove { color: var(--seal-wine); }

.add-relation-btn {
  width: 100%;
  padding: 7px;
  border: 1px dashed var(--male-border);
  border-radius: var(--radius);
  background: var(--card-bg);
  color: var(--seal-slate);
  font-family: var(--font-body);
  font-size: 12.5px;
  cursor: pointer;
  margin-bottom: 4px;
}

.editor-search-results {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--card-bg);
  max-height: 180px;
  overflow-y: auto;
}
.editor-search-results li {
  padding: 7px 9px;
  font-size: 13px;
  cursor: pointer;
  border-bottom: 1px solid var(--paper-line);
}
.editor-search-results li:last-child { border-bottom: none; }
.editor-search-results li:hover { background: var(--paper-panel); }
.editor-search-results li .meta {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-soft);
}

.editor-btn-row {
  display: flex;
  gap: 10px;
  margin-top: 16px;
}
.editor-btn-row .primary-btn,
.editor-btn-row .secondary-btn,
.editor-btn-row .danger-btn {
  flex: 1;
  margin-top: 0;
}

.editor-section-divider {
  border-top: 1px solid var(--paper-line);
  margin: 18px 0 12px;
}

.danger-btn {
  padding: 9px 10px;
  border: 1px solid var(--female-color);
  border-radius: var(--radius);
  background: var(--card-bg);
  color: var(--seal-wine);
  font-family: var(--font-body);
  font-size: 13px;
  cursor: pointer;
}
.danger-btn:hover { background: var(--paper-panel); }
.danger-btn.solid {
  background: var(--seal-wine);
  color: #fff;
  border-color: var(--seal-wine);
}

.editor-back-link {
  border: none;
  background: none;
  font-size: 12px;
  color: var(--ink-soft);
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
  margin-bottom: 10px;
}

.delete-warning {
  font-size: 13px;
  line-height: 1.5;
  background: var(--paper-panel);
  border: 1px solid var(--female-color);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 6px 0 4px;
}

/* ---- Settings modal ----------------------------------------------------- */
.modal-overlay {
  position: absolute;
  inset: 0;
  background: rgba(38, 34, 32, 0.35);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 5vh 16px;
  z-index: 20;
  overflow-y: auto;
}
.modal-overlay[hidden] { display: none; }

.modal {
  background: var(--paper);
  border-radius: 10px;
  border: 1px solid var(--paper-line);
  width: 100%;
  max-width: 460px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
}
.modal-wide { max-width: 720px; }

.legal-links {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--paper-line);
  text-align: center;
  font-size: 12px;
  color: var(--ink-soft);
}
.legal-links .link-btn { padding: 0; }

.doc-viewer-body h2 {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  margin: 20px 0 8px;
}
.doc-viewer-body h2:first-child { margin-top: 0; }
.doc-viewer-body p { line-height: 1.6; margin: 0 0 10px; }
.doc-viewer-body ul { margin: 0 0 10px; padding-left: 20px; line-height: 1.6; }
.doc-viewer-body li { margin-bottom: 4px; }
.doc-viewer-body code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--paper-panel);
  border: 1px solid var(--paper-line);
  border-radius: 3px;
  padding: 1px 5px;
}
.doc-viewer-body table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 14px;
  font-size: 13px;
}
.doc-viewer-body th, .doc-viewer-body td {
  border: 1px solid var(--paper-line);
  padding: 6px 8px;
  text-align: left;
  vertical-align: top;
}
.doc-viewer-body th { background: var(--paper-panel); }

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--paper-line);
}
.modal-header h2 {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  margin: 0;
}

.modal-body {
  padding: 4px 18px 18px;
  overflow-y: auto;
}

.settings-section {
  padding: 16px 0;
  border-bottom: 1px solid var(--paper-line);
}
.settings-section:last-child { border-bottom: none; }
.settings-section h3 {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  margin: 0;
}
.settings-section-header {
  display: grid;
  grid-template-columns: auto 1fr auto auto 20px;
  align-items: center;
  gap: 7px;
  margin-bottom: 8px;
}
.admin-tag {
  font-family: var(--font-mono);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--seal-wine);
  background: #fbeff1;
  border: 1px solid var(--female-color);
  border-radius: 4px;
  padding: 2px 6px;
  justify-self: end;
}
.settings-section-header .sidebar-accordion-toggle {
  margin-left: 0;
  justify-self: end;
}
.settings-hint {
  font-size: 12px;
  color: var(--ink-soft);
  margin: 0 0 10px;
}

.field-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin: 10px 0 5px;
}

.modal-body input[type="text"],
.modal-body input[type="file"],
.modal-body textarea,
.modal select {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--card-bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 13px;
}
.modal-body textarea { resize: vertical; }

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  margin: 12px 0 0;
  cursor: pointer;
}

.primary-btn {
  width: 100%;
  padding: 9px 10px;
  margin-top: 14px;
  border: none;
  border-radius: var(--radius);
  background: var(--seal-slate);
  color: #fff;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 13px;
  cursor: pointer;
}
.primary-btn:hover { opacity: 0.92; }
.primary-btn:disabled { opacity: 0.6; cursor: not-allowed; }

.secondary-btn.as-link {
  display: block;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  color: var(--ink);
}

.settings-result {
  margin-top: 12px;
  padding: 10px 12px;
  border-radius: var(--radius);
  font-size: 12.5px;
  background: var(--paper-panel);
  border: 1px solid var(--paper-line);
}
.settings-result.error {
  border-color: var(--female-color);
  background: #fbeff1;
  color: var(--seal-wine);
  font-weight: 500;
}
.settings-result.saved {
  border-color: var(--male-color);
  color: var(--seal-slate);
}
.settings-result ul {
  margin: 6px 0 0;
  padding-left: 18px;
}

/* ---- Pending-request badge on tree cards -------------------------------- */
.f3 div.has-pending .card-inner::before {
  content: "";
  position: absolute;
  top: 6px;
  right: 6px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #c9982e;
  border: 1px solid #fff;
  z-index: 2;
}

/* ---- Admin tables (Manage users / Pending requests) --------------------- */
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
  margin-top: 6px;
}
.admin-table th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--paper-line);
  padding: 4px 6px;
}
.admin-table td {
  padding: 6px;
  border-bottom: 1px solid var(--paper-line);
  vertical-align: middle;
}
.admin-table input[type="text"],
.admin-table input[type="email"],
.admin-table select {
  width: 100%;
  padding: 4px 6px;
  border: 1px solid var(--paper-line);
  border-radius: 4px;
  background: var(--card-bg);
  font-family: var(--font-body);
  font-size: 12px;
}
.admin-table .row-actions {
  display: flex;
  gap: 6px;
  white-space: nowrap;
}
.admin-table .row-actions button {
  border: none;
  background: none;
  font-size: 11px;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
}
.admin-table .row-actions .save-row { color: var(--seal-slate); }
.admin-table .row-actions .delete-row { color: var(--seal-wine); }

.request-card {
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  padding: 9px 11px;
  margin-bottom: 8px;
  background: var(--card-bg);
  font-size: 12.5px;
}
.request-card .request-meta {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-soft);
  margin-bottom: 4px;
}
.request-card .request-detail {
  margin-bottom: 8px;
  line-height: 1.5;
}
.request-card .request-detail code {
  background: var(--paper-panel);
  padding: 1px 4px;
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: 11px;
}
.request-actions {
  display: flex;
  gap: 8px;
}
.request-actions button {
  flex: 1;
  padding: 6px;
  border-radius: 4px;
  font-size: 12px;
  cursor: pointer;
}
.request-actions .approve-btn {
  border: 1px solid var(--male-color);
  background: var(--card-bg);
  color: var(--seal-slate);
}
.request-actions .reject-btn {
  border: 1px solid var(--female-color);
  background: var(--card-bg);
  color: var(--seal-wine);
}

/* ---- Link visual finishing touches (junction dots, date label box) ------ */
/* Two co-parents with no recorded marriage still need a spousal pairing
   in the data for family-chart to lay out both cards at all (see
   TreeBuilder.php / markSyntheticSpouseLinks() in app.js) - but the
   connecting line itself would misleadingly read as a marriage, so it's
   hidden here. Each parent's own line down to their shared children is a
   separate path element and is completely unaffected. */
/* !important is required here: family-chart's own linkUpdate() sets
   opacity:1 as an *inline* style on every path.link on every render
   (confirmed by reading its source, dist/family-chart.js line ~1297) -
   a plain class rule in this stylesheet can never win against that,
   inline styles beat external rules regardless of selector specificity
   unless marked !important. */
.f3 path.link.link-synthetic-spouse {
  opacity: 0 !important;
}
.f3 circle.link-joint {
  fill: var(--link-color);
}
.f3 rect.link-text-bg {
  rx: 3px;
}

/* ---- Card expand icon + full-detail popup ------------------------------- */
.card-expand-btn {
  position: absolute;
  bottom: 6px;
  right: 8px;
  width: 16px;
  height: 16px;
  padding: 0;
  border: none;
  background: none;
  color: var(--ink-soft);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.55;
}
.card-expand-btn:hover { opacity: 1; color: var(--seal-slate); }

.detail-popup {
  position: absolute;
  z-index: 15;
  width: 250px;
  max-height: 260px;
  overflow-y: auto;
  background: var(--card-bg);
  border: 1px solid var(--paper-line);
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(38, 34, 32, 0.18);
  padding: 12px 14px;
  font-size: 12.5px;
}
.detail-popup[hidden] { display: none; }

.detail-popup-photo {
  width: 100%;
  max-height: 140px;
  object-fit: cover;
  border-radius: 6px;
  margin-bottom: 8px;
  display: block;
}

.detail-popup-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14px;
  margin-bottom: 3px;
  padding-right: 16px;
}
.detail-popup-sex { font-weight: 700; }
.detail-popup-dates {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-soft);
  margin-bottom: 8px;
}
.detail-popup-row {
  margin-bottom: 5px;
  line-height: 1.4;
}
.detail-popup-row .label {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  display: block;
}
.detail-popup-close {
  position: absolute;
  top: 6px;
  right: 8px;
  border: none;
  background: none;
  font-size: 15px;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
}
.detail-popup-close:hover { color: var(--seal-wine); }

.detail-history-toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  color: var(--ink);
  font-family: inherit;
}
.detail-history-chevron { transition: transform 0.15s ease; flex-shrink: 0; }
.detail-history-toggle[aria-expanded="true"] .detail-history-chevron { transform: rotate(180deg); }

.detail-history-body { margin-top: 8px; }

.history-entry {
  border-top: 1px solid var(--paper-line);
  padding-top: 8px;
  margin-top: 8px;
}
.history-entry:first-child { border-top: none; margin-top: 0; padding-top: 0; }

.history-meta {
  font-size: 10px;
  color: var(--ink-soft);
  margin-bottom: 3px;
}
.history-field { line-height: 1.5; }
.history-old, .history-deleted { color: var(--ink-soft); text-decoration: line-through; }
.history-new, .history-added { color: var(--seal-slate); font-weight: 500; }

.history-comment {
  margin-top: 5px;
  font-style: italic;
  color: var(--ink-soft);
}
.history-comment-empty { font-style: normal; }

.history-comment-btn {
  display: block;
  margin-top: 5px;
  border: none;
  background: none;
  padding: 0;
  font-size: 10.5px;
  color: var(--seal-slate);
  text-decoration: underline;
  cursor: pointer;
}

.history-comment-form { margin-top: 6px; }
.history-comment-form textarea { font-size: 12px; }
.history-comment-form .editor-btn-row { margin-top: 8px; }
.history-comment-form .primary-btn,
.history-comment-form .secondary-btn { font-size: 11.5px; padding: 6px 8px; }

/* ---- Contextual help icons + popover ----------------------------------- */
.help-icon {
  width: 19px;
  height: 19px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--paper-line);
  background: var(--paper);
  color: var(--ink-soft);
  font-size: 11px;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.help-icon:hover { background: var(--paper-panel); color: var(--ink); }

.help-popup {
  position: fixed;
  z-index: 60;
  width: 280px;
  max-height: 320px;
  overflow-y: auto;
  background: var(--card-bg);
  border: 1px solid var(--paper-line);
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(38, 34, 32, 0.18);
  padding: 12px 14px;
  font-size: 12.5px;
}
.help-popup[hidden] { display: none; }
.help-popup-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14px;
  margin-bottom: 6px;
  padding-right: 16px;
}
.help-popup-body { line-height: 1.5; }
.help-popup-body p,
.help-popup-admin p { margin: 0; }
.help-popup-body ul,
.help-popup-admin ul {
  margin: 0;
  padding-left: 18px;
}
.help-popup-body li,
.help-popup-admin li {
  margin-bottom: 5px;
}
.help-popup-body li:last-child,
.help-popup-admin li:last-child {
  margin-bottom: 0;
}
.help-popup-admin {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--paper-line);
  line-height: 1.5;
}
.help-popup-contact {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--paper-line);
  font-size: 11px;
  color: var(--ink-soft);
  font-style: italic;
  line-height: 1.4;
}
.help-popup-contact a { color: var(--ink-soft); }
.help-popup-close {
  position: absolute;
  top: 6px;
  right: 8px;
  border: none;
  background: none;
  font-size: 15px;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
}
.help-popup-close:hover { color: var(--seal-wine); }

/* ---- Sidebar section separation --------------------------------------- */
.sidebar-section + .sidebar-section {
  border-top: 2px solid var(--paper-line);
  padding-top: 14px;
  margin-top: 14px;
}
.sidebar-section-header {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 8px;
}
.sidebar-section-header .panel-label { margin-bottom: 0; }
/* The header is a flex row, so only elements with their own margin-left:auto
   get pushed right. .sidebar-accordion-toggle already has that; give the
   help icon the same so it moves with it, and cancel the toggle's own auto
   margin when it directly follows the icon so the two sit together at the
   right edge instead of splitting apart. */
.sidebar-section-header .help-icon { margin-left: auto; }
.sidebar-section-header .help-icon + .sidebar-accordion-toggle { margin-left: 0; }
.sidebar-section-num {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  background: var(--seal-slate);
  color: #fff;
  font-size: 9px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.sidebar-accordion-toggle {
  margin-left: auto;
  border: none;
  background: none;
  color: var(--ink-soft);
  font-size: 11px;
  cursor: pointer;
  padding: 0 2px;
  transition: transform 0.15s ease;
}
.sidebar-accordion-toggle[aria-expanded="true"] { transform: rotate(180deg); }
.sidebar-accordion-body {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--paper-line);
}

.card-content-area {
  margin-bottom: 12px;
}
.content-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  row-gap: 6px;
  gap: 10px;
  font-size: 13px;
  margin-bottom: 12px;
}
.content-row:last-child {
  margin-bottom: 0;
}
.content-row-toggle {
  cursor: pointer;
}
.content-row .picture-mode-picker {
  margin-bottom: 0;
  flex-shrink: 0;
}

.card-style-area {
  background: var(--paper-panel);
  border: 1px solid var(--paper-line);
  border-radius: 8px;
  padding: 10px;
}
.card-style-area + .card-style-area {
  margin-top: 12px;
}
.card-style-area .sidebar-section-header {
  margin-bottom: 8px;
}

#pdfExportBody input[type="text"],
#pdfExportBody select {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--card-bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 13px;
}



/* ---- Style picker (5 structural card layouts) --------------------------- */
.picture-mode-picker {
  display: flex;
  gap: 4px;
  margin-bottom: 4px;
}
.picture-mode-btn {
  flex: 1;
  min-width: 72px;
  padding: 6px 10px;
  font-size: 10.5px;
  border: 1px solid var(--paper-line);
  border-radius: 5px;
  background: #fff;
  cursor: pointer;
  color: var(--ink-soft);
}
.picture-mode-btn.active {
  border-color: var(--seal-slate);
  color: var(--ink);
  font-weight: 600;
}

/* CORRECTED - the assumption behind the old rules here was wrong and
   caused a real bug (a photo still rendering in "None" mode). Checked
   directly against family-chart's actual card-html source: .person-icon
   is ONLY the no-photo fallback wrapper (it wraps the silhouette <svg>
   when a person has no photo_path). When a person DOES have a photo,
   family-chart renders a bare <img> as a direct child of .card-inner,
   as a *sibling* of .person-icon, never inside it - so ":has(img)" on
   .person-icon was always false and the old photo_only rule was really
   just "always hide .person-icon", and the old "None" rule (also just
   targeting .person-icon) never touched the real photo <img> at all.
   Both rules now explicitly target the actual photo element too. */

/* "Photo only" - when a person has no photo, the whole avatar column
   disappears (same as "None"), rather than leaving an empty tinted box.
   A real photo is a sibling of .person-icon, not inside it, so it's
   already untouched by this rule with no extra qualifier needed. */
body.picture-mode-photo_only .f3 .person-icon {
  display: none;
}

/* "None" - remove the whole avatar column, photo or fallback icon
   alike, so the text takes its width. */
body.picture-mode-none .f3 .person-icon,
body.picture-mode-none .f3 .card-inner > img {
  display: none;
}

/* Whenever the avatar column is actually hidden, the text area needs to
   explicitly grow into the freed space - family-chart's own card-label
   has no flex-grow by default, so without this it stays at its natural
   width and the card looks unbalanced (most visible on the stripe/bar
   accent, which then wraps a mismatched content area). */
body.picture-mode-none .f3 .card-label,
body.picture-mode-photo_only .f3 .person-icon + .card-label {
  flex: 1;
}

/* ---- Card layout: narrow (portrait, top-down stack) ---------------------
   "Wide" (default) is family-chart's own .card-image-rect markup, unchanged
   from the library's real shipped defaults (confirmed from its actual CSS,
   not assumed): 200px wide, flex row, 70x70 photo on the left, text
   stacked to its right. "Narrow" overrides that to a flex column (photo on
   top, then up to 6 stacked text rows below - given names, family names,
   DOB, DOD, email, social - built in app.js's buildCardDisplayRows), at
   roughly 2/3 the width and photo size. Reuses whichever of the 5 accent
   styles (stripe/top bar/etc.) is already chosen rather than a separate
   narrow-only treatment, so all existing card-style-* rules apply
   unchanged underneath this. */
body.card-layout-narrow .f3 div.card-inner.card-image-rect {
  width: 120px;
  min-height: 70px;
  flex-direction: column;
  align-items: center;
  padding-top: 8px;
  padding-bottom: 8px;
}
/* Photo/icon kept at the same 70px as wide - narrow's width still has
   room for it, and a matching size reads more consistently between the
   two layouts than scaling it down too. */
body.card-layout-narrow .f3 div.card-image-rect > .person-icon,
body.card-layout-narrow .f3 div.card-image-rect > img {
  width: 70px;
  height: 70px;
  margin-right: 0;
  margin-bottom: 6px;
}
body.card-layout-narrow .f3 div.card-image-rect div.card-label {
  height: auto;
  width: 100%;
  overflow: visible;
  align-items: center;
  text-align: center;
}
body.card-layout-narrow .f3 .card-label > div {
  width: 100%;
}
/* Narrow's per-field rows are much more likely to be empty for any given
   person (no nickname, no date of death, no email, ...) than wide's
   combined rows are, so a genuinely empty row collapses instead of
   leaving a blank line. Left unscoped to narrow specifically since it's
   a strict improvement for wide's own occasional empty contact row too,
   with no downside. */
.f3 .card-label > div:empty {
  display: none;
}

.style-picker {
  display: flex;
  gap: 6px;
  margin-bottom: 4px;
  flex-wrap: wrap;
}
.style-picker-btn {
  width: 40px;
  height: 28px;
  border-radius: 5px;
  border: 1px solid var(--paper-line);
  background: #fff;
  cursor: pointer;
  padding: 0;
  position: relative;
}
.style-picker-btn.active { outline: 2px solid var(--seal-slate); outline-offset: 1px; }

.saved-look-card {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px;
  margin-bottom: 6px;
  border: 1px solid var(--paper-line);
  border-radius: 6px;
  background: var(--card-bg);
  cursor: pointer;
  text-align: left;
}
.saved-look-card:hover { border-color: var(--seal-slate); }
.saved-look-preview {
  position: relative;
  width: 40px;
  height: 28px;
  flex-shrink: 0;
}
.saved-look-name {
  font-size: 12px;
  color: var(--ink);
}

/* ---- Card style: top bar (accent across the top edge, not the left) ----- */
body.card-style-top_bar .f3 div.card-male .card-inner,
body.card-style-top_bar .f3 div.card-female .card-inner,
body.card-style-top_bar .f3 div.card-genderless .card-inner {
  border-left-width: 1px;
  border-top-width: 5px;
  background-color: var(--card-bg);
}
body.card-style-top_bar .f3 div.card-male .card-inner {
  border-color: var(--male-border);
  border-top-color: var(--male-color);
}
body.card-style-top_bar .f3 div.card-female .card-inner {
  border-color: var(--female-border);
  border-top-color: var(--female-color);
}

/* ---- Card style: header band (name row filled, body stays white) -------
   card-inner's own padding is zeroed here rather than negatively offset,
   so the name row's background can touch the card's real top/right edges
   without guessing at exact padding values. The avatar keeps its own
   spacing regardless, since family-chart gives .person-icon its own
   padding/margin independent of card-inner's. */
body.card-style-header_band .f3 div.card-male .card-inner,
body.card-style-header_band .f3 div.card-female .card-inner,
body.card-style-header_band .f3 div.card-genderless .card-inner {
  border-left-width: 1px;
  background-color: var(--card-bg);
  overflow: hidden;
  padding: 0;
}
body.card-style-header_band .f3 div.card-male .card-inner { border-color: var(--male-border); }
body.card-style-header_band .f3 div.card-female .card-inner { border-color: var(--female-border); }
body.card-style-header_band .f3 .card-label > div:first-child {
  margin: 0;
  padding: 4px 8px;
  color: #fdfcf9;
  display: block;
  width: 100%;
  box-sizing: border-box;
}
body.card-style-header_band .f3 .inline-sex-mark {
  color: #fdfcf9 !important;
}
body.card-style-header_band .f3 div.card-male .card-label > div:first-child { background-color: var(--male-color); }
body.card-style-header_band .f3 div.card-female .card-label > div:first-child { background-color: var(--female-color); }
body.card-style-header_band .f3 .card-label > div:not(:first-child) {
  padding: 2px 8px 4px;
}
/* card-label is never actually stretched to the card's full height - its
   height:100% can't resolve without the parent using align-items:stretch,
   and family-chart's own card-image-rect container uses align-items:center
   instead. So card-label stays shrink-wrapped to its own content, and THAT
   whole (short) block is what gets vertically centered inside the taller
   card - pushing the colored name row away from the card's top edge.
   Overriding align-self here (not justify-content inside card-label, which
   has no effect since card-label is never actually tall enough to need
   internal centering) pins the block to the top instead. */
body.card-style-header_band .f3 .card-label {
  align-self: flex-start;
  justify-content: flex-start;
  flex: 1;
  min-width: 0;
}
/* Narrow layout only: the band also wraps the family-name row (row 2),
   not just given names (row 1). Wide already gets this for free - its
   family name lives inside the single combined name string in row 1's
   band - but narrow splits given/family names into two separate rows, so
   row 2 needs its own matching background. Scoped to narrow specifically:
   row 2 in wide is the DOB/DOD lifespan line, which must stay outside
   the band. */
body.card-layout-narrow.card-style-header_band .f3 .card-label > div:nth-child(2) {
  color: #fdfcf9;
  /* Closes a hairline seam that otherwise shows between the two adjacent
     same-color band rows - a sub-pixel rounding artifact from sitting
     inside the pan/zoom canvas's CSS transform. The 1px overlap is
     invisible against the continuous fill; padding-top compensates so
     the text itself doesn't visibly shift up. */
  margin-top: -1px;
  padding-top: 3px;
}
body.card-layout-narrow.card-style-header_band .f3 div.card-male .card-label > div:nth-child(2) { background-color: var(--male-color); }
body.card-layout-narrow.card-style-header_band .f3 div.card-female .card-label > div:nth-child(2) { background-color: var(--female-color); }

/* header_band's text color above is hardcoded to a fixed light color so
   it reads against the style's own colored band - but that hardcoding
   has no awareness of the dimmed state, and its specificity always wins
   over is-dimmed's own generic "mute everything" rule regardless. The
   band's *background* already correctly fades when dimmed (it's driven
   by the --male-color/--female-color custom properties, which is-dimmed
   redefines to --paper-line at the top of this file) - only the
   hardcoded text color was left behind. Overridden here, for both rows
   and the inline sex-mark symbol, whenever a header-band card is dimmed. */
body.card-style-header_band .f3 div.card.is-dimmed .card-label > div:first-child,
body.card-style-header_band .f3 div.card.is-dimmed .card-label > div:nth-child(2),
body.card-style-header_band .f3 div.card.is-dimmed .card-label > div:nth-child(2) strong,
body.card-style-header_band .f3 div.card.is-dimmed .inline-sex-mark {
  color: var(--ink-soft) !important;
}

/* ---- Card style: gradient (colour fades in from the left edge) ---------
   Previously an injected folded-corner triangle - that required
   re-injecting a real DOM element into every card after every redraw
   (family-chart recreates card markup on its own updates), which proved
   unreliable as more redraw paths were added (scope changes, focus
   changes, the overview). A pure CSS gradient is keyed off the same
   .card-male/.card-female classes family-chart itself always sets
   correctly, so there's nothing to fall out of sync. */
body.card-style-corner_tab .f3 div.card-male .card-inner,
body.card-style-corner_tab .f3 div.card-female .card-inner,
body.card-style-corner_tab .f3 div.card-genderless .card-inner {
  border: 1px solid var(--paper-line);
  background-color: var(--card-bg);
  overflow: hidden;
}
body.card-style-corner_tab .f3 div.card-male .card-inner {
  background-image: linear-gradient(to right, var(--male-border), transparent 85%);
}
body.card-style-corner_tab .f3 div.card-female .card-inner {
  background-image: linear-gradient(to right, var(--female-border), transparent 85%);
}

/* ---- Card style: vertical gradient (colour fades in from the top edge) -
   Same technique as the horizontal gradient above, just rotated - "to
   bottom" instead of "to right". Kept as its own style rather than an
   orientation-conditional variant of card-style-corner_tab, so both
   directions stay pickable independent of Wide/Narrow. */
body.card-style-gradient_vertical .f3 div.card-male .card-inner,
body.card-style-gradient_vertical .f3 div.card-female .card-inner,
body.card-style-gradient_vertical .f3 div.card-genderless .card-inner {
  border: 1px solid var(--paper-line);
  background-color: var(--card-bg);
  overflow: hidden;
}
body.card-style-gradient_vertical .f3 div.card-male .card-inner {
  background-image: linear-gradient(to bottom, var(--male-border), transparent 85%);
}
body.card-style-gradient_vertical .f3 div.card-female .card-inner {
  background-image: linear-gradient(to bottom, var(--female-border), transparent 85%);
}

/* ---- Card style: plain outline (thin border only, no accent shape) ------ */
body.card-style-outline .f3 div.card-male .card-inner,
body.card-style-outline .f3 div.card-female .card-inner,
body.card-style-outline .f3 div.card-genderless .card-inner {
  border-left-width: 1px;
  background-color: var(--card-bg);
}
body.card-style-outline .f3 div.card-male .card-inner { border-color: var(--male-color); }
body.card-style-outline .f3 div.card-female .card-inner { border-color: var(--female-color); }

/* "Handwritten" font pairing: upright, clear handwriting style - not a slanted cursive script */
body.font-handwritten .f3 .card-label > div:first-child {
  font-family: 'Patrick Hand', cursive;
  font-weight: 400;
  font-size: 15px;
}
body.font-handwritten.card-layout-narrow .f3 .card-label > div:nth-child(2) {
  font-family: 'Patrick Hand', cursive;
  font-weight: 400;
  font-size: 15px;
}
/* Patrick Hand (Handwritten) has no real bold face loaded - only its
   single regular weight is fetched (see the Google Fonts <link> in
   index.html/login.html, no ":wght@" for it). Forcing font-weight:700
   on a font with no matching loaded weight makes the browser either
   synthesize a fake bold or substitute a different font for just the
   bold text - either way it can look like a different typeface, which
   is exactly what this was meant to avoid (see the base rule near line
   607 that normally forces 700). Reset back to the font's own normal
   weight for this pairing specifically, in both card layouts. */
body.font-handwritten .f3 .card-label > div strong {
  font-weight: 400;
}

/* "Arial" font pairing: plain system font, no web-font loading needed */
body.font-arial .f3 .card-label > div:first-child {
  font-family: Arial, Helvetica, sans-serif;
  font-weight: 600;
}
body.font-arial.card-layout-narrow .f3 .card-label > div:nth-child(2) {
  font-family: Arial, Helvetica, sans-serif;
  font-weight: 600;
}
