/* memoria's own styles.
 *
 * Every colour is a token. Not one literal appears here, so the
 * reader's light/dark choice resolves through theme.css and a passage
 * never paints against the wrong ground. Sheets enforces the same rule
 * with a test; memoria's equivalent is TestNoColourLiteralsInAppCSS.
 */

/* A stable gutter, so the scrollbar arriving between a short page (sign
 * in) and a long one does not shift everything sideways. */
html { scrollbar-gutter: stable; }

[rst-page] { box-sizing: border-box; margin: 0 auto; max-width: 46rem; padding: 1.5rem 1rem 4rem; }

/* The header is estilo's bar (chrome.css) and is not styled here. The
 * footer is exactly the column's width — [rst-page]'s 46rem less its 1rem
 * gutters — so its rule and its text start on the same line as every
 * heading and rule above it (they sat 16px to the left, polish audit
 * §B6). */
[rst-page-footer] {
  border-top: 1px solid var(--rst-line);
  box-sizing: border-box;
  margin: 2rem auto 0;
  padding: 0.9rem 0 2rem;
  width: min(44rem, 100% - 2rem);
}
[rst-inline] { display: inline; }

/* Muted text. An attribute this app owns, because the design system's
 * muted utility is a class, `.rst-cell-mut`, and spelled as an attribute
 * — as every template here did, nineteen times — it styled nothing: the
 * footer, table cells, result meta and every hint rendered at full body
 * colour. The class is wrong for a sentence anyway (nowrap and an
 * ellipsis, written for a table cell), so this is colour only. */
[rst-muted] { color: var(--rst-text-muted); }

/* The search form is rastrillo's [rst-search] field box (tokens.css): the
 * border, radius and focus ring are the FORM's, and the input inside it
 * is transparent. This file used to draw a second bordered box on the
 * input, inside the first — the double border (polish audit §B3). Only
 * the spacing below it is Memoria's. */
[rst-search] { margin-bottom: 1.5rem; }

[rst-section] { border-top: 1px solid var(--rst-line); margin-top: 2.5rem; padding-top: 1rem; }

/* rastrillo's form bar is position:sticky; bottom:0, which is right for a
 * long settings page and wrong here — it floated the submit button over
 * the textarea it belongs under. Ours is an ordinary row. */
[rst-stack] [rst-form-bar] { position: static; margin-top: 1rem; }
[rst-stack] { display: flex; flex-direction: column; gap: 0.35rem; }
[rst-stack] label { font-weight: 560; margin-top: 0.6rem; }
/* A hint inside a label is quiet: muted and regular weight, not the
 * label's 560 (polish audit r3 B7). */
[rst-hint] { font-weight: 400; }
/* border-box: the fields are width 100% PLUS their padding otherwise,
 * which ran 22px past the column at 1440 and off the screen at 390
 * (polish audit §B6). Password fields are here too: the Notion token
 * fields were the browser's own. */
[rst-stack] input[type="text"],
[rst-stack] input[type="email"],
[rst-stack] input[type="password"],
[rst-stack] textarea {
  background: var(--rst-bg);
  border: 1px solid var(--rst-line);
  border-radius: var(--rst-radius-sm, 6px);
  box-sizing: border-box;
  color: var(--rst-text);
  font: inherit;
  max-inline-size: 100%;
  padding: 0.5rem 0.65rem;
  width: 100%;
}
[rst-stack] input:focus-visible,
[rst-stack] textarea:focus-visible {
  border-color: var(--rst-accent);
  outline: 2px solid var(--rst-accent);
  outline-offset: 1px;
}
/* iOS zooms the page into any text control under 16px, and tokens.css
 * brings controls up to 16px where the pointer is coarse. The fields
 * above set `font: inherit` with a selector that outweighs tokens.css's
 * bare `input` and `textarea`, and fell back to 14px: Add content's
 * title and body, and the Notion token. Same rule, same max(), restated
 * here at their weight. TestNoZoomOnTap (make browser) measures them. */
@media (pointer: coarse) {
  [rst-stack] input[type="text"],
  [rst-stack] input[type="email"],
  [rst-stack] input[type="password"],
  [rst-stack] textarea {
    font-size: max(1rem, 1em);
  }
}
/* Pasted prose is prose: the body font, not a code box. */
[rst-stack] textarea { resize: vertical; }
/* A button is as wide as its label, not the column: [rst-stack] is a
 * flex column, which stretched Connect and Replace edge to edge. */
[rst-stack] > button { align-self: flex-start; }

/* The file chooser. The native control stays — it is what works with no
 * script, and it says which files were chosen — but its button is drawn
 * as the app's buttons are, instead of the browser's grey "Choose Files". */
[rst-stack] input[type="file"] { color: var(--rst-text-muted); font: inherit; font-size: var(--rst-fs-sm); max-inline-size: 100%; }
[rst-stack] input[type="file"]::file-selector-button {
  background: var(--rst-surface);
  border: 1px solid var(--rst-line-strong);
  border-radius: var(--rst-radius-sm, 6px);
  color: var(--rst-text);
  cursor: pointer;
  font: inherit;
  font-weight: 560;
  margin-inline-end: 0.75rem;
  padding: 0.4rem 0.8rem;
}
[rst-stack] input[type="file"]::file-selector-button:hover { background: var(--rst-surface-2); }

/* The drop zone. Nothing here paints until static/drop.js has proved it
 * can put a file in the input and written rst-dropzone="ready" — so a
 * page whose script did not load shows an ordinary file input inside an
 * invisible wrapper, rather than a dashed box that swallows drops. The
 * bare [rst-dropzone] selector is deliberately styled with nothing.
 */
[rst-dropzone~="ready"] {
  border: 1px dashed var(--rst-line-strong, var(--rst-line));
  border-radius: var(--rst-radius-sm, 6px);
  padding: 0.7rem 0.75rem;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
/* A file is over the page. The whole document is the drop target — the
 * zone is where it lands, not where it must be aimed — so this has to be
 * visible from wherever the pointer happens to be.
 */
[rst-dropzone~="over"] {
  background: var(--rst-accent-soft, var(--rst-surface-2));
  border-color: var(--rst-accent, var(--rst-line-strong));
  border-style: solid;
}
[rst-dropzone-hint], [rst-dropzone-note] { font-size: 0.8125rem; margin: 0.5rem 0 0; }
/* Muted, and not with .rst-cell-mut: that utility exists only as a class,
 * so `rst-cell-mut` written as an attribute selects nothing — and the
 * class itself is a table-cell rule (nowrap, ellipsis) that would clip
 * this sentence at the edge of the zone.
 */
[rst-dropzone-hint] { color: var(--rst-text-muted); }
/* drop.js unhides the note while it is still empty, so the live region is
 * in the accessibility tree before it ever has to announce a refusal. An
 * empty paragraph with a top margin would push the Store it button down
 * by half a line for no reason anyone could see, so while it is empty it
 * takes no space at all.
 */
[rst-dropzone-note]:empty { margin: 0; }
[rst-dropzone-note~="good"] { color: var(--rst-tone-positive-fg); }
[rst-dropzone-note~="danger"] { color: var(--rst-tone-negative-fg); }
@media (prefers-reduced-motion: reduce) {
  [rst-dropzone~="ready"] { transition: none; }
}

/* The upload tray.
 *
 * It is in the DOM from the moment drop.js runs, because
 * [rst-tray-status] is a live region and a live region has to be in the
 * accessibility tree before it has anything to announce. So "not open"
 * cannot be display:none — that would take the region back out again.
 * Instead the head and the list are hidden, the panel paints nothing,
 * and an empty status paragraph is zero-height, which leaves a fixed
 * element of no size in the corner. pointer-events go with it: an
 * invisible box that swallows clicks over the corner of the page would
 * be worse than a visible one.
 */
[rst-tray] {
  bottom: 1rem;
  font-size: 0.8125rem;
  inset-inline-end: 1rem;
  position: fixed;
  width: min(22rem, calc(100vw - 2rem));
  z-index: 30;
}
[rst-tray]:not([rst-tray~="open"]) { pointer-events: none; }
[rst-tray]:not([rst-tray~="open"]) [rst-tray-head],
[rst-tray]:not([rst-tray~="open"]) [rst-tray-list] { display: none; }
[rst-tray~="open"] {
  background: var(--rst-surface);
  border: 1px solid var(--rst-line);
  border-radius: var(--rst-radius, 8px);
  box-shadow: var(--rst-shadow-pop, none);
  padding: 0.6rem 0.7rem;
}
[rst-tray-head] {
  align-items: center;
  display: flex;
  gap: 0.5rem;
  justify-content: space-between;
}
[rst-tray-title] { font-weight: 620; }
/* 40vh so a drop of twenty-five files scrolls inside the tray rather
 * than growing a panel taller than the window. */
[rst-tray-list] {
  list-style: none;
  margin: 0.5rem 0 0;
  max-height: 40vh;
  overflow-y: auto;
  padding: 0;
}
[rst-tray-row] {
  border-top: 1px solid var(--rst-line);
  display: grid;
  gap: 0.15rem 0.5rem;
  grid-template-columns: 1fr auto;
  padding: 0.4rem 0;
}
[rst-tray-row]:first-child { border-top: 0; }
/* A filename has no spaces to break at, and the tray is narrow. */
[rst-tray-name] { font-weight: 560; overflow-wrap: anywhere; }
[rst-tray-phase] { color: var(--rst-text-muted); text-align: end; }
[rst-tray-row~="good"] [rst-tray-phase] { color: var(--rst-tone-positive-fg); }
/* A refusal is a sentence, not a word: it gets the whole row width and
 * reads left to right under the name it belongs to.
 */
[rst-tray-row~="danger"] [rst-tray-phase] {
  color: var(--rst-tone-negative-fg);
  grid-column: 1 / -1;
  text-align: start;
}
/* accent-color rather than ::-webkit-progress-value and friends, and
 * that is not a detail: styling those pseudo-elements replaces the
 * browser's indeterminate rendering with a static bar, and the
 * indeterminate state is the whole point of phase two — a bar that
 * stops moving while the server works reads as broken.
 */
[rst-tray-bar] {
  accent-color: var(--rst-accent, var(--rst-text));
  block-size: 0.35rem;
  grid-column: 1 / -1;
  inline-size: 100%;
}
[rst-tray-status] { margin: 0.5rem 0 0; }
/* Unhidden and empty from the moment the script runs, so it must cost no
 * space until it says something — and the obvious "fix" for a stray
 * half-line in the corner is to hide the region again, which is the bug
 * this whole arrangement exists to avoid.
 */
[rst-tray-status]:empty { margin: 0; }
[rst-tray-listed] { color: var(--rst-text-muted); margin: 0.4rem 0 0; }

[rst-results] { list-style: none; margin: 0; padding: 0; }
[rst-result] { border-top: 1px solid var(--rst-line); padding: 0.9rem 0; }
[rst-result]:first-child { border-top: 0; }
/* The document's ancestry in its source, above the title. Sits above
   rather than below because it is read as a location — "Handbook ›
   Onboarding ›" then the page — and colours come from tokens so light
   and dark both resolve through theme.css. */
[rst-result-path] {
  /* --rst-text-muted, not --rst-text-mut. This rule was written by
   * copying the trail rule above while it still carried that typo: an
   * undefined token with no fallback makes the whole declaration invalid
   * at computed-value time, so the path would have rendered as ordinary
   * body text — muted in the markup, not muted on the screen. The merge
   * brought the fix and the test that catches the class of it.
   */
  color: var(--rst-text-muted);
  display: block;
  font-size: 0.8125rem;
  margin-bottom: 0.1rem;
}
/* Row titles — a result's, and a document's in the index table — are
 * the row's subject, in the text colour like every list title in the
 * suite, and underlined only on hover. The base link rule (estilo
 * base.css) is zero-specificity, so these win without a fight; prose
 * links (the fact box's citation, "Back to search") keep the accent and
 * the underline, because colour alone must not carry "this is a link". */
[rst-result-title],
[rst-table] td > a:not([rst-btn]) {
  color: var(--rst-text);
  text-decoration: none;
}
[rst-result-title]:hover,
[rst-table] td > a:not([rst-btn]):hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
[rst-result-title] { font-weight: 600; }
[rst-table] td > a:not([rst-btn]) { font-weight: 550; }
[rst-result-trail] {
  /* The token is --rst-text-muted. This said --rst-text-mut, which
   * theme.css does not define and which had no fallback, so the whole
   * declaration was invalid at computed-value time — not "grey went
   * wrong", but the colour rule silently absent, leaving the trail in
   * ordinary body text: muted in the markup, not muted on the screen.
   * TestEveryTokenReferencedHereIsDefinedOrHasAFallback now catches the
   * whole class of that mistake.
   */
  color: var(--rst-text-muted);
  display: block;
  font-size: 0.8125rem;
  margin-top: 0.1rem;
}
[rst-result-snippet] { margin: 0.4rem 0 0.2rem; }
[rst-result-snippet] mark {
  background: var(--rst-accent-soft, var(--rst-line));
  color: inherit;
  padding: 0 0.1em;
}
[rst-result-meta] { font-size: 0.8125rem; margin: 0; }

/* The results on screen no longer describe what is in the box, because
   the last live search did not run. Shown, not merely announced: the
   status region is sr-only, so without this a sighted reader sees older
   results and nothing saying they are older — the confident-zero failure
   AGENTS.md names, in its politest form.

   The sentence comes from the region's own rst-search-stale-note, which
   the server wrote from the same {{define "searchFailed"}} as the
   server-rendered callout. CSS renders the attribute so the words stay
   in the template and no script ever composes a sentence. */
[rst-search-stale]::before {
  color: var(--rst-tone-negative-fg, var(--rst-text));
  content: attr(rst-search-stale-note);
  display: block;
  font-size: 0.9375rem;
  margin-bottom: 0.9rem;
}

/* The primed result: the one Enter goes to. Deliberately the same mark
   as [rst-passage]:target on the document page, because it means the
   same thing in both places — this is the one you are being sent to.
   Set by the server, never by the script; see internal/memoria/query. */
[rst-result-primed] {
  border-inline-start: 3px solid var(--rst-accent, var(--rst-text));
  padding-inline-start: 0.7rem;
}
[rst-result-primed]:first-child { border-top: 0; }
/* Arrowing into the list moves real focus, so the browser's own ring is
   the selection indicator. It is stated here only to give it room: the
   default ring sits tight against 600-weight text and is easy to miss
   when the eye is on the input. */
[rst-result-title]:focus-visible {
  outline: 2px solid var(--rst-accent, var(--rst-text));
  outline-offset: 3px;
}

[rst-prose] { line-height: 1.6; }
[rst-passage] { border-top: 1px solid var(--rst-line); padding: 0.8rem 0; }
[rst-passage]:target { border-inline-start: 3px solid var(--rst-accent, var(--rst-text)); padding-inline-start: 0.7rem; }
[rst-passage-heading] { font-size: 0.9375rem; margin: 0 0 0.4rem; text-transform: none; }
[rst-passage] p { margin: 0; white-space: pre-wrap; }

[rst-breadcrumb] { margin-bottom: 0.75rem; }

[rst-table] { border-collapse: collapse; width: 100%; }
[rst-table] th, [rst-table] td {
  border-bottom: 1px solid var(--rst-line);
  padding: 0.45rem 0.5rem 0.45rem 0;
  text-align: start;
}
[rst-table] th { font-size: 0.8125rem; font-weight: 560; }

/* 320px is the reflow floor the suite holds itself to. The search form
 * stays one row: stacked, rastrillo's align-items: center shrank the input
 * to a clipped 180px in the middle of the column. */
@media (max-width: 26rem) {
  [rst-table] { font-size: 0.875rem; }
  /* The documents table keeps Title, Updated and Delete on a phone:
   * with all five columns it ran 23px past a 320px screen. Source and
   * Passages are on the document's own page. */
  [rst-table] th:nth-child(2), [rst-table] td:nth-child(2),
  [rst-table] th:nth-child(3), [rst-table] td:nth-child(3) { display: none; }
}

/* A zero-result line while the person is still typing (search.js marks
 * the region pending until they pause): whole-token matching finds
 * nothing for "rev" on the way to "revenue", and saying so on every
 * keystroke is noise. Hidden, not removed, so the region's markup is the
 * server's and a pause only has to lift the attribute. */
[rst-search-pending] [rst-search-empty] { visibility: hidden; }
[rst-search-empty] { margin: 0 0 1rem; }

/* The Sources page's lists: rows with a rule between them. They were
 * rastrillo's rounded rst-list card holding bare <li>s, so browser bullets
 * sat inside the card with the text touching its edge. */
[rst-sources] { border-top: 1px solid var(--rst-line); list-style: none; margin: 0.75rem 0 0; padding: 0; }
[rst-sources] > li { border-bottom: 1px solid var(--rst-line); display: grid; gap: 0.35rem; padding: 0.75rem 0; }
[rst-sources] > li > p { margin: 0; }
[rst-source-name] { font-weight: 600; }

/* The Notion import's figures: a two-column list, not the browser's
 * indented one-per-line definition list. */
[rst-facts] { display: grid; gap: 0.35rem 1rem; grid-template-columns: minmax(0, max-content) minmax(0, 1fr); margin: 0.75rem 0 0; }
[rst-facts] dt { color: var(--rst-text-muted); }
[rst-facts] dd { margin: 0; }
@media (max-width: 26rem) {
  [rst-facts] { grid-template-columns: minmax(0, 1fr); }
  [rst-facts] dd { margin-bottom: 0.4rem; }
}

/* Delete, asked first (delete.html). */
[rst-confirm] form { display: flex; flex-wrap: wrap; gap: var(--rst-sp-2); margin-top: 1.25rem; }

/* §8.8's fact box: a validated fact lifted out of the passages below,
   with its citation. Sits between the withholding notice and the result
   list, and is styled to read as an answer rather than as a result —
   the value is the thing the eye should land on. */
[rst-fact-box] {
  border: 1px solid var(--rst-line);
  border-radius: var(--rst-radius);
  padding: 0.7rem 0.9rem;
  margin: 0 0 1rem;
  background: var(--rst-surface);
}
[rst-fact] {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.6rem;
  margin: 0;
}
[rst-fact] + [rst-fact],
[rst-fact-note] + [rst-fact] {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--rst-line);
}
[rst-fact-label] {
  color: var(--rst-text-muted);
  font-size: 0.8125rem;
}
[rst-fact-value] {
  font-weight: 600;
  font-size: 1.0625rem;
}
[rst-fact-cite] {
  font-size: 0.8125rem;
  margin-left: auto;
}
/* §8.8's narrowing line: which of the reader's words chose the rows
   above, and how many candidates that left out. Quiet, under the rows. */
[rst-fact-note] {
  color: var(--rst-text-muted);
  font-size: 0.8125rem;
  margin: 0.5rem 0 0;
}

/* §8.9: a row a model pointed at, rather than one a checksum validated.
   Distinct at a glance, and quieter — the value is still the document's
   bytes, but the claim that it is an address is the model's. */
[rst-fact-model] [rst-fact-value] { font-weight: 500; }
[rst-fact-by] {
  color: var(--rst-text-muted);
  font-size: 0.75rem;
}
