/* Public numerology calculator — shared by the English page at
   /numerology-calculator/ and the Chinese page at
   /zh/numerology-calculator/.

   REWRITTEN 2026-09-20 to Design1.pdf section 5a (and the phone stack
   in 6a): tool first, one result panel, one CTA, phone capture demoted
   to an opt-in link.

   Loads AFTER /style.css, so it inherits the public site's tokens
   (--color-accent, --font-body, --font-mono, the 4px spacing scale)
   rather than redefining them. If a value here looks like a brand
   colour, it is wrong and belongs in /style.css instead. */

.calc-wrap {
  margin: 0 auto;
  max-width: 1240px;
  padding: var(--space-56) var(--space-24) var(--space-112);
}

/* Input left, answer right. One screen: the two numbers a reader is
   meant to compare are never a scroll apart from the field that
   produced them.

   The opt-in is a THIRD grid child rather than a node inside either
   column, and that is load-bearing. On a wide screen it is placed
   under the input, which is where it was asked for. When the columns
   stack it falls last of the three — after the result — because on a
   phone the script scrolls the result into view on submit, so an
   opt-in sitting above it would be scrolled straight past, and the ask
   would land before the reader had seen their own numbers. */
.calc-grid {
  align-items: start;
  display: grid;
  gap: clamp(var(--space-32), 5vw, var(--space-72));
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  /* Row 1 is the input, row 2 absorbs whatever the result column has
     left over. Without the explicit 1fr both rows are `auto`, and the
     spanning result column distributes its own height EQUALLY between
     them — which pushed the opt-in a thousand pixels below the button
     it belongs under. */
  grid-template-rows: auto 1fr;
}

.calc-input { grid-column: 1; grid-row: 1; }
.calc-output { grid-column: 2; grid-row: 1 / -1; }
.calc-grid > .optin-form { grid-column: 1; grid-row: 2; align-self: start; }

.calc-eyebrow {
  color: var(--color-accent);
  font: 400 0.8125rem/1.4 var(--font-mono);
  letter-spacing: 0.14em;
  margin: 0 0 var(--space-16);
  text-transform: uppercase;
}
.calc-eyebrow a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }

.calc-wrap h1 {
  font-size: clamp(2.25rem, 1.7rem + 2.2vw, 3.5rem);
  font-weight: 480;
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0;
}

.calc-lede {
  color: var(--color-ink-soft);
  font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  line-height: 1.6;
  margin: var(--space-16) 0 0;
  max-width: 44ch;
}

/* ---- name, then the date ------------------------------------- */

.calc-form { margin: var(--space-32) 0 0; }

/* Same box as a date field so the two read as one form rather than an
   input bolted above a fieldset. Full width, because a name is not
   three digits. */
.calc-name {
  background: var(--color-surface-2);
  border: 1px solid rgba(255, 60, 74, 0.4);
  border-radius: var(--radius-cards);
  display: flex;
  flex-direction: column;
  margin-bottom: var(--space-20);
  transition: border-color 160ms ease, background 160ms ease;
}
.calc-name:hover { border-color: rgba(255, 60, 74, 0.65); }
.calc-name:focus-within { background: #241010; border-color: var(--color-accent); }

.calc-name label {
  align-items: baseline;
  color: var(--color-ink-soft);
  display: flex;
  font: 400 0.6875rem/1 var(--font-mono);
  gap: var(--space-8);
  letter-spacing: 0.12em;
  padding: var(--space-12) var(--space-16) 0;
  text-transform: uppercase;
}
/* "optional" is a fact about the field, not a second label, so it sits
   quieter and never competes with the field name. */
.calc-name label span {
  color: rgba(201, 192, 193, 0.5);
  letter-spacing: 0.06em;
  text-transform: lowercase;
}

.calc-name input {
  background: none;
  border: 0;
  color: var(--color-ink);
  font: 400 1.125rem/1 var(--font-body);
  min-height: 44px;
  padding: var(--space-8) var(--space-16) var(--space-12);
  width: 100%;
}
.calc-name input:focus { outline: none; }

.dob-set { border: 0; margin: 0; padding: 0; }

.dob-set legend {
  color: var(--color-white);
  font: 480 1.0625rem/1.3 var(--font-body);
  letter-spacing: -0.01em;
  margin-bottom: var(--space-12);
  padding: 0;
}

.dob-fields {
  display: grid;
  gap: var(--space-12);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.dob-field { display: flex; flex-direction: column; }

.dob-field label {
  color: var(--color-ink-soft);
  font: 400 0.6875rem/1 var(--font-mono);
  letter-spacing: 0.12em;
  padding: var(--space-12) var(--space-16) 0;
  text-transform: uppercase;
}

/* The label sits inside the field's own box, so the three inputs read
   as one control rather than three unrelated boxes. */
.dob-field {
  background: var(--color-surface-2);
  border: 1px solid rgba(255, 60, 74, 0.4);
  border-radius: var(--radius-cards);
  transition: border-color 160ms ease, background 160ms ease;
}
.dob-field:hover { border-color: rgba(255, 60, 74, 0.65); }
.dob-field:focus-within { background: #241010; border-color: var(--color-accent); }

.dob-field input {
  background: none;
  border: 0;
  color: var(--color-ink);
  font: 480 1.5rem/1 var(--font-body);
  min-height: 44px;
  padding: var(--space-8) var(--space-16) var(--space-12);
  width: 100%;
}
.dob-field input:focus { outline: none; }
.dob-field input::placeholder { color: rgba(201, 192, 193, 0.35); }

.calc-submit {
  background: var(--color-accent);
  border: none;
  border-radius: var(--radius-buttons);
  color: var(--color-white);
  cursor: pointer;
  font: 400 1rem var(--font-body);
  margin-top: var(--space-16);
  min-height: 52px;
  transition: filter 0.15s ease, transform 0.15s ease;
  width: 100%;
}
.calc-submit:hover { filter: brightness(0.9); }
.calc-submit:active { transform: translateY(1px); }
.calc-submit:focus-visible { outline: 2px solid var(--color-white); outline-offset: 3px; }

.calc-error {
  color: var(--color-accent);
  margin: var(--space-12) 0 0;
}

/* Sits directly under the field it describes rather than being folded
   into one summary at the bottom of the form. */
.calc-field-error {
  color: var(--color-accent);
  font-size: 0.875rem;
  margin: calc(var(--space-16) * -1) 0 var(--space-20);
}


.calc-more {
  border-top: 1px solid rgba(255, 60, 74, 0.14);
  color: var(--color-ink-soft);
  font-size: 0.9375rem;
  line-height: 1.6;
  margin: var(--space-32) 0 0;
  padding-top: var(--space-24);
}
.calc-more a { color: var(--color-accent); }

/* ---- the answer ---------------------------------------------- */

.calc-output:focus { outline: none; }
/* The sticky header would otherwise cover the two numbers when the
   phone scrolls the result into view. */
.calc-output { scroll-margin-top: 88px; }

/* The empty state. It is the first thing in the result column, so the
   column never reads as two dashes and a wall of static explanation. */
.calc-prompt {
  border: 1px dashed rgba(255, 60, 74, 0.35);
  border-radius: var(--radius-cards);
  color: var(--color-ink-soft);
  line-height: 1.6;
  margin: 0 0 var(--space-32);
  padding: var(--space-20) var(--space-24);
}
.calc-prompt strong { color: var(--color-ink); font-weight: 480; }
.calc-output.has-result .calc-prompt { display: none; }

.heads {
  display: grid;
  gap: var(--space-40);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.head-label {
  color: var(--color-ink-soft);
  font: 400 0.75rem/1 var(--font-mono);
  letter-spacing: 0.14em;
  margin: 0;
  text-transform: uppercase;
}

/* The root number is the one the page is found for, so it carries the
   accent; the day master stays white. Two red numerals side by side
   would say they are the same kind of answer, and the whole point of
   the pair is that they are not. */
.head-value {
  color: var(--color-white);
  font: 480 clamp(3.5rem, 2.5rem + 4vw, 5.5rem)/1 var(--font-body);
  letter-spacing: -0.03em;
  margin: var(--space-12) 0 var(--space-16);
}
.head-value-root { color: var(--color-accent); }

/* The state before anything is calculated. An em dash at 5.5rem reads
   as a drawn rule, not as "no value yet", so the placeholder is set
   small and dim — the "read for" copy underneath is the real content of
   the empty state and should be what the eye lands on. */
.head-value.is-empty { color: rgba(201, 192, 193, 0.3); font-size: 2.5rem; }

/* "How to read these two numbers" — the generic explanation, moved out
   from under each number and placed after the arithmetic. What sits
   under a number now is that DIGIT's own trait list; a paragraph that
   reads identically for all nine digits is background, and background
   goes after the mechanics. */
.reading-guide {
  border-top: 1px solid rgba(255, 60, 74, 0.14);
  margin-top: clamp(var(--space-32), 4vw, var(--space-56));
  padding-top: clamp(var(--space-24), 3vw, var(--space-40));
}

.reading-guide > h2 {
  color: var(--color-white);
  font-size: 1.25rem;
  font-weight: 480;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-24);
}

.reading-guide-grid {
  display: grid;
  gap: var(--space-32);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.reading-guide h3 {
  color: var(--color-accent-soft);
  font: 400 0.75rem/1.3 var(--font-mono);
  letter-spacing: 0.12em;
  margin: 0 0 var(--space-12);
  text-transform: uppercase;
}

.reading-guide p {
  color: var(--color-ink-soft);
  font-size: 0.9375rem;
  line-height: 1.65;
  margin: 0 0 var(--space-12);
}
.reading-guide p:last-child { margin-bottom: 0; }
.reading-guide strong { color: var(--color-ink); font-weight: 480; }

/* ---- the per-digit reading ----------------------------------- */

/* The trait list that belongs to THIS digit, from the 数字阴阳能量
   sheet (see script.js's header for what is and is not published).
   Deliberately not a card: it is the second half of the explanation
   above it, so it keeps the same column and is separated by space and
   a rule rather than by a box. */
.traits {
  border-top: 1px solid rgba(255, 60, 74, 0.2);
  margin-top: var(--space-16);
  padding-top: var(--space-16);
}

.traits-head {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin: 0 0 var(--space-12);
}

.traits-head b {
  color: var(--color-white);
  font: 520 0.9375rem/1.3 var(--font-body);
  letter-spacing: 0;
}

.traits-head span {
  color: var(--color-ink-soft);
  font: 400 0.6875rem/1.3 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.traits-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  list-style: none;
  margin: 0;
  padding: 0;
}

.traits-list li {
  background: rgba(255, 60, 74, 0.07);
  border: 1px solid rgba(255, 60, 74, 0.2);
  border-radius: var(--radius-cards-inner);
  display: grid;
  gap: 2px;
  padding: var(--space-8) var(--space-12);
}

.traits-cn { color: var(--color-ink); font-size: 0.9375rem; line-height: 1.35; }
.traits-en { color: var(--color-ink-soft); font-size: 0.75rem; line-height: 1.35; }

/* The 日主 pole is the shadow reading, so its chips sit quieter than
   the 根数 ones rather than shouting the same crimson. */
#master-traits .traits-list li {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.12);
}

/* ---- the arithmetic, as an open cascade ---------------------- */

.cascade { margin-top: clamp(var(--space-32), 4vw, var(--space-56)); }

.cascade summary {
  color: var(--color-ink-soft);
  cursor: pointer;
  font: 400 0.75rem/1.4 var(--font-mono);
  letter-spacing: 0.14em;
  list-style: none;
  padding: var(--space-12) 0;
  text-transform: uppercase;
}
.cascade summary::-webkit-details-marker { display: none; }
.cascade summary::after {
  border-bottom: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  content: "";
  display: inline-block;
  height: 6px;
  margin-left: var(--space-12);
  transform: rotate(45deg) translateY(-2px);
  width: 6px;
}
.cascade[open] summary::after { transform: rotate(-135deg) translateY(-2px); }
.cascade summary:hover { color: var(--color-ink); }
.cascade summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.cascade-body { padding-bottom: var(--space-8); }

/* No boxes. A row of numerals with their labels under them, then the
   two sums, then the apex — the shape of the arithmetic is the
   diagram, and seven bordered boxes inside a bordered box was not. */
.cascade-row {
  display: grid;
  gap: var(--space-16);
  justify-items: center;
  padding-bottom: var(--space-20);
  position: relative;
  text-align: center;
}
.cascade-row-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.cascade-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-inline: auto; max-width: 60%; }
.cascade-row-1 { grid-template-columns: 1fr; padding-bottom: 0; }

.cascade-row + .cascade-row { border-top: 1px solid rgba(255, 60, 74, 0.14); padding-top: var(--space-20); }

.cascade-n {
  color: var(--color-ink);
  display: block;
  font: 480 1.75rem/1 var(--font-body);
}
.cascade-row-1 .cascade-n { color: var(--color-accent); font-size: 2.5rem; }

.cascade-cap {
  color: var(--color-ink-soft);
  display: block;
  font: 400 0.6875rem/1.3 var(--font-mono);
  letter-spacing: 0.08em;
  margin-top: var(--space-8);
}

/* ---- the one CTA --------------------------------------------- */

.gate {
  align-items: center;
  background: linear-gradient(135deg, rgba(255, 60, 74, 0.16), rgba(21, 9, 9, 0.9));
  border-radius: var(--radius-cards);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-24);
  justify-content: space-between;
  margin-top: clamp(var(--space-32), 4vw, var(--space-56));
  padding: var(--space-32);
}

.gate-body { flex: 1 1 260px; }

.gate h2 {
  color: var(--color-white);
  font-size: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
  font-weight: 520;
  letter-spacing: -0.01em;
  line-height: 1.3;
  margin: 0 0 var(--space-8);
}

.gate p { color: var(--color-ink-soft); line-height: 1.6; margin: 0; }

.gate-link {
  align-items: center;
  background: var(--color-accent);
  border-radius: var(--radius-buttons);
  color: var(--color-white);
  display: inline-flex;
  flex: 0 0 auto;
  font: 400 1rem var(--font-body);
  justify-content: center;
  min-height: 52px;
  padding: 0 var(--space-32);
}
.gate-link:hover { filter: brightness(0.9); text-decoration: none; }
.gate-link:focus-visible { outline: 2px solid var(--color-white); outline-offset: 3px; }

/* ---- the opt-in, mounted with the result ---------------------- */

.optin-form {
  background: var(--color-card);
  border: 1px solid rgba(255, 60, 74, 0.18);
  border-radius: var(--radius-cards);
  margin-top: var(--space-24);
  padding: var(--space-24);
}

.optin-title {
  color: var(--color-white);
  font-size: 1.0625rem;
  font-weight: 520;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-8);
}

.optin-intro {
  color: var(--color-ink-soft);
  font-size: 0.875rem;
  line-height: 1.6;
  margin: 0 0 var(--space-20);
}

/* One field per row. The card sits in the input column now, which is
   about 500px wide, and three controls abreast in that space gives a
   name box too narrow to read what you typed. The country code stays
   welded to the number rather than floating as a third field, because
   it is part of one value. */
.optin-row {
  display: grid;
  gap: var(--space-16);
  grid-template-columns: 1fr;
}

.optin-field { display: grid; gap: var(--space-8); min-width: 0; }

.optin-field label {
  color: var(--color-ink-soft);
  font: 400 0.6875rem/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.optin-phone { display: grid; gap: var(--space-8); grid-template-columns: minmax(92px, auto) minmax(0, 1fr); }

.optin-form input,
.optin-form select {
  background: var(--color-surface-2);
  border: 1px solid rgba(255, 60, 74, 0.22);
  border-radius: var(--radius-inputs);
  color: var(--color-ink);
  font: inherit;
  min-height: 48px;
  min-width: 0;
  padding: 0 var(--space-16);
}
.optin-form select { padding-inline: var(--space-12); }
.optin-form input::placeholder { color: rgba(201, 192, 193, 0.45); }
.optin-form input:focus-visible,
.optin-form select:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.optin-send {
  background: var(--color-accent);
  width: 100%;
  border: none;
  border-radius: var(--radius-buttons);
  color: var(--color-white);
  cursor: pointer;
  font: 400 1rem var(--font-body);
  min-height: 48px;
  padding: 0 var(--space-32);
  transition: filter 0.15s ease, transform 0.15s ease;
}
.optin-send:hover { filter: brightness(0.9); }
.optin-send:active { transform: translateY(1px); }
.optin-send[disabled] { cursor: progress; opacity: 0.6; }
.optin-send:focus-visible { outline: 2px solid var(--color-white); outline-offset: 3px; }

/* The promise the whole page rests on, repeated at the one place
   anything is actually transmitted. */
.optin-note {
  color: var(--color-ink-soft);
  font-size: 0.8125rem;
  line-height: 1.5;
  margin: var(--space-16) 0 0;
}

.optin-status { color: var(--color-accent-soft); margin: var(--space-12) 0 0; min-height: 1.2em; }
.optin-done { color: var(--color-ink); margin: 0; }

/* Honeypot: off-screen rather than display:none, because some bots skip
   fields they can see are hidden. */
.optin-hp {
  height: 1px;
  left: -9999px;
  overflow: hidden;
  position: absolute;
  width: 1px;
}

.sr-only {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* ---- questions ------------------------------------------------ */

/* Full width, on the same spine as the homepage's sections: the
   heading on the left, the accordion filling the rest. A 760px column
   inside a 1240px page was the same half-empty-section problem the
   homepage had. */
.calc-prose {
  align-items: start;
  border-top: 1px solid rgba(255, 60, 74, 0.14);
  display: grid;
  gap: var(--space-24) var(--space-56);
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  margin-top: clamp(var(--space-72), 8vw, var(--space-112));
  padding-top: clamp(var(--space-40), 5vw, var(--space-56));
}

.faq-head {
  color: var(--color-white);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
  font-weight: 480;
  letter-spacing: -0.015em;
  margin: 0;
}

.calc-faq-list { min-width: 0; }

.calc-faq { border-top: 1px solid rgba(255, 60, 74, 0.14); }

.calc-faq summary {
  align-items: center;
  color: var(--color-ink);
  cursor: pointer;
  display: flex;
  font-size: 1rem;
  justify-content: space-between;
  list-style: none;
  padding: var(--space-20) 0;
}
.calc-faq summary::-webkit-details-marker { display: none; }
.calc-faq summary::after {
  color: var(--color-accent);
  content: "+";
  font-size: 1.25rem;
  line-height: 1;
  margin-left: var(--space-16);
}
.calc-faq[open] summary::after { content: "\2212"; }
.calc-faq summary:hover { color: var(--color-white); }
.calc-faq summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.calc-faq p {
  color: var(--color-ink-soft);
  line-height: 1.7;
  margin: 0;
  padding: 0 0 var(--space-24);
  max-width: 68ch;
}
.calc-faq a { color: var(--color-accent); }

/* ---------------------------------------------------------------
   Phone (6a) — input on the first screen, both numbers side by side,
   the cascade folded away, one CTA card.
   --------------------------------------------------------------- */

@media (max-width: 900px) {
  /* Stacked: input, then result, then the opt-in last — see the note
     on .calc-grid for why the order matters on a phone. */
  .calc-grid,
  .calc-prose,
  .reading-guide-grid { grid-template-columns: 1fr; }
  .calc-grid { grid-template-rows: none; }
  .calc-input,
  .calc-output,
  .calc-grid > .optin-form { grid-column: 1; grid-row: auto; }
  .calc-wrap { padding-inline: var(--space-16); }
  .calc-output { margin-top: var(--space-16); }
  .cascade-row-2 { max-width: 80%; }
  .gate { flex-direction: column; align-items: stretch; padding: var(--space-24); }
  .gate-body { flex: 0 0 auto; }
  .gate-link { width: 100%; }
}

@media (max-width: 560px) {
  /* Both numbers stay side by side — they are a comparison, and
     stacking them is exactly the "never shares a screen" problem this
     redesign removes. */
  .heads { gap: var(--space-20); }
  /* "ROOT NUMBER · LIFE PATH" wraps to two lines in a 150px column
     while "DAY MASTER" stays on one, which sets the two numbers at
     different heights. The phone label is the short form. */
  .head-label-long { display: none; }
  .head-label { font-size: 0.6875rem; letter-spacing: 0.1em; }
  .head-value { font-size: clamp(2.75rem, 2rem + 6vw, 3.5rem); }
  .traits-cn { font-size: 0.875rem; }
  .dob-field input { font-size: 1.25rem; }
}

@media (prefers-reduced-motion: reduce) {
  .calc-submit, .gate-link, .optin-send { transition: none; }
}

/* ---- read next ------------------------------------------------- */

/* The calculator is the entry point for the whole numerology cluster,
   and every article in it links back here — but until 2026-09-23 this
   page linked out to exactly one of them, so the tool kept whatever
   authority it earned instead of passing any of it on. These are the
   other direction.

   The covers come from /blog-image/<id>, the same route the blog itself
   serves them from, rather than a second copy in /assets/. The ids are
   hardcoded because this page is static and cannot read D1 — that is
   safe rather than fragile, because api/blog-images refuses to delete an
   image a post still uses, so an id in use cannot be recycled. If a post
   is ever given a NEW cover, update the id here too. */
.calc-readnext { margin-top: var(--space-56); }
.calc-readnext ul { list-style: none; margin: var(--space-24) 0 0; padding: 0; }
.calc-readnext li + li { margin-top: var(--space-24); }

.calc-readnext-item {
  align-items: start;
  display: grid;
  gap: var(--space-20);
  grid-template-columns: 148px minmax(0, 1fr);
  text-decoration: none;
}
/* width/height are on the <img> too: the box is reserved before the
   bytes land, so the list does not jump as three covers decode. */
.calc-readnext-thumb {
  aspect-ratio: 16 / 9;
  background: rgba(255, 60, 74, 0.06);
  border: 1px solid rgba(255, 60, 74, 0.16);
  border-radius: 10px;
  display: block;
  height: auto;
  object-fit: cover;
  width: 100%;
}
.calc-readnext-title {
  color: var(--color-accent);
  display: block;
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.35;
}
.calc-readnext-item:hover .calc-readnext-title,
.calc-readnext-item:focus-visible .calc-readnext-title { text-decoration: underline; }
.calc-readnext-item:hover .calc-readnext-thumb,
.calc-readnext-item:focus-visible .calc-readnext-thumb { border-color: rgba(255, 60, 74, 0.4); }
.calc-readnext-desc {
  color: var(--color-ink-soft);
  display: block;
  font-size: 0.9375rem;
  line-height: 1.6;
  margin-top: var(--space-8);
}

/* On a phone the two-column version leaves a 104px cover beside four or
   five lines of wrapped text, so most of the left column is empty and
   the picture is too small to be the reason anyone taps. Stack instead:
   cover full width above its own title, which is what the blog's own
   cards do and what makes the cover worth having here at all. */
@media (max-width: 560px) {
  .calc-readnext-item { gap: var(--space-12); grid-template-columns: minmax(0, 1fr); }
  .calc-readnext-desc { font-size: 0.875rem; }
}
