/* Cash-carta — layout + components (design C1, forked from zaym24 / smartycash-ua).
   Design tokens live in tokens.css and are never redefined here. */

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

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; }

/* One family site-wide. tokens.css still names the design system's display
   face in --font-display (the token is left verbatim), but no @font-face
   declares that face any more; this points every display use — h1/h2,
   .hero/.display, .eyebrow/.amount, the wordmark, the logo initials — at
   Rubik, at the weights each rule already sets (500–800 ship as files). */
:root { --font-display: var(--font-sans); }

/* Project colours that are not in the design system (tokens.css stays a
   verbatim copy): the mobile offer pills and the mobile hero subtitle. */
:root {
  /* Variant A pills: yellow on navy 11.8:1; blue on white with a blue ring 5.6:1. */
  --sc-pill-best-bg: var(--brand-ink);
  --sc-pill-best-fg: var(--brand-amber);
  --sc-pill-rec-bg: #FFFFFF;
  --sc-pill-rec-fg: var(--brand-green-tag);
  --sc-hero-sub: rgba(255, 255, 255, 0.88);
  --z-tile-radius: 16px;
  --z-btn-radius: 12px;

  /* C1 band. ONE gradient box spans the header and the band under it: the
     header paints its top slice (background-size below), the band paints the
     rest, so there is no seam where they meet and the sticky header keeps the
     brand colour after scrolling. --sc-band-h is measured from the top of
     <main>; the diagonal edge runs from 100% (left) up to --sc-band-cut (right). */
  --sc-band-grad: linear-gradient(135deg, var(--brand-sky) 0%, var(--brand-blue) 100%);
  --sc-band-h: 272px;
  --sc-band-cut: 74%;
  /* Flat: a 1px outline instead of a shadow (variant A). */
  --sc-tile-shadow: 0 0 0 1px var(--ink-200);
}
@media (min-width: 900px) {
  :root { --sc-band-h: 360px; --sc-band-cut: 66%; }
}

/* ---- Telegram brand mark ----
   DELIBERATELY OUTSIDE THE PALETTE. DO NOT HARMONISE THIS TO GREEN.

   #229ED9 is Telegram's own colour, and that is the entire point: it is a
   recognisable third-party brand mark, so the user reads the DESTINATION before
   they read the label — the same reason payment buttons keep their own colours
   rather than being restyled to the host site. Green was tried and reverted:
   on mobile the FAB lands directly on a card's green CTA and reads as part of
   it.

   It is not a brand token, so it is not in tokens.css — that file is a verbatim
   copy of the design system and never gains values. It lives here, next to the
   other project-level properties.

   ⚠️ CONTRAST — measured, not remembered. All figures against --brand-ink.

     ink on #229ED9 (default)   5.60:1  passes AA for text
     ink on #1F93CA (hover)     4.90:1  passes AA for text
     #229ED9 vs the white page  3.02:1  passes AA 1.4.11 for the component edge
     #1F93CA vs the white page  3.45:1  ditto, with more margin

   White text was tried first and is only 3.02:1 — fine for the icon as a
   graphical object, but below the 4.5:1 AA minimum for the 14px label. Ink
   keeps Telegram's exact colour, which was the whole point of leaving the
   palette, and matches the CTAs' own ink-on-colour treatment, so it reads as
   deliberate rather than as a contrast workaround.

   ⚠️ THE HOVER IS DARKENED ONLY SLIGHTLY, AND MUST NOT BE LIGHTENED. Darkening
   all the way to #1C8CBF drops ink to 4.47:1 — under AA. The instinct is then
   to lighten instead, but #229ED9 is already only 3.02:1 against a white page,
   so any lightening puts the BUTTON'S OWN EDGE below the 3:1 non-text floor:
   it trades a text failure for a boundary one. A shallower darkening satisfies
   both, and matches --brand-green-600's direction. */
:root {
  --tg-blue: #229ED9;
  --tg-blue-600: #1F93CA;   /* hover / active — see the hover note above */
  /* Mirrors --shadow-cta's shape in the button's own colour, so the FAB stands
     off the page without borrowing the CTA's green glow. */
  --tg-shadow: 0 8px 20px rgba(34, 158, 217, 0.35);
}

/* ---- 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: 24px;
  letter-spacing: -0.03em; color: #FFFFFF;
  display: inline-flex; align-items: baseline;
}
.sc-logo:hover { text-decoration: none; }
.sc-logo-main { color: inherit; }
/* «cash·carta»: the dot is drawn, not typed — U+00B7 sits too low and too
   small in Rubik 800 to read as the brand mark. Decorative; the link's
   aria-label carries the name. */
.sc-logo-dot {
  display: inline-block;
  width: 0.24em; height: 0.24em;
  margin: 0 0.1em;
  border-radius: 50%;
  background: var(--brand-amber);
  align-self: center;
  transform: translateY(0.06em);
}

/* ---- Header ---- */
.hdr {
  position: sticky; top: 0; z-index: 50;
  /* The top slice of the band's gradient box — see --sc-band-grad. */
  background: var(--sc-band-grad);
  background-size: 100% calc(var(--sc-hdr-h) + var(--sc-band-h));
  background-position: 0 0;
  /* No shadow and no border: at the top of the page the header and the band
     are one surface, and any edge between them reads as a seam. */
}
.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: rgba(255, 255, 255, 0.86); }
.hdr-nav a:hover { color: #FFFFFF; text-decoration: none; }
.hdr-right { display: flex; align-items: center; gap: var(--space-3); }

/* ---- Buttons ---- */
.btn {
  font-family: var(--font-sans); font-weight: 800; 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(--z-btn-radius);
  transition: all 120ms cubic-bezier(.2, .7, .2, 1);
  white-space: nowrap;
}
.btn:hover { text-decoration: none; }
/* Navy #0B1B3F on yellow #FFD43B (variant A): 11.8:1, ~11:1 on the #F5C518
   hover. --brand-green is the yellow here — see the mapping note in tokens.css. */
.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 CARD MARKUP, TWO LAYOUTS, switched here in CSS only — offer-grid.js
   never reads the viewport. From 900px: three wide cards per row, exactly as
   production had them (the card rules below are the f09bfb6 rules, verbatim).
   Below 900px: two-column tiles, all of it inside the max-width: 899px block
   further down. Two columns below 900px work only because the tile hides the
   long fields (rrps.display, legal links) — they stay in the card DOM and the
   details <dialog> shows them. */
.sc-offer-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 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;
}
@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. */
.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
   "до 3 369,39%" to "від 1 770,53% до 10 037,98%". */
.sc-offer {
  background: var(--paper);
  border: 0;
  border-radius: var(--z-tile-radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* Variant A: flat — a 1px outline (--sc-tile-shadow), no drop shadow. */
  box-shadow: var(--sc-tile-shadow);
  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-lg);
  }
}

/* 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. */
/* A chip inside the card, not a full-width strip: a blue strip across the top
   of every card merged with the blue band behind the first row (Nick,
   2026-11-08). Tinted, so it reads as a label rather than a second header. */
.sc-offer-badge {
  align-self: flex-start;
  max-width: calc(100% - 2 * var(--space-5));
  margin: var(--space-5) var(--space-5) 0;
  background: var(--ink-100);
  color: var(--brand-ink);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  line-height: 1.2;
  padding: 5px 10px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Blue on pale blue, 5.4:1. The neutral chip is navy on #EAF0F9, 15:1. */
.sc-offer-badge.is-accent { background: var(--brand-green-50); color: var(--brand-green-700); }

.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);
  /* The card's own surface, not --ink-50: a CRM logo that ships on a white
     background then shows no rectangle inside the box. */
  background: var(--paper);
  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 "Реальна річна процентна ставка" 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;
}
/* Values in navy ink (variant A): one accent on the page — the yellow CTA. */


/* ---- Desktop card, compact (from 900px) ----
   Scoped to .sc-offer and to this width: the tile (below 900px) and the
   details dialog (.sc-offer-detail) keep their own layouts.
   Amount, term and rate as label-left / value-right rows; РРПС, whose label
   is long, stays stacked under a hairline. A row whose label and value do not
   fit side by side (a 270px card at 900px) wraps the value under the label,
   still right-aligned. */
@media (min-width: 900px) {
  /* The header wordmark, a step up on desktop (24px read small there). */
  .hdr .sc-logo { font-size: 30px; }

  .sc-offer .sc-offer-head { padding: var(--space-4) var(--space-5) var(--space-1); gap: var(--space-3); }
  .sc-offer .sc-offer-logo,
  .sc-offer .sc-offer-monogram { width: 136px; height: 56px; }
  .sc-offer .sc-offer-logo img { width: 136px; height: 56px; }
  /* A narrow card (≈270px at 900px) cannot fit a 136px logo and a long name
     side by side: the name drops under the logo instead of overflowing. */
  .sc-offer .sc-offer-head { flex-wrap: wrap; row-gap: var(--space-1); }
  .sc-offer .sc-offer-name { font-size: var(--fs-body); min-width: 0; overflow-wrap: anywhere; }

  .sc-offer .sc-offer-rows { gap: var(--space-2); padding: var(--space-3) var(--space-5) var(--space-4); }
  .sc-offer .sc-offer-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    column-gap: var(--space-3);
  }
  .sc-offer .sc-offer-label { font-size: var(--fs-sm); }
  .sc-offer .sc-offer-value { margin: 0 0 0 auto; text-align: right; font-size: 15px; }
  .sc-offer .sc-offer-row--rrps {
    display: block;
    margin-top: var(--space-2);
    padding-top: var(--space-3);
    border-top: 1px solid var(--ink-200);
  }
  .sc-offer .sc-offer-row--rrps .sc-offer-value { text-align: left; margin-top: 2px; }

  .sc-offer .sc-offer-legal { gap: var(--space-1); padding-bottom: var(--space-4); }
  .sc-offer .sc-offer-cta { height: 46px; padding-top: 0; padding-bottom: 0; }
}

/* The offer a push named (?offer=<slug>), outlined for a few seconds after
   landing — offer-grid.js adds and removes the class. */
.sc-offer.is-focus {
  box-shadow: 0 0 0 3px var(--brand-green-700), var(--shadow-md);
  transition: box-shadow 300ms ease;
}

/* Elements that exist only for the tile layout: hidden from 900px (and in
   the dialog), shown by the max-width: 899px block below. */
.sc-offer-pill { display: none; }
.sc-offer-label-short { display: none; }
.sc-offer-more { display: none; }

/* ---- Offer tile (below 900px) ----
   Every rule is scoped to .sc-offer, so the details dialog's content — the
   same classes under .sc-offer-detail — keeps the full layout above. */
@media (max-width: 899px) {
  .sc-offer-grid {
    /* Row gap leaves room for the pill that overhangs each card's top edge. */
    row-gap: var(--space-4);
    column-gap: var(--space-3);
  }

  .sc-offer {
    position: relative;
    /* The pill sits on the top edge; hidden overflow would cut it in half. */
    overflow: visible;
  }

  /* Pills on positions 1 and 2 only (offer-grid.js renders no element for
     3+). Sentence case, fixed text, our colours — not the CRM's badgeText. */
  /* A pill centred on the tile's top edge. */
  .sc-offer-pill {
    display: block;
    position: absolute;
    top: -11px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.2;
    padding: 5px 10px;
    border-radius: var(--radius-pill);
    /* «Рекомендуємо» is white on a white tile: the ring is its edge. */
    box-shadow: inset 0 0 0 1.5px currentColor;
    /* Never truncated: at 320px the pill may run past the tile's edges. */
    white-space: nowrap;
  }
  .sc-offer-pill.is-best {
    background: var(--sc-pill-best-bg);
    color: var(--sc-pill-best-fg);
  }
  .sc-offer-pill.is-rec {
    background: var(--sc-pill-rec-bg);
    color: var(--sc-pill-rec-fg);
  }

  /* The CRM badge strip is a desktop element. */
  .sc-offer .sc-offer-badge { display: none; }

  /* Same top padding on every card, pill or not, so content lines up. */
  .sc-offer .sc-offer-head {
    flex-direction: column;
    justify-content: center;
    gap: 0;
    padding: 24px var(--space-3) var(--space-1);
  }
  .sc-offer .sc-offer-logo,
  .sc-offer .sc-offer-monogram {
    width: 128px;
    max-width: 100%;
    height: 52px;
  }
  .sc-offer .sc-offer-logo img {
    width: 100%;
    height: 100%;
  }

  /* Name: visually hidden, kept in the accessibility tree — the h3 is the
     card's one accessible name (the logo is alt=""). */
  .sc-offer .sc-offer-name {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* Tile face: the sibling sites' structure — short label over value — in C1
     colours, with a third row (Nick, 2026-11-07): «Макс. сума», «Строк»,
     «Ставка для нових». РРПС and the legal links stay in the dialog. Three
     rows, so the gap is a step tighter than on the two-row siblings. */
  .sc-offer .sc-offer-rows {
    gap: 6px;
    padding: var(--space-3) var(--space-3) var(--space-2);
    text-align: center;
  }
  .sc-offer .sc-offer-label { font-size: 13px; font-weight: 500; color: var(--ink-500); }
  .sc-offer .sc-offer-label-full { display: none; }
  .sc-offer .sc-offer-label-short { display: inline; }
  .sc-offer .sc-offer-value {
    font-size: 15px;
    /* Display strings carry U+00A0; on a narrow tile they wrap, not clip. */
    overflow-wrap: anywhere;
  }

  /* In the DOM, byte-identical, but shown in the details dialog instead. */
  .sc-offer .sc-offer-row--rrps,
  .sc-offer .sc-offer-legal { display: none; }

  /* «Детальніше ⌄» — margin-top:auto pins it and the CTA to the card bottom,
     so buttons line up across a row. The chevron is CSS: U+2304 is not in
     the subsetted fonts. */
  .sc-offer-more {
    display: inline-flex;
    align-self: center;
    align-items: center;
    gap: var(--space-2);
    margin: auto var(--space-3) var(--space-2);
    padding: var(--space-2);
    border: 0;
    background: none;
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    color: var(--brand-green-700);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
  }
  .sc-offer-more::after {
    content: '';
    width: 6px;
    height: 6px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
  }
  .sc-offer-more:hover { color: var(--brand-ink); }

  /* Full tile width, 40px, radius 12, navy on yellow, sentence case. */
  .sc-offer .sc-offer-cta {
    align-self: stretch;
    height: 40px;
    margin: 0 var(--space-3) var(--space-3);
    padding: 0 var(--space-2);
    font-size: 14px;
    font-weight: 700;
    white-space: nowrap;
  }
}

/* At 320px «Отримати гроші» at 14px fills the ≈106px tile button edge to edge. */
@media (max-width: 359px) {
  .sc-offer .sc-offer-cta { font-size: 13px; }
}

/* ---- Mobile hero (below 620px) ----
   After the f09bfb6 heading rules above: these refine them on phones. The
   subtitle style is #offers only — «Про кредиторів» keeps its heading look. */
@media (max-width: 620px) {
  .sc-page-head { padding-top: 18px; }
  .sc-page-head h1 {
    font-size: 23px;
    line-height: 1.15;
    font-weight: 800;
  }
  .sc-page-head + .section { padding-top: 6px; }
  #offers .sc-section-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--sc-hero-sub);
    /* 14px to the tags' top edge; the tags overhang the cards by 11px. */
    margin-bottom: 25px;
  }

  /* Header CTA, every page. */
  .hdr-right .btn {
    height: 36px;
    padding: 0 var(--space-4);
    font-size: 13px;
  }
}

/* ---- C1 gradient band (home page only) ----
   Painted by .sc-home::before behind the heading and the top of the first row
   of cards, which overlap its diagonal lower edge. The gradient box is the one
   the header starts (--sc-band-grad), offset by the header height so the two
   meet without a seam. Two soft glows and a faint ring give it depth; all
   decorative, all in the pseudo-element, nothing in the DOM. */
.sc-home {
  position: relative;
  /* Keeps the band's z-index:-1 above the body background, below the cards. */
  isolation: isolate;
}
.sc-home::before {
  content: '';
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  right: 0;
  height: var(--sc-band-h);
  background:
    radial-gradient(circle at 88% 18%, rgba(255, 255, 255, 0.30) 0, rgba(255, 255, 255, 0) 38%),
    radial-gradient(circle at 92% 22%, rgba(255, 255, 255, 0) 30%, rgba(255, 255, 255, 0.16) 30.4%, rgba(255, 255, 255, 0) 31.2%),
    radial-gradient(ellipse at 0% 100%, rgba(13, 70, 190, 0.45) 0, rgba(13, 70, 190, 0) 55%),
    var(--sc-band-grad);
  background-size: 100% 100%, 100% 100%, 100% 100%, 100% calc(var(--sc-hdr-h) + var(--sc-band-h));
  background-position: 0 0, 0 0, 0 0, 0 calc(-1 * var(--sc-hdr-h));
  background-repeat: no-repeat;
  clip-path: polygon(0 0, 100% 0, 100% var(--sc-band-cut), 0 100%);
}
/* Heading and subtitle sit on the band: white, at every width. */
.sc-home .sc-page-head h1 { color: #FFFFFF; text-shadow: 0 2px 12px rgba(10, 40, 120, 0.18); }
.sc-home #offers .sc-section-title { color: var(--sc-hero-sub); font-weight: 600; }
@media (min-width: 621px) {
  .sc-home .sc-page-head { padding-top: var(--space-10); }
  .sc-home #offers .sc-section-title { font-size: var(--fs-lg); margin-bottom: var(--space-10); }
}

/* ---- In-grid note and «Показати ще» ----
   Both are full-width grid items. The note is two elements — after card 3
   (three columns, from 900px) and after card 4 (two columns) — and exactly one
   shows, so it always closes the 2nd row. Its class deliberately does not
   start with "sc-offer": lead-form.js counts grid children by that prefix. */
.sc-grid-note {
  grid-column: 1 / -1;
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--brand-green-50);
  color: var(--brand-ink);
  font-size: var(--fs-body);
  font-weight: 700;
  line-height: 1.35;
  text-align: center;
}
.sc-grid-note--mobile { display: none; }

.sc-more { grid-column: 1 / -1; }
.sc-more-btn {
  width: 100%;
  min-height: 48px;
  justify-content: center;
}

/* Announcement for «Показати ще» — read, never seen. */
.sc-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 899px) {
  .sc-grid-note--desktop { display: none; }
  .sc-grid-note--mobile { display: block; }
  .sc-grid-note {
    padding: var(--space-3) var(--space-4);
    font-size: var(--fs-sm);
  }
}

/* ---- Bottom CTA (bottom-cta.js) ----
   Right after the grid element, before #creditors; rendered only when the
   inline lead form is in the page. Its button scrolls to that form — there is
   no second form. One markup: a row from 900px, stacked below it. */
.sc-bottom-cta {
  margin-top: var(--space-8);
  padding: var(--space-6) var(--space-8);
  border-radius: var(--radius-lg);
  background: var(--brand-ink);
  color: var(--fg-on-dark);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}
.sc-bottom-cta-title {
  margin: 0 0 var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--fs-h4);
  font-weight: 700;
  line-height: 1.25;
  color: var(--fg-on-dark);
}
.sc-bottom-cta-body {
  margin: 0;
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--fg-on-dark);
  opacity: .85;
}
.sc-bottom-cta-btn {
  flex: 0 0 auto;
  justify-content: center;
  min-height: 48px;
}
/* The arrow is drawn, not typed: U+2192 is not in the subsetted fonts. */
.sc-bottom-cta-btn::after {
  content: '';
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-top: 2px solid currentColor;
  transform: rotate(45deg);
  margin-left: var(--space-1);
}

@media (max-width: 899px) {
  .sc-bottom-cta {
    flex-direction: column;
    align-items: stretch;
    padding: var(--space-5);
    gap: var(--space-4);
  }
  .sc-bottom-cta-title { font-size: var(--fs-lg); }
  .sc-bottom-cta-body { font-size: var(--fs-sm); }
  .sc-bottom-cta-btn { width: 100%; }
}

/* ---- Offer details dialog ----
   One native <dialog> (index.html), filled per offer by offer-grid.js. The
   dialog has no padding of its own and .sc-offer-detail fills it, so a click
   whose target is the <dialog> itself can only be the backdrop. */
.sc-offer-dialog {
  width: min(480px, calc(100vw - 2 * var(--space-4)));
  max-height: calc(100vh - 2 * var(--space-4));
  max-height: calc(100dvh - 2 * var(--space-4));
  padding: 0;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--paper);
  color: var(--fg-1);
  box-shadow: var(--shadow-lg);
  overflow: auto;
  overscroll-behavior: contain;
}
.sc-offer-dialog::backdrop { background: rgba(14, 27, 61, 0.55); }

.sc-offer-dialog-close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--ink-50);
  color: var(--brand-ink);
  cursor: pointer;
}
.sc-offer-dialog-close:hover { background: var(--ink-100); }

.sc-offer-detail {
  display: flex;
  flex-direction: column;
  padding: var(--space-6) var(--space-5) var(--space-5);
}
/* Logo beside the name here, clear of the × in the corner. */
.sc-offer-detail .sc-offer-head {
  flex-direction: row;
  text-align: left;
  gap: var(--space-4);
  padding: 0 48px var(--space-4) 0;
}
.sc-offer-detail .sc-offer-rows {
  text-align: left;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-top: 1px solid var(--ink-100);
}
.sc-offer-detail .sc-offer-legal { padding: 0 0 var(--space-5); }
/* The tile rules are scoped to .sc-offer, so this content gets the full
   desktop card styles at every width; only spacing differs. */
.sc-offer-detail .sc-offer-cta { margin: 0; }

/* Body scroll lock while the dialog is open (class set by offer-grid.js). */
html.sc-dialog-open { overflow: hidden; }

/* ---- Lead form ----
   A full-width grid item: after the sixth card it closes row two at 3 columns
   and row three at 2. 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-700); 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-700);
}
.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;
}

/* Tile layout: the skeleton mirrors the tile — two rows, no disclosures, a
   narrow CTA at the bottom. */
@media (max-width: 899px) {
  .sc-offer-skeleton .sc-offer-row:nth-child(3),
  .sc-offer-skeleton .sc-offer-legal { display: none; }
  .sc-offer-skeleton .sc-offer-label .sc-offer-bone,
  .sc-offer-skeleton .sc-offer-value .sc-offer-bone { margin: 0 auto; }
  .sc-offer-skeleton .sc-offer-cta {
    align-self: center;
    width: 128px;
    margin: auto 0 var(--space-4);
  }
}

@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 ("Про кредиторів") ----
   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;

  /* Reserve height while the list is empty, for the same reason the offer grid
     does: this section sits directly above the footer, and the footer moving is
     what produced the 0.68 CLS before. The list is filled in one shot from data
     that is already in memory, so this is the only moment it can shift.

     A viewport unit rather than a pixel guess — the roster is volatile (it has
     moved by four entries in days) and a hardcoded row count goes stale. At the
     current 19 lenders the rendered list is taller than 100vh at every common
     viewport height, so the reservation is fully absorbed and what movement is
     left happens below the fold, where CLS does not count it. min-height only
     ever adds space, so a shorter roster costs whitespace, never a shift. */
  min-height: 100vh;
}

.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
   "Ariba — д…". 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. "ClickCredit — д…" 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 ("Отримати гроші"); .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); }
}

/* ---- Retention: Telegram FAB ----
   Telegram blue — see --tg-blue above for why it sits outside the palette and
   must stay there. Green was tried and reverted: on mobile the FAB lands
   directly on a card's green CTA and reads as part of it rather than as a
   separate destination. */
.sc-rt-fab {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: 45;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-pill);
  background-color: var(--tg-blue);
  /* fill is --brand-ink #0E1B3D. Inline data: URI rather than a file — one
     fewer request, and img-src allows data: so CSP is satisfied. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%230E1B3D'%3E%3Cpath d='M9.6 16.8 9.4 21c.4 0 .6-.2.8-.4l2-1.9 4.1 3c.8.4 1.3.2 1.5-.7l2.7-12.6c.3-1.1-.4-1.6-1.1-1.3L2.6 10.4c-1.1.4-1.1 1-.2 1.3l4.4 1.4L17 6.7c.5-.3.9-.1.6.2z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 26px 26px;
  box-shadow: var(--tg-shadow);
}
.sc-rt-fab[hidden] { display: none; }
@media (hover: hover) {
  .sc-rt-fab:hover { background-color: var(--tg-blue-600); }
}
.sc-rt-fab:active { background-color: var(--tg-blue-600); }

/* The attention ring — scPingBig, lifted from the prototype.
   transform and opacity ONLY: both are composited, so the pulse cannot move a
   single pixel of layout and cannot contribute to CLS. Never animate inset,
   width or height here. */
.sc-rt-fab::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 2px solid var(--tg-blue);
  /* Hidden between cycles: the animation has no fill-mode, so outside it the
     element falls back to this. */
  opacity: 0;
  pointer-events: none;
}
/* Three pulses, then still. The dispatcher schedules the 30s pause between
   cycles and caps them at four — see frequency.visibleMs, which must stay at or
   above 3 x this duration or a cycle is cut off mid-pulse.

   The ring can reach past the viewport edge when the pill is wide, which is
   harmless: the parent is position:fixed, and fixed elements do not contribute
   to the document's scrollable overflow, so there is no horizontal scrollbar —
   the ring is simply clipped as it fades. At <=640px the button is a 56px
   circle and the ring extends ~10px, well inside the 360px viewport. */
.sc-rt-fab.is-attention::before { animation: scPingBig 1.6s ease-out 3; }

@keyframes scPingBig {
  0%   { transform: scale(1);    opacity: .7; }
  100% { transform: scale(1.35); opacity: 0; }
}

/* Below 640px: icon only, and the label slides in for the attention cycle.
   Absolutely positioned, so revealing it costs no layout at all and the reveal
   is a transform — a pill at this width would cover too much of the viewport to
   sit there permanently. */
.sc-rt-fab-label {
  position: absolute;
  right: 64px;
  top: 50%;
  transform: translate(12px, -50%);
  opacity: 0;
  transition: transform 200ms cubic-bezier(.2, .7, .2, 1), opacity 200ms;
  white-space: nowrap;
  pointer-events: none;
  background: var(--tg-blue);
  color: var(--brand-ink);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm);
  font-size: var(--fs-sm);
  font-weight: 700;
}
.sc-rt-fab.is-attention .sc-rt-fab-label { transform: translate(0, -50%); opacity: 1; }

/* From 640px the label is simply always there: one green pill, no animation
   involved. An unlabelled icon does not say what happens when you tap it, and
   now that it goes somewhere the label should be visible from the start.
   ⚠️ 640 is duplicated in retention-manager.js (labelAlwaysVisible) — keep the
   two in step. */
@media (min-width: 640px) {
  .sc-rt-fab {
    width: auto;
    height: auto;
    min-height: 52px;
    display: inline-flex;
    align-items: center;
    padding: var(--space-3) var(--space-5) var(--space-3) 46px;
    background-position: var(--space-4) center;
    background-size: 22px 22px;
  }
  .sc-rt-fab-label {
    position: static;
    opacity: 1;
    background: none;
    box-shadow: none;
    padding: 0;
    color: var(--brand-ink);
  }
  /* Out-specifies the .is-attention rule above, which would otherwise shift the
     now-static label up by half its height whenever a cycle ran. */
  .sc-rt-fab-label,
  .sc-rt-fab.is-attention .sc-rt-fab-label { transform: none; }
}

/* 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,
  .sc-rt-fab-label { transition: none; }
  /* No pulse at all for someone who asked for less motion — not a slower one.
     retention-manager.js also skips the cycle entirely where the label is
     already visible, so it does not silently spend the global cooldown on an
     animation nobody will see.

     ⚠️ THE SELECTOR MUST CARRY .is-attention. Media queries add no specificity,
     so a bare .sc-rt-fab::before (0,1,0) loses to the rule that sets the
     animation (0,2,0) no matter how far down the file it sits — which is
     exactly how this shipped inert once, verified only when a real browser
     still reported the animation under --force-prefers-reduced-motion. */
  .sc-rt-fab.is-attention::before { animation: 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
   "конфіденційності" at 44px is ~380px wide — 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;
  /* The ІПН and the 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, .ftr .sc-logo-main { 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); }
}

/* ---- Retention: web push soft prompt (push_prompt in retention-manager.js) ----
   A card at the top centre, under the header, above everything else. It holds the
   single modal slot (so the bar and the FAB stand down) but does not block the
   page — no backdrop, the page behind stays usable. */
.sc-rt-push {
  position: fixed;
  left: 50%;
  top: calc(var(--sc-hdr-h) + var(--space-4));
  transform: translateX(-50%);
  z-index: 48;
  width: min(420px, calc(100vw - 2 * var(--space-4)));
  padding: var(--space-5);
  background: var(--paper);
  border: 1px solid var(--ink-200);
  border-radius: var(--z-tile-radius);
  box-shadow: var(--shadow-lg);
}
.sc-rt-push[hidden] { display: none; }
.sc-rt-push-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: 0 0 var(--space-4);
}
.sc-rt-push-icon {
  flex: 0 0 56px;
  width: 56px;
  height: 56px;
  border-radius: 14px;
}
.sc-rt-push-title {
  margin: 0;
  font-size: 17px;
  line-height: 1.35;
  font-weight: 800;
  color: var(--brand-ink);
}
.sc-rt-push-actions { display: flex; gap: var(--space-3); }
.sc-rt-push-actions .btn { flex: 1 1 0; justify-content: center; min-height: 44px; padding: 0 var(--space-4); }
/* At the top, under the header, on every width (Nick, 2026-11-09: phones
   too). It shows first, 3s after landing, and the sticky bar waits for it, so
   the top is free while it is up. */
