/* ==========================================================================
   Rabbi Bot — design system
   "Techelet on parchment": deep indigo + gold thread on a warm paper ground.

   Theming is token-level. Every foreground/background pair is defined in ALL
   three theme paths — base (light), prefers-color-scheme: dark, and the two
   explicit [data-theme] toggles — so no color depends on a single override
   path. Style components through the tokens, never with raw hex.
   ========================================================================== */

/* Heebo — self-hosted (SIL OFL 1.1, see fonts/OFL.txt), variable weight 100–900.
   latin + hebrew subsets, served same-origin so nothing is fetched third-party
   and the CSP font-src stays 'self'. This is what makes --font-sans resolve to
   Heebo instead of the system fallback. */
@font-face {
  font-family: "Heebo";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/heebo-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Heebo";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/heebo-hebrew.woff2") format("woff2");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

:root,
:root[data-theme="light"] {
  --paper:      #E9E1CE;   /* warm parchment page ground */
  --card:       #FBF8EF;   /* warm ivory surface */
  --chrome:     #1F2A52;   /* deep indigo masthead / chrome */
  --chrome-ink: #FFFFFF;   /* text on chrome */
  --heading:    #1F2A52;   /* titles + section headings */
  --eyebrow:    #1F2A52;   /* section labels e.g. "ANSWER" */
  --ink:        #2A2318;   /* body text (warm near-black) */
  --ink-soft:   #5C5240;
  /* Was #8B7F68, which measured 3.71:1 on --card and 3.02:1 on --paper — under
     the 4.5:1 floor on every surface it's used on, across ~20 rules (A2-S1).
     Same hue, enough darker to pass: 5.91 on card, 4.82 on paper, 5.37 on the
     gold-tint disclaimer. */
  --ink-faint:  #6A5F49;
  --gold:       #C39B2D;   /* the gold thread: numerals, accents */
  --link:       #3454B4;   /* links + secondary interactive */
  --btn-bg:     #1F2A52;   /* primary button */
  --btn-ink:    #FFFFFF;
  --rule:       #E1D8C2;
  --rule-soft:  #ECE4D2;
  --disc:       #F5EDD6;   /* disclaimer / gold-tint notice */
  --good:       #3E7A55;
  --warn:       #B5652C;
  --shadow:     0 1px 2px rgba(31,42,82,.06), 0 12px 34px rgba(31,42,82,.09);

  --font-sans: "Heebo", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-serif: "Iowan Old Style", Palatino, "Book Antiqua", Georgia, serif;

  /* 68ch measured as ~87 ACTUAL characters per line on /history (A2-S3) — `ch`
     is the width of "0", which is wider than this font's average glyph, so the
     setting reads narrower than it renders. 58ch lands ~74, just inside the
     45–75 range. Shared with the answer prose, which is the point: the reading
     column is the product. */
  --measure: 58ch;
  --radius: 12px;
  --radius-lg: 16px;

  /* Tells the UA which palette its own chrome (scrollbars, carets, form
     controls) should use. Without it, dark mode gets light UA widgets — the
     root cause of the pale unselected mode pill. */
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:#14121C; --card:#1E1B27; --chrome:#141726; --chrome-ink:#FFFFFF;
    --heading:#EAE2D0; --eyebrow:#D8B24E; --ink:#ECE4D2; --ink-soft:#B7A98F; --ink-faint:#9C8E74;  /* was #897C64: 4.13:1 on --card, under AA (A2-S1) */
    --gold:#D8B24E; --link:#8CA3EC; --btn-bg:#3D53A8; --btn-ink:#FFFFFF;
    --rule:#302B3C; --rule-soft:#262232; --disc:#241F14;
    --good:#5FA97C; --warn:#D3894F;
    --shadow: 0 1px 2px rgba(0,0,0,.35), 0 14px 44px rgba(0,0,0,.5);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --paper:#14121C; --card:#1E1B27; --chrome:#141726; --chrome-ink:#FFFFFF;
  --heading:#EAE2D0; --eyebrow:#D8B24E; --ink:#ECE4D2; --ink-soft:#B7A98F; --ink-faint:#9C8E74;  /* was #897C64: 4.13:1 on --card, under AA (A2-S1) */
  --gold:#D8B24E; --link:#8CA3EC; --btn-bg:#3D53A8; --btn-ink:#FFFFFF;
  --rule:#302B3C; --rule-soft:#262232; --disc:#241F14;
  --good:#5FA97C; --warn:#D3894F;
  --shadow: 0 1px 2px rgba(0,0,0,.35), 0 14px 44px rgba(0,0,0,.5);
  color-scheme: dark;
}

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

/* The `hidden` attribute must actually hide things.
   The UA stylesheet's `[hidden] { display: none }` loses to ANY author rule
   that sets `display` — so `.btn { display: inline-flex }` silently defeated
   `button.hidden = true`, and the iOS "Add to home screen" button rendered as a
   live-looking CTA that could do nothing (Safari has no install API to call).
   This had already been patched twice for single components (.notif-optin,
   .notif-ios) before the same trap caught a third. Fixed once, globally, so JS
   that sets .hidden can be trusted anywhere in this stylesheet. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16.5px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--link); }
h1, h2, h3 { color: var(--heading); text-wrap: balance; }

:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }

.term { font-style: italic; color: var(--ink-soft); }

.eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 6px;
}

/* ------------------------------------------------------------ masthead ---- */
.masthead {
  display: flex; align-items: center; gap: 13px; flex-wrap: wrap; row-gap: 10px;
  padding: 15px 26px;
  background: var(--chrome); color: var(--chrome-ink);
}
.masthead .drop { flex: none; color: var(--gold); display: grid; place-items: center; }
.masthead .brand {
  font-size: 21px; font-weight: 700; letter-spacing: .2px; white-space: nowrap;
  color: var(--chrome-ink); text-decoration: none; margin-right: auto;
}
.masthead nav { display: flex; align-items: center; gap: 20px; font-size: 14.5px; }
/* Hit area, not text size (A2-M3). These measured 22px tall — below even the
   24px WCAG 2.5.8 minimum, on the primary navigation of a phone-first product.
   The height comes from padding so nothing about the type changes. */
.masthead nav a {
  color: rgba(255,255,255,.72); text-decoration: none; white-space: nowrap;
  display: inline-flex; align-items: center; min-height: 44px;
}
.masthead nav a:hover { color: #fff; }
.masthead nav .signin {
  background: var(--gold); color: var(--chrome);
  padding: 0 16px; border-radius: 8px; font-weight: 700;
}

/* -------------------------------------------------------------- layout ---- */
/* Capped and centred like every other page container here (.wrap 760, .article
   780, .pricing 860). Uncapped, the rail is fixed at 400px so every pixel of
   viewport past ~900 went to the stage alone: 28/72 at 1440, 17/83 at 2560. The
   ratio drifted, and only one way.
   minmax(0, 1fr) because a bare 1fr floors at auto — one long unbroken ref or
   URL in an answer could push the grid wider than the viewport.
   74, not 62: 62 matched the masthead before A2-M3 (~63.6px). The 44px nav hit
   area at lines 138-141 made it 74, so the old constant left a ~12px scrollbar
   on a page with nothing on it. */
/* Rebalanced: rail 400 -> 460, cap 1180 -> 1120, so the stage goes 780 -> 660.
   The ratio was 1.95:1 stage-to-rail with the divider sitting 190px left of the
   window centre, which is what read as right-heavy however much content the
   stage held. Now 1.43:1.

   Narrowing the stage costs no readability: its prose is capped at --measure
   (58ch) regardless, so the width above that was air. The wider rail is a
   bonus — the composer gets 60px more, which is the box J found cramped. */
.split { display: grid; grid-template-columns: 500px minmax(0, 1fr); max-width: 1120px; margin: 0 auto; min-height: calc(100vh - 74px); }
/* 26px top, against the stage's 30px. Equal padding puts the two boxes level,
   but the h1's line-height sits its letterforms lower inside its box than the
   stage's small-caps eyebrow does, so level boxes read as a headline starting
   below the right column. Four pixels buys back the optical difference. */
.rail  { padding: 26px 30px 32px; border-right: 1px solid var(--rule); }
/* The headline starts level with the right column's first line.
   h1 keeps the UA's 0.67em top margin, which at this size is ~21px — enough to
   push "Ask anything about" visibly BELOW "Recently answered" and make the two
   columns look like they start at different heights. */
.rail > h1 { margin-top: 0; }
.stage { padding: 30px 40px 46px; }

.wrap { max-width: 760px; margin: 0 auto; padding: 40px 22px; }
/* A two-field sign-in form doesn't want 760px and an 810px-wide email input
   (A2-S2). Narrow is for the auth cards specifically; account pages carry more
   (the thread meter, billing) and keep the full width. */
.wrap.narrow { max-width: 520px; }

/* --- Question history ------------------------------------------------- */
.history { max-width: 760px; margin: 0 auto; padding: 40px 22px 56px; }
.history > .eyebrow { color: var(--eyebrow); margin-bottom: 6px; }
.history h1 { font-size: 24px; margin: 0 0 22px; color: var(--heading); }
.history .entry { background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 22px 24px; }
.history .entry + .entry { margin-top: 16px; }
.history .entry time { display: block; font-size: 12.5px; color: var(--ink-faint); margin-bottom: 8px; }
.history .entry .q { font-weight: 700; color: var(--heading); margin: 0 0 10px; }
.history .entry .a { white-space: pre-wrap; color: var(--ink-soft); max-width: var(--measure); margin: 0; }
.history .empty, .history .upgrade { background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 32px 28px; text-align: center; }
.history .upgrade h2 { margin: 0 0 8px; color: var(--heading); font-size: 18px; }
.history .upgrade p, .history .empty p { color: var(--ink-soft); margin: 0 auto 16px; max-width: 48ch; }
.history .pager { display: flex; align-items: center; justify-content: space-between; margin-top: 22px; font-size: 14px; }
.history .pager .muted { color: var(--ink-faint); }

/* --- Pricing ---------------------------------------------------------- */
.pricing { max-width: 860px; margin: 0 auto; padding: 40px 22px 56px; }
.pricing > .eyebrow { color: var(--eyebrow); margin-bottom: 6px; }
.pricing h1 { font-size: 26px; margin: 0 0 8px; color: var(--heading); }
.pricing .lede { color: var(--ink-soft); margin: 0 0 28px; max-width: 60ch; }
/* stretch, not start: the cards carry a CTA at the bottom, and with different
   heights the two buttons sat at different heights on the page. Equal columns
   put them on the same line. */
.plans { display: grid; grid-template-columns: repeat(2, 1fr); gap: 18px; align-items: stretch; }
.plan {
  position: relative; display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--rule);
  border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 26px 26px 28px;
}
/* Pushes each card's CTA to the bottom, so they line up across cards that hold
   different numbers of bullets. */
.plan > .btn, .plan > form, .plan > .hint:last-child { margin-top: auto; }
.plan.featured { border-color: color-mix(in srgb, var(--gold) 55%, var(--rule)); }
.plan .badge { position: absolute; top: -11px; right: 20px; margin: 0; background: var(--gold); color: var(--chrome); font-size: 12px; font-weight: 700; padding: 3px 10px; border-radius: 999px; }
.plan h2 { margin: 0 0 6px; color: var(--heading); font-size: 19px; }
.plan .price { margin: 0 0 4px; }
.plan .price .amount { font-size: 30px; font-weight: 800; color: var(--heading); }
.plan .price .per { color: var(--ink-faint); font-size: 14px; }
/* 15.5px, up from 13px, and --ink-soft instead of --ink-faint. This is the
   annual price — a second thing the reader might buy — and it was the smallest
   text on a page whose whole job is to be legible about money. */
.plan .price-alt { margin: 0 0 16px; font-size: 15.5px; color: var(--ink-soft); }
.plan ul { list-style: none; padding: 0; margin: 0 0 20px; display: flex; flex-direction: column; gap: 9px; }
.plan ul li { color: var(--ink-soft); padding-left: 22px; position: relative; }
.plan ul li::before { content: "\2713"; position: absolute; left: 0; color: var(--good); font-weight: 700; }
.plan ul li.muted { color: var(--ink-faint); }
.plan ul li.muted::before { content: "\2014"; color: var(--ink-faint); }
.plan .btn { display: block; width: 100%; text-align: center; }
.checkout-error { color: var(--warn); font-size: 13px; margin: 10px 0 0; min-height: 1em; }
/* The fair-use math, published rather than hidden behind "unlimited". */
.fair-use { margin: 34px 0 0; max-width: 64ch; }
/* This section IS the pricing explanation — what a thread is, what rolls over,
   what a top-up costs. It was set two sizes below body text, which made the one
   passage that answers "what am I buying" the hardest thing on the page to
   read. Body size, and the heading scaled with it. */
.fair-use h2 { font-size: 20px; color: var(--heading); margin: 0 0 12px; }
.fair-use p { color: var(--ink-soft); font-size: 16.5px; line-height: 1.7; margin: 0 0 14px; }
@media (max-width: 640px) { .plans { grid-template-columns: 1fr; } }

/* --- Notification opt-in (warm prompt) -------------------------------- */
.notif-optin { margin-top: 22px; padding: 16px 18px; background: var(--disc); border: 1px solid var(--rule); border-radius: var(--radius); }
.notif-title { margin: 0 0 4px; font-weight: 700; color: var(--heading); font-size: 15px; }
.notif-sub { margin: 0 0 12px; color: var(--ink-soft); font-size: 13.5px; }
.notif-optin .field { display: block; margin-bottom: 10px; }
.notif-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.notif-actions .btn { padding: 9px 16px; font-size: 14px; }
.notif-status { margin: 10px 0 0; font-size: 13px; color: var(--ink-soft); min-height: 1em; }
.notif-ios { margin-top: 10px; font-size: 13px; color: var(--ink-soft); }

/* -------------------------------------------------------------- buttons --- */
.btn {
  appearance: none; border: 0; cursor: pointer; font: inherit;
  font-weight: 600; font-size: 14.5px;
  padding: 11px 20px; border-radius: 9px;
  background: var(--btn-bg); color: var(--btn-ink);
  transition: filter .18s, transform .08s;
  /* An <a class="btn"> was inheriting the global link underline, so it rendered
     as a bordered box wrapped around an underlined link — two affordances for
     one action (A2-S5). Fixed here rather than per-template so it can't come
     back the next time someone styles a link as a button. */
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none; min-height: 44px;
}
.btn:hover { filter: brightness(1.12); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn.gold { background: var(--gold); color: var(--chrome); }
.btn.ghost { background: transparent; color: var(--ink); border: 1px solid var(--rule); }
.btn.ghost:hover { filter: none; border-color: var(--gold); }

/* -------------------------------------------------------------- composer -- */
.composer { background: var(--card); border: 1px solid var(--rule); border-radius: 14px; box-shadow: var(--shadow); padding: 18px; }
.composer .field {
  display: flex; align-items: center;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 10px;
  padding: 2px 4px 2px 14px; transition: border-color .18s, box-shadow .18s;
}
.composer .field:focus-within { border-color: var(--gold); box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 22%, transparent); }
.composer textarea {
  /* main.js grows it with the text. max-height caps the growth at ~5 lines,
     after which it scrolls internally rather than pushing the Ask button off
     screen.

     min-height is TWO lines, not one. At one line the box was shorter than its
     own placeholder: in the narrow left column "Add a detail, or ask about your
     situation…" wraps, and the second line was clipped mid-word — the reader's
     first sight of the follow-up box was a broken sentence. growToFit measures
     scrollHeight, which ignores the placeholder entirely, so this can't be
     fixed in JS. */
  flex: 1; border: 0; background: transparent; color: var(--ink);
  font: inherit; font-size: 16.5px; line-height: 1.45; padding: 12px 0;
  resize: none; overflow-y: auto;
  min-height: calc(1.45em * 2 + 24px); max-height: calc(1.45em * 5 + 24px);
}
.composer textarea::placeholder { color: var(--ink-faint); }
.composer textarea:focus { outline: none; }
.composer .row { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.composer .row .btn { margin-left: auto; }

/* suggestion prompts */
.suggest { margin-top: 24px; display: flex; flex-direction: column; gap: 10px; }
.suggest .eyebrow { color: var(--ink-faint); }
.suggest button {
  /* 16.5px to match the composer's own text: these ARE questions, and they
     read as a smaller class of thing than the box they populate. */
  text-align: left; font: inherit; font-size: 16.5px; color: var(--ink);
  background: var(--card); border: 1px solid var(--rule); border-radius: 10px;
  padding: 13px 15px; cursor: pointer; transition: border-color .18s;
}
.suggest button:hover { border-color: var(--gold); }

/* --------------------------------------------------------------- answer --- */
.stage > .eyebrow { color: var(--eyebrow); margin-bottom: 14px; }

/* --- The stage's opening state: recently answered ----------------------
   Real published answers, not filler. The hero lede claims every citation is
   checked; this shows it, with the count, before anyone spends a thread. It
   deliberately borrows the archive list's vocabulary (.archive-list, below) —
   the panel and /archive are the same content, so they should read as one
   surface. Gone the moment a question is asked; main.js clears the stage. */
.stage-recent > .eyebrow { color: var(--eyebrow); margin-bottom: 14px; }
.stage-recent ul { list-style: none; padding: 0; margin: 0; }
.stage-recent li { padding: 18px 0; border-bottom: 1px solid var(--rule-soft); }
.stage-recent li:first-child { padding-top: 0; }
.stage-recent li a { font-size: 18.5px; font-weight: 600; color: var(--link); text-decoration: none; }
.stage-recent li a:hover { text-decoration: underline; }
.stage-recent li p { color: var(--ink-soft); font-size: 14.5px; margin: 6px 0 0; max-width: var(--measure); }
/* Same green-check receipt vocabulary as the answer card's source chips, a size
   down: here the count is the claim, there the chips are the proof. */
.stage-recent .receipt { display: inline-block; margin-top: 9px; font-size: 12.5px; font-weight: 600; color: var(--ink-faint); }
.stage-recent .receipt::before { content: "\2713"; color: var(--good); font-weight: 800; margin-right: 6px; }
.stage-more { margin: 18px 0 0; font-size: 14.5px; }
.stage-more a { color: var(--link); text-decoration: none; }
.stage-more a:hover { text-decoration: underline; }

.answer { background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 30px 34px; }
.answer .part { display: grid; grid-template-columns: 34px 1fr; column-gap: 14px; }
.answer .part + .part { margin-top: 22px; padding-top: 22px; border-top: 1px solid var(--rule-soft); }
.answer .num {
  grid-row: span 2; width: 26px; height: 26px; border-radius: 50%;
  background: var(--gold); color: var(--chrome);
  display: grid; place-items: center; font-weight: 700; font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.answer .part h3 { margin: 2px 0 6px; font-size: 16px; font-weight: 700; }
.answer .part > p, .answer .part > ul, .answer .part > .sources { grid-column: 2; }
/* pre-wrap is the fallback for a field with no blank line in it: prose is split
   into real <p> elements server-side, and this keeps a single chunk's own
   newlines rather than collapsing them. */
.answer p { margin: 0; max-width: var(--measure); white-space: pre-wrap; }
/* Real paragraphs need real spacing. They're grid rows in the part's 1fr track,
   so the margin sits between them and never collapses. */
.answer .part > p + p { margin-top: .85em; }
/* Capped like .answer p. The steps sit in the part grid's 1fr track, so on a
   wide stage they ran ~103 characters a line against the 45-75 the --measure
   comment at the top of this file sets as the target. */
.answer .steps { margin: 4px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; max-width: var(--measure); }
.answer .bracha { font-style: italic; color: var(--gold); border-block: 1px dotted var(--rule); padding: 8px 0; }

/* Sources are verified citations — the trust story. Render them as "receipts":
   small, real, tappable source cards with a green verified check, set apart from
   the prose, rather than bare underlined links. Semantic green = "checked",
   separate from the indigo/gold accent. */
.sources { margin-top: 14px; display: flex; flex-wrap: wrap; gap: 8px; }
.sources::before {
  content: "Sources"; flex-basis: 100%;
  font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 2px;
}
.sources a, .sources span {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 600; line-height: 1.3; color: var(--ink-soft);
  background: var(--paper); border: 1px solid var(--rule); border-radius: 9px;
  padding: 7px 12px; text-decoration: none;
  transition: border-color .18s, background .18s, color .18s;
}
.sources a { color: var(--link); }
.sources a::before, .sources span::before {
  content: "\2713"; flex: none; color: var(--good); font-weight: 800; font-size: 11.5px;
}
.sources a:hover {
  color: var(--heading);
  border-color: color-mix(in srgb, var(--link) 45%, var(--rule));
  background: color-mix(in srgb, var(--link) 8%, var(--paper));
}
.sources-note { margin-top: 10px; font-size: 12px; line-height: 1.5; color: var(--ink-faint); }

/* ---------------------------------------------------- progressive thread ----
   The ask page opens as ONE answer under the question; asking a follow-up
   turns the same stage into a threaded exchange. Same card either way — only
   the surrounding chrome changes. */
.q-head { margin: 0 0 14px; }
.q-head .eyebrow { margin-bottom: 3px; }
.q-head h2 {
  margin: 0; font-size: 21px; line-height: 1.3; font-weight: 700;
  color: var(--heading); overflow-wrap: anywhere;
}
.thread-actions { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 14px; }
.t-act {
  display: inline-flex; align-items: center; gap: 7px;
  font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
  color: var(--heading); background: var(--card);
  border: 1px solid var(--rule); border-radius: 10px; padding: 9px 14px;
  transition: border-color .18s, background .18s, color .18s;
}
.t-act:hover { border-color: var(--link); color: var(--link); }
.t-act.ghost { color: var(--ink-soft); background: transparent; }
.t-act.ghost:hover { color: var(--heading); border-color: var(--rule-strong, var(--rule)); }
/* A reader's own message in a threaded exchange — deliberately plainer than
   the answer card, so the sourced answer stays the visual anchor.

   The rule above it is the thread's punctuation. A follow-up starts a new
   exchange, and without a separator the whole thread read as one undifferentiated
   column: several questions and several answers stacked with 4px between the
   question and the answer it belongs to, which is the wrong 4px — it grouped
   each question with the PREVIOUS answer instead of its own. Generous space
   above, close space below, so a question reads as attached to what follows it. */
.turn-you {
  display: flex; justify-content: flex-end;
  margin: 0 0 16px; padding-top: 30px;
  border-top: 1px solid var(--rule-soft);
}
.turn-you p {
  margin: 0; max-width: 82%; padding: 10px 14px;
  font-size: 14.5px; line-height: 1.5; overflow-wrap: anywhere;
  color: var(--heading); background: color-mix(in srgb, var(--link) 9%, var(--card));
  border: 1px solid color-mix(in srgb, var(--link) 22%, var(--rule));
  border-radius: 13px 13px 4px 13px;
}
/* The answer a follow-up produced sits directly under its question, so the
   pair reads as one exchange. Space belongs BETWEEN exchanges, not inside. */
.turn-you + .answer { margin-top: 0; }
.answer + .turn-you { margin-top: 30px; }
@media (max-width: 560px) {
  .q-head h2 { font-size: 18px; }
  .turn-you p { max-width: 88%; }
}

/* Instructional prose on admin screens.
   NOT .hint. .hint is 12.5px in --ink-faint, which is right for "e.g. For Rosh
   Hashanah" beside a label and wrong for a paragraph you have to actually read
   — like the rule that stops us endorsing an item a posek could call pasul.
   --ink-soft on --card is 7.3:1 against --ink-faint's 5.3:1, and two points of
   size, which is the difference between skimmable and squinted at. */
/* Scoped under .wrap .card to outrank `.wrap .card p { margin: 0 0 10px }`
   further down, which is (0,2,1) and would otherwise zero the top margin
   below — the spacing looked applied in the file and measured 0 in the page. */
.wrap .card .admin-note {
  margin: 0 0 14px; max-width: var(--measure);
  font-size: 14.5px; line-height: 1.6; color: var(--ink-soft);
}
.wrap .card .admin-note:last-child { margin-bottom: 0; }
/* A button followed immediately by text reads as a caption on the button.
   Give the explanation room to be its own sentence. */
.wrap .card .admin-edit + .admin-note { margin-top: 22px; }

/* Topic identity. Without a persistent heading the item forms and the delete
   button read as loose controls, and an item meant for one topic could be typed
   into whichever card was on screen — J did exactly that, and only found out
   when the delete button named a topic he wasn't thinking about. */
.supply-topic-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding-bottom: 14px; margin-bottom: 4px; border-bottom: 1px solid var(--rule);
}
.wrap .card .supply-topic-head h2 { margin: 0; }
.supply-count { font-size: 13.5px; color: var(--ink-faint); margin-left: auto; }
.supply-add-head {
  margin: 0 0 4px; font-size: 14.5px; font-weight: 700; color: var(--ink-soft);
}
.supply-item.is-new { border-style: dashed; }
/* Fenced off. It sat flush under the add-item form and read as that form's
   second button, while naming a topic rather than the item just typed. */
.supply-danger { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--rule); }

/* The read-only view of the JSON seed, before it has been adopted. */
.seed-list { margin: 0 0 18px; padding-left: 18px; color: var(--ink-soft); font-size: 14.5px; }
.seed-list > li { margin-bottom: 12px; }
.seed-list ul { margin: 4px 0 0; padding-left: 18px; }
.seed-list .hint { display: block; margin-top: 2px; }

/* User administration. Each action is its own form, so they sit as separate
   rows rather than one wall of controls. */
.user-search { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.user-search label { flex: 1; min-width: 220px; margin-top: 0; }
.user-search .btn { margin-top: 0; }
.user-card .meter-parts { margin: 0 0 6px; }
.user-card .meter-parts .is-comped { color: var(--eyebrow); }
.inline-form { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.inline-form label { margin-top: 0; }
.inline-form input[type="text"], .inline-form select { min-width: 200px; }
.inline-form .btn { margin-top: 0; }
/* The forms are separate elements (one action each), so they need the spacing
   a single form's own layout would have given them. */
.user-card form + form { margin-top: 14px; }
.admin-pager { display: flex; gap: 12px; }

/* The users LIST: one line per reader. Sorting is a query string on the
   column header, so the page needs no JS. Scrolls inside its own box rather
   than widening the page on a phone. */
.user-scroll { overflow-x: auto; margin: 0 0 12px; }
.user-table { border-collapse: collapse; width: 100%; min-width: 30rem; font-size: 14.5px; }
.user-table th, .user-table td {
  text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--rule);
  vertical-align: baseline; white-space: nowrap;
}
.user-table th {
  font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-faint);
}
.user-table th a { color: inherit; text-decoration: none; }
.user-table th a:hover { color: var(--heading); }
.user-table th.is-sorted a { color: var(--heading); }
.user-table th.is-sorted a::after { content: " \25BE"; }
.user-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.user-table th.num { text-align: right; }
.user-table .user-email a { font-weight: 600; }
.user-table tbody tr:hover td { background: var(--rule-soft, transparent); }
.user-table .is-unclaimed { color: var(--eyebrow); font-weight: 600; }
.user-count { margin-top: 0; }
/* Adding a reader is folded away so the list is the first thing on the page. */
.user-add { margin-top: 18px; }
.user-add summary { cursor: pointer; font-weight: 600; color: var(--heading); }
.user-add summary .hint { font-weight: 400; margin-left: 6px; }
.user-add[open] summary { margin-bottom: 4px; }

/* The dashboard's upgrade offer at zero threads: a button with the reason
   beside it, not a text link under the meter that just said you're out. */
.upgrade-block { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 18px; }
.upgrade-block .hint { font-size: 13.5px; color: var(--ink-faint); }

/* The button inside an out-of-threads refusal. The reader is blocked mid-question
   here, so the way forward belongs in the same card as the sentence explaining
   why they stopped — not on another page. */
.error-cta { display: inline-block; margin-top: 16px; }

/* The on-page thread meter, in the rail under the composer. Quiet by default —
   it is a fact, not a nag — and it grows a CTA only at zero. */
/* The count is the reader's own balance, so it is set as information rather
   than as a footnote: 15px, with the number itself larger and in --heading.
   The Upgrade button is pushed to the far end of the row instead of butting
   against the words — inline right after "threads left" it read as part of the
   sentence, and a row with the fact at one end and the action at the other is
   the balance that was missing. */
.thread-meter {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 18px 0 0; font-size: 15px; color: var(--ink-soft);
}
/* The sentence is ONE flex item. As loose children the row's gap landed between
   every word — "0   threads   left". */
.thread-meter-text { display: inline-flex; align-items: baseline; gap: 6px; }
.thread-meter [data-thread-count] {
  font-size: 19px; font-weight: 700; color: var(--heading);
}
.thread-meter .upgrade-cta {
  margin: 0 0 0 auto; padding: 7px 15px; font-size: 14px; align-self: center;
}

/* Session thread switcher. Sits outside #stage — restoring a thread empties
   the stage, which would take the switcher with it. Only appears from the
   second thread on: one thread needs no navigation. */
.thread-switch { margin: 0 0 20px; }
.thread-switch > .eyebrow { color: var(--eyebrow); margin-bottom: 10px; }
.thread-chip {
  display: inline-block; max-width: 260px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: top;
  margin: 0 8px 8px 0; padding: 7px 13px;
  font: inherit; font-size: 13.5px; text-align: left;
  color: var(--ink-soft); background: transparent;
  border: 1px solid var(--rule); border-radius: 999px; cursor: pointer;
}
.thread-chip:hover { color: var(--link); border-color: var(--link); }
/* The current thread is shown, not hidden — the reader needs to see where
   they are, and it is disabled so it can't be "restored" onto itself. */
.thread-chip.is-current {
  color: var(--heading); cursor: default;
  background: color-mix(in srgb, var(--link) 10%, var(--card));
  border-color: color-mix(in srgb, var(--link) 30%, var(--rule));
}
.thread-cold {
  margin: 16px 0 0; padding: 11px 14px; max-width: var(--measure);
  font-size: 14px; line-height: 1.5; color: var(--ink-soft);
  border: 1px dashed var(--rule); border-radius: 10px;
}

/* plain-text answer fallback (before the API returns structured parts) */
.answer .prose { white-space: pre-wrap; max-width: var(--measure); }

/* "How settled is this" — shown for varies/depends/ask_a_rav, never settled.
   Editorial left-rule marks (not status pills): varies/depends are light, calm
   cues; ask_a_rav is a full-width, top-of-answer panel — a dignified deferral,
   never a warning. Semantic hues, no red/error tone. */
.answer .certainty {
  display: inline-block; margin: 0 0 18px;
  padding: 2px 0 2px 12px; border-left: 3px solid var(--rule); border-radius: 0;
  font-size: 12.5px; font-weight: 700; letter-spacing: 0;
  color: var(--ink-soft);
}
.answer .certainty.c-varies { border-color: var(--link); color: var(--link); }
.answer .certainty.c-depends { border-color: var(--gold); color: color-mix(in srgb, var(--gold) 70%, var(--ink)); }
.answer .certainty.c-ask_a_rav {
  display: flex; align-items: center; gap: 11px;
  margin: 0 0 22px; padding: 13px 16px;
  border-left: 4px solid var(--gold); border-radius: 0 10px 10px 0;
  background: color-mix(in srgb, var(--link) 12%, var(--card));
  color: var(--heading); font-size: 13.5px;
}
/* a small gold "seal" — gives the deferral panel presence without alarm */
.answer .certainty.c-ask_a_rav::before {
  content: ""; flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--gold); box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 24%, transparent);
}

/* On an ask_a_rav answer the steps are the takeaway, not a procedure for doing
   the thing: what to bring, and when to ask. Under the generic treatment they
   read as filler. Same gold family as the badge above but deliberately quieter
   (a rule and a wash, no panel, no seal) — the badge stays the loud element and
   this only has to stop the takeaway looking like the rest of the card. */
.answer[data-certainty="ask_a_rav"] .part[data-part="steps"] {
  padding: 14px 0 14px 16px; margin-top: 22px;
  border-top: 0; border-left: 3px solid color-mix(in srgb, var(--gold) 65%, var(--rule));
  border-radius: 0 9px 9px 0;
  background: color-mix(in srgb, var(--gold) 6%, transparent);
}
.answer[data-certainty="ask_a_rav"] .part[data-part="steps"] + .part {
  border-top: 0; padding-top: 0;
}

/* ------------------------------------------------- affiliate supplies ---- */
/* Rendered ONLY when the answer names physical items and affiliate_mode is on.
   Kept visually distinct from the ruling: commerce sits beside the teaching,
   never inside it. Disclosure is required (FTC + Amazon Associates). */
.supplies { margin-top: 22px; background: var(--card); border: 1px solid var(--rule); border-top: 3px solid var(--gold); border-radius: var(--radius); padding: 18px 20px; }
.supplies .head { display: flex; align-items: center; gap: 10px; margin-bottom: 3px; }
.supplies .head .eyebrow { color: var(--gold); margin: 0; }
/* No .tag rule: the "Ad" pill was removed on 2026-08-12. The card's own fineprint
   carries the full Amazon Associates sentence, which is the disclosure that is
   actually required, and a two-letter badge beside it was both redundant and less
   accurate — these are curated affiliate links, not bought placements. */
.supplies .sub { font-size: 13px; color: var(--ink-faint); margin: 0 0 12px; }
.supplies .item { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-top: 1px solid var(--rule-soft); }
.supplies .item:first-of-type { border-top: 0; }
.supplies .item .thumb { flex: none; width: 40px; height: 40px; border-radius: 9px; background: color-mix(in srgb, var(--gold) 14%, var(--card)); color: var(--gold); display: grid; place-items: center; }
.supplies .item .meta { margin-right: auto; }
.supplies .item .name { display: block; font-weight: 700; font-size: 15.5px; }
.supplies .item .desc { display: block; font-size: 13px; color: var(--ink-faint); margin-top: 1px; }
.supplies .item .buy {
  flex: none; font-size: 13.5px; font-weight: 600; white-space: nowrap; color: var(--link);
  text-decoration: none; border: 1px solid var(--rule); border-radius: 8px; padding: 8px 13px;
  transition: border-color .18s, background .18s;
}
.supplies .item .buy:hover { border-color: var(--link); background: color-mix(in srgb, var(--link) 8%, transparent); }
.supplies .fineprint { font-size: 12px; line-height: 1.5; color: var(--ink-faint); margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--rule-soft); }
.supplies .fineprint b { color: var(--ink-soft); }

/* --- affiliate links inside the answer prose --------------------------------
   A curated product link on the word the reader is acting on ("set up the
   menorah"). It must be distinguishable from a SOURCE link at a glance, because
   confusing the two would let commerce borrow the credibility of a verified
   citation. So: blue plus a ✓ is reserved for sources (.sources a below), gold is
   already this site's commerce colour (the shelf's top border and eyebrow), and
   the underline is dotted rather than solid to say "not a citation". Colour is
   never the only signal — the dotted underline carries it too, and every anchor
   has title="Affiliate link".

   Never applied to .part[data-part="ruling"]: the ruling is the halachic bottom
   line and carries no commerce (enforced in supplies.INLINE_FIELDS, not here). */
.answer .inline-buy {
  color: inherit; text-decoration: underline;
  text-decoration-style: dotted; text-decoration-color: var(--gold);
  text-decoration-thickness: 1.5px; text-underline-offset: 2.5px;
}
.answer .inline-buy:hover { color: var(--gold); text-decoration-style: solid; }
/* The proximate disclosure for those links. The shelf's fineprint and the site
   footer both carry the Associates sentence, but a reader can meet and click an
   inline link without reaching either, so this renders under the first part that
   carries one.

   No rule above it, unlike the shelf's fineprint: this sits BETWEEN two parts of
   the answer, and a horizontal line there reads as the end of a section rather
   than as a footnote to the one above. */
.inline-ad-note {
  font-size: 11.5px; line-height: 1.45; color: var(--ink-faint);
  margin: 10px 0 0; font-style: italic;
}

/* --------------------------------------------------- related questions --- */
.related { margin-top: 22px; }
.related .eyebrow { color: var(--ink-faint); margin-bottom: 9px; }
.related .chips { display: flex; flex-wrap: wrap; gap: 8px; }
.related .chip {
  font: inherit; font-size: 13.5px; cursor: pointer; color: var(--link);
  background: transparent; border: 1px solid var(--rule); border-radius: 999px; padding: 7px 14px;
  transition: border-color .18s, background .18s;
}
.related .chip:hover { border-color: var(--link); background: color-mix(in srgb, var(--link) 8%, transparent); }

/* thinking / streaming state */
.thinking { display: flex; align-items: center; gap: 12px; color: var(--ink-faint); font-size: 14px; }
.thinking .dots { display: inline-flex; gap: 5px; }
.thinking .dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--gold); animation: rb-pulse 1.15s ease-in-out infinite; }
.thinking .dots i:nth-child(2) { animation-delay: .18s; }
.thinking .dots i:nth-child(3) { animation-delay: .36s; }
@keyframes rb-pulse { 0%,100% { opacity: .28; transform: translateY(0); } 45% { opacity: 1; transform: translateY(-3px); } }

/* -------------------------------------------- forms (auth / generic) ----- */
.card { background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 30px 32px; }
.form { display: flex; flex-direction: column; gap: 16px; }
.form label { font-size: 13px; font-weight: 600; color: var(--ink-soft); display: flex; flex-direction: column; gap: 6px; }
.form input {
  font: inherit; font-size: 16px; color: var(--ink);
  background: var(--paper); border: 1px solid var(--rule); border-radius: 9px; padding: 12px 14px;
}
.form input:focus-visible { outline: none; border-color: var(--gold); box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 22%, transparent); }
.flash { background: var(--disc); border: 1px solid color-mix(in srgb, var(--gold) 30%, var(--rule)); border-radius: 9px; padding: 11px 14px; font-size: 14px; color: var(--ink-soft); }

/* --- Account / auth / admin surfaces (design-system, was dev-default) - */
.wrap .card > h1 { margin: 0 0 18px; font-size: 22px; }
/* h2 kept the UA's top margin on top of the card's 30px padding, which opened a
   band of dead space above panel headings like "As a reader sees it". */
.wrap .card > h2 { margin: 0 0 16px; font-size: 18px; }
.wrap .card p { margin: 0 0 10px; }
/* A paragraph directly after a form was flush against the submit button — the
   rule above zeroes every top margin in a card, so "Forgot your password?" sat
   welded to "Log in" on login, register, forgot and reset alike. Fixed here
   rather than per page: it is one rule causing it, and it will keep causing it
   on the next form somebody adds. Specificity (0,2,2) to outrank the (0,2,1)
   above — the same trap that made the admin spacing measure 0px. */
.wrap .card form + p { margin-top: 20px; }
.alt { margin: 16px 0 0; font-size: 14px; color: var(--ink-soft); }
/* "Already have an account? [Log in]" — the escape hatch between the two auth
   pages. Set as a row so the control reads as a control, not as the tail of a
   sentence somebody skims past. */
.auth-switch {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  font-size: 15px; margin-top: 18px;
}
.auth-switch .btn { margin: 0; padding: 8px 18px; font-size: 15px; }
/* 14px, up from 12.5px, and --ink-soft (7.3:1) rather than --ink-faint (5.3:1).
   Raised twice by J on the admin screens, but the rule is global and the same
   text sits under form fields readers use too — a size that has to be squinted
   at is not a hint, it is decoration. 12.5px was small enough that it read as
   "not meant to be read", which is the opposite of what these say. */
.hint { color: var(--ink-soft); font-size: 14px; }
.form .hint { margin: 0; }
/* Buttons on one row, each with its own small print beneath it. The print used
   to be a sibling in the same paragraph, so it ran along the row, collided with
   the button and wrapped its tail onto the line below the first button — small
   print for the right-hand button appearing under the left-hand one. */
.account-actions {
  margin: 20px 0 0; display: flex; align-items: flex-start;
  gap: 14px; flex-wrap: wrap;
}
.account-actions > .btn { margin: 0; }
.topup-offer { display: flex; flex-direction: column; gap: 6px; max-width: 300px; }
.topup-offer .btn { margin: 0; }
.topup-offer .hint { line-height: 1.45; }
.admin-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.admin-head h1 { margin: 0; }
.flags { display: flex; flex-direction: column; margin-top: 12px; }
.flag-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px 0; border-top: 1px solid var(--rule-soft); }
.flag-row:first-child { border-top: 0; }
.flag-row span { font-weight: 600; }
/* Admin review warnings. Two tiers on purpose: the plain one is a "don't
   publish this" (a named source with no receipt behind it), the .soft one is a
   heuristic asking for a read (an unsourced custom stated flatly). Styling them
   alike would train the reviewer to skim past both. */
.review-warn {
  margin: 12px 0 0; padding: 11px 14px; font-size: 13px; line-height: 1.55;
  color: var(--heading); background: color-mix(in srgb, var(--warn) 9%, var(--card));
  border-left: 3px solid var(--warn); border-radius: 0 9px 9px 0;
}
.review-warn.soft {
  color: var(--ink-soft); background: transparent;
  border-left-color: color-mix(in srgb, var(--warn) 45%, var(--rule));
}

/* Thread meter (dashboard). The number a subscriber is paying for, on the page
   they pay from — A6 metered threads and nothing ever displayed the count. */
.meter { margin: 18px 0; padding: 14px 16px; border: 1px solid var(--rule); border-radius: 12px; background: var(--paper); }
.meter-total { margin: 0; font-size: 15px; }
.meter-total strong { font-size: 24px; font-weight: 800; color: var(--heading); }
/* Everything in the meter starts at the same left edge.
   The breakdown was an indented bulleted list while the reset date below it was
   flush to the card, so the block had two different left margins and the last
   line looked like it had come adrift. No bullets, no indent — three lines that
   line up. */
.meter-parts {
  margin: 10px 0 0; padding-left: 0; list-style: none;
  color: var(--ink-soft); font-size: 14.5px;
}
.meter-parts li { margin: 3px 0; }
/* The reset date is the answer to "when do I get more", which is the second
   thing anyone looks for here. It was the smallest text on the card. */
.meter .hint {
  margin: 12px 0 0; padding-top: 10px; font-size: 14.5px;
  color: var(--ink-soft); border-top: 1px solid var(--rule-soft);
}
/* Current state, so the row doesn't rely on the button's verb to say whether
   the flag is on — "Affiliate Mode / Enable" is ambiguous, "off [Enable]" isn't. */
.flag-state { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-soft); background: var(--rule-soft); }
.flag-row.is-on .flag-state { color: var(--card); background: var(--good); }

/* --------------------------------------------------------- disclaimer ---- */
.disclaimer {
  background: var(--disc); border: 1px solid color-mix(in srgb, var(--gold) 30%, var(--rule));
  border-radius: 10px; padding: 13px 15px; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft);
}
/* Inside the card the disclaimer had no separation at all, so it sat flush
   under the source receipts. 22px is the card's inter-part rhythm — the same
   gap that separates "Background" from "Step by step". Set on the disclaimer
   rather than as a bottom margin on .sources, so a card whose receipts are the
   last thing in it doesn't gain a trailing gap. */
.answer > .disclaimer { margin-top: 22px; }
.rail .disclaimer { margin-top: 26px; text-align: center; }
/* A4: the verified-citations wedge under the hero h1. */
/* 17px, up from 15px, and 40ch instead of 34ch. This is the site's one-line
   claim about why to trust it, sitting in a 460px column — at 15px/34ch it
   wrapped to three short lines and read as fine print under the headline.
   The bottom margin is the fix for it sitting welded to the composer: it had
   `margin: 10px 0 0` and the composer has no top margin, so "see it." and the
   ask box were touching. */
.hero-lede {
  color: var(--ink-soft); font-size: 17px; line-height: 1.6;
  margin: 12px 0 22px; max-width: 40ch;
}

/* -------------------------------------------------------------- utility --- */
.tabular { font-variant-numeric: tabular-nums; }
.scroll-x { overflow-x: auto; }

/* ---------------------------------------------------------- responsive --- */
@media (max-width: 900px) {
  /* Stack, and reorder so the answer sits right under the ask box; suggestions
     drop below it. display:contents promotes the rail's children into the
     column so they can be ordered against the answer stage. */
  .split { display: flex; flex-direction: column; min-height: 0; }
  .rail { display: contents; }
  .rail > h1 { order: 1; margin: 26px 30px 0; font-size: 21px; }
  .rail > .hero-lede { order: 1; margin: 8px 30px 0; }  /* same order as h1: source order keeps it beneath */
  .composer { order: 2; margin: 16px 30px 0; }
  /* The meter is a direct rail child, so display:contents promotes it into this
     column too — and with no `order` it defaulted to 0 and landed ABOVE the h1,
     at the very top of the page, where nobody reads it as a meter. It shares the
     composer's order value on purpose: source order puts it just beneath the box
     where the threads get spent, and it follows the composer when has-thread
     moves that below the stage. */
  .rail > .thread-meter { order: 2; margin: 12px 30px 0; }
  /* "Or try" goes DIRECTLY under the ask box, as it already does on the desktop
     split, where both live in the rail and the chips follow the composer.
     Until 2026-08-14 the stacked view inverted that: #stage was order 3 and the
     chips order 4, so the two things that belong together were separated by the
     longest block on the page. By the time a reader reached "Or try", the word
     "or" had lost what it referred to — and the chips are the cheapest path to
     the one action this page exists for, which is not where you add scroll.
     At this point the reader has already seen the box; the live question is
     "what do I even ask?", not "can I trust this?" — and the hero lede two
     lines up already carries the trust claim that the panel below reinforces.
     Mobile is where the social traffic lands, so this ordering is the one that
     matters most. */
  .suggest { order: 3; margin: 22px 30px 0; }
  #stage { order: 4; margin: 24px 30px 0; padding: 24px 0 0; border-top: 1px solid var(--rule); }
  /* The two-entry cap was here because the panel sat BETWEEN the composer and
     the chips, so a third entry was scroll a reader had to get past. Below the
     chips nothing is behind it but the disclaimer, so the third row costs
     nobody anything and buys a third homepage link into the archive. */
  .rail > .disclaimer { order: 5; margin: 22px 30px 0; }
  .rail > .notif-optin { order: 6; margin: 20px 30px 36px; }
  /* Needs an explicit order for the same reason .thread-meter did: `.rail:
     display: contents` promotes these into the page column, and a sibling
     without one defaults to 0 and renders ABOVE the h1. It is an offer made
     after an answer, so it belongs last, not first. */
  .rail > .install-prompt { order: 7; margin: 20px 30px 36px; }

  /* Once a thread exists, the reply box moves BELOW it.
     Stacked, the composer sits above the stage, so finishing an answer left
     the reader at the bottom of a long thread with the only way to continue
     scrolled off the top — it read as being sent back to the start rather
     than invited to follow up. On the desktop split the composer is already
     beside the thread and permanently visible, so this is stacked-only.
     `has-thread` is set by main.js from the thread state, never guessed.

     #stage must be pinned back to 3 here. Idle it is 4 so the chips can sit
     under the box, but with a thread open the composer is also 4, and a tie
     falls back to source order — where the rail's children come first, putting
     the reply box ABOVE the answer and re-breaking exactly the bug this block
     was written to fix. */
  body.has-thread #stage { order: 3; }
  body.has-thread .composer { order: 4; margin-top: 24px; }
  body.has-thread .rail > .thread-meter { order: 4; }
  body.has-thread .suggest { order: 5; }
  body.has-thread .rail > .disclaimer { order: 6; }
  body.has-thread .rail > .notif-optin { order: 7; }
  body.has-thread .rail > .install-prompt { order: 8; }
}
@media (max-width: 560px) {
  body { font-size: 16px; }
  .masthead { padding: 13px 16px; gap: 8px; }
  /* The droplet used to be display:none here, to "reclaim width so the nav fits
     one line". Measured in a real browser 2026-08-12, that premise was already
     false: the SIGNED-IN nav needed 390px to hold one line with the logo gone,
     so on a 360-375px phone the header wrapped anyway and the mark was being
     sacrificed for nothing. A brand with no logo on the surface most readers
     arrive on is a worse trade than a nav item.

     At 22px with an 8px gap the header holds one line from 354px signed-out,
     and from 363px signed-in once Pricing steps out (below) — better than the
     390px it needed before. Every current phone is 360px or wider. */
  .masthead .drop svg { width: 22px; height: 25px; }
  .masthead nav a.wide-only { display: none; }
  .masthead .brand { font-size: 18px; }
  .masthead nav { gap: 13px; font-size: 13.5px; }
  .masthead nav .signin { padding: 0 12px; }   /* height comes from min-height */
  .rail > h1, .rail > .hero-lede, .composer, .rail > .thread-meter, #stage,
  .suggest, .rail > .disclaimer, .rail > .notif-optin, .rail > .install-prompt {
    margin-left: 18px; margin-right: 18px;
  }
  .answer { padding: 22px 18px; }
  /* Grid, not `flex-wrap: wrap`. Wrapping let the text block move to its own line
     the moment it was too wide to sit beside the icon, which stranded the icon
     alone on the line above — and only for the items with long names, so the rows
     in one shelf disagreed with each other. A grid pins the icon beside the title
     whatever the text does, and drops the View button underneath, aligned with
     the text rather than floating under the icon. */
  .supplies .item {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    column-gap: 12px; row-gap: 10px; align-items: start;
  }
  .supplies .item .thumb { grid-area: 1 / 1; }
  .supplies .item .meta { grid-area: 1 / 2; margin-right: 0; }
  .supplies .item .buy { grid-area: 2 / 2; justify-self: start; }
}

/* ==========================================================================
   Public archive — the SEO front door.

   A single-column article, not the split ask layout: a stranger arriving from
   a search reads one question and its answer. The answer card itself is the
   shared component, unchanged and unstyled here — everything below is only the
   frame around it, built from the same tokens.
   ========================================================================== */

.article {
  max-width: 780px;
  margin: 0 auto;
  padding: 40px 30px 72px;
}
.article > h1 {
  font-size: clamp(28px, 4.2vw, 40px);
  line-height: 1.18;
  color: var(--heading);
  margin: 6px 0 10px;
  letter-spacing: -0.01em;
}
.dateline {
  color: var(--ink-faint);
  font-size: 13.5px;
  margin: 0 0 22px;
}
.archive-intro {
  color: var(--ink-soft);
  max-width: var(--measure);
  margin: 4px 0 30px;
}

/* Conversion device: sits under the answer, never inside it. */
.archive-ask {
  margin-top: 34px;
  padding: 24px;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.archive-ask-sub {
  color: var(--ink-soft);
  font-size: 14.5px;
  margin: 2px 0 14px;
}
.archive-ask .composer { margin: 0; padding: 0; background: none; border: 0; box-shadow: none; }

.archive-related { margin-top: 34px; }
.archive-related ul, .archive-list { list-style: none; padding: 0; margin: 0; }
.archive-related li { padding: 11px 0; border-bottom: 1px solid var(--rule-soft); }
.archive-related a, .archive-list a { color: var(--link); text-decoration: none; }
.archive-related a:hover, .archive-list a:hover { text-decoration: underline; }

.archive-list li {
  padding: 18px 0;
  border-bottom: 1px solid var(--rule-soft);
}
.archive-list a { font-size: 18.5px; font-weight: 600; }
.archive-list p {
  color: var(--ink-soft);
  font-size: 14.5px;
  margin: 6px 0 0;
  max-width: var(--measure);
}

.pager {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 28px;
  font-size: 14.5px;
  color: var(--ink-faint);
}
.pager a { color: var(--link); text-decoration: none; }
.archive-empty, .archive-back { margin-top: 30px; color: var(--ink-soft); }
.archive-back a { color: var(--link); text-decoration: none; }

@media (max-width: 560px) {
  .article { padding: 28px 18px 56px; }
  .archive-ask { padding: 18px; }
}

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

/* --- Admin: archive review ------------------------------------------------ */
.admin-list { list-style: none; padding: 0; margin: 14px 0 0; }
.admin-list li {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 12px 0; border-bottom: 1px solid var(--rule-soft);
}
.admin-list a { color: var(--link); text-decoration: none; font-weight: 600; }
.admin-list .status {
  font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px; border: 1px solid var(--rule);
  color: var(--ink-soft);
}
.admin-list .status.published { color: var(--good); border-color: var(--good); }
.admin-publish { margin-top: 14px; }
/* The label carries the weight now that .hint is the same size as it. They were
   both --ink-soft at similar sizes, so "Link" and its hint read as one phrase —
   "Link optional — an ASIN is enough". Label louder, hint quieter. */
.admin-edit label {
  display: block; margin: 16px 0 0; font-size: 14px;
  color: var(--heading); font-weight: 600;
}
.admin-edit label .hint { color: var(--ink-faint); font-weight: 400; }
.admin-edit textarea, .admin-edit input[type="text"], .admin-edit select {
  display: block; width: 100%; margin-top: 6px;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--rule); border-radius: 10px;
  padding: 10px 12px; font: inherit; font-size: 15px;
}
.admin-edit textarea { resize: vertical; }
.admin-edit .btn { margin-top: 18px; }

/* Affiliate supplies curation screen. Each item is its own bordered block
   because each item is its own <form> — see the note in admin_supplies.html
   about why this cannot be a table. */
.supply-msgs { margin: 0 0 16px; padding-left: 18px; font-size: 14.5px; }
.supply-msgs .is-error { color: var(--bad, #c2410c); }
.supply-msgs .is-ok { color: var(--good); }
/* A reset link is one long unbroken token; without this it widens the
   card past the viewport on a phone instead of wrapping. */
.supply-msgs li { overflow-wrap: anywhere; }
.supply-sub {
  margin: 22px 0 6px; font-size: 12px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint);
}
.supply-item {
  border: 1px solid var(--rule-soft); border-radius: 12px;
  padding: 4px 16px 18px; margin-top: 12px;
}
.supply-check { display: flex !important; align-items: center; gap: 8px; margin-top: 16px; }
.supply-check input { width: auto; margin: 0; }
.supply-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.supply-actions form { margin: 0; }
/* A hidden item still renders here, dimmed — the whole point of hiding rather
   than deleting is that the curation survives to be switched back on. */
.supply-topic.is-off, .supply-item.is-off { opacity: .55; }

/* --- Site footer ---------------------------------------------------------- */
/* On every page, including archive pages a reader lands on cold from search
   with no session and no onboarding. Uses --ink-soft/--ink-faint on --paper,
   both of which clear AA there (test_css_contrast.py). */
.sitefoot {
  max-width: 760px;
  margin: 40px auto 0;
  padding: 28px 22px 40px;
  border-top: 1px solid var(--rule);
  text-align: center;
}
.sitefoot .foot-boundary {
  margin: 0 auto 16px;
  max-width: 46ch;
  color: var(--ink-soft);
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.55;
}
.sitefoot .foot-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* Row gap does the work when this wraps to two lines at 375px, which it
     does at five links. */
  gap: 10px 20px;
  margin-bottom: 16px;
}
.sitefoot .foot-links a {
  color: var(--link);
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
  /* 44px tall including the padding: these are the smallest tap targets on
     the site and they sit at the very bottom edge of a phone screen. */
  padding: 12px 4px;
}
.sitefoot .foot-links a:hover,
.sitefoot .foot-links a:focus-visible { text-decoration: underline; }
.sitefoot .fineprint {
  margin: 0 auto;
  max-width: 60ch;
  color: var(--ink-faint);
  font-size: 12.5px;
  line-height: 1.6;
}
.sitefoot .fineprint a { color: inherit; }

/* --- Legal pages (/terms, /privacy) --------------------------------------- */
.legal .card > h2 {
  margin: 28px 0 10px;
  font-size: 17px;
  color: var(--heading);
}
.legal .card > h2:first-of-type { margin-top: 22px; }
.legal .card p,
.legal .card li { max-width: var(--measure); color: var(--ink-soft); }
.legal .card ul { margin: 0 0 10px; padding-left: 20px; }
.legal .card li { margin-bottom: 8px; }
.legal .card strong { color: var(--ink); }

/* --- pricing: who each plan is for, and what both share ------------------- */
/* One line under the plan name. Two feature lists side by side tell a reader
   what they get; neither tells them which column is them. */
.plan-for {
  margin: 0 0 12px; font-size: 15px; color: var(--ink-faint);
}
/* Stated once, under both cards, instead of as a bullet inside one of them —
   which read as the free plan having something Premium lacked. */
.shared-features { margin: 26px 0 0; max-width: 64ch; }
.shared-features h2 { font-size: 15px; color: var(--ink-soft); margin: 0 0 10px; font-weight: 700; }
.shared-features ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.shared-features li {
  position: relative; padding-left: 22px;
  color: var(--ink-soft); font-size: 15.5px; line-height: 1.55;
}
.shared-features li::before {
  content: "\2713"; position: absolute; left: 0; color: var(--good); font-weight: 700;
}

/* --- admin navigation ------------------------------------------------------
   The destinations were four text links at the BOTTOM of the dashboard, under
   the usage and spend tables — the way to every other admin screen was the last
   thing on the longest page, and sub-pages could only get back, never sideways. */
.admin-nav {
  display: flex; gap: 10px; flex-wrap: wrap;
  margin: 0 0 22px; padding-bottom: 18px; border-bottom: 1px solid var(--rule);
}
.admin-nav-item {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 14px; min-width: 132px;
  border: 1px solid var(--rule); border-radius: 10px;
  text-decoration: none; color: var(--ink-soft);
}
.admin-nav-item strong { color: var(--heading); font-size: 15px; }
.admin-nav-item .hint { font-size: 12.5px; }
a.admin-nav-item:hover { border-color: var(--link); }
a.admin-nav-item:hover strong { color: var(--link); }
/* You are here — shown, not hidden, so the set of sections stays legible. */
.admin-nav-item.is-current {
  background: color-mix(in srgb, var(--link) 10%, var(--card));
  border-color: color-mix(in srgb, var(--link) 30%, var(--rule));
}

/* --- the upgrade line under an answer ---------------------------------------
   Replaced a premium lock on the composer, so this is the only upgrade prompt
   beside an answer. Deliberately quiet: it sits under halacha, and the dignity of
   the page matters more here than the click. A note, not a card and not a banner
   — the link carries the emphasis, the sentence stays in body ink. */
.upgrade-note {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 18px 0 0; font-size: 14.5px; line-height: 1.6; color: var(--ink-soft);
}
.upgrade-note a {
  color: var(--link); font-weight: 600; text-decoration: none;
  white-space: nowrap;
}
.upgrade-note a:hover { text-decoration: underline; }

/* --- supplies topic jump links ---------------------------------------------
   Plain fragment links to the topic cards' existing ids. The shelf screen is one
   card per topic with a full form per item, so past two or three topics it is a
   long scroll with no way to reach the bottom or to tell which card you are in.
   Chips rather than a sidebar: it matches .admin-nav above, and nothing in this
   stylesheet uses position: sticky, so a floating rail would be a new idiom. */
.supply-jump {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--rule);
}
.supply-jump > .eyebrow { margin: 0 4px 0 0; }
.supply-jump a {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 7px 13px; font-size: 14.5px;
  border: 1px solid var(--rule); border-radius: 999px;
  text-decoration: none; color: var(--ink);
}
.supply-jump a:hover { border-color: var(--link); color: var(--link); }
.supply-jump a .hint { font-size: 12.5px; }
/* The anchor lands the card under the sticky-free masthead with room to breathe,
   rather than flush against the viewport edge. */
.supply-topic { scroll-margin-top: 18px; }

/* --- "add to home screen" hint --------------------------------------------
   Sits in the rail under the disclaimer, shown only after an answer has landed
   and never again once dismissed (install-prompt.js). Quiet by design: it is an
   offer, and the reader came here to ask a question, not to install anything. */
.install-prompt {
  margin-top: 22px; padding: 16px 18px;
  background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius);
}
.install-title { margin: 0; font-size: 15.5px; font-weight: 700; color: var(--heading); }
.install-sub {
  margin: 8px 0 0; font-size: 14.5px; line-height: 1.55; color: var(--ink-soft);
}
.install-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.install-actions .btn { margin: 0; padding: 8px 16px; font-size: 14.5px; }

/* --- your stored questions --------------------------------------------------
   The dashboard block that says what is stored and offers to erase it, plus the
   per-answer delete on /history. Deliberately quiet: this is a control a reader
   should be able to find when they go looking for it, not one that competes
   with the meter and the upgrade offer for attention. */
.account-data {
  margin: 26px 0 0; padding-top: 18px; border-top: 1px solid var(--rule-soft);
}
.account-data h2 { font-size: 16px; margin: 0 0 8px; }
.account-data p { font-size: 14px; line-height: 1.55; color: var(--ink-soft); max-width: var(--measure); }

/* A button that reads as a link. It has to stay a <button> inside a form: a
   GET link that deletes is one prefetch away from firing on its own. */
.linkish {
  background: none; border: 0; padding: 0; font: inherit; font-size: 13px;
  color: var(--ink-faint); text-decoration: underline; cursor: pointer;
}
.linkish:hover { color: var(--ink-soft); }
.entry-delete { margin: 12px 0 0; }
.history .fineprint, .history .upgrade .fineprint {
  font-size: 13px; line-height: 1.55; color: var(--ink-faint); margin: 18px 0 0;
}
