/* ==================================================================
   tx402 utilities — component primitives
   Every rule here resolves colour through a token from tokens.css.
   There is not a single literal colour in this file and `pnpm gate:tokens`
   keeps it that way. Sessions COMPOSE these; they do not invent new ones.
   ================================================================== */

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

code, pre, .mono { font-family: var(--mono); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: var(--s2);
  padding: var(--s2) var(--s4);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  z-index: 10;
}
.skip-link:focus { left: var(--s4); }

/* ------------------------------ shell ------------------------------ */

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

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.wrap-narrow { max-width: var(--narroww); }

.site-header {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--header-bg);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  gap: var(--s5);
  min-height: 64px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-weight: 650;
  color: var(--text);
  letter-spacing: -.01em;
  white-space: nowrap;
}
.brand:hover { text-decoration: none; color: var(--accent); }
.brand .brand-sub { color: var(--text-faint); font-weight: 400; }
/* The mark strokes with currentColor, so it follows the brand link's colour
   through hover and both themes without a second rule. */
.brand svg { width: 22px; height: 22px; flex: none; color: var(--accent); }
.brand:hover svg { color: inherit; }

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
  margin-left: auto;
  font-size: .9rem;
}
.nav a { color: var(--text-muted); }
.nav a:hover, .nav a[aria-current='page'] { color: var(--text); text-decoration: none; }
.nav a[aria-current='page'] { font-weight: 600; }

/* ---------------------------- theme toggle ------------------------- */
/* Both icons are in the markup and CSS picks one, so the button is correct on
   first paint before any script runs. Dark is the default, so the button
   offers the sun; the rules below flip it for every way a page can be light. */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.theme-toggle:hover { color: var(--text); border-color: var(--border-strong); }
.theme-toggle svg { width: 17px; height: 17px; }

.icon-sun { display: block; }
.icon-moon { display: none; }

:root[data-theme='light'] .icon-sun { display: none; }
:root[data-theme='light'] .icon-moon { display: block; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) .icon-sun { display: none; }
  :root:not([data-theme='dark']) .icon-moon { display: block; }
}

main { flex: 1; padding-block: var(--s7) var(--s8); }

.page-head { margin-bottom: var(--s6); }
.page-head h1 {
  font-size: clamp(1.7rem, 3.4vw, 2.3rem);
  line-height: 1.2;
  letter-spacing: -.02em;
  margin: 0 0 var(--s3);
}
.page-head p {
  margin: 0;
  color: var(--text-muted);
  max-width: 62ch;
}

.site-footer {
  border-top: 1px solid var(--border);
  background: var(--bg-alt);
  color: var(--text-faint);
  font-size: .85rem;
  padding-block: var(--s6);
}
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: var(--s5); align-items: baseline; }
.site-footer a { color: var(--text-muted); }
.site-footer .footer-brand { display: inline-flex; align-items: center; gap: var(--s2); }
.site-footer .brand { font-size: .9rem; }
.site-footer .brand svg { width: 18px; height: 18px; }
.site-footer .footer-links { display: flex; flex-wrap: wrap; gap: var(--s4); margin-left: auto; }

/* --------------------------- disclaimer --------------------------- */
/* plan.md §6.2: a band describes the confidence of OUR OBSERVATIONS, not the
   operator, and the page has to say so above the fold. This is that line. It
   is a component so it cannot be forgotten on one page out of seven. */

.observation-note {
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent-line);
  background: var(--accent-soft);
  border-radius: var(--r);
  padding: var(--s3) var(--s4);
  color: var(--text-muted);
  font-size: .875rem;
  margin-bottom: var(--s5);
}
.observation-note strong { color: var(--text); font-weight: 600; }

/* ---------------------------- paste box --------------------------- */

.paste-box {
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: var(--s5);
  box-shadow: var(--shadow);
}
.paste-box label {
  display: block;
  font-size: .85rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: var(--s2);
}
.paste-box .row { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: flex-start; }
.paste-box .row > .field-wrap { flex: 1 1 22rem; }
.paste-box .cf-turnstile { margin-top: var(--s3); }

.field {
  width: 100%;
  height: var(--field-h);
  padding: 0 var(--s4);
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  font-family: var(--mono);
  font-size: .9rem;
}
textarea.field {
  height: auto;
  min-height: 9rem;
  padding: var(--s3) var(--s4);
  line-height: 1.5;
  resize: vertical;
}
.field::placeholder { color: var(--text-faint); }
.field:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  height: var(--btn-h);
  padding: 0 var(--s5);
  border: 1px solid transparent;
  border-radius: var(--r);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease);
}
.btn:hover { background: var(--btn-bg-hover); text-decoration: none; }
.btn[disabled] { opacity: .55; cursor: not-allowed; }
.btn-secondary {
  background: transparent;
  color: var(--text);
  border-color: var(--border-strong);
}
.btn-secondary:hover { background: var(--surface-2); }

.hint { margin: var(--s3) 0 0; font-size: .82rem; color: var(--text-faint); }

/* --------------------------- result card -------------------------- */

.card {
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--r-lg);
  margin-bottom: var(--s5);
  overflow: hidden;
}
.card > header {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.card > header h2 {
  margin: 0;
  font-size: 1rem;
  font-weight: 650;
  letter-spacing: -.01em;
}
.card > header .card-aside { margin-left: auto; color: var(--text-faint); font-size: .82rem; }
.card > .card-body { padding: var(--s5); }
/* The question the tool answers, sitting above the description. It is the
   H3 because it is what a searcher actually typed (plan.md §3.1). */
.card-question { margin: 0 0 var(--s2); font-size: 1.05rem; font-weight: 600; letter-spacing: -.01em; }
.card > .card-body > :first-child { margin-top: 0; }
.card > .card-body > :last-child { margin-bottom: 0; }
.card > footer {
  padding: var(--s3) var(--s5);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  font-size: .82rem;
  color: var(--text-faint);
}

/* -------------------------- key/value table ----------------------- */

.kv {
  width: 100%;
  border-collapse: collapse;
  font-size: .9rem;
}
.kv th, .kv td {
  text-align: left;
  vertical-align: top;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--border);
}
.kv tr:last-child th, .kv tr:last-child td { border-bottom: 0; }
.kv th {
  width: 34%;
  font-weight: 500;
  color: var(--text-muted);
  white-space: nowrap;
}
.kv td { font-family: var(--mono); word-break: break-word; }
.kv td.prose { font-family: var(--sans); }
.kv .kv-note { display: block; font-family: var(--sans); color: var(--text-faint); font-size: .82rem; margin-top: var(--s1); }
/* "not observed" is not the same as "false" — SPEC §6.3. It renders as
   absence, never as a value. */
.kv td.unobserved { color: var(--text-faint); font-style: italic; font-family: var(--sans); }

.table-scroll { overflow-x: auto; }

/* ---------------------------- status pill ------------------------- */
/* Every status is carried by TEXT as well as colour — the landing page's rule,
   and non-negotiable for a tool whose whole output is a judgement. */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: .15rem var(--s3);
  border-radius: var(--r-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface-3);
  color: var(--text-muted);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .01em;
  white-space: nowrap;
}
.pill::before {
  content: '';
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.pill-ok { color: var(--ok); background: var(--ok-soft); border-color: transparent; }
.pill-warn { color: var(--warn); background: var(--warn-soft); border-color: transparent; }
.pill-err { color: var(--err); background: var(--err-soft); border-color: transparent; }
.pill-idle { color: var(--idle); background: var(--surface-3); }
.pill-info { color: var(--accent); background: var(--accent-soft); border-color: transparent; }

/* ---------------------------- code block -------------------------- */

.code {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--bg-alt);
  overflow: hidden;
}
.code > .code-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s4);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  font-size: .78rem;
  color: var(--text-faint);
  font-family: var(--mono);
}
.code > .code-head .code-copy { margin-left: auto; }
.code pre {
  margin: 0;
  padding: var(--s4);
  overflow-x: auto;
  font-size: .85rem;
  line-height: 1.55;
  color: var(--text);
}

/* ---------------------------- empty state ------------------------- */
/* plan.md §5.2: an empty corpus is the CORRECT display for an endpoint we have
   not seen, not a degraded one. It gets a designed state, not an apology. */

.empty {
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: var(--s7) var(--s5);
  text-align: center;
  color: var(--text-muted);
}
.empty h2 {
  margin: 0 0 var(--s3);
  font-size: 1.05rem;
  font-weight: 650;
  color: var(--text);
}
.empty p { margin: 0 auto; max-width: 52ch; }
.empty .empty-detail { margin-top: var(--s4); font-size: .82rem; color: var(--text-faint); }

/* ------------------------------ misc ------------------------------ */

.stack > * + * { margin-top: var(--s5); }
.row-gap { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; }
.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.small { font-size: .85rem; }
.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;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
