/* Ecliptic Studio — UI kit, layer 2: components
   P1 lane 1D. Built S05, 2026-09-09. Requires tokens.css.

   One idea runs through every component: the interactive edge is the plane.
   A hairline sits under a control at rest and becomes the 2px reference rule
   when the control is focused, active or in error. That is the identity doing
   interface work, and it is why the system needs no accent colour. */

/* ---------------------------------------------------------------- base --- */

.ec {
  background: var(--ec-ground);
  color: var(--ec-ink);
  font-family: var(--ec-face-ui);
  font-weight: var(--ec-w-light);
  font-size: var(--ec-t-body);
  line-height: var(--ec-lh-body);
  letter-spacing: var(--ec-track-ui);
  -webkit-font-smoothing: antialiased;
}
.ec *, .ec *::before, .ec *::after { box-sizing: border-box; }

.ec h1, .ec h2, .ec h3 {
  font-weight: var(--ec-w-light);
  line-height: var(--ec-lh-tight);
  color: var(--ec-ink-strong);
  margin: 0;
}
.ec h1 { font-size: var(--ec-t-h1); }
.ec h2 { font-size: var(--ec-t-h2); }
.ec h3 { font-size: var(--ec-t-h3); }
.ec p  { margin: 0 0 var(--ec-s4); max-width: var(--ec-measure); }
.ec p:last-child { margin-bottom: 0; }

/* The one editorial line a page is allowed. Italiana, 40px floor, never lower. */
.ec-display {
  font-family: var(--ec-face-mark);
  font-weight: var(--ec-w-reg);
  font-size: var(--ec-t-display);
  letter-spacing: 0.10em;
  line-height: 1.1;
  color: var(--ec-ink-strong);
}

/* Microtype. Labels, captions, numbers, table heads, state words. Never above 12px. */
.ec-micro {
  font-family: var(--ec-face-micro);
  font-weight: 500;
  font-size: var(--ec-t-micro);
  letter-spacing: var(--ec-track-micro);
  text-transform: uppercase;
  color: var(--ec-ink-muted);
  line-height: 1.4;
}

/* The reference rule. Draws once, then stays. */
.ec-plane { height: var(--ec-hair); background: var(--ec-hairline); border: 0; margin: 0; }
.ec-plane--draw { transform-origin: left; animation: ec-draw var(--ec-draw) var(--ec-draw-ease) both; }
@keyframes ec-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Focus is the plane too. Never a glow, never a rounded outline. */
.ec :focus-visible {
  outline: var(--ec-focus-ring) solid var(--ec-ink-strong);
  outline-offset: 2px;
}

/* ---------------------------------------------------------- navigation --- */

.ec-nav {
  display: flex; align-items: center; gap: var(--ec-s7);
  padding: var(--ec-s4) var(--ec-s6);
  background: var(--ec-surface);
  border-bottom: var(--ec-hair) solid var(--ec-hairline);
}
.ec-nav__mark { display: flex; align-items: center; gap: var(--ec-s3); margin-right: auto; }
.ec-nav__mark svg { width: 22px; height: 22px; color: var(--ec-ink-strong); flex: none; }
.ec-nav__word {
  font-family: var(--ec-face-mark); font-size: 20px; letter-spacing: 0.24em;
  color: var(--ec-ink-strong); text-transform: uppercase; line-height: 1;
  padding-left: 0.24em; /* tracking adds air on the right only; put it back on the left */
}
.ec-nav__links { display: flex; gap: var(--ec-s6); }
.ec-nav a {
  position: relative; color: var(--ec-ink); text-decoration: none;
  font-size: var(--ec-t-small); letter-spacing: var(--ec-track-caps); text-transform: uppercase;
  padding-bottom: var(--ec-s1);
}
.ec-nav a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--ec-plane); background: var(--ec-ink-strong);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--ec-register) var(--ec-register-ease);
}
.ec-nav a:hover::after, .ec-nav a[aria-current]::after { transform: scaleX(1); }
.ec-nav a[aria-current] { color: var(--ec-ink-strong); }

/* Side navigation, for the backend. Same rule, moved to the left edge. */
.ec-sidenav { display: flex; flex-direction: column; gap: var(--ec-s1); }
.ec-sidenav a {
  display: flex; justify-content: space-between; align-items: center;
  padding: var(--ec-s3) var(--ec-s4); color: var(--ec-ink); text-decoration: none;
  font-size: var(--ec-t-small); border-left: var(--ec-plane) solid transparent;
  transition: border-color var(--ec-register) var(--ec-register-ease),
              background var(--ec-register) var(--ec-register-ease);
}
.ec-sidenav a:hover { background: color-mix(in srgb, var(--ec-ink) 5%, transparent); }
.ec-sidenav a[aria-current] { border-left-color: var(--ec-ink-strong); color: var(--ec-ink-strong); }
.ec-sidenav .ec-count { font-family: var(--ec-face-micro); font-size: var(--ec-t-micro); color: var(--ec-ink-muted); }

/* ------------------------------------------------------------- buttons --- */

.ec-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--ec-s2);
  font-family: var(--ec-face-ui); font-weight: var(--ec-w-reg);
  font-size: var(--ec-t-small); letter-spacing: var(--ec-track-caps); text-transform: uppercase;
  padding: var(--ec-s3) var(--ec-s5); min-height: 44px;
  border: var(--ec-hair) solid transparent; border-radius: var(--ec-radius);
  background: none; color: inherit; cursor: pointer; text-decoration: none;
  transition: background var(--ec-register) var(--ec-register-ease),
              color var(--ec-register) var(--ec-register-ease),
              border-color var(--ec-register) var(--ec-register-ease);
}
.ec-btn:disabled, .ec-btn[aria-disabled='true'] { opacity: .38; cursor: not-allowed; }

.ec-btn--primary { background: var(--ec-ink-strong); color: var(--ec-ground); }
.ec-btn--primary:hover:not(:disabled) { background: var(--ec-ink); }

.ec-btn--secondary { border-color: var(--ec-hairline); color: var(--ec-ink); }
.ec-btn--secondary:hover:not(:disabled) { border-color: var(--ec-ink-strong); color: var(--ec-ink-strong); }

/* Text button. The plane draws under it on hover — the identity's one move, at control scale. */
.ec-btn--text { padding-left: 0; padding-right: 0; position: relative; color: var(--ec-ink); }
.ec-btn--text::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 10px; height: var(--ec-hair);
  background: currentColor; transform: scaleX(0); transform-origin: left;
  transition: transform var(--ec-register) var(--ec-register-ease);
}
.ec-btn--text:hover:not(:disabled) { color: var(--ec-ink-strong); }
.ec-btn--text:hover:not(:disabled)::after { transform: scaleX(1); }

/* Destructive is not red. It is the only button that names the object it will destroy,
   and it is always confirmed. Colour was never doing that job. */
.ec-btn--destructive { border-color: var(--ec-hairline); color: var(--ec-ink-muted); }
.ec-btn--destructive:hover:not(:disabled) { border-color: var(--ec-ink-strong); color: var(--ec-ink-strong); }

.ec-btn--small { min-height: 32px; padding: var(--ec-s1) var(--ec-s3); font-size: var(--ec-t-micro); }

.ec-btn__busy { display: inline-block; width: 16px; height: var(--ec-hair); background: currentColor;
  transform-origin: left; animation: ec-busy 1.2s var(--ec-draw-ease) infinite; }
@keyframes ec-busy { 0% { transform: scaleX(0); } 55% { transform: scaleX(1); }
  100% { transform: scaleX(1) translateX(16px); opacity: 0; } }

/* --------------------------------------------------------------- forms --- */

.ec-field { display: block; margin-bottom: var(--ec-s5); max-width: 480px; }
.ec-field__label {
  display: block; margin-bottom: var(--ec-s2);
  font-family: var(--ec-face-micro); font-size: var(--ec-t-micro); font-weight: 500;
  letter-spacing: var(--ec-track-micro); text-transform: uppercase; color: var(--ec-ink-muted);
}
.ec-field__label .ec-req { color: var(--ec-ink-strong); }

/* No boxes. A field is a line with type sitting on it — the plane, at field scale. */
.ec-input, .ec-select, .ec-textarea {
  width: 100%; display: block;
  font-family: var(--ec-face-ui); font-weight: var(--ec-w-light); font-size: var(--ec-t-body);
  color: var(--ec-ink-strong); background: transparent;
  border: 0; border-bottom: var(--ec-hair) solid var(--ec-hairline); border-radius: var(--ec-radius);
  padding: var(--ec-s2) 0; min-height: 40px;
  transition: border-color var(--ec-register) var(--ec-register-ease);
}
.ec-textarea { min-height: 108px; resize: vertical; line-height: var(--ec-lh-body); }
.ec-input::placeholder, .ec-textarea::placeholder { color: var(--ec-ink-muted); }
.ec-input:hover, .ec-select:hover, .ec-textarea:hover { border-bottom-color: var(--ec-ink-muted); }
.ec-input:focus, .ec-select:focus, .ec-textarea:focus {
  outline: none; border-bottom: var(--ec-plane) solid var(--ec-ink-strong);
}
.ec-input:disabled, .ec-textarea:disabled { color: var(--ec-ink-muted); border-bottom-style: dashed; }

.ec-select {
  appearance: none; padding-right: var(--ec-s5); cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 6px center, right 1px center;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}

.ec-field__help { margin-top: var(--ec-s2); font-size: var(--ec-t-small); color: var(--ec-ink-muted); }

/* Error. The rule thickens, a node marks it, and the message states what to do.
   Three carriers, none of them colour. */
.ec-field--error .ec-input,
.ec-field--error .ec-select,
.ec-field--error .ec-textarea { border-bottom: var(--ec-plane) solid var(--ec-signal); }
.ec-field__error {
  display: flex; align-items: center; gap: var(--ec-s2); margin-top: var(--ec-s2);
  font-family: var(--ec-face-micro); font-size: var(--ec-t-micro); letter-spacing: var(--ec-track-micro);
  text-transform: uppercase; color: var(--ec-ink-strong);
}
.ec-field__error::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--ec-node); flex: none;
}

/* Checkbox and radio. Square is a control, round is a node — the one place a circle
   appears outside the mark, and it is the same object: a filled node point. */
.ec-check { display: flex; align-items: flex-start; gap: var(--ec-s3); cursor: pointer; margin-bottom: var(--ec-s3); }
.ec-check input { appearance: none; margin: 3px 0 0; flex: none; width: 16px; height: 16px;
  border: var(--ec-hair) solid var(--ec-ink-muted); background: transparent; cursor: pointer;
  transition: border-color var(--ec-register) var(--ec-register-ease); }
.ec-check input[type='radio'] { border-radius: 50%; }
.ec-check input:checked { border-color: var(--ec-ink-strong); }
.ec-check input[type='checkbox']:checked { background: var(--ec-ink-strong);
  background-image: linear-gradient(to bottom right, transparent 45%, var(--ec-ground) 45%); }
.ec-check input[type='radio']:checked { box-shadow: inset 0 0 0 3px var(--ec-ground), inset 0 0 0 16px var(--ec-ink-strong); }
.ec-check__text { font-size: var(--ec-t-small); }

.ec-fieldset { border: 0; padding: 0; margin: 0 0 var(--ec-s6); }
.ec-fieldset legend { padding: 0 0 var(--ec-s3); }

/* -------------------------------------------------------------- tables --- */

.ec-table-wrap { overflow-x: auto; }
.ec-table { width: 100%; border-collapse: collapse; font-size: var(--ec-t-small); }
.ec-table thead th {
  text-align: left; padding: var(--ec-s3) var(--ec-s4) var(--ec-s2);
  font-family: var(--ec-face-micro); font-weight: 500; font-size: var(--ec-t-micro);
  letter-spacing: var(--ec-track-micro); text-transform: uppercase; color: var(--ec-ink-muted);
  border-bottom: var(--ec-plane) solid var(--ec-hairline); white-space: nowrap;
}
.ec-table tbody td {
  padding: var(--ec-s4); border-bottom: var(--ec-hair) solid var(--ec-hairline);
  vertical-align: baseline;
}
.ec-table tbody tr { transition: background var(--ec-register) var(--ec-register-ease); }
.ec-table tbody tr:hover { background: color-mix(in srgb, var(--ec-ink) 4%, transparent); }
.ec-table__num { text-align: right; font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: 0.06em; color: var(--ec-ink-strong); font-variant-numeric: tabular-nums; }
.ec-table__strong { color: var(--ec-ink-strong); }
.ec-table caption { caption-side: top; text-align: left; padding-bottom: var(--ec-s3); }

/* Status, as a node point and a word. Fill states position on the plane:
   hollow is not started, half is in progress, solid is closed. */
.ec-status { display: inline-flex; align-items: center; gap: var(--ec-s2); white-space: nowrap;
  font-family: var(--ec-face-micro); font-size: var(--ec-t-micro); letter-spacing: var(--ec-track-micro);
  text-transform: uppercase; color: var(--ec-ink); }
.ec-status::before { content: ''; width: 8px; height: 8px; border-radius: 50%; flex: none;
  border: var(--ec-hair) solid var(--ec-ink-muted); }
.ec-status--open::before { background: transparent; }
.ec-status--active::before { background: linear-gradient(to right, var(--ec-node) 50%, transparent 50%);
  border-color: var(--ec-node); }
.ec-status--closed::before { background: var(--ec-node); border-color: var(--ec-node); }
.ec-status--closed { color: var(--ec-ink-strong); }

/* --------------------------------------------------------------- cards --- */

.ec-card {
  background: var(--ec-surface); border: var(--ec-hair) solid var(--ec-hairline);
  border-radius: var(--ec-radius); padding: var(--ec-s6);
}
.ec-card__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--ec-s4);
  margin-bottom: var(--ec-s4); }
.ec-card__title { font-size: var(--ec-t-h3); color: var(--ec-ink-strong); line-height: var(--ec-lh-tight); }
.ec-card__foot { margin-top: var(--ec-s5); padding-top: var(--ec-s4);
  border-top: var(--ec-hair) solid var(--ec-hairline); display: flex; gap: var(--ec-s4); align-items: center; }

/* A card that is a link. The top edge becomes the plane on hover. */
a.ec-card { display: block; text-decoration: none; color: inherit; position: relative; }
a.ec-card::before { content: ''; position: absolute; inset: -1px -1px auto; height: var(--ec-plane);
  background: var(--ec-ink-strong); transform: scaleX(0); transform-origin: left;
  transition: transform var(--ec-register) var(--ec-register-ease); }
a.ec-card:hover::before { transform: scaleX(1); }

/* The number card. One figure, one label, nothing else. */
.ec-figure { font-family: var(--ec-face-ui); font-weight: var(--ec-w-light);
  font-size: var(--ec-t-h1); line-height: 1; color: var(--ec-ink-strong);
  font-variant-numeric: tabular-nums; }
.ec-figure__unit { font-size: var(--ec-t-h3); color: var(--ec-ink-muted); }

/* -------------------------------------------------------------- states --- */

/* One shape used four ways: mark, microtype word, one sentence, one action.
   Only the mark and the sentence change. */
.ec-state { display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--ec-s4); padding: var(--ec-s8) var(--ec-s5); }
.ec-state__mark { width: 44px; height: 44px; color: var(--ec-ink-muted); }
.ec-state__word { font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: var(--ec-track-micro); text-transform: uppercase; color: var(--ec-ink-muted); }
.ec-state__line { font-size: var(--ec-t-lead); color: var(--ec-ink-strong); max-width: 46ch; margin: 0; }
.ec-state__note { font-size: var(--ec-t-small); color: var(--ec-ink-muted); max-width: 52ch; margin: 0; }
.ec-state--error .ec-state__mark { color: var(--ec-ink-strong); }

/* Loading is the plane drawing. Not a spinner — a spinning ring is the mark misused. */
.ec-loading { width: 180px; height: var(--ec-plane); background: color-mix(in srgb, var(--ec-ink) 14%, transparent);
  position: relative; overflow: hidden; }
.ec-loading::after { content: ''; position: absolute; inset: 0; background: var(--ec-ink-strong);
  transform-origin: left; animation: ec-load 1.6s var(--ec-draw-ease) infinite; }
@keyframes ec-load { 0% { transform: scaleX(0); } 60% { transform: scaleX(1); }
  100% { transform: scaleX(1) translateX(100%); } }

/* Skeleton rows, for a table that is still arriving. Hairlines, not grey blocks. */
.ec-skeleton { height: var(--ec-hair); background: color-mix(in srgb, var(--ec-ink) 16%, transparent); }

/* Inline system message. Sits on a rule, states the cause, never apologises. */
.ec-message { display: flex; gap: var(--ec-s4); padding: var(--ec-s4) 0;
  border-top: var(--ec-plane) solid var(--ec-hairline); border-bottom: var(--ec-hair) solid var(--ec-hairline); }
.ec-message--alert { border-top-color: var(--ec-signal); }
.ec-message__node { width: 8px; height: 8px; border-radius: 50%; background: var(--ec-node);
  margin-top: 7px; flex: none; }
.ec-message__body { font-size: var(--ec-t-small); }
.ec-message__body strong { color: var(--ec-ink-strong); font-weight: var(--ec-w-reg); }

@keyframes ec-resolve-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.ec-resolve { animation: ec-resolve-in var(--ec-resolve) var(--ec-resolve-ease) both; }

/* -------------------------------------------------------- small pieces --- */

.ec-tabs { display: flex; gap: var(--ec-s6); border-bottom: var(--ec-hair) solid var(--ec-hairline); }
.ec-tab { position: relative; background: none; border: 0; cursor: pointer; padding: var(--ec-s3) 0;
  font-family: var(--ec-face-micro); font-size: var(--ec-t-micro); letter-spacing: var(--ec-track-micro);
  text-transform: uppercase; color: var(--ec-ink-muted); }
.ec-tab[aria-selected='true'] { color: var(--ec-ink-strong); }
.ec-tab::after { content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: var(--ec-plane);
  background: var(--ec-ink-strong); transform: scaleX(0); transform-origin: left;
  transition: transform var(--ec-register) var(--ec-register-ease); }
.ec-tab[aria-selected='true']::after { transform: scaleX(1); }

.ec-label { display: inline-block; padding: 3px var(--ec-s2);
  border: var(--ec-hair) solid var(--ec-hairline);
  font-family: var(--ec-face-micro); font-size: var(--ec-t-micro); letter-spacing: var(--ec-track-micro);
  text-transform: uppercase; color: var(--ec-ink-muted); }

.ec-pagination { display: flex; align-items: center; gap: var(--ec-s4); padding-top: var(--ec-s4); }
.ec-pagination__count { font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: var(--ec-track-micro); color: var(--ec-ink-muted); margin-right: auto; }

.ec-dialog { background: var(--ec-surface); border: var(--ec-hair) solid var(--ec-hairline);
  border-top: var(--ec-plane) solid var(--ec-ink-strong); padding: var(--ec-s6); max-width: 460px; }
.ec-dialog__actions { display: flex; gap: var(--ec-s4); align-items: center; margin-top: var(--ec-s6); }

.ec-stack > * + * { margin-top: var(--ec-s4); }
.ec-row { display: flex; gap: var(--ec-s4); align-items: center; flex-wrap: wrap; }

/* ---------------------------------------------------------- coordinates ---
   Approved S05. Borrowed from how astronomy notates position and time, and kept
   only where it encodes something the studio genuinely tracks. */

/* The graticule. The plane, graduated: it says where as well as that. Weeks on a
   schedule, hours against a capacity, stages of a build. Replaces the progress bar. */
.ec-graticule { position: relative; padding-top: 14px; }
.ec-graticule::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 8px;
  background:
    repeating-linear-gradient(to right, var(--ec-hairline) 0 1px, transparent 1px 10%),
    repeating-linear-gradient(to right, var(--ec-hairline) 0 1px, transparent 1px 2%);
  background-size: 100% 8px, 100% 4px; background-repeat: no-repeat;
}
.ec-graticule::after { content: ''; position: absolute; top: 0; left: 0; right: 0;
  height: var(--ec-hair); background: var(--ec-hairline); }
.ec-graticule__labels { display: flex; justify-content: space-between;
  font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: var(--ec-track-micro); color: var(--ec-ink-muted); }
.ec-graticule__mark { position: absolute; top: -3px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--ec-node); transform: translateX(-50%); }

/* Coordinate, epoch and designation notation. Every timestamp is UTC in ISO 8601, and
   every record carries a catalogue designation — EC 0114, set like NGC 4594. */
.ec-coord { font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: 0.06em; font-variant-numeric: tabular-nums; color: var(--ec-ink-strong); }
.ec-coord--muted { color: var(--ec-ink-muted); }

/* Contact points. An eclipse has four: the bodies touch, the overlap completes, the overlap
   ends, they separate. A build has the same four and the studio already tracks them.
   Internal vocabulary, like the depths. Never client language. */
.ec-contact { position: relative; display: flex; padding-top: var(--ec-s5); }
.ec-contact::before { content: ''; position: absolute; top: var(--ec-s5); left: 3px; right: 3px;
  height: var(--ec-hair); background: var(--ec-hairline); transform: translateY(-3px); }
.ec-contact__pt { position: relative; flex: 1; display: flex; flex-direction: column;
  align-items: flex-start; gap: var(--ec-s2); }
.ec-contact__pt::before { content: ''; width: 7px; height: 7px; border-radius: 50%;
  border: var(--ec-hair) solid var(--ec-ink-muted); background: var(--ec-ground); margin-top: -3px; }
.ec-contact__pt--done::before { background: var(--ec-node); border-color: var(--ec-node); }
.ec-contact__pt--now::before { border-color: var(--ec-node);
  background: linear-gradient(to right, var(--ec-node) 50%, var(--ec-ground) 50%); }
.ec-contact__label { font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: var(--ec-track-micro); text-transform: uppercase; color: var(--ec-ink-muted); }
.ec-contact__pt--now .ec-contact__label,
.ec-contact__pt--done .ec-contact__label { color: var(--ec-ink-strong); }
.ec-contact__when { font-size: var(--ec-t-small); color: var(--ec-ink-muted); }

/* Grain. Approved S05, and the one texture with a mechanical reason rather than an
   aesthetic one: a large flat dark field bands into visible steps on a cheap panel, and
   monochrome noise is what stops it. Dark grounds only — on paper it reads as a printing
   fault. Put it on the element that owns the ground. */
.ec-grain { position: relative; }
.ec-grain::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  opacity: 0.035; mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}


/* Ephemeris. A table of positions over time, which is what a delivery schedule is. */
.ec-table--ephemeris thead th {
  border-bottom: 0; padding-bottom: var(--ec-s3);
  background:
    repeating-linear-gradient(to right, var(--ec-hairline) 0 1px, transparent 1px 24px) bottom left / 100% 5px no-repeat,
    linear-gradient(var(--ec-hairline), var(--ec-hairline)) bottom left / 100% var(--ec-hair) no-repeat;
}
.ec-table--ephemeris td { font-variant-numeric: tabular-nums; }
.ec-table--ephemeris .ec-utc { font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: 0.06em; color: var(--ec-ink-muted); white-space: nowrap; }

/* ------------------------------------------------------------- sorting ---
   Node glyphs were tried twice and cut, S05. Nathan: they read off-centre next to each
   other and it is an odd use for them — which is the same test that cut longitude and
   latitude. A symbol that has to be explained is decoration.

   Sort reuses what the system already says: the rule under the active column thickens to
   the plane, and a microtype word states the direction. Rule plus word, exactly like every
   other state in the kit. */
.ec-table th[aria-sort] { color: var(--ec-ink-strong);
  border-bottom: var(--ec-plane) solid var(--ec-ink-strong); }
.ec-table--ephemeris th[aria-sort] { background-image: none;  /* keep the head's own
  background-color: killing it makes rows scroll visibly behind a sticky sorted column */
  border-bottom: var(--ec-plane) solid var(--ec-ink-strong); }
.ec-sort { margin-left: var(--ec-s2); color: var(--ec-ink-muted); letter-spacing: var(--ec-track-micro); }

/* ------------------------------------------------------------ meridian ---
   Approved S05. The system owns one horizontal line; this is the vertical one. A single
   hairline at a fixed column that the mark, the headline and the first paragraph align to.
   Drawn on signature surfaces, invisible everywhere else — the alignment holds either way,
   and that is what makes a layout read as composed rather than centred.

   The column is 38.2% by default, which is the short side of a golden division. Put
   .ec-meridian on the section and .ec-meridian__field on anything that sits to its right. */
.ec-meridian { position: relative; }
.ec-meridian::before { content: ''; position: absolute; top: 0; bottom: 0;
  left: var(--ec-meridian-x, 38.2%); width: var(--ec-hair);
  background: var(--ec-hairline); pointer-events: none; }
.ec-meridian--quiet::before { background: none; }
.ec-meridian__field { margin-left: var(--ec-meridian-x, 38.2%); padding-left: var(--ec-s6); }
.ec-meridian__gutter { position: absolute; left: 0; width: var(--ec-meridian-x, 38.2%);
  padding-right: var(--ec-s6); }

/* ------------------------------------------------------------ material ---
   Approved S05. One light source, fixed, and everything material derives from it. The
   guard rail is the whole reason this can exist: **material touches grounds and surfaces,
   never the mark, the plane or type.** T1 and T2 only. T4 gets edge lift and nothing else.
   T5 gets none. */

/* Edge lift. How a physical panel reads: catching light along its top edge, losing it at
   the bottom. One inset line each way, and no drop shadow anywhere in the system. */
.ec-lit { box-shadow: inset 0 1px 0 var(--ec-light-top), inset 0 -1px 0 var(--ec-light-under); }

/* Limb darkening. A star is brighter at its centre than at its edge, because at the edge
   you are looking through more of its atmosphere. The ground gets depth and nothing that
   anyone would call a gradient. Signature grounds only. */
.ec-limb { background:
  radial-gradient(130% 95% at 50% 38%, #101215 0%, #0A0A0A 58%, #050506 100%); }
.ec-work-ground.ec-limb { background:
  radial-gradient(130% 95% at 50% 38%, #1C1F22 0%, #16181A 58%, #101214 100%); }

/* ---------------------------------------------------------- density -----
   Added S05, closing the one gap the kit had: three demo rows prove nothing about forty
   rows of 11px mono on a dark ground, and that is the condition that breaks a system
   built out of hairlines.

   Two things break first. Row padding built for a card is too generous once a list is
   long, and a hairline under every row turns forty rows into forty lines competing with
   the plane. The fix for the second is the graticule's own logic: minor intervals are
   implied, major intervals are drawn. Every fifth row carries the rule; the rest are held
   by alignment. */
.ec-table--dense { font-size: var(--ec-t-small); }
.ec-table--dense thead th { position: sticky; top: 0; z-index: 1;
  background: var(--ec-ground); padding: var(--ec-s2) var(--ec-s3); }
.ec-table--dense tbody td { padding: 6px var(--ec-s3); border-bottom-color: transparent;
  white-space: nowrap; }
.ec-table--dense tbody tr:nth-child(5n) td { border-bottom-color: var(--ec-hairline); }
.ec-table--dense tbody tr:last-child td { border-bottom-color: var(--ec-hairline); }
.ec-table--dense tbody tr:hover td { border-bottom-color: transparent; }
.ec-table--dense tbody tr:hover { background: color-mix(in srgb, var(--ec-ink) 6%, transparent); }
/* Numbers lose the microtype tracking at density. +0.16em is a label setting; a column of
   forty figures needs to align, not to breathe. */
.ec-table--dense .ec-table__num, .ec-table--dense .ec-utc { letter-spacing: 0.02em; }
.ec-table--dense .ec-status { font-size: 10px; letter-spacing: 0.10em; }

/* The scroll region. A long table gets its own, so the page never scrolls sideways and the
   head can stick to something. */
.ec-table-scroll { max-height: 420px; overflow: auto; }

/* -------------------------------------------------- material, adopted ---
   Taken S05 on recommendation, with no objection raised. The glass surface was the one
   item recommended against and it stays out — it is the only element in the system a
   stranger could name the source of. It remains one class away in PROPOSED-extensions.css
   if it is ever wanted. */

/* The terminator. The line between the lit and unlit halves of a body. Where a dark section
   meets a paper one the seam is this, not a hard edge: the reference rule with a short
   falloff above it. 48px, and the only full-bleed gradient in the system. */
.ec-terminator { position: relative; height: 48px; background: var(--ec-ground); }
.ec-terminator::before { content: ''; position: absolute; inset: 0;
  background: linear-gradient(to bottom, transparent 0%, #050506 100%); }
.ec-terminator::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--ec-hair); background: var(--ec-rule); }

/* Depth of field. One layer of the hero field out of focus, which is what a long lens does.
   Hero only, one layer, 3px maximum — past that it reads as a blur filter rather than as
   distance. It is the cheapest way to give a flat page a front and a back. */
.ec-defocus { filter: blur(3px); opacity: 0.6; }

/* Paper tooth. The same noise as grain at 1.5% and a finer frequency, multiplied rather
   than screened. On a light ground 3.5% reads as a printing fault and 1.5% reads as stock.
   Documents only. */
.ec-tooth { position: relative; }
.ec-tooth::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  opacity: 0.015; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='t'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23t)'/%3E%3C/svg%3E");
  background-size: 140px 140px; }

@media (prefers-reduced-motion: reduce) { .ec-defocus { filter: none; } }

/* ------------------------------------------------------------ the loop ---
   Added S05. The retainer's actual product needs a control, and it ships inside every
   system the studio builds — so it belongs here, not invented per project.

   `OFFER-AND-PRICING.md`: one-tap feedback in the flow at the moment of use, plus four
   questions quarterly. It surfaces and prioritises; it never adjusts a production system.

   Three rules the design follows:
   1. **It never interrupts.** It sits at the foot of a step, quiet, and is ignorable
      forever. A modal asking how we are doing is the thing everyone closes.
   2. **No satisfaction, no stars, no NPS.** The options name a mechanism — took too long,
      worked around it, something missing — because those cluster into work, and a rating
      clusters into nothing.
   3. **It answers.** The confirmation says the note reaches the monthly review, because
      telling the person who asked is the step nobody does and the one that turns a user
      into an advocate. */

.ec-feedback { border-top: var(--ec-hair) solid var(--ec-hairline); padding: var(--ec-s4) 0 0;
  margin-top: var(--ec-s5); }
.ec-feedback__prompt { font-family: var(--ec-face-micro); font-size: var(--ec-t-micro);
  letter-spacing: var(--ec-track-micro); text-transform: uppercase; color: var(--ec-ink-muted); }
.ec-feedback__opts { display: flex; flex-wrap: wrap; gap: var(--ec-s5); margin-top: var(--ec-s3); }

/* A node point that fills when chosen — the same object as every other state in the kit. */
.ec-feedback__opt { display: inline-flex; align-items: center; gap: var(--ec-s2);
  background: none; border: 0; padding: 0; cursor: pointer; font-family: var(--ec-face-ui);
  font-size: var(--ec-t-small); font-weight: var(--ec-w-light); color: var(--ec-ink-muted);
  transition: color var(--ec-register) var(--ec-register-ease); }
.ec-feedback__opt::before { content: ''; width: 8px; height: 8px; border-radius: 50%; flex: none;
  border: var(--ec-hair) solid var(--ec-ink-muted);
  transition: background var(--ec-register) var(--ec-register-ease),
              border-color var(--ec-register) var(--ec-register-ease); }
.ec-feedback__opt:hover { color: var(--ec-ink-strong); }
.ec-feedback__opt:hover::before { border-color: var(--ec-ink-strong); }
.ec-feedback__opt[aria-pressed='true'] { color: var(--ec-ink-strong); }
.ec-feedback__opt[aria-pressed='true']::before { background: var(--ec-node); border-color: var(--ec-node); }

/* The note is optional and appears only after a tap. Asking for typing up front is how a
   one-tap control becomes a form nobody fills in. */
.ec-feedback__note { margin-top: var(--ec-s4); }
.ec-feedback__sent { display: flex; align-items: baseline; gap: var(--ec-s3);
  font-size: var(--ec-t-small); color: var(--ec-ink-strong); margin-top: var(--ec-s3); }
.ec-feedback__sent .ec-micro { color: var(--ec-ink-muted); }
