/* sites/public/src/assets/public.css — Lattice register.
   Every value below is a var(--…) token defined in brand/tokens.css.
   Do not hardcode colour, font, size, or spacing here.

   This surface deliberately shares its design language with the workshop hub
   (docs/materials/source/longevity-v2-site): warm paper ground, a system
   display serif at display scale, drawn instrument geometry, and everything
   else kept quiet so those two carry the page. Only the content differs. */

*, *::before, *::after { box-sizing: border-box; }

/* .site-header is sticky, so in-page anchors must clear it: its vertical
   padding (2 x --sp-3) plus the brand lockup — wordmark, two-line couplet and
   attribution — with headroom. */
/* The ground and its wash live on <html>, NOT on <body>. body::before draws the
   grain at z-index -1, and a negative-z-index child paints after the root
   element's background but BEFORE any in-flow descendant's background — so a
   background on <body> would sit on top of the grain and erase it. */
html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--sp-16) + var(--sp-12));
  background-color: var(--ground);
  background-image: var(--wash);
}

body {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: var(--leading-body);
  overflow-x: hidden;
}

/* Paper grain. Fixed, behind everything, and purely optical: it is what stops
   a flat cream fill from reading as an unstyled default background. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: var(--grain);
  background-size: 73px 61px, 109px 89px;
  opacity: var(--grain-opacity);
  mix-blend-mode: var(--grain-blend);
}

a { color: var(--accent-text); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: fixed; z-index: 200; top: var(--sp-3); left: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-radius: var(--radius);
  background: var(--ink); color: var(--ground); text-decoration: none;
  transform: translateY(-200%); transition: transform .2s var(--ease);
}
.skip-link:focus { transform: translateY(0); }

.page-shell { width: min(100%, var(--content)); margin-inline: auto; padding-inline: var(--gutter); }

/* ---- header: an identity lockup, not a line of body text ---------------- */

.site-header {
  position: sticky; top: 0; z-index: 100;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-4); row-gap: var(--sp-2);
  padding: var(--sp-3) var(--gutter);
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}

.brand { display: flex; align-items: center; gap: var(--sp-3); text-decoration: none; color: var(--ink); }

/* The mark is the wordmark's own capital L set in the display face inside a
   hairline ring — derived from the type-only wordmark rather than inventing a
   logo, and a single element to swap when the owner picks one. */
.brand-mark {
  flex: none; width: 2.4rem; height: 2.4rem;
  display: grid; place-items: center;
  border: 1px solid var(--ink); border-radius: 50%;
  font-family: var(--font-display); font-size: var(--step-1); line-height: 1;
  color: var(--accent-text);
}

.brand-copy { display: block; min-width: 0; }
.wordmark {
  display: block;
  font-family: var(--font-display); font-size: var(--step-2); font-weight: 500;
  line-height: 1.05; letter-spacing: -.02em;
}
.wordmark b { font-weight: 500; color: var(--accent-text); }

.epigraph {
  display: block; margin-top: var(--sp-1);
  font-family: var(--font-display); font-style: italic;
  font-size: var(--step--1); line-height: 1.25; color: var(--ink-2);
}
.epigraph span { display: block; }
.epigraph cite {
  display: block; margin-top: var(--sp-1);
  font-family: var(--font-sans); font-style: normal;
  font-size: .85em; letter-spacing: var(--tracking-caps); text-transform: uppercase;
}

.header-end { display: flex; align-items: center; gap: var(--sp-2); }

.site-nav { display: flex; align-items: center; gap: var(--sp-1); }
.site-nav a {
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius);
  color: var(--ink-2); text-decoration: none;
  font-size: var(--step--1); font-weight: 700;
}
.site-nav a:hover { background: var(--ink); color: var(--ground); }

/* The toggle only exists where it can work: the inline head script sets
   data-js, so scripting-off visitors never meet a dead button. */
.theme-toggle { display: none; }
:root[data-js] .theme-toggle {
  display: inline-grid; place-items: center;
  width: 2.6rem; height: 2.6rem; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: transparent; color: var(--ink); cursor: pointer;
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.theme-toggle:hover { border-color: var(--accent); color: var(--accent-text); }
.theme-toggle .icon { display: block; width: 1.25rem; height: 1.25rem; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }

/* ---- hero: copy left, diagram and chat right, hairline between ---------- */

.hero { padding-block: var(--hero-pad); }
.hero-grid {
  display: grid; grid-template-columns: minmax(0, .95fr) minmax(24rem, 1.05fr);
  gap: clamp(var(--sp-8), 5vw, var(--sp-16));
  align-items: center;
}

.hero-copy { min-width: 0; }
.hero-copy h1 {
  margin: 0;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--step-5); line-height: var(--leading-display);
  letter-spacing: var(--tracking-display); text-wrap: balance;
}
/* The hub's split: the subtitle is a second display line at full ink, the lede
   is body prose stepped back to --ink-2. */
.hero-subtitle {
  margin: var(--sp-6) 0 0; max-width: 24ch;
  font-family: var(--font-display); font-weight: 400;
  font-size: var(--step-2); line-height: 1.25; color: var(--ink);
}
.hero-lede {
  margin: var(--sp-6) 0 0; max-width: 44ch;
  font-size: var(--step-1); line-height: 1.5; color: var(--ink-2);
}

/* The vertical rule is the left/right separation the hub uses; the padding is
   the air the diagram needs to stop reading as cramped against the text. */
.hero-side {
  display: grid; gap: var(--sp-12); min-width: 0;
  padding-left: clamp(var(--sp-6), 3vw, var(--sp-12));
  border-left: 1px solid var(--line);
}

/* grid-template-columns is explicit (not left to auto-placement) so children —
   including, below 900px, .orbit's un-boxed contents once it becomes
   display:contents — stack one per row instead of spreading into one row of
   implicit columns. */
.hero-diagram {
  display: grid; grid-template-columns: 1fr;
  gap: var(--sp-8); justify-items: center; min-width: 0;
}

/* ---- the orbit ---------------------------------------------------------- */
/* A square field: drawn geometry underneath, the centre fixed in the middle,
   agent nodes on the ring at the angles lib/orbit.mjs computed. */

.orbit { position: relative; width: min(34rem, 100%); aspect-ratio: 1; margin-inline: auto; }

/* `fill` inherits in SVG, so the default is set once on the root and the one
   filled shape — the arrowhead — overrides it. A `.orbit-canvas path` reset
   would out-specify `.orbit-arrow` and silently erase the arrowheads. */
.orbit-canvas { position: absolute; inset: 0; width: 100%; height: 100%; fill: none; }

/* Construction geometry — dashed, quiet, and deliberately drawn rather than
   implied. The dial is exact: pathLength normalises the circumference to 240
   units, so `1 9` is 24 ticks however the box is sized. */
.orbit-dial { stroke: var(--line-soft); stroke-width: 7; }
.orbit-bound { stroke: var(--line-soft); stroke-width: 1; }
.orbit-ring { stroke: var(--line-soft); stroke-width: 1; stroke-dasharray: 2 6; }
.orbit-core-ring { stroke: var(--line-soft); stroke-width: 1; stroke-dasharray: 2 6; }

/* The loop itself: one solid, directed arc per agent, laid over the dashed
   construction ring. Each arc sits in the gap the node cards leave, so it is
   visible at every viewport where the diagram is drawn at all. Focusing or
   hovering an agent lights the segment of the loop it acts on. */
.orbit-arc { stroke: var(--line); stroke-width: 1.5; stroke-linecap: round; }
.orbit-arrow { fill: var(--line); stroke: none; }
.orbit-arc, .orbit-arrow { transition: stroke .2s var(--ease), fill .2s var(--ease); }
.orbit-edge.is-live .orbit-arc { stroke: var(--accent); stroke-width: 2.6; }
.orbit-edge.is-live .orbit-arrow { fill: var(--accent); }

/* The only thing that moves: a single short arc travelling the loop once every
   34 seconds. A signal circulating, not a spinner. */
.orbit-signal {
  stroke: var(--accent); stroke-width: 2; stroke-linecap: round; opacity: .55;
  animation: orbit-signal 34s linear infinite;
}
@keyframes orbit-signal { from { stroke-dashoffset: 0; } to { stroke-dashoffset: -100; } }

/* The focal point: a solid disc, reversed out, ringed in coral. Everything
   around it is quiet so the eye lands here first. */
.orbit-centre {
  position: absolute; inset: 34%; border-radius: 50%;
  display: grid; place-content: center; text-align: center;
  padding: var(--sp-2);
  background: var(--ink); color: var(--ground);
  box-shadow: 0 0 0 .8rem var(--accent-soft);
}
.orbit-centre strong {
  display: block; font-family: var(--font-display); font-weight: 500;
  font-size: var(--step-1); line-height: 1; letter-spacing: .02em;
}
.orbit-centre small {
  display: block; margin-top: var(--sp-2); max-width: 14ch;
  font-size: .58em; line-height: 1.3;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
}

/* Nodes: quiet cards on a light ground, all four the same size regardless of
   how long the name is — an uneven ring is what made this read as four
   floating boxes rather than a diagram. */
.orbit-node {
  position: absolute;
  left: calc(50% + var(--x) * 36%);
  top: calc(50% + var(--y) * 36%);
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-1);
  width: 8.5rem; min-height: 5.4rem; padding: var(--sp-2) var(--sp-3);
  font: inherit; font-size: var(--step--1); line-height: 1.25; text-align: center;
  color: var(--ink); background: var(--raised);
  border: 1px solid var(--line); border-radius: var(--radius-surface);
  box-shadow: var(--shadow); cursor: pointer;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), transform .2s var(--ease);
}
.orbit-node:hover, .orbit-node:focus-visible, .orbit-node[aria-expanded="true"] {
  border-color: var(--accent); box-shadow: var(--shadow-lift); transform: translate(-50%, -50%) scale(1.04);
}
.orbit-name { font-weight: 700; }

/* The forthcoming marker. It must be unmissable when the node is read and
   subordinate to the name that it annotates — a status line, not a badge that
   outranks the thing it describes. */
.pill {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  font-family: var(--font-mono); font-size: .78em; font-weight: 400;
  letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--ink-2);
}
.pill::before {
  content: ""; width: .38em; height: .38em; border-radius: 50%;
  background: var(--accent); flex: none;
}

/* The detail region reserves its height structurally rather than by a magic
   number: the hint and every body share one grid cell, so the container is
   always exactly as tall as its tallest child, regardless of copy edits or
   agent count — opening one never reflows the hero. */
.orbit-detail {
  display: grid; width: 100%; max-width: 34rem;
  padding: var(--sp-4) var(--sp-6);
  border-left: 3px solid var(--accent);
  background: var(--raised);
}
.orbit-detail-hint, .orbit-detail-body { grid-area: 1 / 1; }
.orbit-detail-hint { margin: 0; color: var(--ink-2); font-size: var(--step--1); }
.orbit-detail-body { transition: opacity .2s var(--ease); }
/* Keep hidden bodies participating in grid layout — so the container still
   measures their height — while removing them from paint, the accessibility
   tree and tab order. opacity:0 would not do the latter two. */
.orbit-detail-body[hidden] { display: block; visibility: hidden; }
.orbit-detail-body h2 {
  font-family: var(--font-display); font-weight: 500; font-size: var(--step-1);
  line-height: 1.1; margin: 0 0 var(--sp-2);
}
.orbit-detail-body p { margin: 0; color: var(--ink-2); font-size: var(--step--1); line-height: 1.55; }

/* ---- the ask panel, under the orbit in the right column ----------------- */

.ask-panel { border-top: 1px solid var(--line-soft); padding-top: var(--sp-8); }
.ask-panel h2 {
  margin: 0 0 var(--sp-6); max-width: 18ch;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--step-3); line-height: var(--leading-heading);
  letter-spacing: -.03em; text-wrap: balance;
}

.chat { display: flex; gap: var(--sp-3); }
.chat input {
  flex: 1 1 auto; min-width: 0; font: inherit; font-size: var(--step-0);
  color: var(--ink); background: var(--raised); border: 1px solid var(--line);
  border-radius: var(--radius-surface); padding: var(--sp-3) var(--sp-4); min-height: 3rem;
}
.chat button {
  flex: none; font: inherit; font-size: var(--step-0); font-weight: 700;
  color: var(--accent-ink); background: var(--accent-fill); border: 1px solid var(--accent-fill);
  border-radius: var(--radius); padding: var(--sp-3) var(--sp-6);
  min-height: 3rem; cursor: pointer;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.chat button:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.chat-note {
  font-size: var(--step--1); color: var(--ink-2);
  margin: var(--sp-3) 0 0; max-width: var(--measure);
}
.chat-log { display: grid; gap: var(--sp-2); margin-top: var(--sp-6); }
.chat-log:empty { margin-top: 0; }
.msg {
  margin: 0; padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-surface);
  max-width: 90%; font-size: var(--step--1); line-height: 1.55;
}
.msg-you { background: var(--ink); color: var(--ground); justify-self: end; }
.msg-bot { background: var(--raised); border: 1px solid var(--line); justify-self: start; }

/* ---- bands -------------------------------------------------------------- */

.band { padding-block: var(--band-pad); border-top: 1px solid var(--line-soft); }
.band.alt { background: var(--raised); }
.band h2 {
  margin: 0 0 var(--sp-8); max-width: 16ch;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--step-4); line-height: var(--leading-heading);
  letter-spacing: -.045em; text-wrap: balance;
}

.kicker {
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--accent-text); margin: 0 0 var(--sp-4);
}

.cards {
  list-style: none; margin: 0 0 var(--sp-16); padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: var(--sp-6);
}
.card {
  background: var(--ground); border: 1px solid var(--line); border-radius: var(--radius-surface);
  padding: var(--sp-8); display: flex; flex-direction: column; gap: var(--sp-3);
  min-width: 0; box-shadow: var(--shadow);
}
.card h3 {
  margin: 0; font-family: var(--font-display); font-weight: 500;
  font-size: var(--step-2); line-height: 1.1; letter-spacing: -.02em;
}
.card p { margin: 0; color: var(--ink-2); }

.lead { display: grid; gap: var(--sp-4); max-width: 34rem; }
.lead label {
  display: grid; gap: var(--sp-1); font-size: var(--step--1); font-weight: 700;
  letter-spacing: .04em; color: var(--ink-2);
}
.lead input, .lead textarea {
  font: inherit; font-size: var(--step-0); font-weight: 400; letter-spacing: normal;
  color: var(--ink); background: var(--ground);
  border: 1px solid var(--line); border-radius: var(--radius-surface);
  padding: var(--sp-3) var(--sp-4); min-height: 3rem;
}
.lead textarea { min-height: 7rem; resize: vertical; border-radius: var(--radius-surface); }
.lead button {
  font: inherit; font-size: var(--step-0); font-weight: 700;
  color: var(--accent-ink); background: var(--accent-fill); border: 1px solid var(--accent-fill);
  border-radius: var(--radius); padding: var(--sp-3) var(--sp-8);
  min-height: 3rem; cursor: pointer; justify-self: start;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.lead button:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.lead [data-lead-status] { font-size: var(--step--1); color: var(--ink-2); margin: 0; min-height: 1.2em; }

a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible {
  outline: 3px solid var(--accent); outline-offset: 3px;
}

footer {
  padding-block: var(--sp-12);
  color: var(--ink-2); font-size: var(--step--1); border-top: 1px solid var(--line);
}
footer p { margin: 0; }

/* ---- responsive --------------------------------------------------------- */

@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-side {
    padding-left: 0; padding-top: var(--sp-12);
    border-left: 0; border-top: 1px solid var(--line);
  }
}

/* Below 900px the orbit becomes a plain list — a 4-node ring is unreadable on
   a phone, and the drawn geometry has nothing left to describe. */
@media (max-width: 900px) {
  .orbit { display: contents; }
  .orbit-canvas { display: none; }
  .orbit-node {
    position: static; transform: none; width: 100%; min-height: 0;
    align-items: flex-start; text-align: left;
    font-size: var(--step-0);
  }
  .orbit-node:hover, .orbit-node:focus-visible, .orbit-node[aria-expanded="true"] { transform: none; }
  .epigraph { display: none; }
  /* .orbit becomes display:contents above, so .orbit-centre would otherwise lose
     its positioning context (no other ancestor is `position`d) and escape to the
     viewport. Flow it as the first item of the list instead. */
  .orbit-centre {
    position: static; inset: auto; aspect-ratio: 1; width: 11rem;
    justify-self: center; margin-bottom: var(--sp-6);
  }
}

@media (max-width: 560px) {
  .header-end { flex-basis: 100%; justify-content: space-between; }
  .brand-mark { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  /* The travelling signal is the one idle animation on the page; under reduced
     motion it stops entirely rather than snapping to a frame repeatedly. */
  .orbit-signal { animation: none; }
  .orbit-node, .orbit-detail-body, .orbit-arc, .orbit-arrow { transition: none; }
}
