/* /upgrade — credit wallet + PayOS checkout + giftcode redemption.

   This page is reached from inside the app (the hub's "Upgrade" pill), not
   from the public site, so it doesn't re-run /about's cinematic scroll
   scene — but it borrows that page's graphic language rather than reading
   as a generic SaaS pricing page: the mono eyebrow, the Archivo Black
   headline with one amber-marked word, the hard ink-shadow card
   (border + offset crimson/ink shadow, same recipe as .login-card /
   .about-letter in style.css), and one margin annotation per package
   calling out its flagship mechanic. Every token used below
   (--ink-950, --amber-300, --font-hand, --font-mono, ...) already lives in
   style.css — nothing new is imported here.

   The hero is a minimal header only ("Plans & Credits"). It used to state
   the credit-penalty policy in full; that was moved out to /tutorial#act-lost
   and then deleted with the rest of that page on 2026-08-27, so RIGHT NOW
   the policy is written down nowhere public — see the note on the hero
   markup in upgrade.html. The wallet buckets and prices are deliberately not
   restated in the hero either: #walletCard and #packagesGrid render them
   live directly underneath it. */

.up-hero {
  background: var(--ink-950);
  color: var(--offwhite);
  padding: clamp(40px, 7vw, 72px) 24px clamp(90px, 10vw, 120px);
}
.up-hero-inner { max-width: 720px; margin: 0 auto; }

.up-eyebrow {
  margin: 0 0 12px;
  font-family: var(--font-mono); font-weight: 600;
  font-size: var(--text-micro); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--slate-400);
}

.up-headline {
  margin: 0 0 18px;
  font-family: var(--font-display); font-weight: var(--weight-black);
  /* Bigger since v1.4.35, for all three heroes together (/history, /account,
     /credits) — they share this class on purpose, see the "same graphic
     register" note in each page's markup. */
  font-size: clamp(2.6rem, 7vw, 4.6rem); line-height: 1.05; letter-spacing: -0.02em;
  color: var(--offwhite);
}
.up-hero-sub {
  margin: 0;
  font-size: var(--text-lg, 1.05rem); line-height: var(--leading-body, 1.6);
  color: var(--slate-300, #cfcfcf);
  max-width: 56ch;
}
.up-hero-sub strong { color: var(--offwhite); }

/* Same muted-link recipe as the footer's .foot-links a (style.css) — an
   underline, not a button, so it reads as a pointer to more detail rather
   than a second call to action competing with the package "Buy" buttons
   below. */
.up-hero-link {
  margin: 14px 0 0;
  font-size: var(--text-sm, 0.9rem);
  color: var(--slate-400);
}
.up-hero-link a { color: var(--amber-300); text-decoration: underline; }
.up-hero-link a:hover { color: var(--offwhite); }

/* ---------------------------------------------------------------------------
   Light hero — /credits ONLY.

   /history and /account keep the dark band above; this page dropped it because
   a full-width black slab directly above the price cards is the wrong opening
   move on the one page whose job is to get paid. Everything here is a modifier,
   so the shared .up-hero rules above stay untouched.

   The tall bottom padding (90-120px) existed purely so #walletCard could pull
   up into the dark band with a negative margin. With a light hero there is
   nothing to pull into, so both the padding and those negative margins go.
   --------------------------------------------------------------------------- */
.up-hero--light {
  background: var(--surface-card);
  color: var(--slate-800);
  border-bottom: var(--border-hair) solid var(--line);
  padding: clamp(36px, 6vw, 60px) 24px clamp(30px, 4vw, 42px);
}
/* slate-500, not the slate-400 the dark hero uses: #9a9a9a on white measures
   2.81:1, below AA for 12.8px text. Measured, not guessed. */
.up-hero--light .up-eyebrow { color: var(--slate-500); }
.up-hero--light .up-headline { color: var(--ink-950); }
.up-hero--light .up-hero-sub { color: var(--slate-500); }
.up-hero--light .up-hero-link { color: var(--slate-500); }
.up-hero--light .up-hero-link a { color: var(--crimson); }
.up-hero--light .up-hero-link a:hover { color: var(--crimson-dark); }

.up-hero-tagline { margin: 0 0 16px; }

/* .up-eyebrow's slate-400 is tuned for the dark hero. Every eyebrow inside the
   body of this page (section head, unlimited banner) sits on a light surface,
   where that grey measures 2.6:1 — under AA. Scoped to #upgradeView so /account
   and /history, whose eyebrows are still on dark, keep the original. */
#upgradeView .up-body .up-eyebrow { color: var(--slate-500); }

/* The landing page's own .ls-hero-mark (landing.css) rebuilt here, because
   landing.css is not one of this page's three stylesheets. Hard shadow, square
   corners and the -1deg tilt are the landing's signature, and a buyer who just
   came from / should recognise it. */
.up-hero-mark {
  display: inline-block; max-width: 100%;
  background: var(--crimson); color: #fff;
  border: 2px solid var(--ink-950);
  box-shadow: 4px 4px 0 var(--ink-950);
  padding: 5px 16px 8px;
  font-family: var(--font-display); font-weight: var(--weight-display);
  font-size: clamp(1.05rem, 2.6vw, 1.35rem); line-height: 1.25;
  transform: rotate(-1deg);
}

/* Same muted-link recipe as .up-hero-link above, for use OUTSIDE .up-hero
   instead — /account's "Nạp thêm credit" link sits on .up-body's white
   background, not the dark hero, and .up-hero-link's colours (slate-400
   base text, amber-300 link, offwhite on hover) are tuned for that dark
   background: low-contrast, then invisible-on-hover against white
   (2026-08-23, reported after shipping). */
.up-body-link {
  margin: 14px 0 0;
  font-size: var(--text-sm, 0.9rem);
  color: var(--slate-500);
}
.up-body-link a { color: var(--crimson); text-decoration: underline; }
.up-body-link a:hover { color: var(--crimson-dark); }

/* Body sits on the app's normal light background. The -56px pull-ups below are
   inert on /credits since the hero went light (.up-hero--light zeroes them at
   the end of this file); they stay here because /account still renders .up-wallet
   under a dark hero and depends on them. */
.up-body { max-width: 1040px; padding-top: 0; }

.up-unlimited {
  margin-top: -56px; position: relative; z-index: 1;
  background: var(--surface-card); border: var(--border-hair) solid var(--line-crisp);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 24px 28px;
}
.up-unlimited .up-eyebrow { color: var(--slate-400); }
.up-unlimited-text { margin: 0; font-size: 0.95rem; color: var(--navy); }
/* /account keeps the pull-up (its hero is still dark); /credits cancels it.
   #upgradeView exists only on this page — /account wraps itself in #accountView. */
#upgradeView .up-unlimited { margin-top: 24px; }

.up-wallet {
  margin-top: -56px; position: relative; z-index: 1;
  display: flex;
  background: var(--surface-card); border: var(--border-hair) solid var(--line-crisp);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.up-wallet-col {
  flex: 1; text-align: center; padding: 22px 12px;
  border-right: 1.5px solid var(--slate-200);
  display: flex; flex-direction: column; gap: 4px;
}
.up-wallet-col:last-child { border-right: none; }
.up-wallet-num {
  font-family: var(--font-display); font-weight: var(--weight-black);
  font-size: 2.1rem; color: var(--crimson); font-variant-numeric: tabular-nums;
}
/* slate-500: this label has always sat on a white card, where slate-400 measures
   2.81:1 — under AA at 10.9px. Pre-existing, found by the contrast sweep run for
   the v1.4.51 rebuild; /account shows the same label on the same white card. */
.up-wallet-label {
  font-family: var(--font-mono); font-weight: 600;
  font-size: 0.68rem; letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--slate-500);
}

/* padding-top leaves room for the badge that hangs 13px above each card; without
   it the first row of badges is clipped by the section above. 240px (not 260px)
   is what lets Starter + Basic + Intensive stay on one row at 1040px. */
.up-packages {
  /* max must stay 1fr, not a fixed cap: auto-fit counts how many tracks fit
     using the MAX size, so minmax(240px, 380px) creates only two columns at
     1040px and drops Intensive onto a second row. Measured, 2026-09-05. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 24px; margin-top: 32px; padding-top: 16px;
}
.up-package {
  position: relative; /* anchor for .up-package-badge */
  background: var(--surface-card); border: var(--border-hair) solid var(--line-crisp);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 26px 26px 24px;
  display: flex; flex-direction: column; gap: 4px;
  margin-bottom: 0; /* override .card's default margin-bottom */
}
.up-package-name {
  font-family: var(--font-display); font-weight: var(--weight-black);
  font-size: var(--text-xl); color: var(--navy); margin: 0;
}
.up-package-credits { font-size: var(--text-base); color: var(--slate-500); margin: 2px 0 0; }
.up-package-price { margin: 14px 0 0; display: flex; align-items: baseline; gap: 8px; }
.up-price-strike { color: var(--slate-400); text-decoration: line-through; font-size: var(--text-base); }
.up-price-now {
  font-family: var(--font-display); font-weight: var(--weight-black);
  font-size: var(--text-2xl); color: var(--navy);
}
.up-package-note { font-size: var(--text-base); color: var(--slate-500); margin: 4px 0 0; }
/* The one margin note per card — the flagship mechanic that pricing copy alone
   doesn't sell as well as a note beside it does. Set in the body face and
   upright since 2026-08-28; at 1.25rem rotated it now competed with the price
   instead of annotating it. */
.up-package-annotation {
  font-family: var(--font-hand); font-weight: 600; color: var(--amber-700);
  font-size: var(--text-lg); line-height: 1.45;
  margin: 10px 0 14px;
}
.up-buy-btn {
  margin-top: auto; width: 100%; padding: 13px 18px; cursor: pointer;
  background: var(--crimson); color: #fff; border: none; border-radius: var(--radius-card);
  font-family: var(--font-display); font-weight: var(--weight-display, 700);
  font-size: var(--text-lg);
  transition: background 0.15s ease, transform 0.08s ease;
}
.up-buy-btn:hover:not(:disabled) { background: var(--crimson-dark); }
.up-buy-btn:active:not(:disabled) { transform: translateY(1px); }
.up-buy-btn:disabled { opacity: 0.6; cursor: not-allowed; }

/* 2026-08-27 pricing rework, tinh chỉnh: while a Basic/Intensive plan is
   active, BOTH plan cards are hidden entirely (renderSubscriptionState(),
   upgrade.js) and this line takes their place instead — a plain status
   line, not a card, so it doesn't compete visually with the one card left
   in the grid (Reload). */
.up-current-plan-status {
  margin: 32px 0 0;
  font-family: var(--font-mono); font-weight: 600;
  font-size: 0.85rem; color: var(--navy);
}
.up-current-plan-status strong { color: var(--crimson); }

/* In-app checkout screen (upgrade.js's openPayBlock) — same hard ink-shadow
   card recipe as .up-package (border + flat offset shadow), rather than a
   modal/overlay: this sits inline where the QR/copy affordances stay next
   to the wallet numbers the student is topping up. */
.up-pay {
  margin-top: 24px;
  background: var(--surface-card); border: var(--border-hair) solid var(--line-crisp);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 26px;
}
.up-pay-grid { display: flex; gap: 28px; flex-wrap: wrap; }
.up-pay-qr {
  width: 220px; height: 220px; flex-shrink: 0;
  border: var(--border-hair) solid var(--line-crisp); border-radius: var(--radius-card);
  background: var(--slate-100, #f2f2f2);
}
.up-pay-info { flex: 1; min-width: 240px; display: flex; flex-direction: column; }
.up-pay-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; border-bottom: 1.5px solid var(--slate-200);
}
.up-pay-label {
  width: 130px; flex-shrink: 0;
  font-family: var(--font-mono); font-weight: 600;
  font-size: 0.66rem; letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--slate-400);
}
.up-pay-value {
  flex: 1; min-width: 0; overflow-wrap: break-word;
  font-family: var(--font-display); font-weight: 700;
  font-size: 0.92rem; color: var(--navy);
}
.up-copy-btn {
  flex-shrink: 0; padding: 6px 12px; cursor: pointer;
  background: #fff; color: var(--navy); border: 1.5px solid var(--navy); border-radius: 8px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.76rem;
}
.up-copy-btn:hover { background: var(--navy); color: #fff; }
.up-pay-status {
  margin: 14px 0 16px;
  font-family: var(--font-mono); font-size: 0.82rem; color: var(--slate-500);
}
.up-pay-status--paid { color: var(--emerald-600); font-weight: 700; }
.up-pay-status--error { color: var(--crimson-dark); font-weight: 700; }

@media (max-width: 560px) {
  .up-pay-grid { flex-direction: column; align-items: center; }
  .up-pay-qr { width: 200px; height: 200px; }
  .up-pay-info { width: 100%; }
}

.up-giftcode {
  margin: 36px 0 60px; padding: 22px 26px;
  background: var(--surface-card); border: var(--border-hair) solid var(--line-crisp); border-radius: var(--radius-card);
}
.up-giftcode-row { display: flex; gap: 10px; margin-top: 4px; }
.up-giftcode-row input {
  flex: 1; padding: 11px 14px; border: var(--border-hair) solid var(--line-crisp); border-radius: var(--radius-card);
  font-size: 0.9rem; text-transform: uppercase; font-family: var(--font-mono);
}
.up-giftcode-row input:focus { outline: none; border-color: var(--crimson); }
.up-redeem-btn {
  padding: 11px 22px; cursor: pointer;
  background: #fff; color: var(--navy); border: var(--border-hair) solid var(--navy); border-radius: var(--radius-card);
  font-family: var(--font-display); font-weight: 600; font-size: 0.88rem;
}
.up-redeem-btn:hover:not(:disabled) { background: var(--navy); color: #fff; }
.up-msg-error { color: var(--crimson-dark); }

@media (max-width: 640px) {
  .up-wallet { flex-direction: column; }
  .up-wallet-col { border-right: none; border-bottom: 1.5px solid var(--slate-200); }
  .up-wallet-col:last-child { border-bottom: none; }
}

/* ---------- Billing conversion V2 ---------- */
/* margin-top: 0, not -58px: the pull-up only made sense while the hero was a
   dark band with 90-120px of bottom padding to sink into. .up-wallet-summary is
   used on /credits alone (/account uses .up-wallet), so this is safe. */
.up-wallet-summary {
  margin-top: 24px;
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--surface-card);
  border: var(--border-hair) solid var(--line-crisp);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.up-wallet-summary > div {
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-right: 1.5px solid var(--slate-200);
}
.up-wallet-summary > div:nth-child(2) { border-right: 0; }
.up-wallet-summary .up-current-plan-status {
  grid-column: 1 / -1;
  margin: 0;
  padding: 14px 24px;
  border-top: 1.5px solid var(--slate-200);
  background: var(--slate-100);
}

.up-section-head h2,
.up-pay-head h2 {
  margin: 0;
  color: var(--navy);
  font-family: var(--font-display);
  font-weight: var(--weight-black);
  line-height: 1.08;
}

.up-regular { margin-top: 42px; }
.up-section-head { display: flex; justify-content: space-between; gap: 24px; align-items: end; }
.up-section-head h2 { font-size: clamp(1.7rem, 4vw, 2.5rem); }
.up-section-head > p { margin: 0; max-width: 40ch; color: var(--slate-500); line-height: 1.55; }
/* weight 700, not 800: Space Grotesk's axis stops at 700 and anything above it
   is browser-faked bold, which smears Vietnamese diacritics (style.css:265). */
.up-session-count { margin: 12px 0 0; font-family: var(--font-display); font-size: 2rem; font-weight: var(--weight-display); color: var(--crimson); }

/* One badge shape for every card, sitting on the top edge. Was .up-best-badge,
   worn only by Intensive to say "Rẻ nhất / buổi"; now each card wears one and it
   names WHO the package is for, which is the question a buyer actually has. */
.up-package-badge {
  position: absolute;
  top: -13px;
  right: 18px;
  margin: 0;
  max-width: calc(100% - 36px);
  padding: 5px 10px;
  border-radius: var(--radius-pill);
  color: #fff;
  background: var(--ink-950);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
}
.up-package-badge--crimson { background: var(--crimson); }

/* Two coloured cards. Every child colour is restated rather than inherited:
   swapping a background under text that hardcodes its own colour is exactly how
   /tutorial ended up with white-on-white labels (STATUS.md trap #3). */
.up-package--starter {
  background: var(--crimson);
  border-color: var(--crimson);
  box-shadow: var(--shadow-lift);
}
.up-package--starter .up-package-name,
.up-package--starter .up-price-now { color: #fff; }
.up-package--starter .up-session-count { color: var(--amber-300); }
/* full white, not 92%: the faded version measured 4.16:1 on crimson, under AA. */
.up-package--starter .up-package-credits { color: #fff; }
.up-package--starter .up-package-annotation { color: #fff; }
.up-package--starter .up-buy-btn { background: #fff; color: var(--crimson-dark); }
.up-package--starter .up-buy-btn:hover:not(:disabled) { background: var(--offwhite); }

.up-package--dark {
  background: var(--ink-950);
  border-color: var(--ink-950);
  box-shadow: var(--shadow-lift);
}
.up-package--dark .up-package-name,
.up-package--dark .up-price-now { color: var(--offwhite); }
.up-package--dark .up-session-count,
.up-package--dark .up-package-annotation { color: var(--amber-300); }
.up-package--dark .up-package-credits { color: var(--slate-300); }
.up-package--dark .up-price-strike { color: var(--slate-400); }

.up-giftcode,
.up-policy {
  margin: 28px 0 0;
  padding: 0;
  background: transparent;
  border: 0;
}
.up-giftcode summary,
.up-policy summary {
  cursor: pointer;
  color: var(--navy);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.up-giftcode-row { margin-top: 14px; max-width: 560px; }
.up-policy { margin-bottom: 54px; }
.up-policy > div { margin-top: 14px; padding: 18px 22px; border-left: 4px solid var(--amber-300); background: var(--surface-card); }
.up-policy p { margin: 0 0 9px; color: var(--slate-600); line-height: 1.55; }
.up-policy p:last-child { margin-bottom: 0; }
.up-checkout-error { margin: 18px 0; }
.up-checkout-notice {
  margin: 18px 0;
  padding: 12px 14px;
  border-left: 4px solid var(--amber-300);
  background: var(--surface-card);
  color: var(--slate-600);
  line-height: 1.55;
}

.up-pay { margin: 26px 0 64px; scroll-margin-top: 24px; }
.up-pay-head { display: flex; justify-content: space-between; gap: 24px; align-items: start; margin-bottom: 20px; }
.up-pay-head h2 { font-size: clamp(1.6rem, 4vw, 2.3rem); }
.up-pay-summary { margin: 8px 0 0; color: var(--slate-500); }
.up-pay-close {
  min-width: 44px; min-height: 44px;
  border: 0; background: transparent; color: var(--slate-500);
  font-size: 2rem; cursor: pointer; line-height: 1;
}
.up-pay-close:focus-visible,
.up-buy-btn:focus-visible,
.up-copy-btn:focus-visible,
.up-redeem-btn:focus-visible,
.up-pay-fallback:focus-visible,
.up-success-link:focus-visible {
  outline: 3px solid var(--amber-300);
  outline-offset: 3px;
}
.up-pay-fallback { color: var(--crimson); text-decoration: underline; margin: -4px 0 14px; }
.up-success-link { display: block; text-align: center; text-decoration: none; }

@media (max-width: 720px) {
  .up-hero { padding: 32px 20px 88px; }
  .up-wallet-summary { grid-template-columns: 1fr; }
  .up-wallet-summary > div { border-right: 0; border-bottom: 1.5px solid var(--slate-200); }
  .up-starter { grid-template-columns: 1fr; padding: 24px 20px; box-shadow: 5px 5px 0 var(--crimson); }
  .up-starter-offer { padding: 20px 16px; }
  .up-section-head { align-items: start; flex-direction: column; }
  .up-packages { grid-template-columns: 1fr; }
  .up-pay { padding: 20px 16px; }
  .up-pay-row { align-items: flex-start; flex-wrap: wrap; }
  .up-pay-label { width: 100%; }
  .up-pay-value { flex-basis: calc(100% - 90px); }
  .up-giftcode-row { flex-direction: column; }
}


/* ---------- /history ----------
   Loaded on history.html alongside style.css and hub-dash.css. Lives here
   rather than in its own file for the same reason /account does: the page
   already borrows this file's hero, and a fourth stylesheet for one page is
   another asset every /history request has to fetch. */
.hist-status { font-size: 16px; color: var(--slate-500); margin: 24px 0; }

.hist-card { margin-bottom: 14px; padding: 0; }
.hist-card > summary { cursor: pointer; list-style: none; padding: 16px 20px; }
.hist-card > summary::-webkit-details-marker { display: none; }
.hist-card[open] > summary { border-bottom: 1px solid var(--slate-100); }
.hist-summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}
.hist-summary-line { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; min-width: 0; }
.hist-date { font-family: var(--font-mono); font-size: 14px; color: var(--slate-500); }
.hist-task {
  font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--crimson); border: 1px solid var(--crimson);
  border-radius: 999px; padding: 2px 8px;
}
.hist-topic { font-size: 16px; color: var(--navy); font-weight: 600; }

/* The one number the student came for: what the lesson moved. */
.hist-bands { display: flex; align-items: baseline; gap: 10px; white-space: nowrap; }
.hist-band-pair { display: flex; align-items: baseline; gap: 6px; }
.hist-band-num { font-family: var(--font-display); font-size: 23px; color: var(--slate-500); }
.hist-band-arrow { color: var(--slate-400); font-size: 16px; }
.hist-band-final { color: var(--navy); }
.hist-band-ceiling { font-size: 12.5px; color: var(--slate-400); }

.hist-body { padding: 16px 20px 20px; }
.hist-chips { margin-bottom: 14px; }
.hist-summary-text { font-size: 15px; line-height: 1.6; color: var(--slate-600); margin: 0 0 12px; }
.hist-count {
  font-family: var(--font-mono); font-size: 12.5px; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--slate-400); margin: 0 0 10px;
}
.hist-empty { font-size: 15px; line-height: 1.6; color: var(--slate-500); margin: 0; }
.hist-note { font-size: 14px; line-height: 1.6; color: var(--slate-500); margin: 8px 0 0; }

/* ---------- whole essay, before vs after ----------
   Side by side, unlike .hist-side below: a whole essay is only useful next to
   the other version, where a changed paragraph lines up with what it replaced.
   Collapsed behind a <details> because most visits to this page are looking
   for a band or one specific fix, not 500 words of prose.

   Columns, not a table: the two essays differ in length, and a table row would
   force them to the same height and leave one side half empty. */
.hist-essays { margin: 0 0 14px; }
.hist-essays-toggle {
  cursor: pointer; list-style: none;
  font-family: var(--font-mono); font-size: 12.5px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--navy);
  padding: 6px 0;
}
.hist-essays-toggle::-webkit-details-marker { display: none; }
.hist-essays-toggle::before { content: "▸ "; display: inline-block; }
.hist-essays[open] .hist-essays-toggle::before { content: "▾ "; }

.hist-essays-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
  margin-top: 10px; align-items: start;
}
/* Two ~45-character columns on a phone read worse than scrolling one, so the
   pair stacks — nộp trước, cuối sau, the same order as the columns. */
@media (max-width: 720px) {
  .hist-essays-grid { grid-template-columns: 1fr; }
}

.hist-essay {
  border: 1px solid var(--slate-200); border-radius: 8px;
  border-top-width: 3px; padding: 10px 12px 12px;
  min-width: 0; /* let long words wrap instead of stretching the column */
}
.hist-essay-before { border-top-color: var(--crimson); }
.hist-essay-after { border-top-color: #16a34a; }
.hist-essay-text {
  margin: 0; font-size: 15px; line-height: 1.7; color: var(--slate-800);
  white-space: pre-wrap; overflow-wrap: break-word;
  /* Long essays get their own scroll rather than pushing the fix list far
     down the card; the two columns cap at the same height so they stay
     comparable side by side. */
  max-height: 460px; overflow-y: auto;
}

.hist-fix {
  border: 1px solid var(--slate-200); border-radius: 8px;
  padding: 12px 14px; margin-bottom: 10px;
}
.hist-fix-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.hist-fix-num { font-family: var(--font-mono); font-size: 12.5px; color: var(--slate-400); }
.hist-fix-crit {
  font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--navy);
}
.hist-fix-mode, .hist-fix-delta {
  font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  border-radius: 999px; padding: 2px 8px;
}
.hist-fix-mode { color: var(--slate-500); background: var(--slate-100); }
.hist-fix-delta { color: #166534; background: #dcfce7; }

/* Before/after stacked, not side by side: these are whole paragraphs, and two
   narrow columns of prose are harder to compare than two full-width blocks. */
.hist-side { border-left: 3px solid var(--slate-200); padding: 2px 0 2px 10px; }
.hist-side + .hist-side { margin-top: 8px; }
.hist-before { border-left-color: var(--crimson); }
.hist-after { border-left-color: #16a34a; }
.hist-side-label {
  display: block; font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--slate-400);
  margin-bottom: 3px;
}
.hist-side-text { margin: 0; font-size: 15px; line-height: 1.65; color: var(--slate-800); white-space: pre-wrap; }
