/* truetothebook — one stylesheet, CSS vars, no framework, no JS.
   Warm paper + deep green; serif display over clean UI text. */
:root {
  --bg: #faf7f1; --card: #ffffff; --ink: #221f1a; --dim: #7b7266;
  --line: #e7dfd0; --accent: #2e6b4f; --accent-hi: #245740;
  --tint: #eef3ee; --radius: 18px;
  --gold: #85590c; --gold-hi: #6a4608; --gold-tint: #f7efdc;
  /* report diff rows — rust rather than alarm red, this is a fix not an error */
  --del: #a8452f; --del-bg: #f9ece7; --ins-bg: #ecf3ee;
  --serif: "Iowan Old Style", "Palatino", Georgia, serif;
}
@media (prefers-color-scheme: dark) {
  :root { --bg: #191712; --card: #211e18; --ink: #ece7dd; --dim: #9a9184;
          --line: #353027; --accent: #5aa07f; --accent-hi: #6fb392;
          --tint: #232a24;
          --gold: #d2a75c; --gold-hi: #e3bd7c; --gold-tint: #2a251a;
          --del: #e0876d; --del-bg: #2b1e1a; --ins-bg: #1d2620; }
}

* { box-sizing: border-box; }
/* `hidden` has to beat our own display rules. The browser's own
   [hidden] { display: none } is a USER-AGENT rule, so any author `display:` on
   the same element wins over it — `form { display: flex }` did exactly that,
   and the quote page's hidden sign-in carrier painted as an empty card above
   "Choose your translator". Attribute selector (0,1,0) outranks a type
   selector (0,0,1), so this needs no !important and no source-order luck. */
[hidden] { display: none; }
body { margin: 0; background: var(--bg); color: var(--ink);
  font: 16px/1.65 system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased; }
main { max-width: 700px; margin: 0 auto; padding: 32px 20px 24px; }

header { text-align: center; padding: 26px 20px 0; }
.brand { font-family: var(--serif); font-weight: 650; font-size: 21px;
  letter-spacing: -0.015em; color: var(--ink); text-decoration: none; }
.brand:hover { color: var(--accent); }

h1, h2 { font-family: var(--serif); letter-spacing: -0.015em; line-height: 1.15;
  font-weight: 650; margin: 0 0 12px; }
h1 { font-size: clamp(32px, 6vw, 44px); }
h2 { font-size: clamp(24px, 4vw, 32px); margin-top: 28px; }
p { margin: 0 0 14px; }
.dim { color: var(--dim); font-size: 14.5px; }
a { color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--accent-hi); }

/* forms */
form { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 22px; margin: 22px 0;
  box-shadow: 0 1px 3px rgb(0 0 0 / 4%); display: flex; flex-wrap: wrap;
  gap: 12px; align-items: center; }
button, input::file-selector-button {
  border: 0; border-radius: 999px; padding: 11px 24px; font: inherit;
  font-weight: 600; cursor: pointer; background: var(--accent); color: #fff;
  transition: background .15s, transform .1s; }
button:hover, input::file-selector-button:hover {
  background: var(--accent-hi); transform: translateY(-1px); }
button:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 35%, transparent); }
input::file-selector-button { background: var(--tint); color: var(--accent);
  margin-right: 12px; }
input[type="file"] { flex: 1 1 100%; min-width: 0; color: var(--dim); }
/* input + its drop hint read as one line; the input shrinks to the native
   button + filename so the hint sits right after them */
.filerow { flex: 1 1 100%; display: flex; align-items: center; gap: 4px;
  flex-wrap: wrap; }
.filerow input[type="file"] { flex: 0 1 auto; }
/* nothing to drag with on a touch screen — don't offer it */
@media (hover: none) { .filerow .dim { display: none; } }
/* once a file is picked its name sits where the hint pointed — the hint is
   done. The input is required, so :valid means exactly "file chosen" */
.filerow input[type="file"]:valid + .dim { display: none; }
input[type="checkbox"] { accent-color: var(--accent); }
select, input[type="email"] { font: inherit; color: var(--ink);
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: 9px 12px; }
input[type="email"] { flex: 1 1 240px; min-width: 0; }
/* the sign-in card's second option: "or" + the Telegram widget as one
   full-width row, so the pair never splits when the flex row wraps */
.or-alt { flex: 1 1 100%; display: flex; align-items: center; gap: 12px; }
label { color: var(--dim); font-size: 14.5px; display: inline-flex;
  gap: 8px; align-items: center; }
/* tier choice: a whole card is the click target, the selected one is tinted */
label.tier { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px;
  width: 100%; background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; padding: 12px 14px; margin-bottom: 8px;
  cursor: pointer; color: var(--ink); font-size: 15px; }
label.tier:hover { border-color: var(--accent); }
label.tier:has(input:checked) { border-color: var(--accent);
  background: var(--tint); }
label.tier .dim { flex: 1 1 100%; font-size: 13.5px; }
input[type="radio"] { accent-color: var(--accent); }
/* "from"/"to" as supertext above their select, with a swap button between */
label.over { flex-direction: column; align-items: flex-start; gap: 3px;
  font-size: 12px; letter-spacing: .04em; text-transform: uppercase; }
.swap { align-self: flex-end; background: var(--tint); color: var(--accent);
  padding: 9px 12px; border-radius: 10px; line-height: 1.2; }
.swap:hover { background: var(--tint); }
/* a submit that isn't the main one (staging's pretend-pay) — same shape, tinted
   so it can't be mistaken for the button that takes money */
button.secondary { background: var(--tint); color: var(--accent); }
button.secondary:hover { background: var(--tint); color: var(--accent-hi); }
form .wide { flex: 1 1 100%; margin: 0; }
/* A form is a wrap-around flex row, so anything narrower than the row shares it
   with whatever comes next — which put the quote page's "Where should we send
   updates?" pane beside the "The translator's brief" heading on any screen wide
   enough to fit both (narrow ones looked right only by accident). A heading and
   a panel each own their row, always. */
form > h2, form > details { flex: 1 1 100%; }
form button[type="submit"] { margin-left: auto; }
form.bare { background: none; border: 0; box-shadow: none; padding: 0; }
form.bare button { font-size: 17px; padding: 13px 30px; }
textarea { flex: 1 1 100%; border: 1px solid var(--line); border-radius: 12px;
  background: var(--bg); color: var(--ink); padding: 14px; resize: vertical;
  font: 13px/1.55 ui-monospace, "SF Mono", Menlo, monospace; }
/* the brief is code-ish and wants the mono face; a question to us is prose */
textarea[name="question"], textarea[name="review"],
textarea[name="custom"] { font: inherit; }

/* trust chips + expandable sections */
details { background: var(--card); border: 1px solid var(--line);
  border-radius: 14px; padding: 14px 18px; margin: 10px 0;
  transition: border-color .15s; }
details:hover { border-color: var(--accent); }
/* The padding lives on <details> (the body text has no wrapper to pad), so the
   summary used to stop short of the card edges and a click on the card's own
   padding hit nothing. Negative margins let the summary eat that padding back
   and claim the whole closed card, without moving a pixel of text. */
details > summary { cursor: pointer; font-weight: 600; list-style: none;
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  margin: -14px -18px; padding: 14px 18px; }
details > summary::-webkit-details-marker { display: none; }
details > summary::after { content: "+"; color: var(--accent);
  font-weight: 500; font-size: 19px; line-height: 1; }
details[open] > summary::after { content: "–"; }
details[open] > summary { margin-bottom: -6px; }  /* -14px padding + 8px gap */
details ul { margin: 8px 0; padding-left: 20px; }
details li { margin: 4px 0; }

/* status */
.status-pill { display: inline-block; background: var(--tint);
  color: var(--accent); border-radius: 999px; padding: 4px 14px;
  font-weight: 650; font-size: 14px; letter-spacing: .02em; }
/* finished book: the download IS that page's action, so it takes a button's
   shape instead of a sentence's underline. The alternative formats sit beside
   it, smaller and tinted, so "an epub unless you say otherwise" reads at a
   glance. The report and brief get their own row of the same tinted pills:
   as bare links under the buttons they read as leftovers */
.dl { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: 4px 0 10px; }
.dl b { font-weight: 600; margin-right: 2px; }
.dl a { border-radius: 999px; text-decoration: none; font-weight: 650;
  font-size: 14px; letter-spacing: .02em; padding: 7px 16px;
  background: var(--tint); color: var(--accent);
  transition: background .15s, color .15s, transform .1s; }
.dl a:hover { color: var(--accent-hi); transform: translateY(-1px); }
/* epub is what the pipeline actually built; the rest are conversions of it,
   so only epub gets the filled treatment */
/* about page: the owner's photo floats beside the story */
.portrait { float: right; width: min(230px, 42vw); height: auto;
  border-radius: var(--radius); margin: 4px 0 14px 18px; }
.dl a.main { background: var(--accent); color: #fff; padding: 9px 20px; }
.dl a.main:hover { background: var(--accent-hi); color: #fff; }
/* done page: the materials row is its own thing between the epub note above
   and the review box below — packed tight, the three read as one blob.
   Only the done card has a .dim directly before a .dl */
.dim + .dl { margin-top: 22px; margin-bottom: 24px; }
/* the working waits (quote sample + status page): one fat bar, the label
   inside it, moving stripes so a 5s poll that changes nothing still looks
   alive. The label is painted twice — dark on the track, white clipped inside
   the fill — so it stays readable at any fill width. .still (terminal states)
   parks the fill flat: stripes on a finished book read as still-working. */
.bar-fat { position: relative; height: 48px; border-radius: 999px;
  background: var(--tint); overflow: hidden; margin: 16px 0 12px; }
.bar-fat .label { position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; font-weight: 600;
  font-size: 15px; color: var(--accent); white-space: nowrap; }
.bar-fat .fill { position: absolute; inset: 0 auto 0 0;
  width: calc(var(--p) * 1%); border-radius: 999px; overflow: hidden;
  background-color: var(--accent);
  background-image: linear-gradient(45deg, rgb(255 255 255 / 13%) 25%,
    transparent 25% 50%, rgb(255 255 255 / 13%) 50% 75%, transparent 75%);
  background-size: 34px 34px; animation: stripes 1.4s linear infinite; }
.bar-fat .fill .label { right: auto;
  width: calc(100% * 100 / var(--p)); color: #fff; }
/* the status label carries real numbers (Passage 1487 of 1502…) — on a small
   phone the nowrap line is wider than the bar, so step the type down */
@media (max-width: 480px) { .bar-fat .label { font-size: 13px; } }
.bar-fat.still .fill { animation: none; background-image: none; }
@keyframes stripes { to { background-position-x: 34px; } }
/* “…” that counts 0→1→2→3 and clears: the heading itself says work is running,
   not just the bar under it. The dots always occupy their space (opacity, not
   content), so the heading never reflows. One keyframes per dot — each turns on
   at a different point but they all clear together, which a shifted delay on a
   shared keyframes cannot express.
   Deliberately NOT behind prefers-reduced-motion: it used to be, and on a
   machine with reduce set the dots sat still — which reads as "nothing is
   happening" on the one screen whose job is to say the opposite. What
   reduce-motion protects against is movement in space; these only fade
   opacity in place, so there is nothing here to trigger. */
.dots span { opacity: 0; animation-duration: 1.6s;
  animation-iteration-count: infinite; }
.dots span:nth-child(1) { animation-name: dot1; }
.dots span:nth-child(2) { animation-name: dot2; }
.dots span:nth-child(3) { animation-name: dot3; }
@keyframes dot1 { 0%, 24.9% { opacity: 0 } 25%, 100% { opacity: 1 } }
@keyframes dot2 { 0%, 49.9% { opacity: 0 } 50%, 100% { opacity: 1 } }
@keyframes dot3 { 0%, 74.9% { opacity: 0 } 75%, 100% { opacity: 1 } }
/* Wait facts as badges — three across on a desktop, one per row on a phone.
   A person waiting on a book does not read a paragraph; they scan. Same shape
   on the quote page's checkout facts, so paying and waiting look like one
   site. Three fixed, then one — NOT `auto-fit`: an intermediate two-column
   stage leaves the third badge sitting alone on its own row, which reads as a
   layout bug. Every .facts row is exactly three. */
.facts { display: grid; gap: 10px; margin: 16px 0 4px;
  grid-template-columns: repeat(3, 1fr); }
@media (max-width: 620px) { .facts { grid-template-columns: 1fr; } }
.facts > div { background: var(--card); border: 1px solid var(--line);
  border-radius: 14px; padding: 12px 14px; display: flex; gap: 11px;
  align-items: flex-start; }
.facts b { display: block; font-weight: 600; font-size: 15px; }
.facts span { color: var(--dim); font-size: 14px; line-height: 1.45; }
.facts .ico { font-size: 19px; line-height: 1.35; flex: none; }
/* the sign-in form on the quote page: one line on a desktop, stacked on a phone */
.signin { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.signin input { flex: 1 1 220px; }
/* live-polling cue: a small spinner rides beside the pill while work is active */
.status-pill + .spinner { margin-left: 9px; width: 15px; height: 15px;
  border-width: 2px; vertical-align: -2px; }
.price { font-family: var(--serif); font-size: clamp(34px, 6vw, 46px);
  font-weight: 650; color: var(--accent); margin: 18px 0 4px; }
/* accepted formats, as status pills. PDF carries its caveat in a hover/focus
   tip — the warning belongs on the one format it's about, not in the intro */
.formats { display: flex; flex-wrap: wrap; justify-content: center; gap: 7px;
  margin: 0 0 4px; }
.tip { position: relative; cursor: help; }
.tip b { color: #c98a2b; }
/* below and right-anchored: PDF is the last pill, so growing down-left can't
   push the tooltip off a narrow screen or over the headline */
.tip-body { position: absolute; right: 0; top: calc(100% + 8px); z-index: 5;
  width: max-content; max-width: min(300px, 78vw); padding: 10px 13px;
  background: var(--card); color: var(--dim); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: 0 4px 14px rgb(0 0 0 / 10%);
  font-weight: 400; font-size: 13.5px; line-height: 1.5; letter-spacing: 0;
  /* opacity-only (not visibility/display) keeps the text in the a11y tree */
  opacity: 0; pointer-events: none; transition: opacity .15s; }
.tip:hover .tip-body, .tip:focus .tip-body, .tip:focus-within .tip-body {
  opacity: 1; }
/* narrow enough that the row wraps: anything anchored to the pill itself ends
   up touching a screen edge, so hand the anchoring to the row (already inside
   the column padding) — the tip spans it, below the pills */
@media (max-width: 640px) {
  .formats { position: relative; }
  .tip { position: static; }
  .tip-body { left: 0; right: 0; width: auto; max-width: none; } }

/* the currency rides with the number — a buyer abroad shouldn't have to guess */
.price .cur { font-size: 0.4em; font-weight: 500; letter-spacing: .06em;
  color: var(--dim); vertical-align: 0.5em; margin-left: 2px; }

/* caution notice (PDF conversion caveat on the quote page) — visible, not
   alarming; amber left edge works on both paper and dark backgrounds */
.warn { border-left: 3px solid #c98a2b; background: var(--card);
  border-radius: 0 12px 12px 0; padding: 12px 14px; }

/* rendered translator's brief (model markdown → HTML): tables read far better
   than raw pipes, which is the whole reason we render it */
.brief { margin: 8px 0 4px; }
.brief h3, .brief h4 { font-family: var(--serif); margin: 16px 0 8px; }
.brief h3 { font-size: 18px; }
.brief h4 { font-size: 16px; }
.brief > :first-child { margin-top: 0; }
.brief p { margin: 0 0 10px; }
.brief ul { margin: 8px 0; padding-left: 20px; }
/* a term's note drops below its rendering so the two can't be read as one
   (brief.py); the separating dash stays in the DOM, hidden, for round-tripping */
.brief-note { display: block; font-size: 13.5px; font-style: italic;
  color: var(--dim); line-height: 1.45; }
.brief-sep { display: none; }
.brief table { width: 100%; margin: 8px 0 14px; border: 1px solid var(--line);
  border-radius: 10px; overflow: hidden; font-size: 14px; }
.brief th, .brief td { white-space: normal; }  /* long translations wrap */
.brief th { background: var(--tint); color: var(--ink);
  text-transform: none; letter-spacing: 0; }

/* the review form is a flex card, so its full-width children have to say so */
#brief-form > .brief, #brief-form > details.raw { flex: 1 1 100%; }

/* brief editor (JS-enhanced review page): editable cells + row controls */
.brief [contenteditable="true"] { cursor: text; }
.brief [contenteditable="true"]:focus { outline: 2px solid var(--tint);
  outline-offset: -2px; border-radius: 3px; }
/* editability affordance: a persistent pen on section headings + a hover cue on
   every editable line, so the brief obviously IS the edit surface. ::after is a
   generated pseudo-element (no DOM node) so brief-edit.js serialize() ignores it */
/* brief.py bumps heading levels +2, so briefs render as h3-h6 — cover them all */
.brief h3[contenteditable="true"]::after, .brief h4[contenteditable="true"]::after,
.brief h5[contenteditable="true"]::after, .brief h6[contenteditable="true"]::after {
  content: " ✎"; color: var(--dim); font-size: .72em; vertical-align: 1px; }
.brief [contenteditable="true"]:hover { outline: 1px dashed var(--line);
  outline-offset: -1px; border-radius: 3px; }
.brief-del { width: 1%; border: 0 !important; background: none !important;
  padding: 0 4px !important; text-align: center; }
.brief-row-btn { font: inherit; font-size: 13px; line-height: 1; cursor: pointer;
  color: var(--dim); background: none; border: 1px solid var(--line);
  border-radius: 999px; padding: 3px 10px; margin: 0 0 14px; }
.brief-row-btn:hover { color: var(--ink); border-color: var(--dim); }
.brief-del .brief-row-btn { border: 0; margin: 0; padding: 0 3px; font-size: 17px; }
.linklike { font: inherit; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--dim); text-decoration: underline; margin: 0 0 12px; }
.linklike:hover { color: var(--ink); }

/* loading: inline spinner + full-screen busy overlay (forms with data-busy) */
.spinner { display: inline-block; width: 18px; height: 18px;
  border: 2.5px solid var(--line); border-top-color: var(--accent);
  border-radius: 50%; animation: spin .8s linear infinite;
  vertical-align: -3px; }
@keyframes spin { to { transform: rotate(360deg); } }
/* place-CONTENT as well as place-items: with two children the implicit rows
   stretch across the whole viewport by default, which parked the caption a
   third of a screen below the spinner and made `gap` do nothing. */
#busy { position: fixed; inset: 0; display: none; place-items: center;
  place-content: center; gap: 14px;
  background: color-mix(in srgb, var(--bg) 55%, transparent);
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  z-index: 10; }
#busy.on { display: grid; }
#busy .spinner { width: 44px; height: 44px; border-width: 4px; }
/* the two children stack as grid rows, so the text needs its own width limit */
#busy p { margin: 0; max-width: 19em; text-align: center; color: var(--dim);
  font-size: 14.5px; padding: 0 16px; }

footer { max-width: 700px; margin: 40px auto 28px; padding: 18px 20px 0;
  border-top: 1px solid var(--line); font-size: 13.5px; color: var(--dim);
  text-align: center; }
/* footer nav as outlined pills — deliberately NOT the filled status-pill look,
   so navigation never reads as a state badge */
.footpills { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px;
  margin-bottom: 12px; }
.footpills a { border: 1px solid var(--line); border-radius: 999px;
  padding: 5px 14px; color: var(--dim); text-decoration: none;
  transition: border-color .15s, color .15s, background .15s; }
.footpills a:hover { color: var(--accent); border-color: var(--accent);
  background: var(--tint); }
/* one pill is a destination, not a legal notice: a returning buyer looking for
   their download should find it without reading the row */
.footpills a.primary { background: var(--tint); border-color: var(--accent);
  color: var(--accent); font-weight: 600; }
/* the second destination pill — gold, because green already means "your
   books" and a publisher landing on a green pill would read it as their own */
.footpills a.gold { background: var(--gold-tint); border-color: var(--gold);
  color: var(--gold); font-weight: 600; }
.footpills a.gold:hover { background: var(--gold-tint);
  border-color: var(--gold-hi); color: var(--gold-hi); }

/* a faint short rule marking where the footer stops addressing people and
   starts addressing crawlers — short and dim on purpose, it separates two
   kinds of text rather than announcing a new section */
.footrule { width: 90px; height: 0; margin: 4px auto 12px; border: 0;
  border-top: 1px solid var(--line); }

/* copyright + running release: the last line addressed to a person, so it
   sits above the crawler rule. Quieter than the promise line above it —
   nobody came here for a version number */
.colophon { margin: 6px 0 10px; font-size: 13px; color: var(--dim); }

/* one quiet text line under the pills: site-wide links to the pair landing
   pages — internal linking the crawler needs, without a second pill row */
.pairlinks { margin-bottom: 12px; }
.pairlinks a { color: var(--dim); }
.pairlinks a:hover { color: var(--accent); }

/* admin dashboard: the only wide page on the site, so it gets the only
   table — and its own scroll container, since 15 columns will never fit a
   phone and the page body must not scroll sideways */
ul.totals { list-style: none; padding: 14px 16px; margin: 0 0 20px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); }
ul.totals li + li { margin-top: 4px; }
.scroll { overflow-x: auto; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--card); }
table { border-collapse: collapse; width: 100%; font-size: 13.5px; }
th, td { padding: 9px 11px; text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line); }
th { font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--dim); font-weight: 600; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--tint); }
td.n, th.n { text-align: right; font-variant-numeric: tabular-nums; }
/* /cost: four columns of numbers don't fit a phone at nowrap, and the human
   column is the whole point of the table — so let its cells wrap instead of
   scrolling the comparison off-screen */
@media (max-width: 620px) {
  table.cost { font-size: 12.5px; }
  table.cost th, table.cost td { white-space: normal; padding: 8px 6px; }
}
.bad { color: #b4472f; }
@media (prefers-color-scheme: dark) { .bad { color: #e0876d; } }
/* admin per-row delete: the global form is a padded card and button a fat
   pill — both unusable in a table cell, so strip back to a bare glyph that
   only colours as a warning on hover */
.admin-del { all: unset; }
.admin-del button { background: none; color: var(--dim); font-size: 17px;
  line-height: 1; padding: 2px 6px; }
.admin-del button:hover { background: none; color: #b4472f; transform: none; }

/* free sample: the buyer's own text beside our translation of it — the A/B
   is the evidence, so the two columns must line up row by row */
.ab { display: grid; grid-template-columns: 1fr 1fr; gap: 0 22px; }
.ab p { margin: 12px 0 0; }
.ab .col-head { font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
  margin-top: 6px; }
.ab .orig { color: var(--dim); }
@media (max-width: 640px) {
  /* stacked: each original sits directly above its translation, marked by a
     rule so the pairing survives losing the columns */
  .ab { grid-template-columns: 1fr; }
  .ab .col-head { display: none; }
  .ab .orig { padding-left: 12px; border-left: 2px solid var(--line);
    margin-bottom: 0; }
}

/* quality report: the fix pair IS the evidence, so the words that changed must
   be findable at a glance — a real fix was one Latin letter inside a Russian
   word, indistinguishable on screen until it was marked */
details.fixes { margin: 0 0 10px; }
details.fixes summary { cursor: pointer; font-size: 14px; color: var(--dim); }
.fix { margin: 10px 0 14px; padding-left: 12px;
  border-left: 2px solid var(--line); }
/* the pair reads as a diff: −/+ gutter, the whole row tinted. Colour is the
   fast signal, never the only one — the markers stay, and del/ins keep the
   strike/underline for anyone who can't use the tint */
.fix.diff { padding-left: 0; border-left: 0; }
.fix .was, .fix .now { display: block; position: relative;
  padding: 3px 10px 3px 24px; border-radius: 7px; }
.fix .was::before, .fix .now::before { position: absolute; left: 8px;
  font-weight: 700; }
.fix .was { background: var(--del-bg); }
.fix .was::before { content: "\2212"; color: var(--del); }
.fix .now { background: var(--ins-bg); margin-top: 2px; }
.fix .now::before { content: "+"; color: var(--accent); }
.fix del { text-decoration: line-through; text-decoration-thickness: 1px;
  color: var(--del); }
.fix ins { text-decoration: underline; text-decoration-thickness: 1px;
  color: var(--accent-hi); font-weight: 600; }
