/* Component-level styles: hero, project card/catalog, quiz, faq.
   Mobile-first base rules; wider-viewport variants in responsive.css.
   Brand rules applied: no rounded corners, no shadows, gold as a single
   accent, Cormorant Garamond for headings/price, Manrope everywhere else. */

/* ---------- Hero (brand-system.md §2, brandbook p.24) ---------- */

.hero {
  position: relative;
  color: var(--color-ivory);
  padding-top: var(--space-5);
  padding-bottom: var(--space-5);
  /* The PHOTOGRAPH is cropped, not the content: clipping both axes here
     is what cut the rate and the gift panel off the bottom of the first
     screen. Only the horizontal axis is clipped now, and with `clip`
     rather than `hidden` — it creates no scroll container, so nothing
     below can be scrolled out of reach. What it does contain is the
     mobile scrim, which deliberately bleeds a gutter past each edge and
     would otherwise widen the whole page by 20px. */
  overflow-x: clip;
  overflow-y: visible;
  isolation: isolate;
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--color-graphite);
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.35;
}

/* Graphite gradient overlay so the headline stays legible while the
   photo remains visible (brandbook p.24: 70%→0% graphite gradient,
   left-to-right). On mobile the layout stacks vertically instead of
   text-left/photo-right, so the base rule darkens top-to-bottom, where
   the headline sits; responsive.css switches to the literal
   left-to-right gradient once the desktop text-left layout applies. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(31, 37, 40, 0.88) 0%, rgba(31, 37, 40, 0.8) 60%, rgba(31, 37, 40, 0.7) 100%);
}

.hero .container {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.hero__title {
  font-family: var(--font-ui);
  font-weight: 300;
  text-transform: uppercase;
  font-size: var(--text-hero-size-mobile);
  line-height: var(--text-hero-line-mobile);
  letter-spacing: var(--text-hero-tracking);
  max-width: 22ch;
  margin: 0 0 var(--space-2);
  /* Variants that embed a literal "\n" (see js/config.js) get an exact
     two-line break here instead of relying on width-based wrapping. */
  white-space: pre-line;
}

.hero__subtitle {
  color: var(--color-sand);
  font-weight: 300;
  max-width: 46ch;
}

.hero .btn-primary,
.hero .btn-secondary {
  margin-top: var(--space-1);
}

.hero .btn-secondary {
  border-color: var(--color-ivory);
  color: var(--color-ivory);
}

/* ---------- Trigger bar ----------
   Compact fact-strip right after the hero. Deliberately NOT a
   `.section` (no 64/120px section spacing) — it's a tight continuation
   of the hero's message, not a new content block (brief §29). Sand
   background matches its brand role: "второй уровень, подложки,
   выделение блоков" (brand-system.md §1). */

.trigger-bar {
  background: var(--color-sand);
  padding: var(--space-2) 0;
}

.trigger-bar__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-1) var(--space-3);
}

.trigger-bar__item {
  font-family: var(--font-ui);
  font-weight: 700;
  text-transform: uppercase;
  font-size: var(--text-overline-size);
  letter-spacing: var(--text-overline-tracking);
  color: var(--color-graphite);
  margin: 0;
}

/* ---------- Catalog freshness ---------- */

.catalog {
  /* Tighter than the standard section top spacing — catalog follows
     the trigger bar directly, and the redesign asks for that specific
     transition to feel dense, not airy (brief §29). Bottom spacing
     stays the normal .section value. */
  padding-top: var(--space-4);
}

.catalog__freshness {
  color: var(--color-olive);
  font-size: var(--text-caption-size);
}

.catalog__grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
}

/* Shown instead of the grid when the catalogue is empty or could not be
   loaded. Deliberately quiet: it is a statement of fact, not an error
   banner — the visitor's next step is the quiz either way. */
.catalog__notice {
  color: var(--color-olive);
  margin: 0;
  max-width: 46ch;
}

/* ---------- Project card (brandbook p.25: photo 3:2, gold rubric,
   Cormorant price, straight corners, no shadow) ---------- */

.project-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--color-ivory);
  display: flex;
  flex-direction: column;
}

.project-card__media {
  position: relative;
  aspect-ratio: 3 / 2;
  background: var(--color-sand);
}

.project-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-card__status {
  position: absolute;
  top: var(--space-1);
  left: var(--space-1);
  background: var(--color-graphite);
  color: var(--color-ivory);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 5px 10px;
}

.project-card__body {
  padding: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.project-card__rubric {
  font-family: var(--font-ui);
  font-weight: 700;
  text-transform: uppercase;
  font-size: var(--text-overline-size);
  letter-spacing: var(--text-overline-tracking);
  color: var(--color-gold);
  margin: 0;
}

.project-card__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--text-h3-size);
  line-height: var(--text-h3-line);
  margin: 0;
}

.project-card__facts {
  list-style: none;
  padding: 0;
  margin: 4px 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: var(--text-body-size);
  color: var(--color-text);
}

.project-card__facts li + li::before {
  content: "·";
  margin-right: 10px;
  color: var(--color-olive);
}

.project-card__price {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
  margin: 4px 0;
}

.project-card__benefits {
  list-style: none;
  padding: var(--space-1) 0 0;
  margin: 4px 0 0;
  border-top: 1px solid var(--color-border);
  display: grid;
  gap: 4px;
  color: var(--color-text-muted);
  font-size: var(--text-caption-size);
}

.project-card__benefits li {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}

.project-card__benefits .icon {
  flex: none;
  margin-top: 2px;
  color: var(--color-olive);
}

.project-card__ctas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

/* ---------- Quiz (brand-system.md §5) ---------- */

.quiz__progress {
  font-size: var(--text-caption-size);
  color: var(--color-olive);
  margin-bottom: var(--space-2);
}

.quiz__card {
  background: var(--color-ivory);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3);
}

.quiz__question {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--text-h3-size);
  line-height: var(--text-h3-line);
  margin: 0 0 var(--space-2);
}

/* Calm, factual context line shown on the step right after an answer —
   never fake urgency (brief §21). */
.quiz__context {
  margin: -8px 0 var(--space-2);
}

.quiz__options {
  display: grid;
  gap: var(--space-1);
}

.quiz__option {
  min-height: var(--button-height);
  border-radius: var(--radius);
  border: 1px solid var(--color-border);
  background: transparent;
  text-align: left;
  padding: 0 var(--space-2);
  font-family: var(--font-ui);
  font-size: var(--text-body-size);
  color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--transition-default), color var(--transition-default);
}

.quiz__option:hover {
  border-color: var(--color-olive);
}

.quiz__option--selected {
  border-color: var(--color-gold);
  color: var(--color-graphite);
  font-weight: 600;
}

/* --- Selector rework: progress, multi-select, transition, result --- */

.quiz__progress {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.quiz__progress-bar {
  flex: 1;
  height: 2px;
  background: var(--color-border);
  overflow: hidden;
}

.quiz__progress-fill {
  display: block;
  height: 100%;
  background: var(--color-gold);
  transition: width var(--transition-default);
}

/* Multi-select needs a visible "chosen" affordance beyond the border,
   since several rows can be active at once. */
.quiz__option--multi {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-top: var(--space-1);
  padding-bottom: var(--space-1);
}

.quiz__check {
  flex: none;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  color: transparent;
  transition: background-color var(--transition-default), border-color var(--transition-default), color var(--transition-default);
}

.quiz__option--selected .quiz__check {
  background: var(--color-gold);
  border-color: var(--color-gold);
  color: var(--color-ivory);
}

.quiz__limit {
  margin: var(--space-1) 0 0;
}

.quiz__next {
  margin-top: var(--space-2);
  width: 100%;
}

.quiz__channels {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-bottom: var(--space-1);
}

.quiz__channel {
  flex: 1 1 auto;
  min-height: var(--touch-target-min);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: transparent;
  font-family: var(--font-ui);
  font-size: var(--text-body-size);
  color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--transition-default);
}

.quiz__channel--selected {
  border-color: var(--color-gold);
  font-weight: 600;
}

.quiz__back {
  margin-top: var(--space-2);
  background: none;
  border: none;
  color: var(--color-olive);
  cursor: pointer;
  padding: var(--space-1) 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-ui);
  font-size: var(--text-caption-size);
}

/* Form fields: no boxed border, underline only; active = gold line
   (brandbook p.25, brand-system.md §5). */
.quiz__contact-form {
  display: grid;
  gap: var(--space-3);
}

.quiz__label {
  display: grid;
  gap: 6px;
  font-family: var(--font-ui);
  font-weight: 700;
  text-transform: uppercase;
  font-size: var(--text-overline-size);
  letter-spacing: var(--text-overline-tracking);
  color: var(--color-olive);
}

.quiz__input {
  min-height: var(--button-height);
  border: none;
  border-bottom: 1px solid var(--color-border);
  border-radius: 0;
  padding: 0 2px;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 16px;
  color: var(--color-text);
  transition: border-color var(--transition-default);
}

.quiz__input:focus {
  outline: none;
  border-bottom: 2px solid var(--color-gold);
}

.quiz__error {
  color: var(--color-error);
  font-size: var(--text-caption-size);
  margin: 0;
}

.quiz__consent {
  margin: -8px 0 0;
}

.quiz__consent a {
  color: var(--color-olive);
}

.quiz__card--done {
  text-align: left;
  border-color: var(--color-gold);
}

/* ---------- FAQ ---------- */

.faq__list {
  display: grid;
}

.faq-item {
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-2) 0;
}

.faq-item__question {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: var(--text-lead-size);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.faq-item__question::-webkit-details-marker {
  display: none;
}

.faq-item__marker {
  position: relative;
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--color-olive);
}

.faq-item__marker-icon {
  position: absolute;
  inset: 0;
  display: flex;
  transition: opacity var(--transition-default);
}

.faq-item__marker-icon--minus {
  opacity: 0;
}

.faq-item[open] .faq-item__marker-icon--plus {
  opacity: 0;
}

.faq-item[open] .faq-item__marker-icon--minus {
  opacity: 1;
}

.faq-item__answer {
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  max-width: 65ch;
}

/* =====================================================================
   SPECIALIST CARD — components/specialist.js

   Lives in the desktop column beside the questions until the first
   answer arrives. Hidden outright on a phone: the layout there is one
   column, and a portrait ahead of the form would only push the first
   question further down.
   ===================================================================== */

.specialist {
  display: none;
}

.specialist__photo {
  margin-bottom: var(--space-2);
  background: var(--color-charcoal);
}

.specialist__photo img {
  display: block;
  width: 100%;
  height: auto;
  /* Straight corners, no shadow — the brandbook applies to a portrait
     as much as to a render. */
  border-radius: 0;
}

.specialist__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.05rem;
  line-height: 1.3;
  color: var(--color-ivory);
  margin: 0 0 var(--space-1);
}

/* A person's name is one unit: it moves to the next line whole or not
   at all. Without this it broke as "Никита / Гаврилов". */
.specialist__name {
  white-space: nowrap;
}

.specialist__lead {
  margin: 0;
  color: var(--color-sand);
  font-size: var(--text-caption-size);
  line-height: 1.5;
}

/* The right column changes hands once, quietly. */
@media (prefers-reduced-motion: no-preference) {
  .page-funnel .specialist,
  .page-funnel .answers-summary {
    animation: veyra-aside-in 260ms ease both;
  }

  @keyframes veyra-aside-in {
    from { opacity: 0; }
    to { opacity: 1; }
  }
}

/* =====================================================================
   ONE-SCREEN FUNNEL
   The public page is offer → selection → result → contact and nothing
   else, so it reads as one continuous dark surface rather than a
   sequence of alternating bands. Uses the brandbook palette unchanged
   (graphite / ivory / sand / gold); the only new idea here is that the
   whole page is dark, not just the hero.
   ===================================================================== */

.page-funnel {
  display: flex;
  flex-direction: column;
  background: var(--color-graphite);
  color: var(--color-text-on-dark);
}

/* The hero fills the first screen but never traps the visitor: the
   selector's first question peeks above the fold on a phone, which is
   what tells them there is something to do here. */
.hero {
  /* Exactly the window minus the fixed header: the photograph is the
     first screen, so it gets the first screen. svh (not vh) so mobile
     browser chrome cannot push the CTA off the bottom. The copy is
     bottom-aligned, which leaves the sky and the horizon in the clear
     above it. */
  min-height: calc(100svh - var(--funnel-header-h));
  display: flex;
  align-items: flex-end;
  padding-top: var(--space-5);
  padding-bottom: var(--space-4);
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* The photograph is the offer's evidence, so on the funnel page it is
   allowed to be a photograph. The generic .hero rules dim it to 0.35
   under an even graphite wash, which is right for a page whose text
   floats anywhere over it — here the copy sits in the bottom half, so
   the scrim can follow the copy instead of covering the whole frame:
   light over the sky and the horizon, heavy exactly where the headline,
   the button and the rate line land. No colour correction, only opacity
   and the brandbook's own graphite. */
/* The photograph is the offer's evidence, so it is shown as a
   photograph: full strength, no wash over it. Legibility is handled by
   a gradient that darkens ONLY the band the copy occupies — the bay,
   the water and the far shore stay bright and in colour. */
.page-funnel .hero__media img {
  opacity: 1;
}

/* Phone: the copy sits in the lower half, so the graphite comes up from
   the bottom and the top of the frame is left alone. */
/* Only a whisper over the frame itself; the copy carries its own
   gradient below. Tying the scrim to the CONTENT rather than to a
   percentage of the image means it keeps covering the text no matter
   how tall the block grows. */
.page-funnel .hero__media::after {
  background: linear-gradient(180deg,
    rgba(31, 37, 40, 0) 0%,
    rgba(31, 37, 40, 0.10) 40%,
    rgba(31, 37, 40, 0.35) 100%);
}

/* The local gradient the brief asks for: graphite fading in from
   nothing exactly where the headline starts and holding to the bottom,
   bled to both edges of the screen. Above it the bay, the beach and the
   sea are untouched. */
.page-funnel .hero__inner {
  position: relative;
  padding-top: var(--space-4);
  padding-bottom: var(--space-2);
}

/* The scrim bleeds to both edges of the screen without the BOX doing
   so: widening the element itself pushed the page 20px wider than the
   viewport once the hero stopped clipping its children. */
.page-funnel .hero__inner::before {
  content: "";
  position: absolute;
  inset: 0 calc(var(--container-padding-mobile) * -1);
  z-index: -1;
  background: linear-gradient(180deg,
    rgba(31, 37, 40, 0) 0%,
    rgba(31, 37, 40, 0.80) 6%,
    rgba(31, 37, 40, 0.94) 14%,
    rgba(31, 37, 40, 0.96) 100%);
}

/* DEPARTURE FROM THE BRANDBOOK'S HERO SPEC, ON PURPOSE.
   Brandbook p. 24 sets the first screen's heading in Manrope Light,
   capitals, 44–56px, tracking +40 — written for a headline of two SHORT
   lines. This offer is a sentence ("Таиланд. Место, куда хочется
   возвращаться."), and in capitals at that size it broke into four
   heavy lines and stopped reading as a phrase.

   So it uses the other brand face: Cormorant Garamond, the accent
   typeface the brandbook assigns to headings (p. 17), in sentence case.
   Both marks stay inside the brand system; only the hero role moves
   from one to the other. See docs/brand-system.md — the reasoning is
   recorded there so a long headline is not put back into Manrope caps.

   The two parts are one heading: same face, size, weight, leading,
   tracking and colour. */
.page-funnel .hero__title {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: none;
  /* 42px on a phone → 60px on a wide desktop, tied to the viewport so
     the phrase keeps its measure between the two. */
  font-size: clamp(2.625rem, 1.6rem + 2.2vw, 3.75rem);
  line-height: 1.02;
  letter-spacing: 0;
  max-width: 17ch;
  /* Only the break after "Таиланд." is ours; the rest is the browser's
     to place, and `balance` keeps it from leaving one word alone. */
  text-wrap: balance;
}

.hero__title-line {
  /* Inherits everything. Declared so the two lines cannot drift apart
     by accident later. */
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

.hero__subtitle {
  max-width: 34ch;
  margin-bottom: var(--space-3);
}

.hero__cta {
  align-self: stretch;
  text-align: center;
}

/* The evidence for the headline. Hidden until a real Bank of Russia
   rate arrives (components/hero.js) — never rendered empty. */
.hero__rate {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  align-items: baseline;
  margin: var(--space-3) 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border-on-dark);
  align-self: stretch;
}

.hero__rate-value {
  font-family: var(--font-display);
  font-size: 1.25rem;
  color: var(--color-ivory);
}

.hero__rate-date {
  font-size: var(--text-caption-size);
  color: var(--color-sand);
  letter-spacing: 0.04em;
}

/* =====================================================================
   RENDERS — the result screen's visual proof (components/renders.js)

   Not catalogue cards, and styled so they cannot be mistaken for any:
   straight corners, no shadow, no border, no caption, no price, nothing
   clickable. Just five frames of what the offer looks like.

   Phone: one horizontal strip moved with a finger. The tiles are 78% of
   the card, so the next one is always partly on screen — that peek is
   the whole affordance, which is why there are no arrows. Snap points
   stop each swipe on a frame instead of anywhere.
   ===================================================================== */

.renders {
  display: flex;
  gap: var(--space-1);
  margin: var(--space-3) calc(var(--space-3) * -1) var(--space-3);
  padding: 0 var(--space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* A momentum scroll that stops mid-frame on iOS looks broken. */
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

/* The strip is the affordance; a scrollbar over a photograph is not. */
.renders::-webkit-scrollbar { display: none; }

.renders:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

.renders__item {
  flex: 0 0 78%;
  margin: 0;
  scroll-snap-align: start;
}

.renders__item img {
  display: block;
  width: 100%;
  /* One height for every frame, whatever the source ratio — five
     different heights would read as five separate objects. */
  height: 200px;
  object-fit: cover;
  border-radius: 0;
}

/* =====================================================================
   ANSWERS SUMMARY — «Что учтём в подборке» (components/answers-summary.js)

   On a phone this is one closed row above the result card: the form
   under it is already long, so the visitor's own answers are offered
   rather than imposed. Straight corners, a hairline, no card chrome —
   it is a note on the page, not a second panel competing with the one
   below it.
   ===================================================================== */

.answers-summary {
  margin-bottom: var(--space-2);
  border-top: 1px solid var(--color-border-on-dark);
  border-bottom: 1px solid var(--color-border-on-dark);
}

.answers-summary__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  min-height: 48px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--color-sand);
  font-family: var(--font-ui);
  font-size: var(--text-overline-size);
  letter-spacing: var(--text-overline-tracking);
  text-transform: uppercase;
  cursor: pointer;
}

.answers-summary__toggle:hover {
  color: var(--color-ivory);
}

.answers-summary__toggle:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

.answers-summary__chevron {
  font-size: 1.25rem;
  line-height: 1;
  color: var(--color-sand);
}

.answers-summary__body {
  padding-bottom: var(--space-2);
}

.answers-summary__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.05rem;
  color: var(--color-ivory);
  margin: 0 0 var(--space-1);
}

/* A description list, because that is what this is: each answer is the
   value of the question above it. */
.answers-summary__list {
  margin: 0;
  display: grid;
  gap: 2px;
}

.answers-summary__term {
  font-size: var(--text-caption-size);
  color: var(--color-sand);
}

.answers-summary__value {
  margin: 0 0 var(--space-1);
  color: var(--color-text-on-dark);
  font-size: var(--text-body-size);
  line-height: 1.4;
}

/* ---------- Worked example under the rate (components/hero.js) ----------
   One arithmetic example, not a table: what an object costs today and
   what the same object would cost if the dollar were higher. The
   difference is the only figure given emphasis, and the caveat under it
   is small but never faint. */

.hero__fx {
  align-self: stretch;
  margin-top: var(--space-2);
  max-width: 34rem;
}

.hero__fx-heading {
  margin: 0 0 6px;
  font-family: var(--font-ui);
  font-size: var(--text-overline-size);
  letter-spacing: var(--text-overline-tracking);
  text-transform: uppercase;
  color: var(--color-sand);
}

.hero__fx-rows {
  display: grid;
  gap: 2px;
}

.hero__fx-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-1);
  margin: 0;
  color: var(--color-text-on-dark);
  font-size: var(--text-body-size);
  line-height: 1.4;
}

.hero__fx-value {
  font-family: var(--font-display);
  font-size: 1.1rem;
  color: var(--color-ivory);
  white-space: nowrap;
}

/* The one number the example exists to show. */
.hero__fx-difference {
  margin: var(--space-1) 0 0;
  padding-top: var(--space-1);
  border-top: 1px solid var(--color-border-on-dark);
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--color-sand);
}

/* Small, and still readable: the caveat is part of the claim, not a
   disclaimer to be hidden, so it keeps 12px rather than dropping to the
   11px caption size. */
.hero__fx-note {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: rgba(243, 239, 231, 0.78);
}

.hero__fx-source {
  color: var(--color-sand);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}

.hero__fx-source:hover,
.hero__fx-source:focus-visible {
  color: var(--color-ivory);
}

/* =====================================================================
   GIFTS (components/gifts.js)

   Two placements of one offer. Both keep the text as real HTML over a
   photograph — never letters baked into a bitmap — so it reflows, scales
   and can be read aloud.

   Teaser (first screen): names only. It must not become a second offer
   competing with the CTA, so it carries no description, no button and
   no bright badge — a dimmed render, a hairline, three numbers.
   ===================================================================== */

.gifts-teaser {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  margin-top: var(--space-3);
  padding: var(--space-2);
  border: 1px solid var(--color-border-on-dark);
  align-self: stretch;
}

.gifts-teaser__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-charcoal);
}

.gifts-teaser__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.8;
}

/* Graphite over the render so the numbers hold at full contrast. The
   gradient, not a flat panel, so the photograph still reads. */
.gifts-teaser__media::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Enough graphite to hold the text at full contrast, little enough
     that the render underneath is still a photograph. Measured, not
     guessed — see the contrast check in the notes for this change. */
  background: linear-gradient(180deg,
    rgba(31, 37, 40, 0.80) 0%,
    rgba(31, 37, 40, 0.88) 100%);
}

/* The heading of a real block, not a technical caption. It was set at
   the 10px rubric size, which made the whole offer read as fine print;
   Cormorant at 19px puts it on the heading scale (brandbook p. 18, H3)
   while staying well below the hero. */
.gifts-teaser__heading {
  margin: 0 0 var(--space-2);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 19px;
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: none;
  color: var(--color-ivory);
}

.gifts-teaser__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
  counter-reset: none;
}

.gifts-teaser__item {
  display: grid;
  grid-template-columns: 2.4ch 1fr;
  align-items: baseline;
  gap: 4px var(--space-1);
}

/* The numbers are the ornament — display serif, sand rather than gold.
   Gold reads beautifully on the flat panels of the result screen, but
   here it sits on a photograph, and measured against the brightest part
   of that render it fell to 2.65:1. Sand keeps the warm accent and
   stays legible over the whole image. */
.gifts-teaser__number {
  font-family: var(--font-display);
  font-size: 1.25rem;
  line-height: 1.2;
  color: var(--color-sand);
  flex: none;
  min-width: 2ch;
}

/* The gift's name carries the offer, so it sits a step above body
   text rather than level with it. */
.gifts-teaser__name {
  color: var(--color-ivory);
  font-size: 17px;
  font-weight: 500;
  line-height: 1.3;
}

/* One line of explanation per gift. Smaller than the name, never small
   in absolute terms: this is the text that says what the visitor
   actually receives, so it does not get the 11px caption size. */
.gifts-teaser__about {
  grid-column: 2;
  margin: 0;
  color: rgba(243, 239, 231, 0.82);
  font-size: 13px;
  line-height: 1.45;
}

/* ---------- Gifts on the result screen ---------- */

.gifts {
  margin: var(--space-3) 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-on-dark);
}

.gifts__heading {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.15rem;
  color: var(--color-ivory);
  margin: 0 0 var(--space-2);
}

.gifts__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.gift {
  border: 1px solid var(--color-border-on-dark);
  background: rgba(243, 239, 231, 0.03);
  overflow: hidden;
}

.gift__media img {
  display: block;
  width: 100%;
  height: 140px;
  object-fit: cover;
}

.gift__body {
  padding: var(--space-2);
}

.gift__number {
  display: block;
  font-family: var(--font-display);
  font-size: 1.1rem;
  line-height: 1;
  color: var(--color-gold);
  margin-bottom: 6px;
}

.gift__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.05rem;
  line-height: 1.3;
  color: var(--color-ivory);
  margin: 0 0 6px;
}

.gift__description {
  margin: 0;
  color: var(--color-text-on-dark);
  font-size: var(--text-body-size);
  line-height: 1.45;
}

/* How this particular gift reaches the visitor. The three differ, so
   this line is not decoration and is not set at caption size. */
.gift__delivery {
  margin: var(--space-1) 0 0;
  color: var(--color-sand);
  font-size: 13px;
  line-height: 1.45;
}

.gift__note {
  margin: 6px 0 0;
  color: var(--color-text-muted-on-dark, rgba(243, 239, 231, 0.55));
  font-size: var(--text-caption-size);
  line-height: 1.4;
}

.gift__download,
.gift__download-slot .gift__download {
  margin-top: var(--space-2);
  width: 100%;
  text-align: center;
}

/* Shown while the file has not been confirmed. Deliberately inert: a
   button that leads nowhere is worse than a sentence that says so. */
.gift__pending {
  margin: var(--space-2) 0 0;
  padding: 0 var(--space-2);
  min-height: var(--button-height);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--color-border-on-dark);
  color: rgba(243, 239, 231, 0.5);
  font-family: var(--font-ui);
  font-size: var(--text-button-size);
  letter-spacing: var(--text-button-tracking);
  text-transform: uppercase;
}

.gift__download-slot {
  margin-top: var(--space-3);
}

/* ---------- Confirmation ---------- */

.quiz__done-list {
  margin: 0 0 var(--space-2);
  padding-left: 1.1em;
  display: grid;
  gap: 8px;
  color: var(--color-text-on-dark);
  line-height: 1.45;
}

/* ---------- Selector on the dark surface ---------- */

.page-funnel .quiz-section {
  padding-top: var(--space-5);
  padding-bottom: var(--space-6);
}

/* Result screen: the promise, then where to send it. The 20ch measure
   that keeps a QUESTION scannable makes this three ragged lines, so the
   result heading gets its own, wider one. */
.page-funnel .quiz__card--result .quiz__question {
  /* The 20ch measure below keeps a QUESTION scannable; this headline is
     a statement, and at 20ch it broke into three ragged lines. */
  max-width: 34ch;
  margin-bottom: var(--space-2);
}

.page-funnel .quiz-section h2 {
  color: var(--color-ivory);
  font-size: var(--text-h2-size);
  margin-top: 0;
}

.page-funnel .quiz-section .section__lead {
  color: var(--color-sand);
  max-width: 40ch;
}

/* The card lifts off the page rather than sitting on a lighter band —
   same graphite family, one step up, hairline border. */
.page-funnel .quiz__card {
  background: var(--color-charcoal);
  border: 1px solid var(--color-border-on-dark);
  color: var(--color-text-on-dark);
  padding: var(--space-3);
}

.page-funnel .quiz__question {
  color: var(--color-ivory);
}

.page-funnel .quiz__context,
.page-funnel .quiz__limit,
.page-funnel .caption,
.page-funnel .quiz__progress {
  color: var(--color-sand);
}

.page-funnel .quiz__option {
  background: transparent;
  border-color: var(--color-border-on-dark);
  color: var(--color-ivory);
}

.page-funnel .quiz__option--selected {
  border-color: var(--color-sand);
  background: rgba(200, 185, 154, 0.12);
}

.page-funnel .quiz__input {
  background: transparent;
  border-color: var(--color-border-on-dark);
  color: var(--color-ivory);
}

.page-funnel .quiz__label,
/* Consents on the dark card. The shared stylesheet leaves links at the
   browser default, which is a dark blue — on graphite that is close to
   unreadable, and these are exactly the links a visitor is entitled to
   read before ticking a box. Sand with an underline, and the legend and
   body text lifted off the background too.

   `.quiz__consent` is the older markup; services/consent-service.js now
   emits `.consent-fields`. Both are listed so a cached page and the
   internal preview keep their contrast. */
.page-funnel .quiz__consent,
.page-funnel .quiz__consent a,
.page-funnel .consent-fields {
  color: var(--color-sand);
}

.page-funnel .consent-fields legend {
  color: var(--color-ivory);
}

.page-funnel .consent-fields a {
  color: var(--color-sand);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.page-funnel .consent-fields a:hover,
.page-funnel .consent-fields a:focus-visible {
  color: var(--color-ivory);
}

.page-funnel .quiz__channel {
  border-color: var(--color-border-on-dark);
  color: var(--color-ivory);
}

.page-funnel .quiz__channel--selected {
  border-color: var(--color-sand);
  background: rgba(200, 185, 154, 0.12);
}

.page-funnel .quiz__back {
  color: var(--color-sand);
}

.page-funnel .quiz__progress-bar {
  background: rgba(243, 239, 231, 0.18);
}

/* ---------- Compact header / short footer ---------- */

.page-funnel + .site-footer,
body:has(.page-funnel) .site-footer {
  background: var(--color-charcoal);
  color: var(--color-sand);
}

body:has(.page-funnel) .site-footer a {
  color: var(--color-sand);
}

/* The header sits directly on the funnel's dark surface, so it shares
   it: a cream bar above a graphite page read as two different sites.
   Scoped to the funnel page so the private stand's other pages, which
   keep the light layout, are unaffected.

   Fixed rather than sticky, and the page reserves the UNSCROLLED height
   below. That is what lets the desktop bar shrink from the full lockup
   to the monogram without the content under it moving: the reserved
   space is a constant, so the swap changes only what is painted inside
   the bar. On a phone the bar is one height throughout — the monogram
   from the very first pixel — so there is nothing to shrink. */
body:has(.page-funnel) {
  --funnel-header-h: 62px;
}

body:has(.page-funnel) .site-header {
  position: fixed;
  inset: 0 0 auto;
  height: var(--funnel-header-h);
  background: var(--color-graphite);
  border-bottom-color: var(--color-border-on-dark);
}

@media (prefers-reduced-motion: no-preference) {
  body:has(.page-funnel) .site-header {
    transition: height 220ms ease, background-color 220ms ease;
  }
}

body:has(.page-funnel) .site-header__inner {
  min-height: 0;
  height: 100%;
  padding-top: 0;
  padding-bottom: 0;
}

/* The constant that makes the swap jump-free. */
.page-funnel {
  padding-top: var(--funnel-header-h);
}

/* The funnel page uses the official monogram in the bar, at every width.
   The full lockup is a STACKED mark — 140px wide is 103px tall — so a
   bar compact enough to leave the first screen intact cannot hold it
   without scaling it below the brandbook's own minimum width. The
   monogram is the mark the brandbook provides for exactly this case. */
body:has(.page-funnel) .site-logo__lockup { display: none; }
body:has(.page-funnel) .site-logo__monogram { display: block; width: 42px; }

/* ---------------------------------------------------------------------
   Premium finish for the funnel: a single warm glow, hairline rules,
   buttons with depth and a thin edge, and motion that never draws
   attention to itself.

   Every colour below is a brandbook token (graphite / ivory / sand /
   gold). Corners stay square — the brandbook is explicit about that
   (§21), so depth comes from light and edge, not from radius.
   --------------------------------------------------------------------- */

/* One soft gold bloom behind the headline, anchored to the left column
   so it lights the text rather than the photograph. Purely decorative
   and non-interactive. */
.page-funnel .hero::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: -20%;
  top: 18%;
  width: 90%;
  aspect-ratio: 1;
  pointer-events: none;
  background: radial-gradient(circle,
    rgba(169, 137, 92, 0.20) 0%,
    rgba(169, 137, 92, 0.08) 38%,
    transparent 68%);
}

@media (min-width: 900px) {
  .page-funnel .hero::before {
    left: -12%;
    width: 60%;
  }
}

/* Hairline that ties the hero to the selector: the eye follows it down
   into the questions instead of hitting a section boundary. */
.page-funnel .quiz-section {
  position: relative;
}

.page-funnel .quiz-section::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--container-padding-mobile);
  right: var(--container-padding-mobile);
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%, var(--color-gold) 18%, var(--color-border-on-dark) 60%, transparent 100%);
  opacity: 0.55;
}

/* Buttons: thin warm edge + a top-light gradient for volume. Square
   corners, per the brandbook. */
.page-funnel .btn-primary {
  background-image: linear-gradient(180deg, rgba(243, 239, 231, 0.16) 0%, transparent 60%);
  border: 1px solid var(--color-sand);
  box-shadow: 0 1px 0 rgba(243, 239, 231, 0.18) inset,
              0 10px 30px -18px rgba(169, 137, 92, 0.85);
  transition: box-shadow 200ms ease, transform 200ms ease, background-color 200ms ease;
}

@media (hover: hover) {
  .page-funnel .btn-primary:hover {
    box-shadow: 0 1px 0 rgba(243, 239, 231, 0.24) inset,
                0 14px 34px -16px rgba(169, 137, 92, 0.95);
  }
}

/* Disabled "Далее". The shared rule fills a disabled primary with sand,
   which on a graphite card is barely darker than the live gold — the
   button looked ready and answered nothing. Here it drops to the card's
   own surface with a dim edge and muted label: clearly a control, just
   as clearly not armed yet. The gradient, the glow and the hover all go
   with it, and the cursor says why.

   Nothing else changes: the moment an option is chosen, quiz.js clears
   `disabled` and every rule above applies again unmodified. */
.page-funnel .btn-primary:disabled {
  background-color: transparent;
  background-image: none;
  border-color: var(--color-border-on-dark);
  color: rgba(243, 239, 231, 0.38);
  box-shadow: none;
  cursor: not-allowed;
}

@media (hover: hover) {
  .page-funnel .btn-primary:disabled:hover {
    background-color: transparent;
    border-color: var(--color-border-on-dark);
    box-shadow: none;
  }
}

/* The selector's options get the same edge treatment, so tapping
   through the questions feels like the same surface as the hero CTA. */
.page-funnel .quiz__option {
  transition: border-color 160ms ease, background-color 160ms ease;
}

.page-funnel .quiz__option--selected {
  box-shadow: 0 0 0 1px var(--color-sand) inset;
}

/* One large question at a time: the brandbook's display face, sized up
   so the step reads as a statement rather than a form field. */
.page-funnel .quiz__question {
  font-size: clamp(1.35rem, 5.2vw, 1.75rem);
  line-height: 1.25;
  max-width: 20ch;
}

/* Progress: a thin gold rule, not a chunky bar. */
.page-funnel .quiz__progress-fill {
  background: var(--color-sand);
}

/* Motion, kept deliberately small: content settles into place, nothing
   slides or bounces. Anyone who has asked for reduced motion gets none
   of it. */
@media (prefers-reduced-motion: no-preference) {
  .page-funnel .hero__inner > *,
  .page-funnel .quiz__card {
    animation: veyra-settle 480ms ease-out both;
  }

  .page-funnel .hero__title { animation-delay: 60ms; }
  .page-funnel .hero__subtitle { animation-delay: 120ms; }
  .page-funnel .hero__cta { animation-delay: 180ms; }
  .page-funnel .hero__rate { animation-delay: 240ms; }
}

@keyframes veyra-settle {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* The page itself is dark on the funnel, not just the sections: the
   default ivory body showed through on overscroll bounce and anywhere
   the funnel did not reach, which flashed cream on a graphite page. */
body:has(.page-funnel) {
  background: var(--color-graphite);
  color: var(--color-text-on-dark);
}

/* The header is fixed, so a section scrolled flush to the top would put
   its heading underneath it. Land just below the SCROLLED bar instead —
   68px on a phone, 64px on desktop, plus a little air. */
.page-funnel .quiz-section {
  scroll-margin-top: 84px;
}

/* Scrolled state: the bar becomes opaque so the mark never sits on top
   of a quiz question it is passing over. */
body:has(.page-funnel) .site-header.is-scrolled {
  background: var(--color-charcoal);
  border-bottom-color: var(--color-border-on-dark);
}
