/* ═══════════════════════════════════════════════════════════
   Heirloom
   Every value here comes from "Heirloom Design System v1".
   Where the design system states a number, that number is used.
   ═══════════════════════════════════════════════════════════ */

:root{
  /* colour */
  --parchment:#FBF8F3; --parchment-sunk:#F4EFE6; --white:#FFFFFF;
  --gold:#A8853F; --gold-deep:#7E6229; --gilt:#D9BE7F;
  --hairline:rgba(168,133,63,0.32); --hairline-soft:rgba(168,133,63,0.18);
  --walnut:#3B2E24; --bark:#6B5847; --ink:#241B14;
  --alarm:#8C3A22;

  --shadow-card:0 1px 2px rgba(59,46,36,0.06), 0 6px 18px -8px rgba(59,46,36,0.10);
  --shadow-lift:0 2px 4px rgba(59,46,36,0.08), 0 12px 26px -10px rgba(59,46,36,0.16);
  --shadow-modal:0 2px 4px rgba(59,46,36,0.06), 0 24px 60px -20px rgba(36,27,20,0.28);

  /* type */
  --display:'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --ui:'Inter', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --mono:'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  /* space — 4px base, no half steps */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px;
  --s-5:24px; --s-6:32px; --s-7:48px; --s-8:64px; --s-9:96px;

  --r-xs:2px; --r-s:4px; --r-m:8px;

  --dur-fast:120ms; --dur:200ms; --dur-slow:320ms;
  --ease-out:cubic-bezier(.2,.8,.25,1); --ease-pen:cubic-bezier(.16,.84,.24,1);

  /* photo normalization: only the default strength lives here. The derived
     values MUST be declared on the photo element itself — a var() inside a
     :root declaration resolves against :root's --s, so deriving them here
     would freeze every face at 0.5 no matter what the person's slider says. */
  --s:0.5;
}

*{ box-sizing:border-box; }
html,body{ height:100%; }
body{
  margin:0; background:var(--parchment); color:var(--walnut);
  font-family:var(--ui); -webkit-font-smoothing:antialiased;
  overscroll-behavior:none;
}
p{ text-wrap:pretty; }
button{ font:inherit; color:inherit; }
[hidden]{ display:none !important; }

/* ── type roles ───────────────────────────────────────────── */
.t-wordmark   { font:300 56px/1 var(--display); letter-spacing:-0.03em; color:var(--ink); }
.t-display-xl { font:300 44px/1.06 var(--display); letter-spacing:-0.025em; color:var(--ink); }
.t-display-l  { font:400 34px/1.10 var(--display); letter-spacing:-0.015em; color:var(--ink); }
.t-display-m  { font:400 26px/1.20 var(--display); letter-spacing:-0.01em; color:var(--walnut); }
.t-display-s  { font:400 19px/1.15 var(--display); color:var(--walnut); }
.t-italic     { font:300 italic 20px/1.3 var(--display); color:var(--bark); }
.t-title      { font:600 18px/1.35 var(--ui); letter-spacing:-0.005em; color:var(--ink); }
.t-body       { font:400 15px/1.6 var(--ui); color:var(--walnut); }
.t-meta       { font:400 13px/1.45 var(--ui); color:var(--bark); }
.t-eyebrow    { font:600 11px/1 var(--ui); letter-spacing:0.08em; text-transform:uppercase; color:var(--bark); }
.t-mono       { font:400 12.5px/1.5 var(--mono); color:var(--walnut); }

/* ── focus, everywhere, one rule ──────────────────────────── */
:where(button, input, textarea, [tabindex]):focus{ outline:none; }
:where(button, input, textarea, [tabindex]):focus-visible{
  outline:none;
  box-shadow:0 0 0 2px var(--parchment), 0 0 0 4px var(--gold);
  border-radius:var(--r-s);
}

/* ── buttons ──────────────────────────────────────────────── */
.btn{
  height:40px; padding:0 var(--s-4); display:inline-flex; align-items:center;
  justify-content:center; gap:var(--s-2);
  font:500 14px/1 var(--ui); border-radius:var(--r-s); border:1px solid transparent;
  background:none; cursor:pointer; white-space:nowrap;
  transition:background var(--dur-fast) var(--ease-out),
             border-color var(--dur-fast) var(--ease-out),
             color var(--dur-fast) var(--ease-out);
}
.btn-primary  { background:var(--walnut); color:var(--parchment); }
.btn-primary:hover  { background:var(--ink); }
.btn-secondary{ background:var(--white); color:var(--walnut); border-color:var(--hairline); }
.btn-secondary:hover{ background:var(--parchment-sunk); border-color:var(--gold); }
.btn-quiet    { color:var(--bark); }
.btn-quiet:hover{ background:var(--parchment-sunk); color:var(--walnut); }
.btn-danger   { color:var(--alarm); border-color:rgba(140,58,34,0.3); }
.btn-danger:hover{ background:rgba(140,58,34,0.06); }
.btn:disabled{ opacity:.45; cursor:not-allowed; }
.btn:disabled:hover{ background:inherit; }
.btn-sm{ height:32px; padding:0 var(--s-3); font-size:13px; }
@media (pointer:coarse){ .btn{ height:44px; } .btn-sm{ height:38px; } }

.plus{ width:13px; height:13px; position:relative; flex:0 0 auto; }
.plus::before,.plus::after{ content:''; position:absolute; background:currentColor; }
.plus::before{ left:6px; top:0; width:1.5px; height:13px; }
.plus::after { top:6px; left:0; height:1.5px; width:13px; }

/* ── fields ───────────────────────────────────────────────── */
.field{ display:block; margin-bottom:var(--s-4); }
.field > .lab{ display:block; font:600 11px/1 var(--ui); letter-spacing:0.08em;
  text-transform:uppercase; color:var(--bark); margin-bottom:var(--s-2); }
.inp{
  width:100%; height:40px; padding:0 12px; background:var(--white);
  border:1px solid var(--hairline); border-radius:var(--r-s);
  font:400 15px/1.6 var(--ui); color:var(--walnut);
}
textarea.inp{ height:auto; min-height:84px; padding:10px 12px; resize:vertical; line-height:1.6; }
.inp::placeholder{ color:rgba(107,88,71,0.5); }
.inp:hover{ border-color:var(--gold); }
.hint{ font:400 13px/1.45 var(--ui); color:var(--bark); margin-top:var(--s-2); }
.hint em{ font-style:normal; color:var(--walnut); font-weight:500; }
.hint-bad{ color:var(--alarm); }
.inp-bad{ border-color:var(--alarm); }

.pills{ display:flex; flex-wrap:wrap; gap:var(--s-2); }
.pill{
  height:34px; padding:0 14px; display:inline-flex; align-items:center;
  border:1px solid var(--hairline); border-radius:17px; background:var(--white);
  font:400 14px/1 var(--ui); color:var(--walnut); cursor:pointer;
  transition:all var(--dur-fast) var(--ease-out);
}
.pill:hover{ background:var(--parchment-sunk); }
.pill[aria-pressed="true"]{ background:var(--walnut); color:var(--parchment); border-color:var(--walnut); }

/* ═══ the cameo ═══════════════════════════════════════════════
   Mat is real padding on a white surface, never a border.
   ratio 4:5 · mat 8% of frame width (min 6, max 14) · ring 1px
   ══════════════════════════════════════════════════════════ */
.cameo{
  width:var(--w,96px); flex:0 0 auto;
  background:var(--white); padding:calc(var(--w,96px) * 0.08);
  border-radius:50% 50% 8px 8px / 34% 34% 6px 6px;
  box-shadow:0 0 0 1px var(--hairline), var(--shadow-card);
  transition:box-shadow var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.cameo-well{
  /* s = strength, 0 → 1, default 0.5. Set per person as --warmth. */
  --s:var(--warmth, 0.5);
  --norm-sat:      calc(1 - 0.52 * var(--s));
  --norm-contrast: calc(1 + 0.12 * var(--s));
  --norm-bright:   calc(1 + 0.06 * var(--s));
  --norm-sepia:    calc(0.32 * var(--s));
  --norm-veil:     calc(0.40 * var(--s));
  width:100%; aspect-ratio:4/5; overflow:hidden; position:relative;
  border-radius:50% 50% 4px 4px / 38% 38% 3px 3px;
  background:var(--parchment);
  box-shadow:inset 0 0 0 1px var(--hairline-soft);
  display:flex; align-items:center; justify-content:center;
}
.cameo-well img{
  width:100%; height:100%; object-fit:cover; display:block;
  object-position:var(--pos, 50% 30%);
  /* The contrast/brightness/saturate/sepia stack is set inline by
     normFilter() in photo.js — see the comment there for why it is not
     written here. The --norm-* tokens above still drive the veil. */
}
/* The warm veil. Opacities taken from the design system's rendered specimens
   (0.20 / 0.13 at s = 0.5, 0.40 / 0.26 at s = 1) rather than from the extra
   × 0.5 / × 0.32 in its code block, which would halve every specimen shown. */
.cameo-well.has-photo::after{
  content:''; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(178deg,
    rgba(217,190,127, var(--norm-veil)),
    rgba(168,133,63,  calc(var(--norm-veil) * 0.65)));
  mix-blend-mode:multiply;
}
.monogram{
  font-family:var(--display); font-weight:300; line-height:1;
  letter-spacing:0.02em; color:var(--gold);
  font-size:calc(var(--w,96px) * 0.36);
}
/* the one place the system changes colour on a size breakpoint */
.cameo.small .monogram{ color:var(--bark); font-weight:400; }

.cameo.dashed{ box-shadow:none; }
.cameo.dashed .cameo-well{ box-shadow:inset 0 0 0 1px var(--hairline-soft); }
.cameo.dashed{ outline:1px dashed var(--gold); outline-offset:0;
  border-radius:50% 50% 8px 8px / 34% 34% 6px 6px; }

/* ═══ the canvas ══════════════════════════════════════════ */
#app{ display:flex; flex-direction:column; height:100dvh; }

.topbar{
  flex:0 0 auto; height:72px; display:flex; align-items:center; gap:var(--s-5);
  padding:0 var(--s-6); background:var(--parchment);
  border-bottom:1px solid var(--hairline); z-index:20;
}
.topbar .wm{ font:300 26px/1 var(--display); letter-spacing:-0.02em; color:var(--ink); }
.topbar .rule{ width:1px; height:28px; background:var(--hairline); }
.topbar .who{ display:flex; flex-direction:column; gap:3px; min-width:0; }
.topbar .who b{ font:400 19px/1.1 var(--display); font-weight:400; color:var(--ink);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.spacer{ flex:1 1 auto; }

.chip-lock{
  display:inline-flex; align-items:center; gap:8px; height:28px; padding:0 12px;
  border-radius:14px; background:var(--white);
  font:600 11px/1 var(--ui); letter-spacing:0.08em; text-transform:uppercase; color:var(--bark);
  border:1px dashed var(--gold);
}
.chip-lock.open{ border-style:solid; border-color:var(--gold); color:var(--walnut); }

.stage{ position:relative; flex:1 1 auto; overflow:hidden; touch-action:none; cursor:grab; }
.stage.dragging{ cursor:grabbing; }
.stage-inner{ position:absolute; left:0; top:0; transform-origin:0 0; will-change:transform; }
#branches{ position:absolute; left:0; top:0; overflow:visible; pointer-events:none; }

/* generation markers, in the left margin of the canvas */
.genmark{ position:absolute; left:34px; display:flex; align-items:baseline; gap:10px;
  transform:translateY(-50%); white-space:nowrap; pointer-events:none; }
.genmark i{ font:300 34px/1 var(--display); font-style:normal; color:var(--gold); }
.genmark span{ font:600 11px/1 var(--ui); letter-spacing:0.08em; text-transform:uppercase; color:var(--bark); }

/* ── a person in the tree ─────────────────────────────────── */
.node{
  position:absolute; width:110px; margin-left:-55px;
  display:flex; flex-direction:column; align-items:center;
  background:none; border:none; padding:0; cursor:pointer; text-align:center;
}
.node .nm{ font:400 19px/1.2 var(--display); color:var(--walnut); margin-top:10px;
  overflow-wrap:anywhere; }   /* a 40-letter surname must not shove the row */
.node .dt{ font:400 12px/1.4 var(--ui); color:var(--bark); }
.node:hover .cameo{ box-shadow:0 0 0 1px var(--gold), var(--shadow-lift); transform:translateY(-2px); }
.node:hover .nm{ color:var(--ink); }
.node:hover .cameo-well{ --s:calc(var(--warmth, 0.5) * 0.7); }
.node.selected .cameo{ box-shadow:0 0 0 1.5px var(--gold), 0 2px 4px rgba(59,46,36,0.06), 0 16px 34px -14px rgba(59,46,36,0.20); }
.node.faded{ opacity:.34; transition:opacity var(--dur) var(--ease-out); }

.node-add .cameo{ background:none; box-shadow:none; }
.node-add .cameo-well{ background:none; box-shadow:inset 0 0 0 1px var(--hairline);
  border:1px dashed var(--gold); color:var(--gold); }
.node-add:hover .cameo-well{ background:var(--parchment-sunk); }
.node-add .nm{ font:400 14px/1.3 var(--ui); color:var(--bark); }

/* the marriage rule + diamond live in the SVG layer */

/* ── detail panel ─────────────────────────────────────────── */
.panel{
  position:absolute; right:0; top:0; bottom:0; width:380px; z-index:15;
  background:var(--white); border-left:1px solid var(--hairline);
  box-shadow:var(--shadow-modal); overflow-y:auto;
  transform:translateX(100%); transition:transform var(--dur-slow) var(--ease-out);
  display:flex; flex-direction:column;
}
.panel.open{ transform:none; }
.panel-head{ padding:var(--s-5) var(--s-5) var(--s-4); border-bottom:1px solid var(--hairline-soft);
  display:flex; gap:var(--s-4); align-items:flex-start; }
.panel-body{ padding:var(--s-5); flex:1 1 auto; }
.panel-foot{ padding:var(--s-4) var(--s-5); border-top:1px solid var(--hairline-soft);
  display:flex; gap:var(--s-2); flex-wrap:wrap; background:var(--parchment); }
.panel .close{ position:absolute; right:var(--s-3); top:var(--s-3); width:32px; height:32px;
  border:none; background:none; cursor:pointer; color:var(--bark); border-radius:var(--r-s); }
.panel .close:hover{ background:var(--parchment-sunk); color:var(--ink); }

.rowset{ border-top:1px solid var(--hairline-soft); margin-top:var(--s-5); padding-top:var(--s-4); }
.rowset > .t-eyebrow{ margin-bottom:var(--s-3); display:block; }
.rel{ display:flex; align-items:center; gap:var(--s-3); padding:7px 0; }
.rel .nm{ font:400 15px/1.3 var(--ui); color:var(--walnut); flex:1 1 auto;
  text-align:left; background:none; border:none; cursor:pointer; padding:0; }
.rel .nm:hover{ color:var(--ink); text-decoration:underline; text-decoration-color:var(--hairline);
  text-underline-offset:3px; }
.rel .kind{ font:400 12px/1 var(--ui); color:var(--bark); }

.attrib{ display:flex; align-items:center; gap:10px; padding:6px 0; }
.attrib .av{ width:22px; height:22px; border-radius:50%; flex:0 0 auto;
  background:var(--parchment-sunk); border:1px solid var(--hairline-soft);
  display:flex; align-items:center; justify-content:center;
  font:400 11px/1 var(--display); color:var(--gold-deep); }
.attrib .tx{ font:400 13px/1.45 var(--ui); color:var(--bark); }
.attrib .tx b{ font-weight:500; color:var(--walnut); }

/* ── modal ────────────────────────────────────────────────── */
.scrim{ position:fixed; inset:0; background:rgba(36,27,20,0.42); z-index:60;
  display:flex; align-items:center; justify-content:center; padding:var(--s-5);
  animation:fade var(--dur) var(--ease-out); }
@keyframes fade{ from{ opacity:0 } }
.modal{
  background:var(--white); border-radius:var(--r-m); box-shadow:var(--shadow-modal);
  width:min(560px, 100%); max-height:min(88dvh, 860px); overflow-y:auto;
  border:1px solid var(--hairline-soft);
}
.modal-head{ padding:var(--s-5) var(--s-6) var(--s-4); }
.modal-body{ padding:0 var(--s-6) var(--s-5); }
.modal-foot{ padding:var(--s-4) var(--s-6); border-top:1px solid var(--hairline-soft);
  display:flex; justify-content:flex-end; gap:var(--s-2); background:var(--parchment);
  border-radius:0 0 var(--r-m) var(--r-m); position:sticky; bottom:0; }

/* ── photo studio ─────────────────────────────────────────── */
.studio{ display:grid; grid-template-columns:minmax(0,1fr) 190px; gap:var(--s-5); align-items:start; }
.reticle-wrap{ position:relative; background:var(--parchment-sunk); border-radius:var(--r-s);
  overflow:hidden; border:1px solid var(--hairline-soft); cursor:crosshair; touch-action:none; }
.reticle-wrap img{ display:block; width:100%; height:auto; }
.reticle-ghost{ position:absolute; inset:0; pointer-events:none;
  background:rgba(36,27,20,0.46); }
.reticle-hole{ position:absolute; pointer-events:none;
  border-radius:50% 50% 4px 4px / 38% 38% 3px 3px;
  box-shadow:0 0 0 9999px rgba(36,27,20,0.46), 0 0 0 1px var(--gilt) inset;
  outline:1px solid var(--gilt); }
.reticle-dot{ position:absolute; width:18px; height:18px; margin:-9px 0 0 -9px;
  border-radius:50%; border:1.5px solid var(--white); background:rgba(168,133,63,0.9);
  box-shadow:0 0 0 1px rgba(36,27,20,0.35); pointer-events:none; }

.dropzone{ border:1px dashed var(--gold); border-radius:var(--r-s); background:var(--white);
  padding:var(--s-7) var(--s-5); text-align:center; cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out); }
.dropzone:hover{ background:var(--parchment-sunk); }
.dropzone.over{ background:var(--parchment-sunk); border-style:solid; }

.warmth{ display:flex; gap:0; border:1px solid var(--hairline); border-radius:var(--r-s);
  overflow:hidden; background:var(--white); }
.warmth button{ flex:1 1 0; height:36px; border:none; background:none; cursor:pointer;
  font:400 12.5px/1 var(--mono); color:var(--bark); border-right:1px solid var(--hairline-soft); }
.warmth button:last-child{ border-right:none; }
.warmth button:hover{ background:var(--parchment-sunk); }
.warmth button[aria-pressed="true"]{ background:var(--walnut); color:var(--parchment); }

/* ── toast ────────────────────────────────────────────────── */
.toast{
  position:fixed; left:50%; bottom:var(--s-6); transform:translateX(-50%);
  z-index:90; max-width:min(520px, calc(100vw - 32px));
  display:flex; align-items:center; gap:10px; padding:11px 18px;
  border-radius:var(--r-s); font:400 14px/1.4 var(--ui);
  background:var(--ink); color:var(--parchment); box-shadow:var(--shadow-modal);
  animation:toastin var(--dur) var(--ease-out);
}
.toast.info{ background:var(--white); color:var(--walnut); border:1px solid var(--hairline); }
.toast.bad{ background:var(--alarm); color:#fff; }
@keyframes toastin{ from{ opacity:0; transform:translate(-50%, 8px) } }

/* ── the gate ─────────────────────────────────────────────── */
#gate{ min-height:100dvh; display:flex; align-items:center; justify-content:center;
  padding:var(--s-5); background:var(--parchment); }
.gate-card{ width:min(420px, 100%); text-align:center; position:relative; padding:var(--s-8) var(--s-5); }
.gate-orn{ position:absolute; inset:0; pointer-events:none; }
.gate-orn i{ position:absolute; width:64px; height:64px; }
.gate-orn i:nth-child(1){ left:0; top:0; border-left:1px solid var(--gold); border-top:1px solid var(--gold); }
.gate-orn i:nth-child(2){ right:0; top:0; border-right:1px solid var(--gold); border-top:1px solid var(--gold); }
.gate-orn i:nth-child(3){ left:0; bottom:0; border-left:1px solid var(--gold); border-bottom:1px solid var(--gold); }
.gate-orn i:nth-child(4){ right:0; bottom:0; border-right:1px solid var(--gold); border-bottom:1px solid var(--gold); }
.rule-di{ display:flex; align-items:center; justify-content:center; gap:14px; margin:var(--s-5) 0; }
.rule-di span{ width:52px; height:1px; background:var(--gold); }
.rule-di i{ width:7px; height:7px; background:var(--gilt); transform:rotate(45deg); }

/* ── empty / first run ────────────────────────────────────── */
.firstrun{ position:absolute; inset:0; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:var(--s-4); text-align:center; padding:var(--s-5); }

/* dates go quiet below 0.7 zoom — the name still has to read */
body.zoomed-out .node .dt{ display:none; }

/* ── mobile spine (< 480px) ───────────────────────────────── */
.spine{ position:relative; padding:var(--s-4) 0 200px; }
.spine-svg{ position:absolute; left:0; top:0; overflow:visible; pointer-events:none; }
.srow{
  position:absolute; display:flex; align-items:center; gap:12px; height:56px;
  background:none; border:none; padding:0 8px 0 0; cursor:pointer; text-align:left;
  width:auto; min-width:0;   /* max-width is set per row in renderSpine() */
}
.srow > div{ min-width:0; }
/* A very long surname ellipses rather than dragging the row off the screen. */
.srow .nm{ font:400 17px/1.15 var(--display); color:var(--walnut);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.srow .dt{ font:400 12px/1.4 var(--ui); color:var(--bark); }

@media (max-width:1199px){
  .panel{ right:0; left:0; top:auto; width:auto; height:min(72dvh, 620px);
    border-left:none; border-top:1px solid var(--hairline);
    border-radius:14px 14px 0 0; transform:translateY(100%); }
  .panel.open{ transform:none; }
}
/* the phone scrolls; it does not pan */
@media (max-width:479px){
  .stage{ overflow-y:auto; overflow-x:hidden; touch-action:pan-y; cursor:default;
    -webkit-overflow-scrolling:touch; }
  .stage-inner{ position:relative; transform:none !important; }
  .genmark{ display:none; }
}

@media (max-width:767px){
  .topbar{ height:60px; padding:0 var(--s-4); gap:var(--s-3); }
  .topbar .wm{ font-size:22px; }
  .topbar .who{ min-width:0; }
  .topbar .who b{ font-size:16px; }
  /* Four controls plus a wordmark do not fit at 380px. The page is obviously
     Heirloom without saying so; the tree scrolls rather than pans, so "Fit"
     has no job. The lock chip stays — it is how a viewer knows where they
     stand — and the Add button keeps its verb, losing only its noun. */
  .topbar .wm, .topbar .rule, #btn-fit, .topbar .who span{ display:none; }
  .long{ display:none; }
  #btn-history{ padding:0 var(--s-3); }
  .studio{ grid-template-columns:1fr; }
  .t-display-xl{ font-size:32px; }
  .t-display-l{ font-size:28px; }
  .t-eyebrow{ font-size:12px; }
}

@media (prefers-reduced-motion: reduce){
  *{ animation-duration:1ms !important; transition-duration:1ms !important; }
  [data-draw]{ stroke-dashoffset:0 !important; }
}
@keyframes drawBranch{ to{ stroke-dashoffset:0; } }
