/* ==========================================================================
   Tenet — 00-tokens: the rival wardrobe, reset, base typography.

   Tenet is the v3 console app over the shared vault data: a knowledge
   CONSOLE, not a library. Grotesque system sans for prose and display,
   mono for data and micro-labels, hairline rules, one confident accent
   per theme. Its wardrobe is its OWN key (wv:tenet:theme, applied as
   html[data-tntheme=...]) — fully independent of the Athenaeum's 19
   packs and Drift's Nocturne.

   Token contract (every pack redefines ALL of these):
     --paper / --paper-2 / --paper-3     surfaces (page, raised, inset)
     --ink / --ink-soft / --ink-faint    text (body, secondary, tertiary)
     --line / --line-soft                hairlines
     --accent / --accent-ink / --accent-soft   the one confident accent
     --pos / --neg / --warn              semantic tones (connection chips)
     --shadow                            elevation

   Orthogonal to the wardrobe: the SKIN axis (05-skins.css, applied as
   html[data-tenet-skin=...], key wv:tenet:skin). Skins redefine ONLY the
   typography/density/shape variables declared in the "skin variables"
   block below — never a color token. "standard" has no block on purpose:
   its values ARE these :root defaults (the pixel-parity contract).

   WCAG luminance table (verified 2026-07-12, scripted relative-luminance
   check; body ink vs paper must be >= 7:1 in every pack).

   faint/paper REBASED 2026-07-21 (AIL r1 code-lane #9): the original
   column below showed 13 of 14 packs failing small-text AA (only acid
   cleared 4.5) — --ink-faint renders resume-source text, footer links,
   reader markers and backlink summaries at 11px. Every failing pack's
   --ink-faint was rebased to >= 4.5:1 against --paper, same hue/
   saturation, lightness-only (acid was already passing and is
   unchanged). "faint/paper (old->new)" replaces the single old column:

     theme        ink/paper soft/paper faint/paper (old->new)  accent/paper
     bond             17.79       7.64        3.77 -> 4.51         6.71
     graphite         15.05       7.47        3.96 -> 4.54         8.68
     acid             16.03       8.45        4.72 (unchanged)    13.02
     blueprint        14.02       7.51        4.13 -> 4.54         8.00
     signal           14.90       6.47        3.33 -> 4.50         4.77
     ultraviolet      14.54       7.36        3.98 -> 4.51         8.04
     cathode          13.62       7.19        4.09 -> 4.51         9.73
     frost            14.32       6.29        3.21 -> 4.50         5.53
     ledger           15.00       6.69        3.48 -> 4.54         5.57
     meridian         14.64       7.56        4.11 -> 4.53         8.49
     tangerine        15.53       6.67        3.59 -> 4.51         4.55
     press            14.25       6.28        3.16 -> 4.50         6.68
     nightshift       15.84       7.99        3.99 -> 4.52        11.24
     redline          14.54       7.00        3.86 -> 4.51         5.34
   ========================================================================== */

:root {
  --sans: ui-sans-serif, -apple-system, "Helvetica Neue", "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas,
    "Liberation Mono", monospace;

  /* Bond — the paper-white flagship. Stark white, near-black ink,
     International-Klein-ish blue. The default. */
  --paper: #ffffff;
  --paper-2: #f6f7f9;
  --paper-3: #eef0f3;
  --ink: #15181d;
  --ink-soft: #4d545f;
  --ink-faint: #6b717d; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #707783, 4.21:1 on --paper-2 (the Library filter input's own
    ground) -> 4.57:1. Same hue, darker only. Prior rebase (was #7d848f,
    3.77:1 -> 4.51:1) only ever checked --paper. */
  --line: rgba(21, 24, 29, 0.14);
  --line-soft: rgba(21, 24, 29, 0.07);
  --accent: #2244ee;
  --accent-ink: #ffffff;
  --accent-soft: rgba(34, 68, 238, 0.09);
  --pos: #147a4d;
  --neg: #c22f2f;
  --warn: #7d5c00;
  --shadow: 0 1px 2px rgba(21, 24, 29, 0.05), 0 8px 24px rgba(21, 24, 29, 0.07);

  /* layout + type scale (theme-independent) */
  --maxw: 1180px;
  --measure: 680px;
  --radius: 6px;
  --radius-sm: 4px;
  --dock-h: 58px;

  /* skin variables — the ONLY properties a skin block (05-skins.css) may
     redefine. Defaults below ARE the "standard" skin: they carry exactly
     the literal values this stylesheet used before the skin axis existed,
     so a device with no skin chosen renders pixel-identically. */
  --font-body: var(--sans);        /* page prose face */
  --font-data: var(--mono);        /* data, numerals, micro-labels */
  --font-reader: var(--font-body); /* the reader body (.tn-body) face */
  --body-size: 15.5px;             /* body font-size */
  --body-leading: 1.55;            /* body line-height */
  --reader-size: 17px;             /* .tn-body font-size */
  --reader-leading: 1.68;          /* .tn-body line-height */
  --row-pad: 11px 8px;             /* .tn-row padding */
  --tile-pad: 14px 16px;           /* console .tile padding */
  --cell-pad: 8px 10px;            /* dashboard table th/td padding */
  --hairline: 1px;                 /* structural rule weight */
}

[data-tntheme="graphite"] {
  /* Graphite — the dark neutral daily driver. */
  --paper: #141619;
  --paper-2: #1b1e22;
  --paper-3: #22262b;
  --ink: #e8eaee;
  --ink-soft: #a0a7b1;
  --ink-faint: #7f8691; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #78808b, 4.19:1 on --paper-2 -> 4.56:1. Same hue, lighter only.
    Prior rebase only ever checked --paper. */
  --line: rgba(232, 234, 238, 0.14);
  --line-soft: rgba(232, 234, 238, 0.07);
  --accent: #8ab4ff;
  --accent-ink: #10131a;
  --accent-soft: rgba(138, 180, 255, 0.12);
  --pos: #56d364;
  --neg: #ff7b72;
  --warn: #e3b341;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 30px rgba(0, 0, 0, 0.45);
}

[data-tntheme="acid"] {
  /* Acid — electric chartreuse on black-olive. The loud one. */
  --paper: #121407;
  --paper-2: #191c0c;
  --paper-3: #212512;
  --ink: #eaf2cf;
  --ink-soft: #a8b487;
  --ink-faint: #7b885e; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #79855c, 4.39:1 on --paper-2 -> 4.56:1. Same hue, lighter only.
    Acid was the one pack that already cleared --paper unchanged in the r1
    rebase, but --paper-2 wasn't checked at all until now. */
  --line: rgba(234, 242, 207, 0.15);
  --line-soft: rgba(234, 242, 207, 0.07);
  --accent: #b7e929;
  --accent-ink: #131503;
  --accent-soft: rgba(183, 233, 41, 0.11);
  --pos: #9ee493;
  --neg: #ff8f80;
  --warn: #e8c268;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 10px 30px rgba(0, 0, 0, 0.5);
}

[data-tntheme="blueprint"] {
  /* Blueprint — duotone drafting blue: everything derives from one hue. */
  --paper: #0d1a2b;
  --paper-2: #122238;
  --paper-3: #183048;
  --ink: #d8e8f8;
  --ink-soft: #93accc;
  --ink-faint: #748ba6; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #6c84a1, 4.16:1 on --paper-2 -> 4.56:1. Same hue, lighter only.
    Prior rebase only ever checked --paper. */
  --line: rgba(216, 232, 248, 0.16);
  --line-soft: rgba(216, 232, 248, 0.08);
  --accent: #6fb4fb;
  --accent-ink: #061220;
  --accent-soft: rgba(111, 180, 251, 0.12);
  --pos: #6fd39a;
  --neg: #ff8ba0;
  --warn: #ecc65f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 30px rgba(0, 0, 0, 0.45);
}

[data-tntheme="signal"] {
  /* Signal — warm gallery paper with a signal-red accent. */
  --paper: #f4f1ea;
  --paper-2: #ece8de;
  --paper-3: #e3ded1;
  --ink: #201d17;
  --ink-soft: #5b564b;
  --ink-faint: #6c685c; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #736e62, 4.15:1 on --paper-2 -> 4.55:1. Same hue, darker only.
    Prior rebase only ever checked --paper. */
  --line: rgba(32, 29, 23, 0.15);
  --line-soft: rgba(32, 29, 23, 0.07);
  --accent: #c23a08;
  --accent-ink: #ffffff;
  --accent-soft: rgba(194, 58, 8, 0.1);
  --pos: #0f6b40;
  --neg: #b3261e;
  --warn: #7a5900;
  --shadow: 0 1px 2px rgba(32, 29, 23, 0.06), 0 8px 24px rgba(32, 29, 23, 0.08);
}

[data-tntheme="ultraviolet"] {
  /* Ultraviolet — deep violet dark, lavender accent. */
  --paper: #150f21;
  --paper-2: #1c152c;
  --paper-3: #241d38;
  --ink: #e6e0f3;
  --ink-soft: #a79dc4;
  --ink-faint: #867da1; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #81779d, 4.24:1 on --paper-2 -> 4.58:1. Same hue, lighter only.
    Prior rebase only ever checked --paper. */
  --line: rgba(230, 224, 243, 0.15);
  --line-soft: rgba(230, 224, 243, 0.07);
  --accent: #b29bff;
  --accent-ink: #150f21;
  --accent-soft: rgba(178, 155, 255, 0.12);
  --pos: #7fd8a8;
  --neg: #ff8fa3;
  --warn: #e7c15f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 10px 30px rgba(0, 0, 0, 0.5);
}

[data-tntheme="cathode"] {
  /* Cathode — amber phosphor terminal, near-black bronze paper. */
  --paper: #161005;
  --paper-2: #1e160a;
  --paper-3: #271d0e;
  --ink: #f2d8a2;
  --ink-soft: #b39d72;
  --ink-faint: #907f56; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #8b7a53, 4.26:1 on --paper-2 -> 4.56:1. Same hue, lighter only.
    Prior rebase only ever checked --paper. */
  --line: rgba(242, 216, 162, 0.16);
  --line-soft: rgba(242, 216, 162, 0.08);
  --accent: #ffa724;
  --accent-ink: #1a1103;
  --accent-soft: rgba(255, 167, 36, 0.11);
  --pos: #a4d97f;
  --neg: #ff9878;
  --warn: #ffd166;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 10px 30px rgba(0, 0, 0, 0.55);
}

[data-tntheme="frost"] {
  /* Frost — cool morning light, teal accent. */
  --paper: #eef2f6;
  --paper-2: #e4eaf1;
  --paper-3: #d9e2ec;
  --ink: #17222d;
  --ink-soft: #4c5a67;
  --ink-faint: #5f6a75; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #64707b, 4.18:1 on --paper-2 -> 4.56:1. Same hue, darker only.
    Prior rebase only ever checked --paper. */
  --line: rgba(23, 34, 45, 0.15);
  --line-soft: rgba(23, 34, 45, 0.07);
  --accent: #0d6a7d;
  --accent-ink: #ffffff;
  --accent-soft: rgba(13, 106, 125, 0.1);
  --pos: #0e6e44;
  --neg: #b02a37;
  --warn: #7a5900;
  --shadow: 0 1px 2px rgba(23, 34, 45, 0.06), 0 8px 24px rgba(23, 34, 45, 0.08);
}

[data-tntheme="ledger"] {
  /* Ledger — warm gray account-book paper, oxide-green entries. */
  --paper: #f1efe8;
  --paper-2: #e9e6dd;
  --paper-3: #dfdbcf;
  --ink: #1c1b15;
  --ink-soft: #565349;
  --ink-faint: #6b665b; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #716c60, 4.19:1 on --paper-2 -> 4.58:1. Same hue, darker only.
    Prior rebase only ever checked --paper. */
  --line: rgba(28, 27, 21, 0.15);
  --line-soft: rgba(28, 27, 21, 0.07);
  --accent: #2e6a4b;
  --accent-ink: #ffffff;
  --accent-soft: rgba(46, 106, 75, 0.1);
  --pos: #1f6b43;
  --neg: #b02f27;
  --warn: #7a5900;
  --shadow: 0 1px 2px rgba(28, 27, 21, 0.06), 0 8px 24px rgba(28, 27, 21, 0.08);
}

[data-tntheme="meridian"] {
  /* Meridian — deep chart-room navy, brass instruments. */
  --paper: #0b1322;
  --paper-2: #101a2e;
  --paper-3: #17233c;
  --ink: #dde5f1;
  --ink-soft: #98a6bf;
  --ink-faint: #76849b; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #707e97, 4.23:1 on --paper-2 -> 4.59:1. Same hue, lighter only.
    Prior rebase only ever checked --paper. */
  --line: rgba(221, 229, 241, 0.15);
  --line-soft: rgba(221, 229, 241, 0.07);
  --accent: #d5a94a;
  --accent-ink: #191003;
  --accent-soft: rgba(213, 169, 74, 0.12);
  --pos: #6fd39a;
  --neg: #ff8ba0;
  --warn: #e8c268;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 30px rgba(0, 0, 0, 0.45);
}

[data-tntheme="tangerine"] {
  /* Tangerine — sunny cream, one loud orange. The bold light one. */
  --paper: #fdf4e3;
  --paper-2: #f7ecd4;
  --paper-3: #f0e2c3;
  --ink: #271a09;
  --ink-soft: #645440;
  --ink-faint: #786854; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #7e6e58, 4.20:1 on --paper-2 -> 4.58:1. Same hue, darker only.
    Prior rebase only ever checked --paper. */
  --line: rgba(39, 26, 9, 0.16);
  --line-soft: rgba(39, 26, 9, 0.08);
  --accent: #bd4d00;
  --accent-ink: #ffffff;
  --accent-soft: rgba(189, 77, 0, 0.1);
  --pos: #206b3f;
  --neg: #b3261e;
  --warn: #7a5900;
  --shadow: 0 1px 2px rgba(39, 26, 9, 0.06), 0 8px 24px rgba(39, 26, 9, 0.08);
}

[data-tntheme="press"] {
  /* Slate Press — cool gray duotone: every tone is the one slate hue. */
  --paper: #eceef1;
  --paper-2: #e2e5ea;
  --paper-3: #d6dae1;
  --ink: #191f28;
  --ink-soft: #4e5766;
  --ink-faint: #5e6673; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #646d7a, 4.15:1 on --paper-2 -> 4.59:1. Same hue, darker only.
    Prior rebase only ever checked --paper. */
  --line: rgba(25, 31, 40, 0.15);
  --line-soft: rgba(25, 31, 40, 0.07);
  --accent: #3d5471;
  --accent-ink: #ffffff;
  --accent-soft: rgba(61, 84, 113, 0.11);
  --pos: #0e6e44;
  --neg: #b02a37;
  --warn: #7a5900;
  --shadow: 0 1px 2px rgba(25, 31, 40, 0.06), 0 8px 24px rgba(25, 31, 40, 0.08);
}

[data-tntheme="nightshift"] {
  /* Nightshift — true-black OLED watch floor, cyan readouts. */
  --paper: #000000;
  --paper-2: #0a0d10;
  --paper-3: #13171b;
  --ink: #d8e1e7;
  --ink-soft: #92a2ac;
  --ink-faint: #6b7d87; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #667781, 4.19:1 on --paper-2 -> 4.55:1. Same hue, lighter only.
    Prior rebase only ever checked --paper. */
  --line: rgba(216, 225, 231, 0.16);
  --line-soft: rgba(216, 225, 231, 0.08);
  --accent: #38cfe6;
  --accent-ink: #001318;
  --accent-soft: rgba(56, 207, 230, 0.1);
  --pos: #4cd97b;
  --neg: #ff7b72;
  --warn: #e3b341;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 10px 30px rgba(0, 0, 0, 0.65);
}

[data-tntheme="redline"] {
  /* Redline — dark warm gray pit wall, racing red. */
  --paper: #191412;
  --paper-2: #211b18;
  --paper-3: #2a2320;
  --ink: #ece4de;
  --ink-soft: #ab9e94;
  --ink-faint: #8f8278; /* rebased 2026-07-21 (AIL r3 lane G, finding #5):
    was #897c72, 4.20:1 on --paper-2 -> 4.56:1. Same hue, lighter only.
    Prior rebase only ever checked --paper. */
  --line: rgba(236, 228, 222, 0.15);
  --line-soft: rgba(236, 228, 222, 0.07);
  --accent: #ff4438;
  --accent-ink: #1f0503;
  --accent-soft: rgba(255, 68, 56, 0.11);
  --pos: #93d67e;
  --neg: #ff9878;
  --warn: #ecc65f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 10px 30px rgba(0, 0, 0, 0.55);
}

/* ---- reset ------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--body-size);
  line-height: var(--body-leading);
  -webkit-font-smoothing: antialiased;
  font-kerning: normal;
}

img, svg { max-width: 100%; }

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

input, textarea, select { font: inherit; color: inherit; }

h1, h2, h3, h4, p { margin: 0; }

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

::selection { background: var(--accent-soft); }

/* ---- shared primitives -------------------------------------------------- */

/* micro-label: THE Tenet voice — mono, uppercase, tracked wide.
   Promoted one step (sol V3 #1, 2026-07-12): +1px, slightly looser
   tracking (small caps read as a blob at tight spacing — widening it is
   the legibility fix, not a style change), and one step darker
   (ink-faint -> ink-soft, a strictly higher-contrast tier per the
   luminance table above) — still the console's mono-caps voice, just
   readable at a glance instead of squinted at. */
/* The console's key-label voice. Carried by inline stat labels (spans) and by
   the real h1/h2 tile headings alike, so it must neutralise heading margins
   and size rather than inherit them. */
.tn-k {
  font-family: var(--font-data);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0;
  line-height: 1.4;
  display: inline;
}

/* data numerals — mono, tabular */
.tn-num {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
}

.tn-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  font-size: 13px;
  font-weight: 550;
  color: var(--ink);
  white-space: nowrap;
}
.tn-btn:hover { background: var(--paper-2); text-decoration: none; }
.tn-btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.tn-btn.primary:hover { filter: brightness(1.06); }
.tn-btn.on {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

.tn-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--font-data);
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  white-space: nowrap;
}
a.tn-chip:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.tn-chip.acc { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

/* progress meter (thin bar) */
.tn-meter {
  height: 4px;
  border-radius: 2px;
  background: var(--paper-3);
  overflow: hidden;
}
.tn-meter > i {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 2px;
}

/* rows (list navigation unit — j/k walks [data-k]) */
.tn-rows { display: flex; flex-direction: column; }
.tn-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: var(--row-pad);
  border-bottom: var(--hairline) solid var(--line-soft);
  color: var(--ink);
  border-radius: var(--radius-sm);
}
.tn-row:hover { background: var(--paper-2); text-decoration: none; }
.tn-row.kn-cur {
  background: var(--accent-soft);
  box-shadow: inset 2px 0 0 var(--accent);
}
.tn-row-main { flex: 1; min-width: 0; }
/* reveal, don't truncate (sol V3 #3): titles get two lines before they
   clip — a long claim earns a second line rather than an ellipsis after
   a handful of words. This was already the `.tile .tn-row-title`
   override (console tiles); it is now the base behavior everywhere a
   `.tn-row` appears (library, canon, load-bearing, notes, …), so the
   tile-scoped override below only needs to restate its font-size. */
.tn-row-title {
  font-size: 14.5px;
  font-weight: 550;
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* source labels: same two-line reveal + one type-scale step darker/
   larger (sol V3 #1 + #3) so a long source title gets room instead of
   clipping after a few characters. */
.tn-row-sub {
  margin-top: 2px;
  font-family: var(--font-data);
  font-size: 12px;
  color: var(--ink-soft);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.tn-row-side {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-data);
  font-size: 12px;
  color: var(--ink-soft);
}

.tn-dot {
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--line);
}
.tn-dot.read { background: var(--pos); border-color: var(--pos); }

/* empty state */
.tn-empty {
  padding: 40px 16px;
  text-align: center;
  color: var(--ink-faint);
  font-family: var(--font-data);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* a way out of the dead end — normal-case buttons in the uppercase
   micro-copy block, matching .tn-btn everywhere else in the console */
.tn-empty-actions {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 14px;
  text-transform: none;
  letter-spacing: normal;
}

/* toast */
.tn-toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--dock-h) + 18px);
  transform: translate(-50%, 8px);
  z-index: 90;
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
  color: var(--ink);
  font-family: var(--font-data);
  font-size: 12px;
  box-shadow: var(--shadow);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.tn-toast.show { opacity: 1; transform: translate(-50%, 0); }

/* boot state */
.tn-boot {
  display: grid;
  place-items: center;
  min-height: 40vh;
  color: var(--ink-faint);
}
.tn-boot-line {
  font-family: var(--font-data);
  font-size: 12px;
  letter-spacing: 0.22em;
}

/* motion */
@keyframes tn-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}
.tn-in { animation: tn-in 0.22s ease both; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001s !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   Tenet — 05-skins: the SKIN axis — typography / density / shape.

   Orthogonal to the wardrobe (data-tntheme) in 00-tokens.css: skins never
   touch a color token (--paper/--ink/--accent/--pos/...), only the skin
   variables declared at :root in 00-tokens.css (--font-body/--font-data/
   --font-reader/--body-size/--body-leading/--reader-size/--reader-leading/
   --row-pad/--tile-pad/--cell-pad/--hairline) plus the shape/measure
   tokens (--radius/--radius-sm/--measure). Swapped via data-tenet-skin on
   :root, key wv:tenet:skin — per-device, like the theme.

   [data-tenet-skin="standard"] has no block here on purpose — its values
   ARE the :root defaults in 00-tokens.css (the exact literals TENET
   rendered with before the skin axis existed). That is the pixel-parity
   contract: adding this file must not move a single computed style for
   the default skin, because there is nothing here to override it with.
   wire / journal / dense below are the only three skins that redefine
   anything.
   ========================================================================== */

[data-tenet-skin="wire"] {
  /* Wire — the terminal soul. Mono-everything (prose included), tighter
     metrics, corners squared off, structural hairlines a half-step
     heavier. Data face stays --mono, now carrying the whole page. */
  --font-body: var(--mono);
  --body-size: 14.5px;
  --body-leading: 1.5;
  --reader-size: 15.5px;
  --reader-leading: 1.6;
  --measure: 640px;
  --row-pad: 9px 8px;
  --tile-pad: 12px 14px;
  --cell-pad: 7px 10px;
  --hairline: 1.5px;
  --radius: 0px;
  --radius-sm: 0px;
}

[data-tenet-skin="journal"] {
  /* Journal — the long-read skin. The reader gets a humanist reading
     face at a larger size over a wider measure; the console around it
     calms down: airier rows and tiles, slightly softer corners. Data
     labels stay mono — the instrument panel keeps its voice. */
  --font-reader: "Iowan Old Style", Charter, Georgia, "Times New Roman",
    serif;
  --reader-size: 18.5px;
  --reader-leading: 1.78;
  --measure: 720px;
  --body-size: 16px;
  --body-leading: 1.6;
  --row-pad: 13px 8px;
  --tile-pad: 16px 18px;
  --radius: 8px;
  --radius-sm: 5px;
}

[data-tenet-skin="dense"] {
  /* Dense — operator-max. Same faces as standard, everything compacted:
     smaller body, tighter leading, compact tiles, tighter table cells,
     narrower reading measure. Density without the mono commitment. */
  --body-size: 14px;
  --body-leading: 1.45;
  --reader-size: 15.5px;
  --reader-leading: 1.55;
  --measure: 640px;
  --row-pad: 7px 6px;
  --tile-pad: 10px 12px;
  --cell-pad: 5px 8px;
}

/* ==========================================================================
   Tenet — 06-materials: the MATERIAL skin pair — glass + alloy.

   Two additional TN_SKINS entries (SPEC-materials-docket.md §A), layered
   ONTO whichever data-tntheme is currently active. Unlike the typography-
   only packs in 05-skins.css, these two deliberately DO touch surface
   treatment — that is the entire point of a "material" (translucency/
   depth/speculars over ANY color theme). They ride the SAME axis as wire/
   journal/dense (TN_SKINS, data-tenet-skin, wv:tenet:skin) — an additive
   registry entry, not a new key (spec rule 3/5; 00-core.js/40-library.js
   need no engine edits, only the two new TN_SKINS rows). The token
   contract is identical in name to the sibling v1 lane's
   (webapp/static/css/06-materials.css, which this file mirrors):

     --mat-surface         panel bg (cards, command palette)
     --mat-surface-strong  panel bg (header / mobile dock — persistent
                            chrome, denser for reliable legibility)
     --mat-edge             inset "edge" treatment (glass: inner light
                            border; alloy: bevel pair)
     --mat-elev             outer drop shadow (glass: airy/faint; alloy:
                            tight/hard)
     --mat-blur             backdrop-filter value ("none" for alloy, which
                            is opaque)
     --mat-backdrop         a very subtle layer behind the whole app
                            (glass: mesh gradient to refract; alloy:
                            brushed repeating-linear-gradient texture)
     --mat-sheen             (alloy only) static specular sweep for cards/
                            buttons

   Every value is DERIVED from the active theme's own custom properties
   (--paper* / --ink / --accent via color-mix()) — never a hardcoded theme-
   specific hex — so the pair renders correctly across all 14 wardrobe
   packs. color-mix() with the literal white/black keywords below is a
   theme-relative lighten/darken of each theme's OWN --paper-2/--ink
   tokens (the standard, theme-agnostic way to fake a specular highlight
   or a bevel shadow — highlights trend white, recesses trend black,
   regardless of whether the underlying theme is light or dark); it is
   not a hardcoded per-theme color. Tenet carries no --gilt secondary
   accent (a v1-only token); the backdrop's second glow reuses --accent
   at a lower mix instead of introducing a token this shell doesn't have.

   Because every rule below is scoped under [data-tenet-skin="glass"] /
   [data-tenet-skin="alloy"], standard/wire/journal/dense (and no skin at
   all) never match a single selector here — their computed styles stay
   untouched byte-for-byte (the skin pixel-parity contract documented in
   00-tokens.css and 05-skins.css).

   One deliberate divergence from the v1 file this mirrors: v1 applies its
   fixed backdrop via `body::after` at z-index:0, which sits behind v1's
   own #shell only because #shell already carries `position: relative;
   z-index: 1;` as a base rule (webapp/static/css/10-layout.css). Tenet has
   no single wrapper like that — #tn-header/#tn-view/#tn-dock are
   independent siblings, none z-indexed — so promoting one to intercept a
   z-indexed pseudo-element would either miss the others or require
   touching base rules outside this lane's ownership (css/** only, plus
   the TN_SKINS registry). Applying --mat-backdrop as body's OWN
   background-image instead sidesteps the stacking question entirely (an
   element's own background can never paint over its descendants) and
   renders the identical visual result: a fixed, very subtle layer visible
   in the gutters around panels and, for glass, refracted further through
   the translucent surfaces on top of it. The token CONTRACT (names,
   derivation, fallback rules) is unchanged from v1; only this one
   application mechanism is shell-shaped.
   ========================================================================== */

/* ---- pack tokens ----------------------------------------------------------
   Panel alpha kept inside the spec's own ~0.55-0.7 range: 60% for cards/
   palette, 70% (the top of that range) for the header/dock, since
   persistent chrome needs the most reliable legibility. */

[data-tenet-skin="glass"] {
  --mat-surface: color-mix(in srgb, var(--paper-2) 60%, transparent);
  --mat-surface-strong: color-mix(in srgb, var(--paper-2) 70%, transparent);
  --mat-edge:
    inset 0 1px 0 0 color-mix(in srgb, white 50%, transparent),
    inset 0 0 0 1px color-mix(in srgb, white 14%, transparent);
  --mat-elev:
    0 2px 6px color-mix(in srgb, var(--ink) 5%, transparent),
    0 22px 56px color-mix(in srgb, var(--ink) 13%, transparent);
  --mat-blur: blur(18px) saturate(1.4);
  --mat-backdrop:
    radial-gradient(60% 50% at 18% -10%,
      color-mix(in srgb, var(--accent) 16%, transparent), transparent 60%),
    radial-gradient(55% 45% at 88% 110%,
      color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%);
}

[data-tenet-skin="alloy"] {
  /* vertical brushed-metal gradient: each theme's own paper tokens, pushed
     lighter at the top and darker at the bottom — the "#f2f2f4 -> #d8d8dc
     feel" from the spec, expressed relative to the theme instead of as a
     literal hex pair, so it works the same way in light and dark packs. */
  --mat-surface: linear-gradient(
    180deg,
    color-mix(in srgb, var(--paper-2) 88%, white 12%) 0%,
    color-mix(in srgb, var(--paper-3) 84%, black 6%) 100%
  );
  --mat-surface-strong: linear-gradient(
    180deg,
    color-mix(in srgb, var(--paper) 90%, white 10%) 0%,
    color-mix(in srgb, var(--paper-2) 84%, black 8%) 100%
  );
  --mat-edge:
    inset 1px 1px 0 0 color-mix(in srgb, white 45%, transparent),
    inset -1px -1px 0 0 color-mix(in srgb, black 30%, transparent);
  --mat-elev:
    0 1px 2px color-mix(in srgb, var(--ink) 20%, transparent),
    0 6px 14px color-mix(in srgb, var(--ink) 24%, transparent);
  --mat-blur: none;
  --mat-backdrop: repeating-linear-gradient(
    95deg,
    color-mix(in srgb, var(--ink) 4%, transparent) 0px,
    transparent 1px,
    transparent 3px
  );
  --mat-sheen: linear-gradient(
    115deg,
    transparent 30%,
    color-mix(in srgb, white 18%, transparent) 46%,
    transparent 62%
  );
}

/* ---- hard rule 1: reduced-transparency + no-backdrop-filter fallbacks ----
   Both packs collapse --mat-surface(-strong) to the plain opaque paper-2
   panel and drop the blur entirely. This is also the contrast-scrim
   floor: the panel becomes fully solid, so body text always sits on a
   background at least as opaque as the theme's own --paper-2 — the same
   surface every other skin already meets its contrast promise against
   (00-tokens.css luminance table). Order matters: these two blocks must
   stay AFTER the pack blocks above so they win the cascade when either
   condition matches (equal-specificity, later wins). */

@media (prefers-reduced-transparency: reduce) {
  [data-tenet-skin="glass"],
  [data-tenet-skin="alloy"] {
    --mat-surface: var(--paper-2);
    --mat-surface-strong: var(--paper-2);
    --mat-blur: none;
  }
}

@supports not (backdrop-filter: blur(1px)) {
  [data-tenet-skin="glass"],
  [data-tenet-skin="alloy"] {
    --mat-surface: var(--paper-2);
    --mat-surface-strong: var(--paper-2);
    --mat-blur: none;
  }
}

/* ---- backdrop: a fixed, very subtle layer behind the whole app --------- */

[data-tenet-skin="glass"] body,
[data-tenet-skin="alloy"] body {
  background-image: var(--mat-backdrop);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

/* ---- panel surfaces: cards + command palette ---------------------------- */

[data-tenet-skin="glass"] .tile,
[data-tenet-skin="alloy"] .tile,
[data-tenet-skin="glass"] .tn-panel,
[data-tenet-skin="alloy"] .tn-panel,
[data-tenet-skin="glass"] .tn-kpi,
[data-tenet-skin="alloy"] .tn-kpi,
[data-tenet-skin="glass"] .tn-swatch,
[data-tenet-skin="alloy"] .tn-swatch,
[data-tenet-skin="glass"] .tn-path-card,
[data-tenet-skin="alloy"] .tn-path-card,
[data-tenet-skin="glass"] .tn-note-card,
[data-tenet-skin="alloy"] .tn-note-card,
[data-tenet-skin="glass"] .tn-domcard,
[data-tenet-skin="alloy"] .tn-domcard,
[data-tenet-skin="glass"] .tn-record,
[data-tenet-skin="alloy"] .tn-record,
[data-tenet-skin="glass"] .pal-panel,
[data-tenet-skin="alloy"] .pal-panel {
  border: none;
  background: var(--mat-surface);
  box-shadow: var(--mat-edge), var(--mat-elev);
  backdrop-filter: var(--mat-blur, none);
  -webkit-backdrop-filter: var(--mat-blur, none);
}

/* ---- panel surfaces: header + mobile dock (persistent chrome) ---------- */

[data-tenet-skin="glass"] #tn-header,
[data-tenet-skin="alloy"] #tn-header,
[data-tenet-skin="glass"] #tn-dock,
[data-tenet-skin="alloy"] #tn-dock {
  background: var(--mat-surface-strong);
  box-shadow: var(--mat-edge), var(--mat-elev);
  backdrop-filter: var(--mat-blur, none);
  -webkit-backdrop-filter: var(--mat-blur, none);
}

/* ---- state-indicator fixups ----------------------------------------------
   Two existing rules share the same (attribute + class) specificity as
   the "panel surfaces: cards" block above; source order alone would let
   this file's `border: none` / `box-shadow` silently drop the state cue
   under glass/alloy. Restate both at higher specificity so they keep
   reading correctly — neither touches the pinned mobile/focus/aria
   machinery, only background/box-shadow/filter. */

/* .tn-swatch[aria-pressed="true"] — "this is your current theme" ring
   (20-views.css) */
[data-tenet-skin="glass"] .tn-swatch[aria-pressed="true"],
[data-tenet-skin="alloy"] .tn-swatch[aria-pressed="true"] {
  border: none;
  box-shadow: 0 0 0 1px var(--accent), var(--mat-edge), var(--mat-elev);
}

/* .tn-domcard:hover — background swap (30-reader-dash.css); restated as a
   brightness lift so hovering still reads without breaking the
   translucent/metal surface underneath it. */
[data-tenet-skin="glass"] .tn-domcard:hover,
[data-tenet-skin="alloy"] .tn-domcard:hover {
  background: var(--mat-surface);
  filter: brightness(1.05);
}

/* ---- alloy speculars: static sheen sweep on cards/buttons ---------------
   No animation/transition is added — the sheen is a fixed gradient, and
   the blanket "@media (prefers-reduced-motion: reduce)" rule already in
   00-tokens.css covers any transition this file's hover states rely on
   (hard rule 2), so nothing extra is needed here. .tn-btn gets ONLY this
   overlay (no background/blur swap), so button contrast stays untouched
   byte-for-byte. */

[data-tenet-skin="alloy"] .tile,
[data-tenet-skin="alloy"] .tn-panel,
[data-tenet-skin="alloy"] .tn-kpi,
[data-tenet-skin="alloy"] .tn-swatch,
[data-tenet-skin="alloy"] .tn-path-card,
[data-tenet-skin="alloy"] .tn-note-card,
[data-tenet-skin="alloy"] .tn-domcard,
[data-tenet-skin="alloy"] .tn-record,
[data-tenet-skin="alloy"] .tn-btn {
  position: relative;
}

[data-tenet-skin="alloy"] .tile::before,
[data-tenet-skin="alloy"] .tn-panel::before,
[data-tenet-skin="alloy"] .tn-kpi::before,
[data-tenet-skin="alloy"] .tn-swatch::before,
[data-tenet-skin="alloy"] .tn-path-card::before,
[data-tenet-skin="alloy"] .tn-note-card::before,
[data-tenet-skin="alloy"] .tn-domcard::before,
[data-tenet-skin="alloy"] .tn-record::before,
[data-tenet-skin="alloy"] .tn-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--mat-sheen);
  pointer-events: none;
}

/* ==========================================================================
   Tenet — 10-shell: header, wordmark, nav, mobile dock, command palette,
   reading-progress hairline.
   ========================================================================== */

/* ---- top progress hairline (reader scroll) ------------------------------ */

#tn-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 0;
  z-index: 80;
  background: var(--accent);
}

/* ---- header -------------------------------------------------------------- */

#tn-header {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 26px;
  height: 54px;
  padding: 0 20px;
  background: var(--paper);
  border-bottom: var(--hairline) solid var(--line);
}

.tn-wordmark {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--ink);
  flex-shrink: 0;
}
.tn-wordmark:hover { text-decoration: none; }
.tn-mark {
  width: 11px;
  height: 11px;
  background: var(--accent);
  border-radius: 2px;
}
.tn-word {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 750;
  letter-spacing: 0.32em;
  margin-right: -0.32em; /* optically re-center the tracked caps */
}

#tn-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
}
.tn-nav-item {
  padding: 6px 11px;
  border-radius: var(--radius-sm);
  font-family: var(--font-data);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.tn-nav-item:hover { color: var(--ink); background: var(--paper-2); text-decoration: none; }
.tn-nav-item.active {
  color: var(--accent);
  background: var(--accent-soft);
}

.tn-header-right {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

.tn-cmd-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 6px 8px 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
  color: var(--ink-soft);
  font-size: 13px;
}
.tn-cmd-btn:hover { border-color: var(--accent); color: var(--ink); }
.tn-cmd-kbd {
  font-family: var(--font-data);
  font-size: 11.5px;
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--paper);
  color: var(--ink-soft);
}

.tn-gear {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: 16px;
}
.tn-gear:hover { background: var(--paper-2); color: var(--ink); text-decoration: none; }
.tn-gear.active { color: var(--accent); background: var(--accent-soft); }

/* ---- main ---------------------------------------------------------------- */

#tn-view {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 22px 20px calc(var(--dock-h) + 40px);
  outline: none;
}

.tn-viewhead { margin-bottom: 18px; }
.tn-viewhead .tn-title {
  margin-top: 6px;
  font-size: clamp(24px, 3.4vw, 32px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.12;
}
.tn-viewhead .tn-sub {
  margin-top: 6px;
  font-size: 14px;
  color: var(--ink-soft);
  max-width: 60ch;
}

/* ---- mobile dock ---------------------------------------------------------- */

#tn-dock {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 70;
  display: none;
  align-items: stretch;
  height: calc(var(--dock-h) + env(safe-area-inset-bottom, 0px));
  padding: 0 8px env(safe-area-inset-bottom, 0px);
  background: var(--paper);
  border-top: var(--hairline) solid var(--line);
}
.tn-dock-item {
  flex: 1;
  min-height: 44px; /* reliable tap area (sol V3 #2) — #tn-dock's own
    height (--dock-h: 58px) already clears this; the explicit floor keeps
    the guarantee independent of that token */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  color: var(--ink-soft);
  font-family: var(--font-data);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.tn-dock-item:hover { text-decoration: none; }
.tn-dock-item.active { color: var(--accent); }
.tn-dock-glyph { font-size: 16px; line-height: 1; }

/* the center command button — still the largest, most prominent dock
   target (it's the primary entry point, per Opus r2's "prominent mobile
   entry" note), but resting on NEUTRAL ink/paper tokens rather than
   --accent: r1 stills-lane (2026-07-21) confirmed via live render that an
   accent-colored resting pill reads as a SECOND active destination next to
   the true current tab (.tn-dock-item.active, which is the one thing
   --accent is allowed to mean: "you are here") — on Console, both the
   Console glyph/label AND the Search pill read accent at once, looking
   like a broken nav state. Solid neutral ink/paper keeps this the largest,
   boldest, unmissable pill (shape + size carry "the special one," not
   color) without borrowing the location-indicator color; a real press
   still gets the full solid-accent feedback via :active, now a deliberate
   MOMENTARY accent flash rather than the resting state. */
.tn-dock-cmd {
  flex: 1.4;
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  margin: 0 6px;
  border-radius: 999px;
  border: 1px solid var(--ink);
  background: var(--paper-2);
  color: var(--ink);
  font-size: 13px;
  font-weight: 650;
}
.tn-dock-cmd:active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow);
}

@media (max-width: 720px) {
  #tn-header { gap: 12px; padding: 0 14px; }
  #tn-nav, .tn-cmd-btn { display: none; }
  #tn-dock { display: flex; }
  #tn-view { padding-left: 14px; padding-right: 14px; }
}
@media (min-width: 721px) {
  #tn-view { padding-bottom: 60px; }
  .tn-toast { bottom: 26px; }
}

/* ---- command palette ------------------------------------------------------ */

.pal-scrim {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(8, 10, 14, 0.5);
}

/* palette open transition (Opus V3 #4, opacity-only fix 2026-07-21 AIL r3
   lane G / sol56-high finding #2): a dedicated keyframe rather than
   reusing the generic .tn-in (which many unrelated elements share and
   which translates on Y). This USED to animate `transform: scale(0.98)
   -> none` for a scale+fade "surface" feel — but with `animation ...
   both`, the retained 100% frame's `transform: none` permanently WON
   over `.pal-panel`'s own static `transform: translateX(-50%)` for the
   entire open/close lifecycle (animation values outrank a regular
   declaration on the same property), silently un-centering the desktop
   panel (640px panel at 1024px viewport = 128px clipped, worse at
   narrower widths). Fixed by dropping `transform` from the keyframes
   entirely — opacity-only fade, no scale — so the static
   `translateX(-50%)` below is never contested and the panel centers
   correctly at every breakpoint. Reduced-motion is handled globally
   (00-tokens.css collapses every animation-duration to 0.001s), so no
   local guard is needed. Do NOT reintroduce a transform here (e.g.
   `translateX(-50%) scale(...)`) without also auditing the <=720px
   override below, which sets its OWN unconditional `transform: none` —
   an animation-authored transform would win over that too and break the
   mobile full-width layout the same way it broke desktop centering. */
@keyframes tn-pal-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
.pal-panel {
  position: fixed;
  z-index: 101;
  top: 11vh;
  left: 50%;
  transform: translateX(-50%);
  width: min(640px, calc(100vw - 24px));
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--paper);
  box-shadow: var(--shadow);
  overflow: hidden;
  animation: tn-pal-in 0.12s ease both;
}

.pal-input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
  border-bottom: 1px solid var(--line);
}
.pal-glyph {
  font-family: var(--font-data);
  color: var(--accent);
  font-size: 14px;
  font-weight: 700;
}
#pal-input {
  flex: 1;
  height: 52px;
  border: none;
  outline: none;
  background: transparent;
  font-size: 15.5px;
  color: var(--ink);
}
#pal-input::placeholder { color: var(--ink-faint); }

#pal-results {
  max-height: min(52vh, 460px);
  overflow-y: auto;
  padding: 6px;
}

.pal-group { padding: 8px 10px 3px; }

.pal-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border-radius: var(--radius);
  text-align: left;
  color: var(--ink);
  font-size: 14px;
}
.pal-item:hover { background: var(--paper-2); }
.pal-item[aria-selected="true"] {
  background: var(--accent-soft);
  box-shadow: inset 2px 0 0 var(--accent);
}
.pal-item-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* matched-query highlight (Opus V3 #1): the accent tint, not a jarring
   flat highlight color, so it reads as "this is why it matched" rather
   than shouting over the result. esc()-escaped on the way in — see
   palMarkQuery() in 20-palette.js — so this only ever wraps plain text. */
.pal-item-label mark {
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 2px;
  padding: 0 1px;
}
.pal-item-hint {
  flex-shrink: 0;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-data);
  font-size: 11.5px;
  color: var(--ink-soft);
}
.pal-item-kind {
  flex-shrink: 0;
  font-family: var(--font-data);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.pal-foot {
  display: flex;
  gap: 16px;
  padding: 8px 16px;
  border-top: 1px solid var(--line);
  font-family: var(--font-data);
  font-size: 11.5px;
  color: var(--ink-soft);
}

.pal-empty {
  padding: 26px 16px;
  text-align: center;
  font-family: var(--font-data);
  font-size: 12px;
  color: var(--ink-faint);
}

@media (max-width: 720px) {
  .pal-panel {
    /* the base rule's left:50% + translateX(-50%) is meant to center a
       640px-max panel on wide viewports — mobile wants the opposite
       layout (flush full-width, no centering), so this override still
       needs its own explicit `transform: none` regardless of the
       open-animation fix above. (Historical note: tn-pal-in USED to
       animate `transform` too, which happened to make this override's
       `transform: none` redundant with the animation's own end state
       during open/close — that coincidence is gone now that tn-pal-in is
       opacity-only, so this declaration is the ONLY thing keeping mobile
       un-centered; do not remove it.) */
    top: 0;
    left: 0;
    width: 100vw;
    transform: none;
    border-radius: 0 0 10px 10px;
    border-top: none;
  }
  #pal-results { max-height: 60vh; }
}

/* --- home-link touch floor (query-independent) ----------------------------
   .tn-wordmark ("Tenet — console home", href=#/) and .tn-doors a (the
   sibling-door exit links to the Athenaeum/Drift) are the shell's actual
   "go home" paths, not just dense chrome. Measured live: .tn-wordmark's
   real box is 21.7px tall and .tn-doors a's is 17px tall on a FINE
   pointer — under even the 24px WCAG 2.5.8 floor, let alone 44px — because
   the touch-target floor below only fires under (pointer: coarse). That is
   exactly the residue Drift's own AIL r2 pass flagged and fixed in
   10-flow.css ("previously gated behind pointer:coarse, which left the
   TRUE (query-independent) target at 34/42px"). These two get the same
   unconditional floor regardless of pointer type. Visible logo/text size
   is untouched: min-height/min-width only pad the invisible hit box, and
   both already draw wider than 44px so min-width never grows anything. */
.tn-wordmark,
.tn-doors a {
  min-height: 44px;
  min-width: 44px;
  justify-content: center;
  display: inline-flex;
  align-items: center;
}

/* --- touch target floor --------------------------------------------------
   The console is deliberately dense on a pointer device — 11px data type and
   6px padding are the operator-console voice, not an accident. On a coarse
   pointer that density puts the header controls at 22-36px, well under the
   44px a finger needs. Raise only the hit area, and only where there is no
   mouse: the visual rhythm on desktop is unchanged.
   (.tn-wordmark / .tn-doors a moved to the unconditional rule above —
   those two are primary "home" navigation, not just dense chrome, so they
   need the floor on every pointer type, not only coarse ones.) */
@media (pointer: coarse) {
  .tn-nav-item,
  .tn-cmd-btn,
  .tn-btn,
  .tn-gear {
    min-height: 44px;
    min-width: 44px;
    justify-content: center;
    display: inline-flex;
    align-items: center;
  }
}

/* ==========================================================================
   Tenet — 20-views: operator console tiles, library, settings wardrobe,
   paths, canon, my-notes, synthesis notes.
   ========================================================================== */

/* ---- console -------------------------------------------------------------- */

.tn-console-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.tn-console-statline {
  font-family: var(--font-data);
  font-size: 12px;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
}

.tn-tiles {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 12px;
}
.tile {
  grid-column: span 4;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  padding: var(--tile-pad);
  min-width: 0;
}
.tile.w6 { grid-column: span 6; }
.tile.w8 { grid-column: span 8; }
.tile.w12 { grid-column: span 12; }
.tile-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.tile-big {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: 26px;
  font-weight: 650;
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.tile-big small {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-faint);
}
.tile-cap {
  margin-top: 4px;
  font-size: 12px;
  color: var(--ink-soft);
}

/* resume tile */
.tile-resume-title {
  font-size: 16.5px;
  font-weight: 650;
  letter-spacing: -0.01em;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.tile-resume-src {
  margin: 4px 0 12px;
  font-family: var(--font-data);
  font-size: 11px;
  color: var(--ink-faint);
}

/* queue tile rows reuse .tn-row at tighter padding; the two-line title
   clamp is now the base .tn-row-title behavior (00-tokens.css), so this
   override only needs the tile's tighter font-size. */
.tile .tn-row { padding: 8px 6px; }
.tile .tn-row-title { font-size: 13.5px; }

.tile-slot {
  flex-shrink: 0;
  font-family: var(--font-data);
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent-soft);
  background: var(--accent-soft);
  border-radius: 3px;
  padding: 2px 6px;
}

/* session tile */
.tile-session-nums {
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
}
.tn-spark { display: block; width: 100%; height: 38px; margin-top: 10px; }
.tn-spark .spark-line { fill: none; stroke: var(--accent); stroke-width: 1.6; }
.tn-spark .spark-fill { fill: var(--accent); opacity: 0.12; stroke: none; }

/* action strip */
.tn-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
}

/* console footer — the sibling doors */
.tn-doors {
  margin-top: 26px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  font-family: var(--font-data);
  font-size: 11px;
  color: var(--ink-faint);
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
}

@media (max-width: 980px) {
  .tile, .tile.w6, .tile.w8 { grid-column: span 6; }
  .tile.w12 { grid-column: span 12; }
}
@media (max-width: 720px) {
  .tn-tiles { gap: 10px; }
  .tile, .tile.w6, .tile.w8, .tile.w12 { grid-column: span 12; }

  /* stills Major: queue titles were heavily truncated while the
     .tile-slot badge (PATH/WILD/etc.) sat as a fixed-width sibling
     eating horizontal room from .tn-row-main. Move the badge above the
     title instead of beside it, so the title gets the row's full width
     for its (already 2-line-clamped) claim. Scoped to `.tile .tn-row`
     only — the Reading Paths "stop" badge (40-library.js) is a plain
     `.tn-row` outside any `.tile` and is unaffected. */
  .tile .tn-row { flex-wrap: wrap; row-gap: 4px; }
  .tile .tn-row .tile-slot { flex: 0 0 100%; }
}

/* ---- library --------------------------------------------------------------- */

.tn-domchips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 8px;
  margin-bottom: 12px;
  scrollbar-width: thin;
}
.tn-domchips .tn-chip { flex-shrink: 0; }

.tn-filter {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
}
.tn-filter input {
  flex: 1;
  height: 40px;
  border: none;
  outline: none;
  background: transparent;
  font-size: 14px;
  color: var(--ink);
}
.tn-filter input::placeholder { color: var(--ink-faint); }
.tn-filter .tn-k { flex-shrink: 0; }

.tn-minimeter {
  width: 90px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.tn-minimeter .tn-meter { flex: 1; }

/* source detail header */
.tn-srchead { margin-bottom: 16px; }
.tn-srchead .tn-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.tn-srchead .tn-meterrow {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 380px;
  margin-top: 12px;
}
.tn-srchead .tn-meterrow .tn-meter { flex: 1; }

/* ---- settings: the wardrobe ------------------------------------------------- */

.tn-wardrobe {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
  margin-top: 12px;
}
.tn-swatch {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-align: left;
  overflow: hidden;
}
.tn-swatch[aria-pressed="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.tn-swatch-preview {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  margin-bottom: 8px;
  border: 1px solid rgba(128, 128, 128, 0.25);
}
.tn-swatch-aa { font-weight: 700; font-size: 15px; }
.tn-swatch-dot { width: 10px; height: 10px; border-radius: 50%; }
.tn-swatch-name { font-size: 13px; font-weight: 650; color: var(--ink); }
.tn-swatch-tag {
  font-family: var(--font-data);
  font-size: 11px;
  color: var(--ink-soft);
}

.tn-set-block { margin-top: 28px; }
.tn-set-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line-soft);
}
.tn-set-row p { font-size: 13px; color: var(--ink-soft); max-width: 52ch; }
/* sync-key field — the .tn-filter input chrome, standalone */
.tn-key-input {
  flex-shrink: 0;
  width: 160px;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
  outline: none;
  font-size: 14px;
  color: var(--ink);
}
.tn-key-input::placeholder { color: var(--ink-faint); }
.tn-key-input:focus { border-color: var(--accent); }

/* ---- paths ------------------------------------------------------------------ */

.tn-path-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 12px;
}
.tn-path-card .tn-meterrow {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 320px;
  margin: 10px 0 4px;
}
.tn-path-card .tn-meterrow .tn-meter { flex: 1; }
.tn-path-steps { margin-top: 10px; }

/* ---- my notes ----------------------------------------------------------------- */

.tn-note-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 10px;
}
.tn-note-card blockquote {
  margin: 8px 0 0;
  padding: 6px 12px;
  border-left: 2px solid var(--accent);
  background: var(--paper-2);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: 13px;
  color: var(--ink-soft);
}
.tn-note-card .tn-note-text {
  margin-top: 8px;
  font-size: 13.5px;
  white-space: pre-wrap;
}

/* ---- synthesis note (bridge/debate/chain) reader ------------------------------- */

.tn-notehead .tn-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }

/* ==========================================================================
   Tenet — 30-reader-dash: the editorial reader + the analytics dashboard.
   ========================================================================== */

/* ---- reader ---------------------------------------------------------------- */

.tn-article {
  max-width: var(--measure);
  margin: 0 auto;
}

.tn-byline {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-family: var(--font-data);
  font-size: 12px;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}
.tn-byline a { color: var(--ink-soft); }
.tn-byline a:hover { color: var(--accent); }
.tn-byline .sep { opacity: 0.5; }

.tn-headline {
  margin: 12px 0 14px;
  font-size: clamp(26px, 4.6vw, 40px);
  font-weight: 750;
  letter-spacing: -0.025em;
  line-height: 1.08;
}

/* inline connection chips at the top of the article */
.tn-connchips {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.conn-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink);
  background: var(--paper-2);
}
.conn-chip:hover { text-decoration: none; border-color: currentColor; }
.conn-chip .conn-type {
  font-family: var(--font-data);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.conn-chip .conn-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 34ch;
  color: var(--ink-soft);
}
.conn-chip.tone-pos .conn-type { color: var(--pos); }
.conn-chip.tone-neg .conn-type { color: var(--neg); }
.conn-chip.tone-warn .conn-type { color: var(--warn); }
.conn-chip.tone-flat .conn-type { color: var(--accent); }

/* body: comfortable measure — face/size/leading are skin variables
   (standard = the sans console defaults; journal swaps in a humanist
   reading face at a larger size) */
.tn-body {
  font-family: var(--font-reader);
  font-size: var(--reader-size);
  line-height: var(--reader-leading);
  color: var(--ink);
}
.tn-body p { margin: 0 0 1.15em; }
.tn-body h2, .tn-body h3, .tn-body h4 {
  margin: 1.6em 0 0.5em;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.25;
}
.tn-body h2 { font-size: 21px; }
.tn-body h3 { font-size: 18px; }
.tn-body h4 {
  font-family: var(--font-data);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.tn-body h2.sec, .tn-body h3.sec {
  padding-top: 0.6em;
  border-top: 1px solid var(--line-soft);
}
.tn-body ul, .tn-body ol { margin: 0 0 1.15em; padding-left: 1.4em; }
.tn-body li { margin-bottom: 0.35em; }
.tn-body blockquote {
  margin: 0 0 1.15em;
  padding: 2px 0 2px 16px;
  border-left: 2px solid var(--accent);
  color: var(--ink-soft);
}
.tn-body code {
  font-family: var(--font-data);
  font-size: 0.88em;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  padding: 1px 5px;
}
.tn-body hr { border: none; border-top: 1px solid var(--line); margin: 1.6em 0; }
.tn-body a.wl { color: var(--accent); }
.tn-body .wl-dead { color: var(--ink-faint); border-bottom: 1px dotted var(--line); }
.tn-body strong { font-weight: 700; }

.tn-end {
  margin: 26px 0;
  text-align: center;
  font-family: var(--font-data);
  font-size: 11px;
  letter-spacing: 0.3em;
  color: var(--ink-faint);
}

/* compact toolbar */
.tn-toolbar {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  padding: 12px 0;
  border-top: var(--hairline) solid var(--line);
  border-bottom: var(--hairline) solid var(--line);
}

/* note + highlights */
.tn-mynote { margin-top: 22px; }
.tn-mynote textarea {
  width: 100%;
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
  color: var(--ink);
  font-size: 13.5px;
  line-height: 1.5;
  resize: vertical;
  min-height: 64px;
}
.tn-hl-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 8px;
}
.tn-hl-item blockquote {
  flex: 1;
  margin: 0;
  padding: 6px 12px;
  border-left: 2px solid var(--warn);
  background: var(--paper-2);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: 13px;
  color: var(--ink-soft);
}
.tn-hl-x {
  color: var(--ink-faint);
  font-size: 13px;
  padding: 4px 6px;
  border-radius: var(--radius-sm);
}
.tn-hl-x:hover { color: var(--neg); background: var(--paper-2); }

/* floating highlight affordance */
#tn-hl-float {
  position: absolute;
  z-index: 95;
  display: none;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-data);
  font-size: 11px;
  box-shadow: var(--shadow);
}
#tn-hl-float.show { display: inline-flex; }

/* backlinks fold */
.tn-backlinks { margin-top: 22px; }
.tn-backlinks summary {
  cursor: pointer;
  font-family: var(--font-data);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* pager */
.tn-pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 26px;
}
.tn-pager a {
  display: block;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  min-width: 0;
}
.tn-pager a:hover { background: var(--paper-2); text-decoration: none; border-color: var(--accent); }
.tn-pager a.next { text-align: right; }
.tn-pager .tn-pager-title {
  display: block;
  margin-top: 4px;
  font-size: 13.5px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* path context bar */
.tn-flowbar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  padding: 8px 12px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-soft);
  font-family: var(--font-data);
  font-size: 11px;
  color: var(--ink-soft);
}
.tn-flowbar .tn-meter { flex: 1; }

/* ---- dashboard --------------------------------------------------------------- */

.tn-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
.tn-kpi {
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px;
}
.tn-kpi .tile-big { font-size: 22px; }

.tn-panel {
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: var(--tile-pad);
  margin-bottom: 14px;
}
.tn-panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

/* heatmap — wide content scrolls inside its own container */
.hm-scroll { overflow-x: auto; padding-bottom: 6px; }
.hm-svg { display: block; }
.hm-cell { fill: var(--accent); rx: 2px; }
.hm-cell[data-lvl="0"] { fill: var(--paper-3); }
.hm-cell[data-lvl="1"] { fill-opacity: 0.3; }
.hm-cell[data-lvl="2"] { fill-opacity: 0.55; }
.hm-cell[data-lvl="3"] { fill-opacity: 0.8; }
.hm-cell[data-lvl="4"] { fill-opacity: 1; }
.hm-label {
  font-family: var(--font-data);
  font-size: 9.5px;
  fill: var(--ink-soft);
}

/* domain sparkline grid */
.tn-domgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 10px;
}
.tn-domcard {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 10px 12px;
  color: var(--ink);
  min-width: 0;
}
.tn-domcard:hover { background: var(--paper-2); text-decoration: none; }
.tn-domcard .dname {
  font-size: 12.5px;
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tn-domcard .dnum {
  font-family: var(--font-data);
  font-size: 11.5px;
  color: var(--ink-soft);
  margin-top: 2px;
}
.dspark { display: block; width: 100%; height: 26px; margin-top: 6px; }
.dspark polyline { fill: none; stroke: var(--accent); stroke-width: 1.4; }

/* sortable source table — scrolls inside its own container */
.tn-tablewrap { overflow-x: auto; }
.tn-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  min-width: 640px;
}
.tn-table th {
  position: sticky;
  top: 0;
  background: var(--paper);
  text-align: left;
  padding: var(--cell-pad);
  border-bottom: var(--hairline) solid var(--line);
  white-space: nowrap;
}
.tn-table th button {
  font-family: var(--font-data);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.tn-table th button:hover { color: var(--accent); }
.tn-table th button[data-dir]::after {
  content: attr(data-arrow);
  color: var(--accent);
}
.tn-table td {
  padding: var(--cell-pad);
  border-bottom: var(--hairline) solid var(--line-soft);
  white-space: nowrap;
}
.tn-table td.grow {
  white-space: normal;
  min-width: 220px;
}
.tn-table td .tn-meter { width: 70px; display: inline-block; vertical-align: middle; }
.tn-table tbody tr:hover { background: var(--paper-2); }

/* records strip */
.tn-records {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
}
.tn-record {
  padding: 10px 12px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}
.tn-record .rv {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: 17px;
  font-weight: 650;
}
.tn-record .rk { margin-top: 2px; }
