/* css/sections.css — per-block layout, "Function" (apothecary journal) system.
   Source of truth: design_ver4.md. See css/variables.css for the token
   rationale and the note on what the old "Мастерская" system dropped. */

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-taupe);
  transition: padding 0.2s ease;
}
.site-header__inner {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 18px 16px;
  transition: padding 0.2s ease;
}
.site-header.is-scrolled .site-header__inner { padding-top: 10px; padding-bottom: 10px; }
.site-header__logo {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 1.25rem;
  text-decoration: none;
  color: var(--color-text);
}
.site-header__nav {
  display: flex; align-items: center; gap: var(--space-3); margin-left: auto;
  padding-right: var(--space-2); border-right: 1px solid var(--color-taupe);
}
.site-header__nav a { text-decoration: none; font-weight: 400; font-size: var(--text-caption); letter-spacing: -0.32px; }
.site-header__nav a:hover { text-decoration: underline; text-underline-offset: 3px; }
.site-header__social { display: flex; gap: 14px; }
.site-header__social a { text-decoration: none; font-weight: 400; font-size: var(--text-caption); letter-spacing: -0.32px; }
.site-header__cta { white-space: nowrap; }
.site-header__hamburger { display: none; background: none; border: none; cursor: pointer; padding: 8px; }
.site-header__hamburger span { display: block; width: 22px; height: 2px; background: var(--color-text); margin: 4px 0; }

@media (max-width: 860px) {
  .site-header__nav { display: none; }
  .site-header__hamburger { display: block; }
  .site-header.is-menu-open .site-header__nav {
    display: flex; flex-direction: column; align-items: flex-start;
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--color-bg);
    padding: var(--space-2);
    border-bottom: 1px solid var(--color-taupe);
    border-right: none; /* the desktop divider line would run down the
      dropdown's right edge otherwise */
  }
  .site-header.is-menu-open .site-header__nav a { padding: 10px 4px; width: 100%; }
  /* CTA and hamburger both stay visible — CTA is a sibling, not nested in .site-header__nav */
}
@media (max-width: 400px) {
  /* logo + social + CTA + hamburger together don't fit one row at the
     narrowest common phone width (375px, e.g. iPhone SE) — social icons
     are dropped from the header row (they're duplicated in the footer)
     to leave room for the CTA and hamburger, which are the two
     interactive elements that must stay reachable. */
  .site-header__social { display: none; }
  .site-header__inner { gap: var(--space-1); padding-left: 12px; padding-right: 12px; }
  /* Dropping social alone left the logo ~20-25px short of fitting on one
     line even so ("Наталья Семёнова" measured ~154px, available ~130-156px
     depending on exact viewport) — it wrapped to 2 lines, which grew the
     header's own height and made it visually collide with the hero content
     right below on scroll (reported on real 360-390px phones). Shrinking
     the logo slightly + forcing one line closes that gap without touching
     the CTA/hamburger, which need to stay full-size as the tap targets. */
  .site-header__logo { font-size: 1.05rem; white-space: nowrap; }
}

/* ---------- hero: full-bleed photo, dark warm overlay, overlaid copy —
   the one place this system breaks from "text column beside a framed
   photo": Function's own spec calls for full-bleed hero photography with
   a dark overlay so light text stays legible, so the hero photo becomes
   the whole section's background instead of a side-by-side column. */
#hero { position: relative; min-height: 640px; display: flex; align-items: center; overflow: hidden; }
.hero__bg { position: absolute; inset: 0; background: #554c45; }
.hero__bg img,
.hero__bg-img,
.hero__bg-media {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 35%;
}
/* The clip is a phone-shot portrait video, re-encoded at 1080px wide (its
   native width — see CHANGELOG 2026-08-22, re-cut from a higher-resolution
   source of the same footage). The 1700px cap keeps side margins at or
   under ~15% of viewport width up to ~2429px wide (0.92*1700/0.7 — covers
   every realistic desktop/laptop screen); the browser's upscale tops out
   around ~1.57x at that cap (worse than a tighter cap would give, but a
   1080px source is as wide as this footage exists at — going wider would
   upscale past native resolution for no real detail gain). Those margins
   fall back to .hero__bg's solid color, faded into the video with a mask
   so there's no hard seam. The photo/poster fallback (reduced-motion, or
   before the video swaps in) stays full-bleed since a still photo doesn't
   carry this artifact. */
@media (min-width: 900px) {
  .hero__bg-media {
    left: 50%; right: auto;
    transform: translateX(-50%);
    width: min(92%, 1700px);
    mask-image: linear-gradient(90deg, transparent, #000 90px, #000 calc(100% - 90px), transparent);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 90px, #000 calc(100% - 90px), transparent);
  }
}
.hero__bg::after {
  content: '';
  position: absolute; inset: 0;
  background:
    linear-gradient(100deg, rgba(42,20,10,0.72) 0%, rgba(42,20,10,0.5) 45%, rgba(42,20,10,0.22) 100%),
    linear-gradient(0deg, rgba(20,10,5,0.32), rgba(20,10,5,0.32));
}
/* Left edge lines up with every other section's .container content start
   (not the raw viewport edge) — on screens above the 1280px page max-width
   that's an inset from the edge; below it, it's the same 32px/20px gutter
   every other section uses. Function's own spec calls this hero
   left-aligned, not viewport-edge-hugging. */
.hero__inner {
  position: relative; z-index: 1; padding: 80px 32px;
  padding-left: max(32px, calc((100% - 1280px) / 2 + 32px));
}
.hero__copy { max-width: 640px; }
/* h1 otherwise inherits body's ink color — invisible against the dark
   photo overlay behind it. */
/* ~18% tighter than the base h1,h2 line-height (1.1 → 0.9) — the hero
   headline wraps to 3-4 lines, so a tighter line-height visibly shrinks the
   block and pulls the subhead/CTA below it closer along with it. */
/* text-wrap: balance evens out the line lengths across however many lines
   the headline wraps to at a given width, instead of the default greedy
   wrap leaving a short, lopsided last line. */
.hero__copy h1 { color: var(--color-bg); text-shadow: 0 2px 20px rgba(20,10,5,0.35); line-height: 0.9; text-wrap: balance; }
.eyebrow-pill {
  display: block;
  font-size: var(--text-label); font-weight: 600; letter-spacing: -0.28px; text-transform: uppercase;
  color: var(--color-bg);
  margin-bottom: var(--space-2);
}
.hero__subhead {
  font-size: var(--text-body-lg); font-weight: 300; line-height: 1.4; letter-spacing: -0.41px;
  color: var(--color-bg); opacity: 0.88; max-width: 42ch;
}
/* Moved above the H1 (index.html) as the first thing a visitor reads —
   plain text sitting close to the headline it introduces, not a separate
   pill/badge competing with it for attention. */
.hero__trust { margin: 0 0 6px; font-size: var(--text-caption); letter-spacing: -0.2px; color: var(--color-bg); opacity: 0.85; }
.hero__cta-row { margin: var(--space-3) 0 var(--space-2); width: fit-content; }
.hero__visual-wrap { display: none; } /* photo now lives in .hero__bg — see note above */

@media (max-width: 860px) {
  #hero { min-height: 560px; }
  .hero__inner { padding: 48px 24px; padding-left: 24px; }
}
@media (max-width: 480px) {
  #hero { min-height: 520px; }
  .hero__inner { padding: 40px 20px; padding-left: 20px; }
  .eyebrow-pill { font-size: var(--text-label); }
}

/* ---------- PAS ("Знакомо?") ---------- */
.pas { background: var(--color-accent-detail); }
#pas h2 { text-align: center; }
/* Two panels ("plaques") — one for the 5 original pains, one for the 3
   "freeze/defrost" ones — each a card (.pas's own background is already
   --color-accent-detail/Aged Paper, so each panel uses --color-bg/Parchment
   instead to still read as a raised card against it) with its own bulleted
   list inside. */
.pas__panels { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-2); margin: 0 0 var(--space-3); }
.pas__panel { background: var(--color-bg); border-radius: var(--radius-card); padding: var(--space-3); }
.pas__list { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-1); }
.pas__list li { padding-left: 24px; position: relative; font-weight: 500; }
.pas__list li::before { content: '•'; position: absolute; left: 0; color: var(--color-accent-fill); font-weight: 700; font-size: 1.3em; line-height: 1.1; }
/* keep in sync with --breakpoint-mobile in variables.css */
@media (max-width: 720px) {
  .pas__panels { grid-template-columns: 1fr; }
}
.pas__bridge { font-size: var(--text-body-lg); font-weight: 400; color: var(--color-charcoal); }

/* ---------- about ---------- */
.about__inner { display: grid; grid-template-columns: 0.9fr 1.4fr; gap: var(--space-4); }
.about__copy { min-width: 0; }
/* Photo now sits as a plain contained image — Function's imagery rule
   ("in lower sections, contained with 24px radius") has no polaroid/
   rotation concept, so the old white-frame + handwritten-caption treatment
   was dropped in favor of the system's own photo language. */
.about__photo { width: 100%; aspect-ratio: 3/4; object-fit: cover; border-radius: var(--radius-card); }
/* Was a plain-text fact list (.about__facts, bare fragments like "9 лет в
   кондитерском деле") sitting below 3 separate prose paragraphs — the
   fragments duplicated the paragraphs' own numbers and read as filler.
   Replaced with icon+sentence rows carrying the fuller context sentence
   itself, using the same line-icon language as .why-choose-item__icon
   below (24 viewBox, 1.6 stroke, round caps) rather than a new one. */
.about__highlights { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-3) 0; list-style: none; padding: 0; }
.about__highlights li { display: flex; align-items: flex-start; gap: var(--space-2); }
.about__highlight-icon { flex: none; display: inline-flex; margin-top: 2px; }
.about__highlight-icon svg { width: 26px; height: 26px; color: var(--color-accent-fill); }
.about__highlights li span:last-child { color: var(--color-charcoal); }
/* Thumbnails are 30% larger than the original 150x110 — small enough at
   the old size that the certificates' text was unreadable. At this size 5
   thumbnails no longer reliably fit one row, so the gallery reuses the
   shared .carousel/.carousel__track component (same pattern as reviews and
   student-results) instead of the old bespoke nowrap-row-that-becomes-a-
   scroll-strip-under-760px layout — .carousel__track already provides
   overflow-x/scroll-snap/arrow-scroll at every width. */
.cert-gallery { gap: 16px; margin-top: var(--space-2); }
.cert-thumb-wrap { position: relative; flex: none; }
.cert-thumb {
  display: block; width: 195px; height: 143px; border-radius: 16px; object-fit: cover; cursor: pointer;
  border: 2px solid transparent; scroll-snap-align: start;
}
.cert-thumb:hover, .cert-thumb:focus-visible { border-color: var(--color-accent-fill); }
@media (max-width: 760px) {
  .cert-thumb { width: 221px; height: 161px; }
}
/* Desktop-only enlarge-on-hover peek, additive to the click-to-lightbox
   above (which stays the only interaction on touch — see
   initCertificateHoverPreview in js/certificates-gallery.js for why). */
.cert-hover-preview {
  position: fixed; z-index: 1000;
  width: min(60vw, 440px); max-height: 70vh;
  object-fit: cover; border-radius: 16px;
  box-shadow: rgba(42,43,47,0.25) 0px 16px 48px 0px;
  opacity: 0; transform: scale(0.96);
  transition: opacity 0.16s ease, transform 0.16s ease;
  pointer-events: none;
}
.cert-hover-preview.is-visible { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
  .cert-hover-preview { transition: none; }
}
/* background/radius/padding come from the shared .card class already on
   this element in the markup (why-choose-item card) — only the layout is
   specific to this component. */
.why-choose-item {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px;
  transition: background-color .18s ease, outline-color .18s ease;
  outline: 1px solid transparent; outline-offset: -1px;
}
.why-choose-item:hover { background: #F1E6D5; outline-color: var(--color-taupe); }
.why-choose-item__icon { display: inline-flex; }
.why-choose-item__icon svg { width: 34px; height: 34px; color: var(--color-text); }
.why-choose-item strong { display: block; font-family: var(--font-body); font-weight: 600; }
.why-choose-item p { margin: 0; color: var(--color-charcoal); }
.about__learning { text-align: center; margin-top: var(--space-4); }
.about__learning h3 { margin-top: var(--space-5); }
.why-choose-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); margin: var(--space-3) auto 0; max-width: 960px; }
@media (max-width: 760px) {
  /* Fixed 2x3 grid all the way down to the smallest phones — a single
     column made each card read as a long, disconnected list; six short
     phrases read faster as a compact table. */
  /* minmax(0, 1fr), not bare 1fr — same track-overflow risk as
     .catalog__grid above (see its comment); applied here too since this
     grid shares the same "2 equal columns of card content" shape. */
  .why-choose-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-2); }
  .why-choose-item { padding: var(--space-2); }
  .why-choose-item__icon svg { width: 28px; height: 28px; }
}
.cert-lightbox {
  position: fixed; inset: 0; background: rgba(42,43,47,0.75);
  display: flex; align-items: center; justify-content: center; z-index: 1000; padding: var(--space-2);
}
.cert-lightbox img { max-width: 90vw; max-height: 85vh; border-radius: 8px; }
@media (max-width: 860px) {
  .about__inner { grid-template-columns: 1fr; }
}

/* ---------- techcard preview — sits between "about" and "catalog" so the
   format is clear right before someone starts browsing what to buy. Aged
   Paper background gives it a tonal step against the Parchment sections on
   either side, per Function's slow alternating rhythm. ---------- */
.techcard-preview { background: var(--color-accent-detail); }
.eyebrow-label {
  display: block; text-align: center; font-size: var(--text-label); font-weight: 600; letter-spacing: -0.28px; text-transform: uppercase;
  color: var(--color-accent-fill); margin-bottom: var(--space-2);
}
.techcard-preview__copy { text-align: center; margin: 0 auto var(--space-4); max-width: 640px; }
.techcard-preview__copy h2 { text-align: center; }
.techcard-preview__copy p { color: var(--color-charcoal); }
/* Two previews, matched to a shared height rather than a shared width —
   the video lesson is landscape (1000x480) and the techcard is portrait
   (562x1000); forcing equal columns would either crop the video hard or
   leave the techcard tiny. Aligning on height instead lets each keep its
   real shape, like two photos of different formats laid side by side. */
.techcard-preview__grid { display: flex; gap: var(--space-4); justify-content: center; align-items: flex-end; flex-wrap: wrap; }
.techcard-preview__item { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
/* Framed like a physical page — a plain video floating on the Aged Paper
   background read as unfinished; the Parchment card + badge gives it the
   same "this is a real document" presence the other cards in the system
   have. */
.techcard-preview__frame {
  position: relative; height: 380px;
  background: var(--color-bg); border-radius: var(--radius-card); padding: 12px;
}
.techcard-preview__video {
  display: block; height: 100%; width: auto; aspect-ratio: 562/1000;
  border-radius: 14px; object-fit: cover;
}
.techcard-preview__frame--wide .techcard-preview__video { aspect-ratio: 1000/480; }
.techcard-preview__badge {
  position: absolute; top: 22px; left: 22px;
  background: var(--color-bg); border: 1px solid var(--color-taupe);
  border-radius: var(--radius-pill); padding: 4px 12px;
  font-size: var(--text-label); font-weight: 600; letter-spacing: -0.28px; text-transform: uppercase;
  color: var(--color-text);
}
/* Lesson video is click-to-play with real sound, not the site's usual
   silent looping ambience — a narrated clip autoplaying muted would need a
   second click just to hear it, and browsers block unmuted autoplay
   outright. .icon-btn is the shared circular-button look (see its
   definition in components.css); --play sizes it up as the primary call
   to action centered on the frame, while the mute toggle stays small in
   the corner once playback has started. */
.techcard-preview__play {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  background: var(--color-bg);
}
.icon-btn--play { width: 56px; height: 56px; font-size: 1.4rem; padding-left: 3px; }
.techcard-preview__mute {
  position: absolute; bottom: 14px; right: 14px;
  width: 36px; height: 36px; font-size: 1rem;
  background: var(--color-bg);
}
.techcard-preview__caption { font-size: var(--text-caption); color: var(--color-charcoal); text-align: center; max-width: 220px; }
@media (max-width: 860px) {
  .techcard-preview__grid { flex-direction: column; align-items: stretch; }
  .techcard-preview__item { align-items: center; }
  .techcard-preview__frame { height: auto; width: 100%; max-width: 340px; }
  .techcard-preview__video { height: auto; width: 100%; }
}

/* ---------- catalog ---------- */
#catalog h2, .catalog__subhead { text-align: center; }
.catalog__subhead { max-width: 60ch; color: var(--color-charcoal); margin-left: auto; margin-right: auto; }
/* Was one dense paragraph carrying 3 unrelated reassurances (downloads
   forever / personal support / instant email delivery) after the count —
   split into a short row of icon+phrase items, same visual language as
   .about__highlights above (reused icon: the chat bubble here is the exact
   .why-choose-item__icon "Обратная связь" glyph, a deliberate echo). */
.catalog__highlights {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3);
  max-width: 780px; margin: var(--space-3) auto 0; list-style: none; padding: 0;
}
.catalog__highlights li { display: flex; align-items: flex-start; gap: 10px; flex: 1 1 200px; max-width: 240px; text-align: left; }
.catalog__highlight-icon { flex: none; display: inline-flex; margin-top: 2px; }
.catalog__highlight-icon svg { width: 24px; height: 24px; color: var(--color-accent-fill); }
.catalog__highlights li span:last-child { font-size: var(--text-caption); color: var(--color-charcoal); }
.catalog__filters { display: flex; gap: var(--space-2); margin: var(--space-3) 0; flex-wrap: wrap; justify-content: center; }
/* <label> is inline by default — its text and the <select> just flowed like
   text, so once the label's own line wrapped (narrow phones) the select
   dropped to a second line flush against the label's own left edge instead
   of staying centered under the (centered) text above it. Making the label
   itself a wrapping, centered flex row keeps both the text and the select
   centered on their own line at any width. */
.catalog__filters label { display: inline-flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: 8px; text-align: center; }
.catalog__filters select {
  /* Native select arrows sit flush at the box's own edge — on a pill this
     crowded it right up against the curved corner ("стрелка сильно прижата
     вправо"). appearance:none + a drawn chevron with real right padding
     fixes that instead of fighting the browser default across engines. */
  padding: 10px 40px 10px 18px; border-radius: var(--radius-pill); border: 1px solid var(--color-ash);
  font-family: var(--font-body); font-size: var(--text-caption); letter-spacing: -0.32px;
  background-color: var(--color-bg); color: var(--color-text);
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232A2B2F' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 16px;
}
.catalog__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--space-3); }

.product-card {
  display: flex; flex-direction: column; gap: 8px;
  background: var(--color-accent-detail);
  border-radius: var(--radius-card);
  padding: 16px 16px 20px;
  transition: background-color .18s ease, outline-color .18s ease;
  outline: 1px solid transparent; outline-offset: -1px;
}
.product-card:hover { background: #F1E6D5; outline-color: var(--color-taupe); }
.product-card__media { position: relative; border-radius: 16px; overflow: hidden; aspect-ratio: 4/3; }
.product-card__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.product-card__media img.media-whole { opacity: 0; transition: opacity 0.35s ease; }
.product-card__media img.media-cut { transition: opacity 0.35s ease; }
.product-card__media:hover img.media-whole { opacity: 1; }
.product-card__media:hover img.media-cut { opacity: 0; }
.product-card__badge {
  position: absolute; top: 10px; left: 10px; z-index: 1;
  background: var(--color-bg); border: 1px solid var(--color-taupe);
}
.product-card__level-badge {
  position: absolute; top: 10px; right: 10px; z-index: 1;
}
/* Same accent color for both (the system has only one — see the
   --color-mustard note in variables.css) but opposite treatment: advanced
   is a solid filled "expert" seal, beginner is a light outline "starter"
   chip — so which is which reads at a glance without a second hue. */
.product-card__level-badge--advanced { background: var(--color-accent-fill); border-color: var(--color-accent-fill); color: #fff; }
.product-card__level-badge--beginner { background: var(--color-bg); border-color: var(--color-accent-fill); color: var(--color-accent-fill); }
.product-card__name {
  font-family: var(--font-heading); font-weight: 500; font-size: var(--text-card-title); line-height: 1.15;
  margin: 4px 0 0; min-height: 2.4em; color: var(--color-text);
}
/* Reserved to roughly the longest description's 3 lines (not the ~6 lines
   the original 7.75rem block fit) — enough that every card's "Что
   внутри?" link lands on the same row, without the large dead gap shorter
   descriptions used to leave above it. */
.product-card__text { font-size: var(--text-caption); min-height: 4.05rem; color: var(--color-charcoal); }
.product-card__count { font-size: var(--text-caption); color: var(--color-muted); }
.product-card__footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: auto; }
.product-card__composition { list-style: none; padding: 0; margin: 8px 0 0; font-size: var(--text-caption); display: none; }
.product-card__composition.is-open { display: grid; gap: 4px; }
.product-card__composition-item { display: flex; align-items: center; gap: 8px; }
.product-card__composition-item img { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; flex-shrink: 0; }
.product-card__expand {
  background: none; border: none; text-decoration: underline; cursor: pointer;
  font-family: var(--font-body); font-size: var(--text-caption); padding: 6px 0; align-self: flex-start; color: var(--color-text);
}

/* 2-up compact mobile card: below tablet width the grid is forced to two
   columns (rather than the auto-fill default, which would collapse to one
   column under ~550px). The description/count/composition
   stay collapsed by default so a ~170px card doesn't outgrow its neighbour,
   but — unlike the old version, which hid them with no way back — "Что
   внутри?" stays visible and tapping it reveals the same full detail block
   desktop shows inline, via the existing is-open toggle in catalog.js (no
   JS change needed: :has() lets the card-level layout react to the
   composition list's state). The inline footer (price+buy) stays hidden in
   both states; .mobile-buy-bar is the one price/CTA affordance on mobile,
   collapsed or expanded, so there's never a duplicate buy button. */
/* keep in sync with --breakpoint-mobile in variables.css and catalog.js's MOBILE_BREAKPOINT_PX */
@media (max-width: 720px) {
  /* minmax(0, 1fr), not bare 1fr — bare 1fr expands to minmax(auto, 1fr),
     and "auto" lets a track grow to its content's min-content width. A
     product-card's own intrinsic minimum turned out wider than half the
     available row, so both tracks silently overflowed the grid (and the
     whole page got a horizontal scrollbar on real phones — confirmed on
     a 360px-wide viewport, reported by the site owner). minmax(0, 1fr)
     forces the track to actually respect the container width instead. */
  .catalog__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .product-card { padding: 10px 10px 14px; border-radius: 16px; gap: 6px; }
  .product-card__media { border-radius: 12px; }
  .product-card__badge { font-size: var(--text-label); padding: 3px 9px; top: 6px; left: 6px; }
  /* Moved to the bottom-right corner on the narrow 2-up grid — at this
     width "ВИДЕО"/"ВИДЕО+PDF" and "ДЛЯ НОВИЧКОВ"/"ДЛЯ ОПЫТНЫХ" both sitting
     on the top row overlapped each other regardless of text length. */
  .product-card__level-badge { font-size: var(--text-label); padding: 3px 9px; top: auto; bottom: 6px; right: 6px; left: auto; }
  .product-card__name {
    font-size: var(--text-caption); line-height: 1.2;
    /* Reserves 2 full lines regardless of actual title length — a 1-line
       title (e.g. "Тарт «Клубничное сердце»") used to leave the card
       shorter than a 2-line neighbour (e.g. "Шоколадно-вишнёвый чизкейк"),
       shifting "Что внутри?" and the card's bottom edge out of alignment
       between cards in the same grid row. */
    min-height: 2.4em;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .product-card__text,
  .product-card__count,
  .product-card__composition { display: none; }
  .product-card__text { min-height: 0; }
  .product-card__expand { font-size: var(--text-caption); }
  .product-card__footer { display: none; }

  .product-card:has(.product-card__composition.is-open) .product-card__text,
  .product-card:has(.product-card__composition.is-open) .product-card__count { display: block; }
  .product-card:has(.product-card__composition.is-open) .product-card__composition { display: grid; }
}

/* Mobile product modal — tapping a compact card (anywhere except the buy
   button) moves that same card DOM node in here full-screen, with
   composition pre-opened by initMobileProductModal() in catalog.js. Reusing
   the :has() rule above means the card shows its full desktop-equivalent
   content automatically once composition.is-open is set — no separate
   "expanded" styling needed for the modal content itself. */
.product-modal-overlay { position: fixed; inset: 0; z-index: 900; background: var(--color-bg); }
.product-modal-overlay[hidden] { display: none; }
.product-modal__close {
  position: fixed; top: 16px; right: 16px; z-index: 901;
}
/* scroll-behavior is an inherited property — without this override, this
   container would inherit `smooth` from html (base.css, meant for #anchor
   nav links) onto its own internal overflow scroll, animating every
   programmatic scroll-into-view instead of jumping instantly. */
/* Bottom padding needs real headroom, not just cosmetic spacing: once
   content pushes the card taller than the viewport (e.g. expanding "Что
   внутри?" pushes the mobile "Купить" button further down), the browser
   needs somewhere to scroll the last bit of content past the container's
   own bottom edge. */
.product-modal__scroll { height: 100%; overflow-y: auto; scroll-behavior: auto; padding: 72px 16px 100px; }
.product-modal__slot .product-card:hover { background: var(--color-accent-detail); } /* no hover-tint inside the modal — it's already "selected" */
/* inverse of the --breakpoint-mobile boundary — keep in sync with variables.css and catalog.js's MOBILE_BREAKPOINT_PX */
@media (min-width: 721px) {
  /* Modal is a mobile-only affordance; on desktop the card layout never
     changes shape, so nothing needs to move for it to render correctly
     even if left in the slot (defensive only — JS never opens it above
     720px). */
  .product-modal-overlay { display: none !important; }
}

.reviews__pending-note { color: var(--color-muted); font-size: var(--text-caption); }
#reviews h2, #student-results h2 { text-align: center; }

/* Shared horizontal carousel (reviews + student results). */
.carousel { display: flex; align-items: center; gap: 8px; margin-top: var(--space-3); }
.carousel__track {
  display: flex; gap: var(--space-3); overflow-x: auto; scroll-snap-type: x mandatory;
  padding: 4px 2px 12px; flex: 1; min-width: 0; scrollbar-width: none;
}
.carousel__track::-webkit-scrollbar { display: none; }
.carousel__arrow {
  flex: none; width: 44px; height: 44px; border-radius: var(--radius-pill);
  border: 1.5px solid var(--color-text); background: transparent; /* ink border — matches .icon-btn's close-button family */
  font-family: var(--font-body); font-size: 1.2rem; line-height: 1; cursor: pointer; color: var(--color-text);
  display: flex; align-items: center; justify-content: center;
}
.carousel__arrow:hover, .carousel__arrow:focus-visible { background: rgba(42, 43, 47, 0.06); }
@media (max-width: 600px) {
  .carousel__arrow { display: none; } /* swipe/scroll works fine; arrows just crowd narrow screens */
}

.testimonial-card {
  flex: none; width: 264px; scroll-snap-align: start;
  background: var(--color-accent-detail);
  border-radius: var(--radius-card);
  padding: 10px;
}
.testimonial-card img { width: 100%; min-height: 160px; border-radius: 14px; display: block; background: var(--color-taupe); }

.student-result-card {
  flex: none; width: 240px; scroll-snap-align: start; display: flex; flex-direction: column; gap: 10px;
  background: var(--color-accent-detail); border-radius: var(--radius-card); padding: 10px;
}
.student-result-card img { aspect-ratio: 4/3; width: 100%; object-fit: cover; border-radius: 16px; }
.student-result-card__name { font-weight: 500; font-size: var(--text-caption); padding: 0 4px 4px; color: var(--color-charcoal); }
.student-result-card__name a { color: inherit; }

/* ---------- bundles ---------- */
#bundles h2, .bundles__subhead { text-align: center; }
.bundles__subhead { color: var(--color-charcoal); }
.bundles__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--space-3); margin: var(--space-3) 0; }
.bundle-card {
  display: flex; flex-direction: column; gap: 10px;
  transition: background-color .18s ease, outline-color .18s ease;
  outline: 1px solid transparent; outline-offset: -1px;
}
.bundle-card:hover { background: #F1E6D5; outline-color: var(--color-taupe); }
.bundle-card h3 { font-family: var(--font-heading); font-weight: 500; font-size: var(--text-card-title); }
.bundle-card [data-testid="bundle-buy"] { margin-top: auto; }
.bundle-card__items-label { font-weight: 600; font-size: var(--text-caption); color: var(--color-text); margin: 0; }
.bundle-card__items { list-style: none; padding: 0; margin: 0; display: grid; gap: 4px; }
.bundle-card__items li { padding-left: 22px; position: relative; font-size: var(--text-caption); color: var(--color-charcoal); }
.bundle-card__items li::before { content: '✓'; position: absolute; left: 0; color: var(--color-accent-fill); font-weight: 700; }
.bundle-card__savings {
  font-size: var(--text-caption); font-weight: 600; color: var(--color-text); background: var(--color-bg);
  border: 1px solid var(--color-taupe);
  display: inline-block; padding: 4px 12px; border-radius: var(--radius-pill); width: fit-content;
}
.gift-cert { margin: var(--space-3) auto 0; max-width: 480px; }
.gift-cert > h3, .gift-cert > p { text-align: center; }

/* ---------- lead magnet — the section that gets Function's one reserved
   "editorial statement" moment (italic accent word), sitting on Aged Paper
   as the tonal step down from the surrounding Parchment sections. ---------- */
.lead-magnet { background: var(--color-accent-detail); }
.lead-magnet__inner { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: var(--space-4); align-items: center; }
.lead-magnet__photo { width: 100%; height: 100%; min-height: 300px; aspect-ratio: 3/4; object-fit: cover; border-radius: var(--radius-card); }
.lead-magnet__content { display: flex; flex-direction: column; }
.lead-magnet__body { font-weight: 300; font-size: var(--text-body-lg); line-height: 1.4; letter-spacing: -0.41px; color: var(--color-charcoal); max-width: 46ch; }
.lead-magnet__features { list-style: none; padding: 0; margin: 0 0 var(--space-2); display: grid; gap: 8px; }
.lead-magnet__features li { padding-left: 28px; position: relative; font-size: var(--text-caption); }
.lead-magnet__features li::before { content: '✓'; position: absolute; left: 0; color: var(--color-accent-fill); font-weight: 700; }
.lead-magnet__social-proof { font-weight: 600; font-size: var(--text-caption); }
.lead-magnet__form { margin-top: var(--space-3); }
.lead-magnet__form-row { display: flex; gap: 12px; }
.lead-magnet__form-row .field__input { flex: 1; }
.lead-magnet__form .field__checkbox-row { margin-top: 10px; }
.lead-magnet__result, .gift-cert__result { font-size: var(--text-caption); font-weight: 600; min-height: 1.2em; margin-top: 8px; }

/* Success popup shown after the lead-magnet form submits (js/leadmagnet.js)
   — its own small overlay rather than reusing .checkout-overlay, matching
   how .cert-lightbox also keeps its own dedicated overlay CSS. */
.leadmagnet-success-overlay {
  position: fixed; inset: 0;
  background: rgba(42,43,47,0.45);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000; padding: var(--space-2); overflow-y: auto;
}
.leadmagnet-success-modal {
  max-width: 420px; width: 100%; position: relative; margin: auto;
  background: var(--color-bg);
  border-radius: var(--radius-card);
  padding: var(--space-3);
  box-shadow: rgba(42,43,47,0.1) 12px 32px 80px 0px;
}
.leadmagnet-success-modal h3 { margin: 0 var(--space-3) 0 0; font-size: var(--text-body-lg); }
.leadmagnet-success-modal p { font-size: var(--text-caption); color: var(--color-charcoal); margin: var(--space-1) 0 0; }
.leadmagnet-success-close { position: absolute; top: 12px; right: 12px; }

@media (max-width: 860px) {
  .lead-magnet__inner { grid-template-columns: 1fr; }
  .lead-magnet__photo { min-height: 300px; }
}
@media (max-width: 480px) {
  .lead-magnet__form-row { flex-direction: column; }
}

/* ---------- FAQ ---------- */
.faq-list { display: grid; gap: 4px; margin: var(--space-3) 0; }
.faq-item { border-bottom: 1px solid var(--color-taupe); padding-bottom: 12px; }
.faq-item__question {
  width: 100%; text-align: left; background: none; border: none;
  font-family: var(--font-body); font-weight: 600; font-size: 1rem; letter-spacing: -0.37px;
  padding: 12px 0; cursor: pointer; color: var(--color-text);
}
.faq-item__answer { display: none; padding-top: 4px; color: var(--color-charcoal); }
.faq-item__answer.is-open { display: block; }

/* ---------- footer — still only in-system colors (ink + parchment), no
   new hue, so the inverted dark band stays within Function's "one accent"
   discipline while reading as the page's natural close. ---------- */
.site-footer { background: var(--color-text); color: var(--color-bg); padding: var(--space-4) 0; margin-top: var(--space-5); }
.site-footer__inner { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.site-footer__social { display: flex; gap: var(--space-2); font-weight: 600; }
.site-footer__requisites { font-size: var(--text-caption); opacity: 0.7; }

[data-animate] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.4s ease, transform 0.4s ease;
}
[data-animate].is-visible { opacity: 1; transform: translateY(0); }

/* Per-card mobile buy row — the only price/CTA affordance left once the
   2-up compact card (above) hides the inline footer. */
.mobile-buy-bar { display: none; }
/* keep in sync with --breakpoint-mobile in variables.css and catalog.js's MOBILE_BREAKPOINT_PX */
@media (max-width: 720px) {
  .mobile-buy-bar {
    /* Centered price+button pair with a fixed gap, not edge-pinned via
       space-between — reads as one balanced unit instead of the button
       drifting toward whichever edge the container happens to have.
       Compact by default so it fits the ~150px 2-up catalog card without
       wrapping; the product modal (much wider container) loosens this up
       below since it has real room to breathe. */
    display: flex; align-items: center; justify-content: center; gap: 8px;
    background: var(--color-bg);
    border: 1px solid var(--color-taupe);
    border-radius: var(--radius-pill);
    padding: 6px 10px;
    margin-top: 4px;
    /* .product-card is a column flex container (align-items: stretch by
       default) — once max-width caps this bar below the stretched size
       (the product-modal override below), it stays pinned to the left
       edge instead of centering as a block. align-self makes the whole
       pill center in its parent, not just its own content. No visible
       effect on the plain catalog card above, which has no max-width to
       shrink below the full stretched width in the first place. */
    align-self: center;
  }
  .mobile-buy-bar .price { font-size: var(--text-body-lg); white-space: nowrap; }
  /* scroll-margin-bottom: a native "scroll into view" (e.g. the product
     modal bringing this button back on-screen once content above it — like
     expanding "Что внутри?" — has pushed it further down) otherwise
     scrolls the *minimum* distance needed, landing the target flush against
     the viewport's bottom edge with zero clearance below it. Reserves real
     breathing room instead — must be set on the button itself (the actual
     scroll-into-view target), not a lower-specificity ancestor. */
  .mobile-buy-bar .btn--primary { padding: 10px 12px; font-size: var(--text-caption); min-height: 44px; scroll-margin-bottom: 80px; }

  /* The full-screen product modal moves the whole .product-card in here —
     .mobile-buy-bar inherits its column-flex parent's stretch and would
     otherwise span the modal's full width, pushing price/button apart
     (see the 2026-08 design-polish critique). Capped narrower + roomier
     internal spacing than the compact catalog-card default above. */
  [data-testid="product-modal-overlay"] .mobile-buy-bar {
    max-width: 220px; gap: 14px; padding: 8px 16px;
  }
  [data-testid="product-modal-overlay"] .mobile-buy-bar .btn--primary {
    padding: 12px 18px;
  }
}

/* thank-you.html — reuses .card from components.css, just centers it */
.thank-you-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}
.thank-you-page__card {
  max-width: 480px;
  text-align: center;
}
.thank-you-page__card .btn {
  margin-top: var(--space-3);
  display: inline-block;
}
