/* Smartycash VN — layout + components.
   Design tokens live in tokens.css and are never redefined here. */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body { background: var(--paper); }

img { max-width: 100%; display: block; }
button { font-family: inherit; cursor: pointer; }
a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; }

/* Header height, needed in two places: the header itself and the retention bar
   that hangs directly below it. A literal in both is how they drift apart. */
:root { --sc-hdr-h: 72px; }



/* ---- Layout ---- */
.wrap { max-width: 1200px; margin: 0 auto; padding: 0 var(--space-6); }
.section { padding: var(--space-20) 0; }
.section.tinted { background: var(--bg-section); }

/* Skip link — visible only on keyboard focus. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 400;
  background: var(--brand-ink); color: var(--fg-on-dark);
  padding: var(--space-3) var(--space-5); border-radius: 0 0 var(--radius-md) 0;
  font-size: var(--fs-sm); font-weight: 700;
}
.skip-link:focus { left: 0; text-decoration: none; }

/* ---- Logo wordmark ---- */
.sc-logo {
  font-family: var(--font-display); font-weight: 800; font-size: 22px;
  letter-spacing: -0.03em; color: var(--brand-ink);
  display: inline-flex; align-items: baseline;
}
.sc-logo:hover { text-decoration: none; }
.sc-logo-accent { color: var(--brand-green); }
.sc-logo-pip {
  width: 7px; height: 7px; border-radius: var(--radius-pill);
  background: var(--brand-green); margin-left: 3px;
  align-self: flex-start; margin-top: 2px;
}

/* ---- Header ---- */
.hdr {
  position: sticky; top: 0; z-index: 50;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--ink-100);
}
.hdr-inner {
  display: flex; align-items: center; justify-content: space-between;
  height: var(--sc-hdr-h); gap: var(--space-6);
}
.hdr-nav { display: flex; gap: var(--space-8); }
/* 600, not 500: the 500 weight was dropped from the shipped font set —
   one nav rule did not justify another 19 KB face. */
.hdr-nav a { font-size: var(--fs-sm); font-weight: 600; color: var(--fg-2); }
.hdr-nav a:hover { color: var(--brand-ink); text-decoration: none; }
.hdr-right { display: flex; align-items: center; gap: var(--space-3); }

/* ---- Buttons ---- */
.btn {
  font-family: var(--font-sans); font-weight: 700; font-size: var(--fs-sm);
  border: 0; display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-4) var(--space-6); border-radius: var(--radius-pill);
  transition: all 120ms cubic-bezier(.2, .7, .2, 1);
  white-space: nowrap;
}
.btn:hover { text-decoration: none; }
/* Ink on green, not white on green. White on --brand-green is 2.3:1 and fails
   WCAG AA (4.5:1); ink gives 7.4:1 and pairs two brand colours. Still 5.1:1
   against the darker --brand-green-600 hover, so the hover state passes too.
   Set here rather than in tokens.css, which stays a verbatim copy of the
   design system. */
.btn-primary { background: var(--brand-green); color: var(--brand-ink); box-shadow: var(--shadow-cta); }
.btn-primary:hover { background: var(--brand-green-600); transform: translateY(-1px); }
.btn-primary:active { transform: translateY(0); box-shadow: var(--shadow-xs); }
.btn-ghost { background: transparent; color: var(--brand-ink); border: 1.5px solid var(--ink-300); }
.btn-ghost:hover { border-color: var(--brand-ink); background: var(--ink-50); }
.btn-lg { padding: var(--space-5) var(--space-8); font-size: var(--fs-body); }

:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-sm); }

/* ---- Offer grid ----
   ONE column on mobile is a hard requirement, not a preference: at two
   columns the longest row label wraps to three lines and the cards fall apart.
   Measured on UA's statutory APR label, which was the longest string any locale
   has put here; Vietnamese is shorter but the rule is about the worst case, and
   the worst case comes back the moment a disclosure label does.
   There is deliberately no 2-column breakpoint. */
.sc-offer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);

  /* Reserve a viewport's worth of height so the footer never sits inside the
     initial viewport.
     CLS only counts shifts of elements that are visible, so when three
     skeletons left the page short enough for the footer to be above the fold,
     the 19 real cards arriving pushed it down and the footer alone accounted
     for the entire 0.68 — the cards contributed nothing.
     Why exactly 100vh and not less: with C px of content above the grid and a
     viewport of H, the footer clears the fold only when C + k*H > H, i.e.
     k > 1 - C/H. C is ~270px, so 80vh works at H=800 but fails at H=1440.
     100vh is the only multiplier safe at every viewport height. */
  min-height: 100vh;

  /* Grid's default align-content is stretch, which would spread auto rows to
     fill that reserved height and inflate the cards. Keep rows natural and let
     the spare space fall to the bottom. */
  align-content: start;
}

/* ---- The reservation, released once there is something better to reserve ----

   100vh above is a guess made with no information, and it is the only safe
   guess there is: nothing is known about the roster until the fetch resolves,
   so the grid is padded to a viewport in BOTH states and the footer is off
   screen in both, which is what makes the transition uncountable as a shift.
   The cost is the padding. MEASURED in headless Chrome at 1440 wide against a
   3-offer roster: 504px of blank above the footer at a 900px viewport, 1044px
   at 1440px, on every visit.

   A second visit is not guessing. offer-grid.js remembers what the grid came
   to last time and reserves exactly that, inline, in px. This rule is what
   takes the viewport reservation out of its way.

   ⚠️ WHAT IT REMEMBERS IS A MEASURED HEIGHT, NOT A CARD COUNT, and the
   difference is not academic. lead-form.js mounts itself into this grid after
   the cards, so reserving for the roster alone leaves that insertion
   unreserved: measured, a count-only reservation scored CLS 0.0929 at 1440x900
   and 0.8583 at 1440x1440, against 0.0003 for the blind 100vh it replaced. The
   grid's own measured height already contains everything that mounts into it.

   ⚠️ THE ATTRIBUTE IS SET ONCE, BEFORE THE SKELETONS, AND NEVER DURING A
   RENDER. Releasing the reservation after the cards arrive would move the
   footer up into the viewport at the moment the content lands, which is the
   0.68 shift with its sign flipped — the very thing the reservation exists to
   stop. A visit either reserves for its whole life or does not reserve at all.

   Where this lands, measured on a repeat visit with the reservation applied,
   against the LIVE host at smartycash-vn.cash on 2026-10-01:

     1280x800    CLS 0        1440x900   CLS 0
     1440x1080   CLS 0        1440x1200  CLS 0
     390x844     CLS 0        1440x1440  CLS 0
     1440x1900   CLS 0

   All four pages, not just this one: / , /privacy-policy, /terms and
   /cookies-policy all score 0 at 1280x800, 1440x900, 1440x1080 and 390x844.
   First visits score 0.0003, from the header nav reflowing as the webfont
   swaps — not from this rule.

   ⚠️ THE TWO FIGURES THAT USED TO STAND HERE (0.0223 at 1440x1200 and
   0.1075 at 1440x1440) ARE GONE, AND NOT BECAUSE ANYONE FIXED THEM. They were
   a parse-time move of .section.tinted and .ftr, and that move is STILL THERE
   — it is simply below the fold now. #creditors gained real content in
   9b6745a, which lengthened the document and pushed the footer to y=2177, so
   the move no longer reaches the viewport and a shift off screen is not
   counted. That is the SAME HIDING the viewport-tall reservation used to do,
   arrived at from the other direction.

   Do not read the zeroes as the layout being settled. Drive the viewport to
   1440x2000 and it returns at 0.0057; 1440x2600 scores 0.1465 and 1440x3100
   scores 0.4536, where the shift is .ftr and #creditors entering layout at
   ~180ms together with a 7px horizontal jump of every .wrap — the page
   crossing its own scrollbar threshold, 1425px wide to 1440px. It reaches the
   viewport only above roughly 1945px of viewport HEIGHT, which no shipping
   display has. SHORTENING THIS PAGE BY ~800px WOULD MAKE IT A USER-VISIBLE
   SHIFT AGAIN, with nothing in the grid having changed. Fixing it properly is
   still a separate job on the document's own progressive layout, not on the
   grid.

   Re-measure against the live host, never wrangler dev, and never at a
   viewport the hardware cannot produce: this machine's panel caps the CSS
   viewport at 1536x730, so every figure above came from headless Chrome with
   Emulation.setDeviceMetricsOverride. */
.sc-offer-grid[data-reserve='known'] { min-height: 0; }
@media (min-width: 900px) {
  .sc-offer-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---- Page + section headings ----
   Modest on purpose: there is no hero yet, so this should read as the page's
   heading, not a bolted-on SEO title. Sizes come from the type scale in
   tokens.css; only spacing is set here. */
/* ---- Vietnamese diacritic headroom ----
   Vietnamese stacks two marks on one vowel — ế is e + circumflex + acute, ộ is
   o + circumflex + dot below, ữ is u + horn + tilde — so a line box needs room
   both above the cap height and below the baseline that Ukrainian never asked
   for. tokens.css sets h1 to line-height 1.1 and h2 to 1.15, which leaves
   essentially no half-leading: on a heading that wraps, the marks above the
   second line meet the descenders of the first.

   Corrected HERE and not in tokens.css. tokens.css is the design system, copied
   verbatim, and never gains values — the type scale, the weights and the
   tracking are all still its own. This only buys vertical room.

   ⚠️ NOT YET CONFIRMED IN A BROWSER, and it cannot be until the fonts are
   re-subsetted: the five .woff2 carry no Vietnamese precomposed glyphs at all
   (0 of U+1EA0-1EF9), so every accented character currently renders from the
   fallback stack, whose vertical metrics are not Montserrat's. Check at 360px
   after re-subsetting, and tune these two numbers then if needed. */
.sc-page-head h1,
.sc-legal-head h1 { line-height: 1.2; }
.sc-section-title { line-height: 1.25; }

.sc-page-head { padding-top: var(--space-12); }
.sc-page-head h1 { margin: 0; }
/* The heading block already provides the top gap. */
.sc-page-head + .section { padding-top: var(--space-8); }

.sc-section-title { margin: 0 0 var(--space-8); }

@media (max-width: 620px) {
  /* --fs-h1 is a fixed 44px; step both down one rung of the same scale. */
  .sc-page-head h1 { font-size: var(--fs-h2); }
  .sc-section-title { font-size: var(--fs-h3); }
}

/* ---- Offer card ----
   Flex column with the CTA pinned by margin-top:auto, so buttons line up
   across a row despite very variable content height: rrps.display runs from a
   bare upper bound to a full two-sided range, and the CRM decides which. */
.sc-offer {
  background: var(--paper);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-sm);
  transition: transform 160ms cubic-bezier(.2, .7, .2, 1), box-shadow 160ms;
}

/* Hover only where hovering is real — on touch this leaves a stuck
   transformed card after a tap. */
@media (hover: hover) {
  .sc-offer:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-md);
  }
}

/* Badge strip. Brand palette only — the partner's own label_color is ignored.
   Green for the first three positions, ink for the rest; prominence comes from
   the CRM's ranking, not from a taxonomy of our own. */
.sc-offer-badge {
  background: var(--brand-ink);
  color: var(--fg-on-dark);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: var(--space-2) var(--space-5);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Ink on green for contrast — see .btn-primary. The ink badge keeps white. */
.sc-offer-badge.is-accent { background: var(--brand-green); color: var(--brand-ink); }

.sc-offer-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-5) var(--space-2);
}

/* Fixed plate: logos arrive as svg/png/jpg/webp at unknown aspect ratios, and
   partners swap them seasonally. Reserving the box is the CLS defence. */
.sc-offer-logo {
  flex: 0 0 auto;
  width: 96px;
  height: 48px;
  border-radius: var(--radius-md);
  background: var(--ink-50);
  overflow: hidden;
}
.sc-offer-logo img {
  width: 96px;
  height: 48px;
  object-fit: contain;
  display: block;
}
.sc-offer-monogram {
  width: 96px;
  height: 48px;
  border-radius: var(--radius-md);
  background: var(--brand-ink);
  color: var(--brand-green);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-h4);
  display: flex;
  align-items: center;
  justify-content: center;
}

.sc-offer-name {
  font-family: var(--font-sans);
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--brand-ink);
  margin: 0;
  line-height: 1.25;
}

/* Label above, value below on every row. A label-left/value-right split breaks
   at 360px, where a statutory APR label alone eats most of the width. */
.sc-offer-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
}
.sc-offer-label {
  display: block;
  font-size: var(--fs-sm);
  color: var(--fg-3);
  line-height: 1.3;
}
.sc-offer-value {
  display: block;
  font-weight: 700;
  color: var(--brand-ink);
  line-height: 1.35;
  margin-top: 2px;
}

/* Mandatory disclosures: visibly clickable, deliberately subordinate to the CTA. */
.sc-offer-legal {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: 0 var(--space-5) var(--space-5);
}
.sc-offer-legal a,
.sc-offer-legal span {
  font-size: var(--fs-xs);
  color: var(--fg-3);
  line-height: 1.35;
}
.sc-offer-legal a {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.sc-offer-legal a:hover { color: var(--brand-green-700); }

.sc-offer-cta {
  margin: auto var(--space-5) var(--space-5);
  justify-content: center;
}

/* ---- Lead form ----
   A full-width grid item: at 3 columns it closes row two, at 1 column it lands
   after the sixth card. Either way it breaks up the wall of cards. */
.sc-lead {
  grid-column: 1 / -1;
  background: var(--bg-section);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius-lg);
  padding: var(--space-8);

  /* Holds the box steady when the form is replaced by the thank-you, so the
     grid does not reflow around it. Sized to the taller (form) state. */
  min-height: 340px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.sc-lead-heading {
  font-family: var(--font-sans);
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--brand-ink);
  margin: 0 0 var(--space-2);
}
.sc-lead-text {
  color: var(--fg-2);
  margin: 0 0 var(--space-5);
}

.sc-lead-form { display: flex; flex-direction: column; gap: var(--space-4); }
.sc-lead-field { display: flex; flex-direction: column; gap: var(--space-2); }
.sc-lead-label { font-size: var(--fs-sm); font-weight: 600; color: var(--fg-2); }

.sc-lead-input {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  /* >=44px touch target. */
  padding: var(--space-3) var(--space-4);
  min-height: 48px;
  border: 1.5px solid var(--ink-200);
  border-radius: var(--radius-md);
  background: var(--paper);
  color: var(--brand-ink);
  outline: none;
  width: 100%;
}
.sc-lead-input:focus { border-color: var(--brand-green); box-shadow: var(--ring-focus); }

.sc-lead-error {
  font-size: var(--fs-sm);
  color: var(--danger);
  margin: 0;
  min-height: 1.2em; /* reserved, so showing an error does not shift the button */
}
.sc-lead-error:empty { min-height: 0; }

.sc-lead-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}
.sc-lead-consent input {
  width: 22px;
  height: 22px;
  margin: 0;
  flex: 0 0 auto;
  accent-color: var(--brand-green);
}
.sc-lead-consent-label {
  font-size: var(--fs-sm);
  color: var(--fg-3);
  line-height: 1.4;
}
.sc-lead-consent-label a { color: var(--brand-green-700); text-decoration: underline; }

.sc-lead-submit { justify-content: center; min-height: 48px; }
.sc-lead-submit:disabled {
  background: var(--ink-200);
  color: var(--fg-muted);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}

.sc-lead-done { text-align: center; }

/* ---- Grid states ---- */
.sc-offer-state {
  grid-column: 1 / -1;
  text-align: center;
  padding: var(--space-16) var(--space-5);
}
.sc-offer-state h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-h4);
  color: var(--brand-ink);
  margin: 0 0 var(--space-2);
}
.sc-offer-state p {
  color: var(--fg-3);
  margin: 0 0 var(--space-5);
}

/* Skeletons reuse the real card structure so the layout does not move when
   data lands — a spinner would collapse the section and jump the page. */
.sc-offer-skeleton { pointer-events: none; }
.sc-offer-skeleton .sc-offer-badge,
.sc-offer-skeleton .sc-offer-logo,
.sc-offer-skeleton .sc-offer-bone {
  background: var(--ink-100);
  color: transparent;
  border-radius: var(--radius-sm);
  animation: scSkeletonPulse 1.4s ease-in-out infinite;
}
.sc-offer-skeleton .sc-offer-badge { border-radius: 0; }
.sc-offer-skeleton .sc-offer-bone { display: block; height: 1em; }
.sc-offer-skeleton .sc-offer-label .sc-offer-bone { width: 62%; }
.sc-offer-skeleton .sc-offer-value .sc-offer-bone { width: 84%; height: 1.2em; }
.sc-offer-skeleton .sc-offer-cta {
  height: 48px;
  background: var(--ink-100);
  box-shadow: none;
  animation: scSkeletonPulse 1.4s ease-in-out infinite;
}

@keyframes scSkeletonPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .55; }
}
@media (prefers-reduced-motion: reduce) {
  .sc-offer { transition: none; }
  .sc-offer-skeleton .sc-offer-badge,
  .sc-offer-skeleton .sc-offer-logo,
  .sc-offer-skeleton .sc-offer-bone,
  .sc-offer-skeleton .sc-offer-cta { animation: none; }
}

/* ---- Lender legal reference (the #creditors accordion) ----
   Built on native <details>/<summary>. Everything interactive here — keyboard
   operation, the open/closed state, the exclusive-accordion behaviour via the
   shared `name` attribute — belongs to the browser. There is no JS accordion
   and there must not be one. */
.sc-cred-intro {
  max-width: 68ch;
  /* .sc-section-title already supplies the gap below the h2. */
  margin: calc(-1 * var(--space-5)) 0 var(--space-8);
  color: var(--fg-2);
}

.sc-cred-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 860px;

  /* ⚠️ NO HEIGHT RESERVATION HERE, AND ITS ABSENCE IS MEASURED RATHER THAN
     ASSUMED. This carried min-height: 100vh, reserving a full viewport for a
     one-column list of collapsed summaries that cannot fill one — the same
     defect removed from .sc-offer-grid, where the equivalent reservation held
     over a thousand pixels of blank above the footer at a tall viewport.

     The removed comment justified the reservation with a roster size from
     another geo, which is the shape of staleness this file now avoids: the
     roster is request-dependent and no rule here may assume its size.

     Reserving for a late-filled container is a real technique and it may still
     be warranted for this one — it sits between the grid and the footer and
     fills on the same event. But the reservation strategy for the grid was
     first chosen by reasoning, measured at CLS 0.858, and replaced by one found
     through measurement. So this ships with none, the shift is measured across
     the viewport matrix, and a reservation — if the numbers ask for one — is
     chosen from those numbers and lands on its own. */
}

.sc-cred {
  border: 1px solid var(--ink-200);
  border-radius: var(--radius-md);
  background: var(--paper);
  /* No overflow:hidden. The global :focus-visible ring is a box-shadow, and a
     clipping ancestor would swallow it on every summary. */
}
.sc-cred[open] { box-shadow: var(--shadow-sm); }

.sc-cred summary {
  cursor: pointer;
  padding: var(--space-4) var(--space-5);
  /* >=44px tap target at 360px, where the name and entity stack. */
  min-height: 56px;

  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-1) var(--space-4);

  /* Both are needed to drop the default disclosure triangle: the standard
     property, and the WebKit pseudo-element for older Safari. */
  list-style: none;
}
.sc-cred summary::-webkit-details-marker { display: none; }
.sc-cred summary:hover .sc-cred-name { color: var(--brand-green-700); }

/* Chevron as a pseudo-element rather than a node: it is pure decoration and
   this keeps it out of the accessibility tree without an aria-hidden span. */
.sc-cred summary::after {
  content: '';
  grid-column: 2;
  grid-row: 1 / -1;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--fg-3);
  border-bottom: 2px solid var(--fg-3);
  transform: rotate(45deg);
  transform-origin: 60% 60%;
  transition: transform 160ms cubic-bezier(.2, .7, .2, 1);
}
.sc-cred[open] summary::after { transform: rotate(-135deg); }

/* Overrides the 24px h3 from tokens.css — this is a list row, not a heading
   users are meant to scan at heading size. */
.sc-cred-name {
  grid-column: 1;
  grid-row: 1;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--brand-ink);
  line-height: 1.3;
  margin: 0;
}
.sc-cred-entity {
  grid-column: 1;
  grid-row: 2;
  font-size: var(--fs-sm);
  color: var(--fg-3);
  line-height: 1.35;
  min-width: 0;
}

.sc-cred-body {
  border-top: 1px solid var(--ink-100);
  padding: var(--space-4) var(--space-5) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* Stacked by default; label/value columns only where there is room for them. */
.sc-cred-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2px;
  font-size: var(--fs-sm);
}
.sc-cred-label { color: var(--fg-3); font-weight: 600; }
.sc-cred-value {
  color: var(--fg-2);
  line-height: 1.5;
  /* Addresses and worked cost examples are long single strings; without this a
     360px viewport gets a horizontal scrollbar. */
  overflow-wrap: anywhere;
}
.sc-cred-value a {
  color: var(--brand-green-700);
  text-decoration: underline;
  text-underline-offset: 2px;
}

@media (min-width: 900px) {
  /* Name left, legal entity right — the entity is the thing that appears
     nowhere else on the page. */
  .sc-cred summary { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; }
  .sc-cred-entity { grid-column: 2; grid-row: 1; }
  .sc-cred summary::after { grid-column: 3; grid-row: 1; }

  .sc-cred-row { grid-template-columns: 160px 1fr; gap: var(--space-4); }
}

/* Kept here rather than in the block above, which sits earlier in the file:
   at equal specificity the later rule wins, so an earlier `transition: none`
   would be silently overridden by the chevron's own rule. */
@media (prefers-reduced-motion: reduce) {
  .sc-cred summary::after { transition: none; }
}

/* ---- Retention: sticky offer bar ----
   Fixed, never in flow. That is the CLS answer: the bar cannot push content
   because it never occupies any, and the slide is a transform rather than an
   animated height. z-index sits below the header's 50 so it can never cover
   the logo, and top hangs it off the header rather than over it. */
.sc-rt-bar {
  position: fixed;
  top: var(--sc-hdr-h);
  left: 0;
  right: 0;
  z-index: 40;
  background: var(--brand-ink);
  color: var(--fg-on-dark);
  box-shadow: var(--shadow-md);
  transform: translateY(-100%);
  transition: transform 240ms cubic-bezier(.2, .7, .2, 1);
}
.sc-rt-bar.is-open { transform: translateY(0); }
.sc-rt-bar[hidden] { display: none; }

/* TWO ROWS BY DEFAULT — text on its own line, the two buttons below it.
   Single-row was a desktop layout applied everywhere: at 360px the CTA and the
   dismiss control left about 90px for the text, which truncated offers to
   a lender name plus one truncated word. Shrinking the font would have kept the
   same broken shape with
   smaller letters; the text needs the full width, so it gets its own row.
   Height is irrelevant to CLS here — the bar is fixed and occupies no flow. */
.sc-rt-bar-inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2) var(--space-3);
  align-items: center;
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
}

/* Full width, and allowed to WRAP to two lines rather than truncate.
   Both halves of the string come from the CRM — lender display names and
   amountMax.display — so no amount of copy tightening can guarantee a fit at
   360px. A name followed by a single truncated character is worse than two
   lines. Two is the cap: a third
   line turns the strip into a panel. */
.sc-rt-bar-text {
  grid-column: 1 / -1;
  min-width: 0;
  font-size: var(--fs-sm);
  line-height: 1.35;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  /* A single unbroken token (a long lender name) must still not scroll the
     page sideways. */
  overflow-wrap: anywhere;
}

/* Two equal full-width buttons. Both are real targets — the dismiss is no
   longer a small × competing with a large CTA. */
.sc-rt-bar-cta,
.sc-rt-bar-close {
  min-height: 44px;
  width: 100%;
  min-width: 0;
  justify-content: center;
  padding: var(--space-2) var(--space-4);
  font-size: var(--fs-sm);
  /* The CRM supplies the CTA label — "Nhận tiền" on the VN roster, and it is
     the CRM's string, not ours; .btn sets nowrap, so a
     long one ellipsises inside its half rather than overflowing the bar. */
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-flex;
}
.sc-rt-bar-cta { grid-column: 1; box-shadow: none; }

.sc-rt-bar-close {
  grid-column: 2;
  background: transparent;
  border: 1.5px solid rgba(255, 255, 255, 0.35);
  color: var(--fg-on-dark);
}
.sc-rt-bar-close:hover { border-color: var(--fg-on-dark); background: rgba(255, 255, 255, 0.1); }

@media (min-width: 900px) {
  /* One row where there is room for one. 56px is the cap: a taller strip stops
     reading as a strip. */
  .sc-rt-bar-inner {
    display: flex;
    gap: var(--space-4);
    height: 56px;
    padding-top: 0;
    padding-bottom: 0;
  }
  /* One line again — the 56px row has no space for two, and at this width
     there is room for the whole string anyway. */
  .sc-rt-bar-text { flex: 1 1 auto; -webkit-line-clamp: 1; line-clamp: 1; }
  .sc-rt-bar-cta,
  .sc-rt-bar-close { flex: 0 0 auto; width: auto; padding: var(--space-2) var(--space-5); }
}

/* Kept beside the rules it overrides — at equal specificity the later one wins,
   so an earlier block would be silently inert. */
@media (prefers-reduced-motion: reduce) {
  .sc-rt-bar { transition: none; }
}

/* ---- Legal pages (/privacy-policy, /cookies-policy, /terms, /unsubscribe) ----
   These four pages change exactly one thing about the shell: the content
   column. Header, footer, type scale and colours are shared with index.html and
   must stay shared — a legal page that looks like a different site reads as one
   somebody else wrote.

   1200px of legal text is ~150 characters a line and genuinely unreadable;
   760px lands at roughly 90, which is as wide as this type wants to go. */
.wrap-narrow { max-width: 760px; }

/* ⚠️ THE CLAMPS ARE NOT DECORATION. --fs-h1 is a fixed 44px, and
   a single long heading word at 44px runs ~380px — wider than a 360px viewport's
   content box, which is a horizontal scrollbar on the page the compliance
   check looks at first. index.html never hit this because its longest heading
   word is six characters. overflow-wrap is the second line of defence for a
   word no clamp can save. */
.sc-legal-head { padding-top: var(--space-12); }
.sc-legal-head h1 {
  margin: 0;
  font-size: clamp(28px, 6.4vw, var(--fs-h1));
  overflow-wrap: break-word;
}
.sc-legal-updated { margin: var(--space-3) 0 0; font-size: var(--fs-sm); color: var(--fg-3); }
.sc-legal-head + .section { padding-top: var(--space-8); }

.sc-legal h2 {
  margin: var(--space-12) 0 var(--space-4);
  font-size: clamp(22px, 4.6vw, var(--fs-h2));
  overflow-wrap: break-word;
}
.sc-legal h2:first-child { margin-top: 0; }
.sc-legal h3 { margin: var(--space-8) 0 var(--space-3); font-size: var(--fs-h4); }
.sc-legal p { margin: 0 0 var(--space-4); }
.sc-legal ul { margin: 0 0 var(--space-4); padding-left: var(--space-6); }
.sc-legal li { margin-bottom: var(--space-2); color: var(--fg-2); line-height: 1.6; }
/* main.css resets `a` to inherit + no underline, which is right for the nav and
   the cards and wrong here: a link buried in a wall of legal text has to be
   visible without hovering it. */
.sc-legal a { color: var(--brand-green-700); text-decoration: underline; text-underline-offset: 2px; }

/* Controller details — partials/controller.html, included by all four pages. */
.sc-legal-controller {
  margin: 0 0 var(--space-6);
  padding: var(--space-5);
  background: var(--bg-section);
  border-radius: var(--radius-md);
}
.sc-legal-controller-title { margin: 0 0 var(--space-4); font-weight: 700; color: var(--fg-1); }
.sc-legal-defs { margin: 0; display: grid; grid-template-columns: 1fr; }
.sc-legal-defs dt { font-size: var(--fs-sm); font-weight: 600; color: var(--fg-3); }
.sc-legal-defs dd {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-sm);
  color: var(--fg-2);
  line-height: 1.5;
  /* A tax number and a street address are long unbroken strings. Without this a
     360px
     viewport gets a horizontal scrollbar — same reason as .sc-cred-value. */
  overflow-wrap: anywhere;
}
.sc-legal-defs dd:last-child { margin-bottom: 0; }

/* Cookie entries. A table would need its own horizontal scroll on a phone;
   this is the .sc-cred-row pattern instead — stacked by default, label/value
   columns only where there is room. Must sit AFTER `.sc-legal h3`: equal
   specificity, so the later rule is the one that wins. */
.sc-cookie {
  margin: 0 0 var(--space-4);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.sc-cookie h3 {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-body);
  overflow-wrap: anywhere;
}
.sc-cookie-row { display: grid; grid-template-columns: 1fr; gap: 2px; font-size: var(--fs-sm); }
.sc-cookie-label { color: var(--fg-3); font-weight: 600; }
.sc-cookie-value { color: var(--fg-2); line-height: 1.5; overflow-wrap: anywhere; }

/* Unsubscribe form. Reuses the lead form's field, input, error and submit
   classes verbatim — two phone fields on one site that look different is a
   support ticket. What it does NOT reuse is .sc-lead-consent, and must not:
   there is no consent checkbox on that page. */
.sc-unsub {
  margin: 0 0 var(--space-12);
  padding: var(--space-6);
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  /* Reserved so swapping the form for the confirmation does not pull the page
     up under the user's finger mid-tap. */
  min-height: 250px;
}
.sc-unsub-form { display: flex; flex-direction: column; gap: var(--space-4); max-width: 420px; }
/* These three out-specify `.sc-legal p` / `.sc-legal h2` (0,1,1), which would
   otherwise win on source order and put a section-sized margin on a paragraph
   inside the form. */
.sc-legal .sc-unsub-lead { margin: 0 0 var(--space-5); }
.sc-legal .sc-unsub .sc-lead-error { margin-bottom: 0; }
.sc-legal .sc-unsub-done-heading { margin: 0 0 var(--space-3); font-size: var(--fs-h3); }

@media (min-width: 640px) {
  .sc-legal-defs { grid-template-columns: 160px 1fr; column-gap: var(--space-4); }
  .sc-cookie-row { grid-template-columns: 140px 1fr; gap: var(--space-4); }
}

/* ---- Footer: representative cost example (index.html only) ---- */

/* Its own element rather than a child of .ftr, because partials/footer.html is
   shared with the four legal pages and this block is index-only. It reads as
   the footer's first band: same ink, and the footer drops its own top padding
   directly after it so the seam is invisible. The adjacent-sibling selector is
   what keeps that scoped — a page without the block keeps the normal footer. */
.ftr-calc { background: var(--brand-ink); color: var(--fg-on-dark); padding: var(--space-16) 0 0; }
.ftr-calc + .ftr { padding-top: var(--space-10); }

.ftr-calc-body {
  display: flex; flex-direction: column; gap: var(--space-3);
  /* Wider than .disclosure's 460px: this is prose to be read, not a caption
     beside the logo. Matches .sc-cred-list, the other long-form block. */
  max-width: 860px;
}
.ftr-calc p {
  font-size: var(--fs-xs); line-height: 1.6;
  color: rgba(255, 255, 255, 0.55); margin: 0;
}

/* Reserved while empty, so loan-example.js filling it moves nothing below it.
   Same reasoning as .sc-cred-list, and the same trade: min-height only ever
   adds space, so a shorter sentence costs whitespace and never a shift. 1.6em
   is exactly one line of the rule above.

   MEASURED IN CHROME, not guessed, over 320-1280px and against two sentences:
   today's figures and a deliberately fatter roster (7-digit amount, 4-digit
   term, 7-digit RRPS). The real wrap is 6 lines at 320px, 5 at 360px, 4 to
   ~480px, 3 to ~700px and 2 from 768px, identical for both sentences — so the
   reservation below clears the worst case at every width with a line to spare.
   Re-measure if the copy changes; it is the copy, not the numbers, that sets
   the line count.

   The three breakpoints are main.css's existing ones. Between 540px and 640px
   the reservation is three lines fatter than needed, which is deliberate: an
   over-reservation on a flat dark band is invisible and lasts only until the
   fetch resolves, while an under-reservation is the shift this exists to stop.

   [hidden] wins over this: when the offers never arrive the paragraph collapses
   entirely rather than leaving a reserved gap. */
.ftr-calc-ranges { min-height: calc(7 * 1.6em); }

@media (min-width: 640px) {
  .ftr-calc-ranges { min-height: calc(4 * 1.6em); }
}
@media (min-width: 880px) {
  .ftr-calc-ranges { min-height: calc(3 * 1.6em); }
}

/* ---- Footer ---- */
.ftr { background: var(--brand-ink); color: var(--fg-on-dark); padding: var(--space-16) 0 var(--space-8); }
.ftr-grid {
  display: grid; grid-template-columns: 1.4fr 1fr;
  gap: var(--space-10); margin-bottom: var(--space-12);
}
.ftr-logo { margin-bottom: var(--space-5); }
.ftr .sc-logo { color: var(--fg-on-dark); }
/* h3, not h6 — h6 after the cards' h3 skips heading levels. Restyled down to
   an eyebrow here, so the selector must out-specify the bare h3 in tokens.css.
   .ftr-calc shares it: same band, same eyebrow, and the uppercase is CSS so the
   markup keeps a readable, copyable heading. */
.ftr h3, .ftr-calc h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: rgba(255, 255, 255, 0.5);
  line-height: 1.3;
  margin: 0 0 var(--space-4);
}
.ftr a { display: block; font-size: var(--fs-sm); color: rgba(255, 255, 255, 0.8); padding: 5px 0; }
.ftr a:hover { color: var(--fg-on-dark); }
.ftr .disclosure {
  font-size: var(--fs-xs); color: rgba(255, 255, 255, 0.55);
  line-height: 1.6; max-width: 460px; margin: 0;
}
.ftr-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.1); padding-top: var(--space-6);
  display: flex; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap;
  font-size: var(--fs-xs); color: rgba(255, 255, 255, 0.5);
}

@media (max-width: 880px) {
  .hdr-nav { display: none; }
  .ftr-grid { grid-template-columns: 1fr; gap: var(--space-8); }
}
