/* tenzin.com.au/thoughts — generated pages share this stylesheet.
   Palette and type follow site/ and hub/ so all three read as one family.
   Tag accents are the four moment accents from site/site.js plus the hub's
   terracotta, so nothing new was invented for this section.

   NOTE: this file is served with Cache-Control: no-cache via the filename rule
   in site/.htaccess (`^(site|hub|…|thoughts)\.(js|css)$`). If you rename it,
   rename it there too or browsers will pair fresh markup with a stale sheet. */

:root {
  --bg: #100e0c;
  --ink: #e8e0d6;
  --ink-bright: #f2ebe2;
  --ink-dim: #a99c8c;
  --ink-faint: #7a6e63;
  --line: #2a2320;
  --panel: #1a1410;
  --mono: 'IBM Plex Mono', ui-monospace, monospace;
  --sans: 'Space Grotesk', system-ui, sans-serif;

  /* GENERIC ACCENTS, not a tag palette. These were `--tag-mind` and friends —
     a second hardcoded copy of a vocabulary that no longer exists, and one of
     them had already gone stale (`--tag-ethics` for what the declaration
     called `craft`). Nothing here decides a region's colour any more: that
     comes from the declared ramp, applied by rank. */
  --accent-clay: #c98b6b;
  --accent-gold: #d4a94a;
  --accent-amber: #ef9563;
  --accent-sage: #93a67c;
  --accent-green: #4bbd68;
  --accent-rose: #cf7a6a;
  --accent-none: #7a6e63;

  --sel-bg: #d6d0c6;
  --sel-ink: #16130f;
  --s-deep: #0c0a09;
  --s-sheet: #1a1410f2;
  --s-veil: #14110ecc;
  --s-ctl: #17130f;
  --ink-body: #d8cbbc;
  --ink-muted: #8d8177;
  --ink-label: #6b6057;
  --ink-dimmest: #5d554d;
  --line-2: #3a332d;
  --line-ctl: #2f2823;
  --line-ctl-h: #4a4038;
  --wl-under: #d4a94a55;
  --ref-dot: #7c8aa8;

  /* The GRAPH's colours. It is drawn on a canvas, so these cannot be applied by
     the cascade — thoughts.js reads them off the root element with
     getComputedStyle and repaints when the theme changes. They live here rather
     than in the script because a canvas colour and a CSS colour that disagree
     is exactly the drift this file exists to prevent, and because the public
     page needs the same values with no script change. */
  --g-region: #e8e0d6;
  /* ── THE EDGES, AND THEY ARE MEASURED AGAINST `--s-deep` ────────────────
     These four were "barely there at continent distance" decoration, chosen by
     eye when a wikilink was the only edge on the map. They are INFORMATION now
     — every relationship draws, all the time — and measured against the canvas
     they sit on they were not there at all: composited at the fade's floor over
     `--s-deep`, an argument edge came out at 1.113 contrast, a citation 1.066
     and a derivation 1.042. Only `lit` cleared 1.4, which is exactly the bug as
     reported: lines appeared on hover and at no other time.

     The order is the meaning and it is preserved — an argument was earned by a
     sentence, a citation is a debt to somebody else's work, a derivation is
     where the words came from — but each rung now clears the ground:

       --g-edge       1.45   argument
       --g-edge-cite  1.33   citation
       --g-edge-raw   1.25   derivation
       --g-edge-lit   2.00   whatever you are pointing at

     `contrast-check.mjs` asserts the floor and the ordering, because "it is
     painted" and "it can be seen" are different claims and the first one is
     what the renderer's test could see. */
  --g-edge: #68625e;
  --g-edge-cite: #55525b;
  --g-edge-raw: #4a4542;
  --g-edge-lit: #9f9792;
  --g-stroke: #5d554d;
  --g-stroke-hover: #a99c8c;
  --g-label: #c4b8aa;
  --g-label-lit: #f2ebe2;
  /* A concept's star. */
  --g-concept: #e0b95a;
  --g-node-default: #7a6e63;
  --g-halo: rgba(20, 17, 14, .85);
  --g-plate: rgba(16, 14, 12, .78);
  --g-plate-edge: rgba(16, 14, 12, .26);
  /* The 2% lift an opened block gets. It has to INVERT with the theme — a white
     wash is simply not there on paper — which is the one thing a single literal
     could not do, and it was the last of them in roadmap.css. */
  --tint-lift: #ffffff05;
}


/* Light theme. THE PUBLIC SITE NOW SETS THIS ATTRIBUTE and is light outright —
   it has no Settings to put a toggle in, so it is a fixed choice rather than a
   preference, and the gated page goes on storing one per browser. This block
   was written for that toggle and sat unused here for months, which is why
   about.css and roadmap.css had dark literals nothing had ever asked to
   invert.
   Tag accents are deliberately unchanged: they are saturated hues that carry
   node identity on either ground, and re-picking them per theme would mean two
   answers to "what colour is `systems`". */
:root[data-theme="light"] {
  --bg: #f9f6f0;
  --ink: #221c17;
  --ink-bright: #17120f;
  --ink-dim: #4a4037;
  --ink-faint: #6a6055;
  --line: #ded5c6;
  --panel: #fffdf9;

  --sel-bg: #3a322a;
  --sel-ink: #faf7f2;
  --s-deep: #f1ebe0;
  --s-sheet: #fffdf9f7;
  --s-veil: #f9f6f0cc;
  --s-ctl: #fffdf9;
  --ink-body: #2d261f;
  --ink-muted: #5b5147;
  --ink-label: #776b60;
  --ink-dimmest: #8b7f74;
  --line-2: #ded5c6;
  --line-ctl: #d6ccbb;
  --line-ctl-h: #9c8f7d;
  --wl-under: #7a560680;
  --ref-dot: #5b6b88;

  --g-region: #4a4037;
  /* Same four contrasts against the light canvas — 1.45 / 1.33 / 1.25 / 2.00.
     The light values were as invisible as the dark ones (1.136 / 1.107 / 1.074
     / 1.425), which is what a palette chosen against one ground in a sitting
     does. */
  --g-edge: #928b7f;
  --g-edge-cite: #a49fab;
  --g-edge-raw: #b7b0a6;
  --g-edge-lit: #49423a;
  --g-stroke: #a89a86;
  --g-stroke-hover: #5b5147;
  --g-label: #4a4037;
  --g-label-lit: #17120f;
  --g-concept: #9a6f12;
  --g-node-default: #8b7f74;
  --g-halo: rgba(249, 246, 240, .9);
  --g-plate: rgba(249, 246, 240, .82);
  --g-plate-edge: rgba(249, 246, 240, .28);
  --tint-lift: #0000000a;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* iOS Safari inflates text inside any block wider than the viewport. The
     overflow above put every library row into exactly that state, so the
     summary rendered close to title size on a phone and looked like a font-size
     bug rather than a layout one. Fixing the overflow removes the trigger; this
     removes the mechanism. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
::selection { background: var(--sel-bg); color: var(--sel-ink); }

.eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ── masthead, shared ─────────────────────────────────────────────────── */

.top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 26px clamp(22px, 5vw, 64px);
  border-bottom: 1px solid var(--line);
}

.top__home {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  transition: color .3s ease;
}

.top__home:hover { color: var(--ink); }
.top__here { font-family: var(--mono); font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim); }

/* ── index: graph + library ───────────────────────────────────────────── */

.wrap {
  max-width: 1080px;
  margin: 0 auto;
  padding: clamp(34px, 7vh, 64px) clamp(22px, 5vw, 64px) 96px;
}

h1.page-title {
  margin: 14px 0 0;
  font-size: clamp(38px, 7vw, 74px);
  font-weight: 700;
  line-height: .92;
  letter-spacing: -.045em;
  text-transform: uppercase;
  color: var(--ink-bright);
}

.page-sub {
  margin: 18px 0 0;
  max-width: 52ch;
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink-dim);
}

/* The one link out of the graph, to the explainer. Underlined explicitly
   because this stylesheet sets `a { text-decoration: none }` globally for the
   library rows, so an unstyled anchor here is invisible as a link. */
.page-sub__how {
  white-space: nowrap;
  color: var(--ink-bright);
  border-bottom: 1px solid var(--ink-faint);
  padding-bottom: 1px;
  transition: border-color .25s ease;
}

.page-sub__how:hover { border-color: var(--ink-bright); }

.views {
  display: flex;
  gap: 8px;
  margin: clamp(30px, 5vh, 46px) 0 20px;
  padding-bottom: 0;
}

.views__btn {
  padding: 9px 18px;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}

.views__btn:hover { color: var(--ink); border-color: var(--ink-faint); }
.views__btn[aria-selected="true"] { background: var(--panel); border-color: var(--ink-faint); color: var(--ink-bright); }

/* graph */

.graph {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--s-deep);
  overflow: hidden;
}

.graph__canvas { display: block; width: 100%; height: clamp(380px, 58vh, 620px); touch-action: none; cursor: grab; }
.graph__canvas:active { cursor: grabbing; }

.graph__tip {
  position: absolute;
  pointer-events: none;
  z-index: 2;
  max-width: 280px;
  padding: 11px 14px;
  background: var(--s-sheet);
  border: 1px solid var(--line);
  border-radius: 10px;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .16s ease, transform .16s ease;
}

.graph__tip[data-on="1"] { opacity: 1; transform: none; }
.graph__tip-title { font-size: 14px; font-weight: 600; line-height: 1.25; color: var(--ink-bright); }
.graph__tip-sum { margin-top: 6px; font-size: 12px; line-height: 1.5; color: var(--ink-dim); }
.graph__tip-meta { margin-top: 8px; font-family: var(--mono); font-size: 9px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint); }

/* Zoom controls, built by thoughts.js into .graph. Bottom-left rather than
   bottom-right: the tooltip is positioned from the pointer and drifts to the
   right edge, and on a phone the right side is where a thumb rests. */
/* Wraps the canvas alone, so the controls anchor to the canvas rather than to
   `.graph`, which is taller — it carries the legend and the hint underneath,
   and the controls landed on top of them. */
.graph__stage { position: relative; }

.graph__zoom {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 3;
  display: flex;
  gap: 6px;
}

.graph__zoombtn {
  min-width: 40px;
  min-height: 40px;
  padding: 0 10px;
  background: var(--s-veil);
  border: 1px solid var(--line);
  border-radius: 9px;
  color: var(--ink-dim);
  font-family: var(--mono);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  transition: color .2s ease, border-color .2s ease;
}

.graph__zoombtn:last-child { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; }
.graph__zoombtn:hover { color: var(--ink-bright); border-color: var(--ink-faint); }
.graph__zoombtn:focus-visible { outline: 2px solid var(--ink-faint); outline-offset: 2px; }

.graph__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
  padding: 14px clamp(14px, 2vw, 20px);
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.legend__item { display: flex; align-items: center; gap: 7px; }
.legend__dot { width: 8px; height: 8px; border-radius: 999px; background: var(--accent-none); }
.legend__dot--hollow { background: none; border: 1px solid var(--ink-faint); }

.graph__hint {
  padding: 0 clamp(14px, 2vw, 20px) 14px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-dimmest);
}

/* ── sources (/thoughts/sources) ──────────────────────────────────────────
   In the shared sheet rather than inlined, unlike the explainer: this page is a
   list, its styles sit naturally beside .lib__row, and this file already has a
   no-cache rule by filename. The explainer inlines because it carries a lot of
   bespoke demo CSS and would otherwise need its own entry in that rule. */

.srcs { margin-top: clamp(26px, 5vh, 44px); }

.srcg { margin-bottom: clamp(28px, 5vh, 46px); }

.srcg__h {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 4px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.srcg__n { color: var(--ink-faint); font-size: 9px; }

.srcl { margin: 0; padding: 0; list-style: none; }

.src {
  padding: 17px 0;
  border-bottom: 1px solid var(--line);
}

.src:last-child { border-bottom: 0; }
/* Deep-linked from elsewhere: /thoughts/sources/#seeing-like-a-state */
.src:target { background: var(--panel); box-shadow: 0 0 0 14px var(--panel); border-radius: 2px; }

.src__t { display: block; font-size: 17px; font-weight: 600; line-height: 1.3; color: var(--ink-bright); }
.src__t--link { border-bottom: 1px solid var(--line-2); padding-bottom: 1px; transition: border-color .25s ease; }
.src__t--link:hover { border-color: var(--ink-bright); }

.src__by { display: block; margin-top: 5px; font-family: var(--mono); font-size: 11px; letter-spacing: .06em; color: var(--ink-faint); }
.src__note { margin: 10px 0 0; max-width: 62ch; font-size: 14px; line-height: 1.66; color: var(--ink-dim); }

.src__out { display: flex; flex-wrap: wrap; align-items: baseline; gap: 7px 10px; margin-top: 12px; }

.src__outk {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-dimmest);
}

.src__link {
  padding: 4px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 12.5px;
  color: var(--ink);
  transition: border-color .2s ease, color .2s ease;
}

.src__link:hover { border-color: var(--ink-faint); color: var(--ink-bright); }
.src__out--none { font-size: 12.5px; color: var(--ink-dimmest); }

.src__empty { max-width: 60ch; font-size: 15px; line-height: 1.7; color: var(--ink-dim); }
.src__empty code { font-family: var(--mono); font-size: .88em; color: var(--ink); }

/* library */

.search {
  display: block;
  width: 100%;
  max-width: 420px;
  margin-bottom: 22px;
  padding: 12px 15px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  font-family: inherit;
  font-size: 16px; /* 16px avoids the iOS zoom-on-focus */
}

.search:focus { outline: none; border-color: var(--ink-faint); }
/* THE CLEAR BUTTON IN A SEARCH FIELD is the browser's own, and WebKit draws it
   in its own blue — the one colour on the page nobody chose. Replaced with the
   same cross drawn as a mask, so it takes the page's ink in either theme. */
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  margin-left: 8px;
  cursor: pointer;
  background: var(--ink-faint);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3 3l8 8M11 3l-8 8' stroke='currentColor' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3 3l8 8M11 3l-8 8' stroke='currentColor' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
input[type="search"]::-webkit-search-cancel-button:hover { background: var(--ink); }

.lib { display: grid; gap: 12px; }

.lib__row {
  display: grid;
  /* ONE column, and the meta strip goes underneath. It used to be
     `minmax(0, 1fr) auto` — title on the left, metadata on the right — and at
     1280px that gave roughly a third of the row to the title and the rest to a
     stage pill, a progress hint, a visibility word, three tags, a date and two
     buttons. Titles wrapped to two lines beside a strip of labels nobody scans
     by. The title is the thing you scan by, so it gets the width; the phone
     layout was already doing this and it was the better one.

     minmax(0, 1fr), not 1fr: a grid item's min-width defaults to `auto`, which
     is its longest unbreakable word. Defensive rather than load-bearing — the
     two things that actually hold the row inside a phone are the wrapping meta
     strip and `overflow-wrap` below, and mutating either of those fails the
     tests while mutating this one does not. It stays because the default is
     still wrong for a column that must be allowed to shrink. */
  grid-template-columns: minmax(0, 1fr);
  align-items: baseline;
  gap: 12px 24px;
  padding: 20px clamp(18px, 2.4vw, 26px);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  transition: border-color .2s ease, transform .2s ease;
}

.lib__row:hover { transform: translateY(-2px); border-color: var(--ink-faint); }
/* THE ICON LEADS THE TITLE ON ONE LINE, so the eye meets the kind before the
   words and every row has its first line in the same place. Both libraries draw
   it — the gated page has three kinds to tell apart and the public one has two
   — and the rules live here, once, for the same reason that page renders with
   this sheet at all.
   `flex: 1 1 auto` on the title, not just `min-width: 0`: a flex item sizes to
   its CONTENT by default, so wrapping the title in this row shrank it from the
   full column to the width of its own words. The row was once
   `minmax(0,1fr) auto`, which gave a third of the width to the title and the
   rest to a strip of labels nobody scans by; an icon must not quietly
   reintroduce that. */
.lib__head { display: flex; align-items: baseline; gap: 9px; min-width: 0; }
.lib__head .lib__title { flex: 1 1 auto; min-width: 0; }
/* A CANVAS, not an SVG, and that is the point: it is painted by the graph's own
   `pagePath` / `bookPath` through `THOUGHTS_GLYPH`, so the page in this list and
   the page on the map cannot become different drawings. `flex: none` or it is
   squeezed to nothing beside a long title. */
.lib__icon { flex: none; width: 15px; height: 15px; align-self: center; opacity: .85; }
.lib__row:hover .lib__icon { opacity: 1; }
/* `overflow-wrap: anywhere` on both: a title or a summary can legitimately hold
   a url or a hyphenless compound longer than a phone is wide, and the only
   alternatives are overflowing the page or clipping the words. */
.lib__title { font-size: clamp(19px, 2.6vw, 25px); font-weight: 700; line-height: 1.12; letter-spacing: -.02em; color: var(--ink-bright); overflow-wrap: anywhere; }
.lib__sum { margin-top: 8px; font-size: 14px; line-height: 1.6; color: var(--ink-dim); max-width: 60ch; overflow-wrap: anywhere; }
/* Below the title and the summary, wrapping, on every width. It carries seven
   or eight things and none of them is what you are reading the row for. */
.lib__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-top: 10px; font-family: var(--mono); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); white-space: normal; }
.lib__meta > * { white-space: nowrap; }
.lib__seed { color: var(--ink-faint); }
.lib__empty { padding: 30px 0; font-family: var(--mono); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint); }

.tag {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* ── reading view ─────────────────────────────────────────────────────── */

.read { max-width: 660px; margin: 0 auto; padding: clamp(40px, 9vh, 92px) clamp(22px, 6vw, 40px) 110px; }

.read__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px 16px; }

h1.read__title {
  margin: 20px 0 0;
  font-size: clamp(31px, 5.4vw, 50px);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -.035em;
  color: var(--ink-bright);
  text-wrap: balance;
}

.read__sum { margin: 20px 0 0; font-size: 17px; line-height: 1.6; color: var(--ink-dim); font-style: italic; }

.read__body { margin-top: clamp(34px, 6vh, 52px); font-size: 17.5px; line-height: 1.78; color: var(--ink); }
/* `.read__body p { margin: 0 }` was here and silently won: a class+type selector
   outranks `.read__body > * + *` (class only), so it zeroed the paragraph gap and
   the essay ran together as one block. Zero the margin on the child selector at
   the same specificity instead, and let the later rule add the gap back. */
.read__body > * { margin: 0; }
.read__body > * + * { margin-top: 1.35em; }
.read__body h2 { margin: 2em 0 0; font-size: 15px; font-weight: 600; letter-spacing: .04em; color: var(--ink-bright); }
.read__body h3 { margin: 1.7em 0 0; font-size: 14px; font-weight: 600; color: var(--ink-dim); }
.read__body strong { color: var(--ink-bright); font-weight: 600; }
.read__body em { font-style: italic; }
.read__body ul, .read__body ol { margin: 0; padding-left: 1.3em; }
.read__body li + li { margin-top: .5em; }
.read__body blockquote { margin: 0; padding-left: 20px; border-left: 2px solid var(--line); color: var(--ink-dim); font-style: italic; }
.read__body code { font-family: var(--mono); font-size: .88em; background: var(--panel); padding: 2px 5px; border-radius: 4px; }
.read__body pre { margin: 0; padding: 16px 18px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; overflow-x: auto; }
.read__body pre code { background: none; padding: 0; }
.read__body a[href^="http"] { color: var(--ink-bright); border-bottom: 1px solid var(--ink-faint); }

/* A wikilink to a written thought is solid; to an unwritten one it is dotted
   and dimmed, so the prose tells you the difference before you click. */
.wl { color: var(--accent-gold); border-bottom: 1px solid var(--wl-under); transition: border-color .2s ease; }
.wl:hover { border-bottom-color: var(--accent-gold); }
.wl--unwritten { color: var(--ink-dim); border-bottom: 1px dotted var(--ink-faint); }

.seed-note {
  margin-top: clamp(30px, 5vh, 44px);
  padding: 18px 20px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 2px solid var(--ink-faint);
  border-radius: 0 10px 10px 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-dim);
}

.backlinks { margin-top: clamp(48px, 8vh, 76px); padding-top: 26px; border-top: 1px solid var(--line); }
.backlinks__list { margin: 16px 0 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.backlinks__list a { font-size: 15px; color: var(--ink); border-bottom: 1px solid var(--line); padding-bottom: 3px; }
.backlinks__list a:hover { border-color: var(--ink-faint); }

.foot { margin-top: clamp(40px, 6vh, 60px); display: flex; flex-wrap: wrap; gap: 12px 26px; font-family: var(--mono); font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint); }
.foot a:hover { color: var(--ink-dim); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001s !important; transition-duration: .001s !important; }
  .lib__row:hover { transform: none; }
}

/* ── references in the graph ──────────────────────────────────────────────
   A source is a square, not a dot: colour is already carrying two meanings
   (fill vs stroke is written vs unwritten, and the fill itself is the tag), so
   shape is the only axis left. Deliberately outside the six-tag palette. */
.legend__dot--ref {
  border-radius: 2px;
  background: var(--ref-dot);
}

/* The toggle sits with the hint rather than the legend: the legend describes
   what you are looking at, this changes it. */
/* ── THE COG, AND THE PANEL IT OPENS ──────────────────────────────────────
   It replaced `.graph__toggle` (a `Hide sources` button in the map's corner)
   and `.whos` (a row of writer buttons above the tabs) — two surfaces, two
   vocabularies, neither of them where somebody goes looking for a filter. One
   cog, top right of the map and again over the list, opening one panel: the
   same shape the gated page arrived at after six controls had each owned a
   piece of one question.

   TOP RIGHT, where the old toggle was bottom right. A control that configures
   what you are looking at belongs at the head of it, and the bottom right of a
   canvas is where the zoom buttons the renderer builds already live. */
.cog {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  color: var(--ink-muted);
  background: var(--s-ctl);
  border: 1px solid var(--line-ctl);
  border-radius: 999px;
  cursor: pointer;
}
.cog svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
.cog:hover { color: var(--ink-body); border-color: var(--line-ctl-h); }
.cog[aria-expanded="true"] { color: var(--ink-bright); border-color: var(--line-ctl-h); }
/* Over the LIST the cog has no canvas to sit on, so it is a flow element in its
   own strip. Absolute there would land it on the first row's border, which is
   how the dock's close control read as a rendering fault rather than a button. */
.libbar { display: flex; justify-content: flex-end; margin: 0 0 12px; }
.libbar .cog { position: static; }

.pf {
  margin: 0 0 18px;
  padding: 14px clamp(14px, 2vw, 18px);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
}
.pf__g { margin: 0 0 14px; }
.pf__g:last-child { margin-bottom: 0; }
.pf__lab {
  display: block;
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
/* A row per switch, wrapping, because Subjects is as many as the corpus has
   earned a colour for and Writers is however many people are here. */
.pf__g { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
.pf__lab { width: 100%; margin-bottom: 4px; }
.pf__r { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--ink-body); cursor: pointer; }
/* 44px of touch target on a coarse pointer without 44px of ink: the padding is
   the target and the label is what you see. */
@media (pointer: coarse) {
  .pf__r { min-height: 44px; }
}
.pf__r input { accent-color: var(--accent-sage); width: 15px; height: 15px; }
.pf__n { white-space: nowrap; }
.pf__c { font-family: var(--mono); font-size: 10px; color: var(--ink-faint); }
.pf__dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
/* The bucket everything unfiled falls into, and everything under the colour
   floor with it — hollow, because it is the ABSENCE of a subject rather than
   one that failed to earn a colour. */
.pf__dot--none { background: none; border: 1px solid var(--ink-faint); }

.search { width: 100%; }


/* What kind of thing the row is. An entry and a seed sit in one list and read
   alike — same title, same summary, same date — so without this the only thing
   telling a stranger that one of them has no words to open is the click. */
.lib__kind { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint); }

/* Whose library a source came out of. Drawn only when the page holds more than
   one writer's, for the same reason the writer filter is. */
.src__who { display: block; margin-top: 3px; font-family: var(--mono); font-size: 10px; letter-spacing: .06em; color: var(--ink-faint); }
/* A citing thought with no page of its own: the words, without the affordance.
   Only the owner's thoughts are built as files here. */
.src__link--flat { cursor: default; color: var(--ink-label); }

/* ── phones: touch and type ───────────────────────────────────────────────
   Keyed on the INPUT DEVICE, not the width. A phone in landscape is 667px
   wide — above any sensible width breakpoint — and that exact assumption is
   what broke the Mandelbrot page. `pointer: coarse` is the thing that is
   actually true about a finger.

   Two problems, both measured rather than guessed. Every tap target on these
   pages was under 40px and the back link was 13px tall; and the mono
   micro-type ran to 9px, which is a desktop mannerism — it reads as texture on
   a large screen and as nothing at all at arm's length. Desktop is untouched:
   this only ever raises a floor.

   It lives at the END of the file on purpose. These selectors are the same
   specificity as the base rules they override, so at equal weight the later one
   wins — sitting earlier in the file, the `.tag` floor here was silently losing
   to the `.tag` rule below it and the type stayed at 9px. */

@media (pointer: coarse) {
  /* Hit area comes from padding, never from font-size — growing the type to
     make a target tappable changes the design; growing the box does not. */
  .top { padding-top: 8px; padding-bottom: 8px; }

  .top__home,
  .top__here {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: 12px;
  }

  /* The negative margin keeps the enlarged box optically aligned with the
     text beside it rather than pushing the masthead apart. */
  .top__home { margin: -12px 0; }

  .views__btn { min-height: 44px; padding: 0 20px; font-size: 12px; }

  .page-sub__how {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin: -10px 0;
  }

  .eyebrow,
  .graph__hint,
  .graph__legend { font-size: 12px; }

  /* ── the masthead, tightened ──
     Moving the stage key, the capture box and the transcripts strip below the
     tabs left 313px still above them on a 390x844 phone, and 145px of that was
     padding rather than anything to read: `clamp(34px, 7vh, 64px)` of wrap
     padding on top of a header that already has its own, then 14px and 18px
     of margin between three lines of text. The words stay; the air between
     them is what a small screen cannot afford. */
  .wrap { padding-top: 20px; }
  h1.page-title { margin-top: 8px; }
  .page-sub { margin-top: 10px; }

  .lib__seed,
  .lib__stage,
  .lib__empty,
  .tag { font-size: 12px; }

  /* A library row is the primary target on the whole page. It also STACKS:
     side by side, the meta column (stage, progress, visibility, tags, date and
     two buttons) is 452px of nowrap content that cannot shrink, so on a 390px
     phone it took the row 753px off the side of the screen. */
  .lib__row {
    grid-template-columns: minmax(0, 1fr);
    padding-top: 15px;
    padding-bottom: 15px;
    gap: 10px;
  }
  /* The wrapping meta strip is now the default at every width, so only the
     tighter gap is left here. `white-space: nowrap` still holds INSIDE each
     pill, which is what it was for — keeping "6 posts, then accept" from
     breaking mid-phrase. */
  .lib__meta { gap: 8px 10px; }

  /* Sources: the title is the link, so it carries the hit area, and the
     "what came out of it" pills are targets in their own right. */
  .src { padding-top: 20px; padding-bottom: 20px; }
  .src__t--link { display: inline-flex; align-items: center; min-height: 44px; }
  .src__by, .src__outk, .src__out--none { font-size: 12px; }
  .src__link { min-height: 44px; display: inline-flex; align-items: center; padding: 0 14px; font-size: 13px; }
  .srcg__h { font-size: 12px; }
  /* The count beside a kind heading, and the date at the end of a library row.
     The date span carries no class of its own, so it has to be reached through
     its parent — found by measuring, not by reading. */
  .srcg__n { font-size: 12px; }
  .lib__meta > span { font-size: 12px; }

  .foot { gap: 4px 20px; font-size: 12px; }
  .foot a { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ── the people panel ───────────────────────────────────────────────────────
   Where a click on the public people graph lands. It is an overlay rather than
   a page because a person and a continent are not files — there is nothing to
   navigate to — and the same reason the gated reader is an overlay.
   Three ways out (the button, Escape, the backdrop), because an overlay with
   one is a trap on whichever device does not have it. */
.pg { position: fixed; inset: 0; z-index: 40; }
.pg__back { position: absolute; inset: 0; background: rgba(8, 7, 10, 0.72); }
.pg__sheet {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(46rem, 100%);
  background: var(--bg, #17151b);
  border-left: 1px solid #2a2430;
  overflow-y: auto;
  padding: 0 1.4rem 3rem;
}
/* Sticky rather than floated: a float reserves space only at its original
   position, so the control sails over the text as soon as you scroll. */
.pg__bar {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1rem 0 0.7rem;
  background: var(--bg, #17151b);
  border-bottom: 1px solid #2a2430;
  margin-bottom: 1.2rem;
}
.pg__title { margin: 0; font-size: 1.35rem; letter-spacing: 0.01em; }
.pg__close {
  flex: none; width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 1px solid #2a2430; border-radius: 8px;
  color: inherit; font-size: 1.3rem; line-height: 1; cursor: pointer;
}
.pg__item + .pg__item { margin-top: 2.2rem; border-top: 1px solid #241f2b; padding-top: 1.6rem; }
.pg__item h3 { margin: 0 0 0.2rem; font-size: 1.1rem; overflow-wrap: anywhere; }
.pg__by { margin: 0 0 0.9rem; font-size: 0.82rem; opacity: 0.6; }
.pg__none { opacity: 0.7; }

/* A row in the list a node opens onto. Clicking a person or a continent used to
   render every essay under it in full; this is the level that was missing
   between the graph and the reading. */
.pg__row {
  display: grid; width: 100%; gap: 0.25rem;
  text-align: left; background: none; cursor: pointer;
  border: 0; border-top: 1px solid #241f2b;
  padding: 1rem 0.4rem; color: inherit; font: inherit;
}
.pg__row:first-of-type { border-top: 0; }
.pg__row:hover { background: rgba(255, 255, 255, 0.03); }
.pg__row:focus-visible { outline: 2px solid var(--accent, #d4a94a); outline-offset: -2px; border-radius: 6px; }
.pg__row-t { font-size: 1.05rem; font-weight: 600; overflow-wrap: anywhere; }
/* .pg__by is a <p> in the reading view and a <span> in a row, so the row resets
   the margin rather than the shared rule carrying two sets of spacing. */
.pg__row .pg__by { margin: 0; }
.pg__row-s { font-size: 0.9rem; opacity: 0.75; line-height: 1.5; overflow-wrap: anywhere; }
/* Back to the list. Only present one level in, which is what makes it mean
   something — a control that is always there says nothing about where you are. */
.pg__up {
  flex: none; min-height: 40px; padding: 0 0.85rem;
  display: inline-flex; align-items: center;
  background: none; border: 1px solid #2a2430; border-radius: 8px;
  color: inherit; font: inherit; font-size: 0.85rem; cursor: pointer;
}
.pg__up:hover, .pg__close:hover { border-color: #4a4256; }
/* The title sits between two controls and must be the part that gives way. */
.pg__title { min-width: 0; overflow-wrap: anywhere; }
@media (pointer: coarse) { .pg__row { padding: 1.1rem 0.4rem; } }
@media (max-width: 620px) { .pg__sheet { width: 100%; border-left: none; } }
/* Whose thought a library row is. Only the public page sets it — the gated
   library is one person's by construction and would just repeat their name. */
.lib__by { opacity: 0.75; }
